有关使用访问跟踪的指南

Visits 功能简化了位置跟踪过程,使其更高效,以实现许多应用的实用目的。 访问定义为用户进入和退出的重要地理区域。 访问类似于 地理围栏 ,即仅当用户进入或退出某些感兴趣的区域时,应用才会收到通知,而无需持续的位置跟踪,这可能会耗尽电池使用时间。 但是,与地理围栏不同,访问区域在平台级别动态标识,不需要由各个应用显式定义。 此外,选择哪些访问应用将通过单个粒度设置进行处理,而不是通过订阅各个位置来处理。

初步设置

在继续操作之前,请确保你的应用能够访问设备的位置。 需要声明 Location 清单中的功能,并调用 Geolocator.RequestAccessAsync 方法,以确保用户授予应用位置权限。 有关如何执行此操作的详细信息,请参阅 “获取用户的位置 ”。

请记住将 Geolocation 命名空间添加到类。 本指南中的所有代码片段都需要这样做。

using Windows.Devices.Geolocation;

检查最新的访问

使用访问跟踪功能的最简单方法是检索上次已知的与访问相关的状态更改。 状态更改是平台记录的事件,用户进入/退出重要位置、自上次报告以来存在重大移动或用户的位置丢失(请参阅 VisitStateChange 枚举)。 状态更改由 Geovisit 实例表示。 若要检索上次记录状态更改的 Geovisit 实例,只需在 GeovisitMonitor 类中使用指定的方法即可。

注意

检查上次记录的访问不保证系统当前正在跟踪访问。 若要在访问发生时跟踪访问,必须在前台监视访问或注册后台跟踪(请参阅以下部分)。

private async void GetLatestStateChange() {
    // retrieve the Geovisit instance
    Geovisit latestVisit = await GeovisitMonitor.GetLastReportAsync();

    // Using the properties of "latestVisit", parse out the time that the state 
    // change was recorded, the device's location when the change was recorded,
    // and the type of state change.
}

分析 Geovisit 实例(可选)

以下方法将 Geovisit 实例中存储的所有信息转换为易于阅读的字符串。 它可用于本指南中的任何方案,以帮助提供所报告访问的反馈。

private string ParseGeovisit(Geovisit visit){
    string visitString = null;

    // Use a DateTimeFormatter object to process the timestamp. The following
    // format configuration will give an intuitive representation of the date/time
    Windows.Globalization.DateTimeFormatting.DateTimeFormatter formatterLongTime;
    
    formatterLongTime = new Windows.Globalization.DateTimeFormatting.DateTimeFormatter(
        "{hour.integer}:{minute.integer(2)}:{second.integer(2)}", new[] { "en-US" }, "US", 
        Windows.Globalization.CalendarIdentifiers.Gregorian, 
        Windows.Globalization.ClockIdentifiers.TwentyFourHour);
    
    // use this formatter to convert the timestamp to a string, and add to "visitString"
    visitString = formatterLongTime.Format(visit.Timestamp);

    // Next, add on the state change type value
    visitString += " " + visit.StateChange.ToString();

    // Next, add the position information (if any is provided; this will be null if 
    // the reported event was "TrackingLost")
    if (visit.Position != null) {
        visitString += " (" +
        visit.Position.Coordinate.Point.Position.Latitude.ToString() + "," +
        visit.Position.Coordinate.Point.Position.Longitude.ToString() + 
        ")";
    }

    return visitString;
}

监视前台的访问

上一节中使用的 GeovisitMonitor 类还处理在一段时间内侦听状态更改的方案。 为此,可以实例化此类、为其事件注册处理程序方法以及调用 Start 该方法。

// this GeovisitMonitor instance will belong to the class scope
GeovisitMonitor monitor;

public void RegisterForVisits() {

    // Create and initialize a new monitor instance.
    monitor = new GeovisitMonitor();
    
    // Attach a handler to receive state change notifications.
    monitor.VisitStateChanged += OnVisitStateChanged;
    
    // Calling the start method will start Visits tracking for a specified scope:
    // For higher granularity such as venue/building level changes, choose Venue.
    // For lower granularity in the range of zipcode level changes, choose City.
    monitor.Start(VisitMonitoringScope.Venue);
}

在此示例中,该方法 OnVisitStateChanged 将处理传入的 Visit 报告。 相应的 Geovisit 实例通过事件参数传入。

private void OnVisitStateChanged(GeoVisitWatcher sender, GeoVisitStateChangedEventArgs args) {
    Geovisit visit = args.Visit;
    
    // Using the properties of "visit", parse out the time that the state 
    // change was recorded, the device's location when the change was recorded,
    // and the type of state change.
}

当应用完成对与访问相关的状态更改的监视后,它应停止监视器并注销事件处理程序(s)。 每当应用暂停或关闭时,也应执行此操作。

public void UnregisterFromVisits() {
    
    // Stop the monitor to stop tracking Visits. Otherwise, tracking will
    // continue until the monitor instance is destroyed.
    monitor.Stop();
    
    // Remove the handler to stop receiving state change events.
    monitor.VisitStateChanged -= OnVisitStateChanged;
}

