Android后台单进程保活处理实践
0x00、如何实现一个后台进程
-
自定义的
Service
,实现相应的业务逻辑 -
在
Manifest
文件中配置Service
组件,需配置process
属性,然后指定intent-filter
和priority
属性,如下示例:<service android:name="com.stfl.SSClientService" android:process=":ssclient"> <intent-filter android:priority="1000"> <action android:name="io.telebox.ssclient.service" /> </intent-filter> </service>
priority
属性值越大,优先级越高。
0x01、如何重启进程
-
重写
Service
的OnDestroy
方法。主要逻辑是监听到Service
被杀掉后,发送一个Broadcast
通知,重新启动Service
进程。根据不同的业务逻辑,可以选择是否调用killProcess
方法,彻底清理所有进程资源。可参考如下:@Override public void onDestroy() { super.onDestroy(); logger.info("SSClient is stop!"); // 根据业务需要决定是否调用killProcess,这里我是推荐调用此方法,清理资源。 Process.killProcess(Process.myPid()); // 服务关掉通知 Intent intent = new Intent(SSClientReceiver.ACTION_DISCONNECTED); intent.setComponent(new ComponentName(getPackageName(), SSClientReceiver.class.getName())); sendBroadcast(intent); }
需要注意的是,需要设置
component
否则在8.0手机会出现Background execution not allowed
的错误。
0x02、实现监听进程通知
-
继承
BroadcastReceiver
并实现onReceive()
方法。实现参考:@Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action == null) { action = ""; } switch (action) { case ACTION_START: Log.d("SSClient", "start by receiver."); SSClientService.startSService(context); break; case ACTION_DISCONNECTED: Log.d("SSClient", "disconnected,restart by receiver."); SSClientService.startSService(context); onSSClientDisconnected(); break; case ACTION_CONNECTED: onSSClientConnected(); break; } }
在接收到
ACTION_START
或ACTION_DISCONNECTED
时,就调用启动Service
的方法。 -
在
Manifest
文件中配置Broadcast
,示例参考:<receiver android:name="com.stfl.SSClientReceiver" android:enabled="true"> <intent-filter> <action android:name="io.telebox.ssclient.start" /> </intent-filter> </receiver>
0x03、需要注意的问题
-
配置
Service
优先级priority
属性,尽量提高Service
的优先级 -
Android 8.0手机发送通知时会出现
Background execution not allowed
的错误,intent要设置component。Intent intent = new Intent("receiver-action-string"); intent.setComponent(new ComponentName(getPackageName(),"receiver-class-name")); sendBroadcast(intent);
-
Android 8.0后台服务启动时出现
java.lang.IllegalStateException: Not allowed to start service Intent
错误。// 1.启动Service的方法中修改为 Intent service = new Intent(SSClientService.ACTION); service.setPackage(context.getPackageName()); // kill:java.lang.IllegalStateException: Not allowed to start service Intent if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { context.startForegroundService(service); } else { context.startService(service); } // 2.在Service类中调用startForeground方法 public void onCreate() { super.onCreate(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForeground(Integer.MAX_VALUE, new Notification()); //这个id不要和应用内的其他同志id一样,不行就写 int.maxValue() //context.startForeground(SERVICE_ID, builder.getNotification()); } }
-
Android 8.1出现
android.app.RemoteServiceException: Bad notification for startForeground
修改
startForeground
方法。@TargetApi(Build.VERSION_CODES.O) private void startForeground() { int nid = 1010; String channelId = createNotificationChannel(); Notification.Builder builder = new Notification.Builder(this, channelId); Notification notification = builder.setOngoing(false) .setSmallIcon(R.mipmap.ic_small_logo) .setPriority(Notification.PRIORITY_MIN) .setCategory(Notification.CATEGORY_SERVICE) .build(); startForeground(nid, notification); } @TargetApi(Build.VERSION_CODES.O) private String createNotificationChannel() { String channelId = "my_service"; String channelName = "My Background Service"; NotificationChannel chan = new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE); chan.setLightColor(Color.BLUE); chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE); NotificationManager service = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); service.createNotificationChannel(chan); return channelId; }