应用程序死机时的地理围栏

Posted

技术标签:

【中文标题】应用程序死机时的地理围栏【英文标题】:Geofencing when app is dead 【发布时间】:2018-05-17 21:34:31 【问题描述】:

当应用停止运行时,来自 Google 的新地理围栏 API 不会触发任何事件。我尝试使用PendingIntent.getBroadcast()PendingIntent.getService(),但只能在应用打开时获取转换事件。

我遵循了代码实验室的 this 教程,但修改了代码以使用新的 GeofencingClient

更新

这就是我创建待处理意图的方式:

private PendingIntent getGeofencePendingIntent() 
    if (mGeofencePendingIntent != null) 
        Log.d(TAG, "Pending intent is already there");
        return mGeofencePendingIntent;
    
    Log.d(TAG, "Creating a new pending intent");

    // In case I'm using Service, the second parameter will be GeoIntentService.class
    Intent mIntent = new Intent(mContext, GeoReceiver.class);

    // In case I'm using Service, the method will be getService(...)
    mGeofencePendingIntent = PendingIntent.getBroadcast(mContext, 0, mIntent, PendingIntent.FLAG_UPDATE_CURRENT);
        return mGeofencePendingIntent;
 

我的 GeoReceiver 的 onReceive 方法中的代码。不知何故,在onHandleIntent 方法中使用服务时,代码是相似的

@Override
public void onReceive(Context context, Intent intent) 
    Log.d(TAG, "onReceive");
    if (intent == null) 
        Log.e(TAG, "Intent is null");
        return;
    
    final String action = intent.getAction();
    if (ACTION_ADD_GEOFENCE.equals(action)) 
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
        if (geofencingEvent.hasError()) 
            String errMsg = GeofenceExceptionMessages.getErrorString(context, geofencingEvent.getErrorCode());
            Log.e(TAG, "onReceive Error: " + errMsg);
            return;
        
        int geofenceTransition = geofencingEvent.getGeofenceTransition();
        if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
                    geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) 

            List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();

            NotificationHelper helper = new NotificationHelper();

            String geofenceTransitionDetails = helper.getGeofenceTransitionDetails(
                    context,
                    geofenceTransition,
                    triggeringGeofences
            );

            helper.sendNotificaiton(context, geofenceTransitionDetails);
            Log.i(TAG, "onReceive: " + geofenceTransitionDetails);
        
     else 
        Log.d(TAG, "Diffirent Action: " + action);
    

【问题讨论】:

您能否发布设置您的 IntentService 和 IntentService 本身的代码? @ThomasStevens 请检查更新 GeoReceiver 类扩展了 IntentService? @ThomasStevens 不,我的 GeoReceiver 扩展了 BroadcastReceiver 和 GeoIntentService 扩展了 IntentService IntentService 是一个专门用于在您的应用程序处于后台(或前台)时运行短任务的类,当您的应用程序处于后台时,广播接收器不会接收任何内容,IntentService 会。 【参考方案1】:

地理围栏不起作用,因为某些设备默认只允许某些白名单应用的后台服务。如果您的应用也必须这样工作,则意味着您必须从设置中启用 AutoStart,下面的代码将帮助您让用户为您的应用启用自动启动。如果 AutoStart 是启用后,您的服务将在后台正常运行。

为了手机更好的性能,一些公司会停止应用程序的所有后台服务(“一些被列入白名单的应用程序将只有在后台做服务的权限,例如:WhatsApp、谷歌应用程序和公认的应用程序。” )。所以如果我们的应用也必须像那样工作,我们必须启用自动启动服务。

自动启动:

android 系统启动时,它会发出启动完成事件。 Android 应用程序可以侦听并捕获此事件以执行特定操作,例如自动启动 Activity 或服务。

目前,无法确定AutoStart是否启用。 因此,您可以将它们重定向到设置并告诉用户启用它。我正在为大多数常见电话公司提供重定向代码,这将终止后台服务。(仅向用户显示一次,使用 SharedPreference 管理它,正如我之前所说无法确定是启用还是禁用。)

为了实现这一目标,

AndroidManifest.xml 中声明权限。在应用程序声明节点之前将 android.permission.RECEIVE_BOOT_COMPLETED 权限添加到应用程序的清单文件中:

这是必须调用的函数代码。

