使用Google Play服务位置在Android中唤醒过多的警报管理器

时间:2017-09-08 11:53:30

标签: android memory location google-play-services

我在Google Play Console上收到了Android Vital关于警报管理器唤醒过多的性能报告:

https://developer.android.com/topic/performance/vitals/wakeup.html

我使用Google Play服务中的Location API在后台请求位置更新。在报告中,它显示唤醒唤醒过多是由LocationListener的com.google.android.location.ALARM_WAKEUP_LOCATOR引起的。

导致警报的代码段下方:

private synchronized void buildGoogleApiClient() {
    mGoogleApiClient = new GoogleApiClient.Builder(context)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build();
    mGoogleApiClient.connect();
}

/**
 * Runs when a GoogleApiClient object successfully connects.
 */
@Override
public void onConnected(Bundle connectionHint) {
    try {
        // Set location request
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(5 * 60000);
        mLocationRequest.setFastestInterval(60000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        mLocationRequest.setMaxWaitTime(30 * 60000);

        // Create a pending intent to listening on location service when the update become available
        Intent mIntent = new Intent(context, LocationReceiver.class);
        mIntent.setAction(LocationReceiver.LOCATION_EXTRA);
        mPendingIntent = PendingIntent.getBroadcast(context, 42, mIntent, PendingIntent.FLAG_CANCEL_CURRENT);
        // Permission check before launching location services
        if (ContextCompat.checkSelfPermission(context,
                Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mPendingIntent);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

此警报唤醒是否与Google Play服务中的Location API相关联?

有人知道如何解决这个问题吗?

1 个答案:

答案 0 :(得分:30)

Google Location api

这是com.google.android.location.ALARM_WAKEUP_LOCATOR每隔60秒唤醒设备并使其保持唤醒状态长达15秒的问题,导致严重的电池排水问题。

fixes已通过外部apps进行电话使用,并教会用户如何实际撤销应用的权限。

解决方案

以下提供了减少应用电池使用量的工具。

为应用程序提供定制解决方案是不可能的,因为解决方案取决于应用程序的用例和业务逻辑以及您希望通过位置更新实现的成本效益分析。

时间间隔

电池性能问题并不令人意外。从以下设置:

mLocationRequest.setInterval(5 * 60000);
mLocationRequest.setFastestInterval(60000);
mLocationRequest.setMaxWaitTime(30 * 60000);

您的间隔设置为每5分钟更新一次(5 * 60000毫秒)。那是一天24小时。如果每5分钟成功更新一次:每天12次/小时= = 288次。

最快间隔设置为1分钟(60000)。虽然这是可用的,因为位置可以在设备的其他位置访问,但它仍会在您的应用中使用电源)。

maxwaittime只有30分钟。这意味着该设备最多只能被唤醒并每天至少轮询48次。

Increase these times

  

setMaxWaitTime ...这可以消耗更少的电量并提供更准确的位置,   取决于设备的硬件功能。你应该设置这个   如果您不需要,价值可以满足您的需求   立即定位交付。 ...

在Android 8中,Google将请求数限制为few per hour。考虑将此作为设置请求间隔的指南。

限制更新次数

可以限制特定时间范围内的更新次数。通过在使用更新次数后主动取消位置请求或在请求上设置expiration。这样,应用程序可以通过在应用程序内的某个触发器上创建请求并进行仔细管理来进行管理,以便它不会无休止地继续。创建流程很困难,因为我不知道应用程序的用例。

setNumUpdates (int numUpdates)

  

默认位置会不断更新,直到请求为止   明确删除,但您可以选择请求一组数量   更新。例如,如果您的应用程序只需要一个新鲜的   location,然后在传递之前调用此方法的值为1   请求位置客户。

停止位置更新

另一种选择是在不需要时管理stop locations updates。这些链接提供了在活动失去焦点时调用此实例的示例,但是可以在应用程序中实现,满足某些要求(在您的业务逻辑中)或甚至通过让用户打开和关闭它从应用程序本身。

电池优化

确保您的应用不是ignoring battery optimizations

位移

另外,管理setSmallestDisplacement (float smallestDisplacementMeters)将有助于微调应用程序,具体取决于业务逻辑。

以下是在更新的问题之前编写的。

开发人员和用户都可以设置应用更新的频率。 intervalspriorities

对于developer

您可以在发出位置请求时设置这些,例如:

protected void createLocationRequest() {
    LocationRequest mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(10000);
    mLocationRequest.setFastestInterval(5000);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
}

PRIORITY_NO_POWER还有一个设置,这意味着该应用只会在用户要求时获取更新。

对于用户。

您需要Prompt the User to Change Location Settings

task.addOnSuccessListener(this, new OnSuccessListener<LocationSettingsResponse>() {
    @Override
    public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
        // All location settings are satisfied. The client can initialize
        // location requests here.
        // ...
    }
});

task.addOnFailureListener(this, new OnFailureListener() {
    @Override
    public void onFailure(@NonNull Exception e) {
        int statusCode = ((ApiException) e).getStatusCode();
        switch (statusCode) {
            case CommonStatusCodes.RESOLUTION_REQUIRED:
                // Location settings are not satisfied, but this can be fixed
                // by showing the user a dialog.
                try {
                    // Show the dialog by calling startResolutionForResult(),
                    // and check the result in onActivityResult().
                    ResolvableApiException resolvable = (ResolvableApiException) e;
                    resolvable.startResolutionForResult(MainActivity.this,
                            REQUEST_CHECK_SETTINGS);
                } catch (IntentSender.SendIntentException sendEx) {
                    // Ignore the error.
                }
                break;
            case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                // Location settings are not satisfied. However, we have no way
                // to fix the settings so we won't show the dialog.
                break;
        }
    }
});

在用户设置中manage changes使用位置回拨。

  

...您可以使用新的LocationCallback课程代替现有的课程   LocationListener另外还有LocationAvailability次更新   到位置更新,每当设置时给你一个简单的回调   可能已经改变了会影响当前的一组   LocationRequests。

Google has addressed此问题在Android 8中。

  

为了降低功耗,Android 8.0(API级别26)   限制后台应用程序检索用户当前频率的频率   地点。应用只能分别接收几次位置更新   小时。

     
    

注意:这些限制适用于正在运行的设备上使用的所有应用     Android 8.0(API级别26)或更高版本,无论应用程序的目标是什么     SDK版本。

  

如果使用报警管理器。

这可能与Alarm Manager

有关

这不是一个简单的解决方案,最终您需要重写如何安排更新。

  1. 为了至少减慢问题,您需要调试代码并查找Alarm Manager调用或创建计划的任何实例并减少间隔。

  2. 之后,您需要重写应用程序的整个位置更新计划部分。

  3. Fix the Problem

      

    确定应用中计划唤醒警报的位置   降低触发这些警报的频率。这里有一些   提示:

         
        
    • 查找set()中包含AlarmManager或ELAPSED_REALTIME_WAKEUP标志的各种RTC_WAKEUP方法的调用。
    •   
    • 我们建议您在警报的标签名称中包含您的包裹,班级或方法名称,以便您可以轻松识别您的位置。   警报设置的来源。以下是一些其他提示:      
          
      • 在名称中留下任何个人识别信息(PII),例如电子邮件地址。否则,设备记录_UNKNOWN   而不是警报名称。
      •   
      • 不能以编程方式获取类或方法名称,例如通过调用getName(),因为它可能会被Proguard混淆。   而是使用硬编码的字符串。
      •   
      • 不要为闹钟标签添加计数器或唯一标识符。系统将无法聚合以此方式设置的警报   因为它们都有唯一的标识符。
      •   
    •   
         

    解决问题后,请确认您的唤醒警报正在运行   正如预期的那样运行以下ADB命令:

         
        

    adb shell dumpsys alarm

      
         

    此命令提供有关警报系统状态的信息   设备上的服务。有关详细信息,请参阅dumpsys

    如果您对上述任何内容存在特殊问题,则需要使用mcve发布另一个问题。

    要改进应用程序,它将涉及重写Best Practices中提到的代码。 不要使用闹钟管理器来安排后台任务,如果手机处于睡眠状态,则位置将被视为后台任务。另外,你说这是一项背景任务。使用JobSchedulerFirebase JobDispatcher

    如果警报管理器是更好的选择(它不在这里),在这里阅读Scheduling Repeating Alarms很重要,但是你需要Understand the Trade-offs

      

    设计不良的警报可能会导致电池耗尽并对服务器造成很大负担。