Android应用保活四步曲
本文针对国内手机厂商对于后台应用无法保活的限制下,用户还希望你的应用能在后台保持运行。
ps:如果用户不给权限,除非加入系统白名单,否则你的应用会死的一干二净。
第一步:利用Service类onStartCommand()方法返回值
public class YourService extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//这里默认返回就START_STICKY,当然也可以根据需求返回其他的值
return super.onStartCommand(intent, flags, startId);
// return START_STICKY 或 START_REDELIVER_INTENT
}
}
这种方式算是中规中矩了,也不违反官方的编程规范。但是如果用户频繁的杀死你的应用,或者在设置中手动强制停止你的Service,那么重新启动的时机就不好说了,有可能一直启动不起来了。
第二步:捕获系统通知
利用接收一些频繁产生通知,让自己应用重新启动。例如 网络变化、屏幕解锁、充电、停止充电、新图片、新视频等。现在大部分第三方推送都会用这种通知的监听。
目前这种方式有两个弊端:
- 从7.0系统开始,针对全局应用的通知增加了限制,例如非前台应用无法接收到网络变化、新图片、新视频的通知。个人在测试时(8.1系统),如果应用被杀死后,什么通知也唤醒不了应用。
- 国内系统的手机管理工具内,有自启动限制功能,这也会导致应用无法重启。
第三步:请求自启动权限 + JobService
如果系统限制你的应用自启动,那么乖乖的请求用户去把自启动权限打开吧,否则永远没戏。
经过在oppo的机器上测试,就算是允许了应用自启动,我们应用也不会立即重新启动,这个启动时机无法确定。
为了尽快让自己应用启动起来,可以添加JobService周期任务,在最短周期内唤醒自己的应用。其中,小米推送就是利用了JobService这种机制。当然,只能在5.0以上的系统使用。
//定义一个Service,继承JobService
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class KeepAliveJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
Timber.d("----onStartJob-----");
return true;
}
@Override
public boolean onStopJob(JobParameters params) {
Timber.d("----onStopJob-----");
return true;
}
}
//不要忘了在清单文件中声明
<service
android:name=".service.KeepAliveJobService"
android:enabled="true"
android:exported="true"
android:permission="android.permission.BIND_JOB_SERVICE" />
//最后在应用启动时,开启这个Service
private void startKeepAliveJobService(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
JobInfo.Builder builder = new JobInfo.Builder(245, new ComponentName(context, KeepAliveJobService.class));
builder.setPeriodic(6 * 60 * 1000);
//Android 7.0+ 增加了一项针对 JobScheduler 的新限制,最小间隔只能是下面设定的数字
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
builder.setPeriodic(JobInfo.getMinPeriodMillis(), JobInfo.getMinFlexMillis());
}
builder.setPersisted(true);
JobScheduler scheduler = (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE);
if (scheduler != null) {
scheduler.schedule(builder.build());
}
}
}
第四步:利用通知监听服务
经过上面三步,基本上能在拥有自启动权限的情况下,能不定时机的唤醒自己的应用了。大家别忘了,国内厂商系统除了自启动权限,还有什么后台限制运行、锁屏清除应用等牛逼限制。能让用户一并把这些限制给我们的App放开肯定是最好的,不过经过实践发现,有些设置入口很深,一般用户根本找不到,我们的App也无法直接跳转这些设置页面。
咳咳,那么有没有一种权限,比较好设置,允许后不管怎么清除应用,都可以马上重新启动呢?
那就是通知监听服务(NotificationListenerService),读者可以在手机开发者模式中,查看正在运行服务中,有一些服务有一个“通知侦听器”的设置,而这些服务是怎么杀都杀不死的,例如某某0手机助手。它就是开启了通知监听服务,并获取到了权限,大家可以自行尝试。下面是使用方法(4.4系统以上适用):
//新建NotificationListenerService子类
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class MyNotificationListenerService extends NotificationListenerService {
private static final String TAG = "MyNotificationListenerS";
@Override
public void onNotificationPosted(StatusBarNotification sbn) {
super.onNotificationPosted(sbn);
Log.i(TAG, "onNotificationPosted: " + sbn.toString());
}
@Override
public void onNotificationRemoved(StatusBarNotification sbn) {
super.onNotificationRemoved(sbn);
Log.i(TAG, "onNotificationRemoved: " + sbn.toString());
}
@Override
public void onListenerConnected() {
super.onListenerConnected();
Log.i(TAG, "onListenerConnected: ");
}
@Override
public void onListenerDisconnected() {
super.onListenerDisconnected();
Log.i(TAG, "onListenerDisconnected: ");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy: ");
}
}
//在清单文件中声明
<service android:name=".feature.notification.MyNotificationListenerService"
android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
>
<intent-filter>
<action android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>
//开启服务
Intent intent = new Intent(getBaseContext(), MyNotificationListenerService.class);
startService(intent);
//检查是否有该权限
Set<String> pckNames = NotificationManagerCompat.getEnabledListenerPackages(getApplicationContext());
if (pckNames != null && pckNames.contains(getPackageName())) {
Toaster.showLongToast(getApplicationContext(), "有权限");
} else {
Toaster.showLongToast(getApplicationContext(), "无权限");
}
//跳转设置页
String ACTION_NOTIFICATION_LISTENER_SETTINGS = "android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS";
startActivity(new Intent(ACTION_NOTIFICATION_LISTENER_SETTINGS));
感觉有点流氓,不过是必须在用户允许的情况才能使用,且用且珍惜。
补充:通知监听服务在oppo和vivo设备上能立即被重新启动。在小米设备不是很及时。