private void enableAutoStart() 
    if (Build.BRAND.equalsIgnoreCase("xiaomi")) 
      new MaterialDialog.Builder(MainActivity.this).title("Enable AutoStart")
        .content(
          "Please allow AppName to always run in the background,else our services can't be accessed.")
        .theme(Theme.LIGHT)
        .positiveText("ALLOW")
        .onPositive(new MaterialDialog.SingleButtonCallback() 
          @Override
          public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) 

            Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.miui.securitycenter",
              "com.miui.permcenter.autostart.AutoStartManagementActivity"));
            startActivity(intent);
          
        )
        .show();
     else if (Build.BRAND.equalsIgnoreCase("Letv")) 
      new MaterialDialog.Builder(MainActivity.this).title("Enable AutoStart")
        .content(
          "Please allow AppName to always run in the background,else our services can't be accessed.")
        .theme(Theme.LIGHT)
        .positiveText("ALLOW")
        .onPositive(new MaterialDialog.SingleButtonCallback() 
          @Override
          public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) 

            Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.letv.android.letvsafe",
              "com.letv.android.letvsafe.AutobootManageActivity"));
            startActivity(intent);
          
        )
        .show();
     else if (Build.BRAND.equalsIgnoreCase("Honor")) 
      new MaterialDialog.Builder(MainActivity.this).title("Enable AutoStart")
        .content(
          "Please allow AppName to always run in the background,else our services can't be accessed.")
        .theme(Theme.LIGHT)
        .positiveText("ALLOW")
        .onPositive(new MaterialDialog.SingleButtonCallback() 
          @Override
          public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) 
            Intent intent = new Intent();
            intent.setComponent(new ComponentName("com.huawei.systemmanager",
              "com.huawei.systemmanager.optimize.process.ProtectActivity"));
            startActivity(intent);
          
        )
        .show();
     else if (Build.MANUFACTURER.equalsIgnoreCase("oppo")) 
      new MaterialDialog.Builder(MainActivity.this).title("Enable AutoStart")
        .content(
          "Please allow AppName to always run in the background,else our services can't be accessed.")
        .theme(Theme.LIGHT)
        .positiveText("ALLOW")
        .onPositive(new MaterialDialog.SingleButtonCallback() 
          @Override
          public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) 
            try 
              Intent intent = new Intent();
              intent.setClassName("com.coloros.safecenter",
                "com.coloros.safecenter.permission.startup.StartupAppListActivity");
              startActivity(intent);
             catch (Exception e) 
              try 
                Intent intent = new Intent();
                intent.setClassName("com.oppo.safe",
                  "com.oppo.safe.permission.startup.StartupAppListActivity");
                startActivity(intent);
               catch (Exception ex) 
                try 
                  Intent intent = new Intent();
                  intent.setClassName("com.coloros.safecenter",
                    "com.coloros.safecenter.startupapp.StartupAppListActivity");
                  startActivity(intent);
                 catch (Exception exx) 

                
              
            
          
        )
        .show();
     else if (Build.MANUFACTURER.contains("vivo")) 
      new MaterialDialog.Builder(MainActivity.this).title("Enable AutoStart")
        .content(
          "Please allow AppName to always run in the background.Our app runs in background else our services can't be accesed.")
        .theme(Theme.LIGHT)
        .positiveText("ALLOW")
        .onPositive(new MaterialDialog.SingleButtonCallback() 
          @Override
          public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) 
            try 
              Intent intent = new Intent();
              intent.setComponent(new ComponentName("com.iqoo.secure",
                "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity"));
              startActivity(intent);
             catch (Exception e) 
              try 
                Intent intent = new Intent();
                intent.setComponent(new ComponentName("com.vivo.permissionmanager",
                  "com.vivo.permissionmanager.activity.BgStartUpManagerActivity"));
                startActivity(intent);
               catch (Exception ex) 
                try 
                  Intent intent = new Intent();
                  intent.setClassName("com.iqoo.secure",
                    "com.iqoo.secure.ui.phoneoptimize.BgStartUpManager");
                  startActivity(intent);
                 catch (Exception exx) 
                  ex.printStackTrace();
                
              
            
          
        )
        .show();
    
  

【讨论】:

谢谢你的回答,我会试试的。但是请解释一下 AutoStart 是什么以及将这段代码放在哪里? 如果你是从自己的项目中复制代码,至少要去掉所有细节 @Motassem Jalal 和 Tim Castelijns ,代码已更新为相关信息。 如果我需要在模拟器上测试怎么办?如何启用此功能? 自动启动将在运行自定义操作系统(如 MiUi)的设备上运行,模拟器通常在股票或 AOSP Roms 上运行,不限制任何应用程序在后台运行,因此不需要自动启动。

以上是关于应用程序死机时的地理围栏的主要内容,如果未能解决你的问题,请参考以下文章

地理围栏 - 检查用户是不是已经在围栏内

获取 Android 应用程序用户进入地理围栏的方向

应用程序终止时停止地理围栏

仅在需要时重新注册地理围栏

地理围栏 PendingIntent 始终为空

应用程序在后台 ios 中时地理围栏不起作用