监视后台的访问

还可以在后台任务中实现 Visit 监视,以便即使应用未打开,也可在设备上处理与访问相关的活动。 这是推荐的方法,因为它更具通用性和节能性。

本指南将使用创建和注册进程外后台任务中的模型,其中主应用程序文件位于一个项目中,后台任务文件位于同一解决方案中的单独项目中。 如果你不熟悉实现后台任务,建议你主要遵循该指南,并在下面进行必要的替换来创建访问处理后台任务。

注意

在以下代码片段中,为了简单起见,缺少一些重要的功能,例如错误处理和本地存储。 有关后台访问处理的可靠实现,请参阅 示例应用

首先,请确保应用已声明后台任务权限。 在 Application/Extensions Package.appxmanifest 文件的元素中,添加以下扩展名(Extensions如果元素尚不存在)。

<Extension Category="windows.backgroundTasks" EntryPoint="Tasks.VisitBackgroundTask">
    <BackgroundTasks>
        <Task Type="location" />
    </BackgroundTasks>
</Extension>

接下来,在后台任务类的定义中,粘贴以下代码。 Run此后台任务的方法将触发器详细信息(包含 Visits 信息)传递到单独的方法中。

using Windows.ApplicationModel.Background;

namespace Tasks {
    
    public sealed class VisitBackgroundTask : IBackgroundTask {
        
        public void Run(IBackgroundTaskInstance taskInstance) {
            
            // get a deferral
            BackgroundTaskDeferral deferral = taskInstance.GetDeferral();
            
            // this task's trigger will be a Geovisit trigger
            GeovisitTriggerDetails triggerDetails = taskInstance.TriggerDetails as GeovisitTriggerDetails;

            // Handle Visit reports
            GetVisitReports(triggerDetails);         

            finally {
                deferral.Complete();
            }
        }        
    }
}

定义 GetVisitReports 同一类中的某个位置的方法。

private void GetVisitReports(GeovisitTriggerDetails triggerDetails) {

    // Read reports from the triggerDetails. This populates the "reports" variable 
    // with all of the Geovisit instances that have been logged since the previous
    // report reading.
    IReadOnlyList<Geovisit> reports = triggerDetails.ReadReports();

    foreach (Geovisit report in reports) {
        // Using the properties of "visit", parse out the time that the state 
        // change was recorded, the device's location when the change was recorded,
        // and the type of state change.
    }

    // Note: depending on the intent of the app, you many wish to store the
    // reports in the app's local storage so they can be retrieved the next time 
    // the app is opened in the foreground.
}

接下来,在应用的主项目中,需要执行此后台任务的注册。 创建一个注册方法,该方法可由某些用户操作调用,或在每次激活类时调用。

// a reference to this registration should be declared at the class level
private IBackgroundTaskRegistration visitTask = null;

// The app must call this method at some point to allow future use of 
// the background task. 
private async void RegisterBackgroundTask(object sender, RoutedEventArgs e) {
    
    string taskName = "MyVisitTask";
    string taskEntryPoint = "Tasks.VisitBackgroundTask";

    // First check whether the task in question is already registered
    foreach (var task in BackgroundTaskRegistration.AllTasks) {
        if (task.Value.Name == taskName) {
            // if a task is found with the name of this app's background task, then
            // return and do not attempt to register this task
            return;
        }
    }
    
    // Attempt to register the background task.
    try {
        // Get permission for a background task from the user. If the user has 
        // already responded once, this does nothing and the user must manually 
        // update their preference via Settings.
        BackgroundAccessStatus backgroundAccessStatus = await BackgroundExecutionManager.RequestAccessAsync();

        switch (backgroundAccessStatus) {
            case BackgroundAccessStatus.AlwaysAllowed:
            case BackgroundAccessStatus.AllowedSubjectToSystemPolicy:
                // BackgroundTask is allowed
                break;

            default:
                // notify user that background tasks are disabled for this app
                //...
                break;
        }

        // Create a new background task builder
        BackgroundTaskBuilder visitTaskBuilder = new BackgroundTaskBuilder();

        visitTaskBuilder.Name = exampleTaskName;
        visitTaskBuilder.TaskEntryPoint = taskEntryPoint;

        // Create a new Visit trigger
        var trigger = new GeovisitTrigger();

        // Set the desired monitoring scope.
        // For higher granularity such as venue/building level changes, choose Venue.
        // For lower granularity in the range of zipcode level changes, choose City. 
        trigger.MonitoringScope = VisitMonitoringScope.Venue; 

        // Associate the trigger with the background task builder
        visitTaskBuilder.SetTrigger(trigger);

        // Register the background task
        visitTask = visitTaskBuilder.Register();      
    }
    catch (Exception ex) {
        // notify user that the task failed to register, using ex.ToString()
    }
}

这可以确定命名空间中调用VisitBackgroundTask的后台任务类将使用触发器类型执行某些操作locationTasks

你的应用现在应该能够注册访问处理后台任务,并且每当设备记录与访问相关的状态更改时,都应激活此任务。 需要填写后台任务类中的逻辑,以确定对此状态更改信息执行的操作。