前言:service是一种计算类型的组件,也是运行在UI线程中;service中同样也不能进行耗时操作,否则就会导致无响应,AMS就会给我弹个ANR(前台服务比后台服务的时间更短,超过就会ANR;至于多少时间就无响应,请可自行查找)。
一、首先,对service进行简单的介绍,对于service在AndroidManifest注册,只说一下process属性,不指定该属性的时候,该service依赖于app的默认进程;当为其指定属性的时候,该service便作为一个独立的进程,假设在service的process指定如下属性:
android:process="com.example.anyview.KeepLiveService"
运行项目,上图可以看到控制台有两个可选的进程,第一个是默认的进程名,第二个则是刚才指定的进程;
同时,我们在Application中的onCreate打印了一行日志,下图:
可以看到控制台的输出日志,下图:
根据输出日志可以看到,我们的日志在两个进程中分别执行一次,由此可得出onCreate也执行了两次。
根据以上结果,我们可以对于一些初始化操作进行优化,一般只需在默认进程中进行初始化即可
public boolean isMainProcess() {
int myPid = Process.myPid();
ActivityManager manager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = manager.getRunningAppProcesses();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (myPid == appProcess.pid && appProcess.processName.equals(getPackageName())) {
return true;
}
}
return false;
}
使用上述代码判断是否主进程,这样可以加快app启动响应!这里需要注意的是在gradle更新之后,appId和包名有不同的作用,一般更多是根据包名找资源,appId作为区分不同应用。有些第三方平台也对此做了区分。如果你的项目包名跟appId不一样,那么默认进程就是appId
二、service的启动方式:
service的启动分为startService和bindService两种
1.startService(intent):首次启动service的时候,依次执行onCreate,startCommand方法!因为通过该方式启动的service不会随着应用退出而结束,必须通过调用stopService方法才能停止服务。多次startService,不会多次创建onCreate,后面会直接走startCommand方法!
2.bindService(intent,mConnection,Context.BIND_AUTO_CREATE):onBind方法!因为通过该方式启动的service在程序是和启动者绑定的,当前者退出了,该服务的unBindService方法会被调用,停止service服务!
需要注意的是,从Android 5.0之后,如果目标service和启动service的界面不在同一应用,客户端不止需要setAction("xxxx"),还需要在setPackage("目标service所在项目的包名"),如下图所示。
三、保活的两种操作:
(一)可以配合使用广播实现,这种方法适用于正常关闭的情况:
1.每当service关闭的时候,在service的onDestroy方法中发送一个广播:
Intent intent = new Intent("com.example.testBroadCastReceiver");
sendBroadcast(intent);
2.然后注册一个广播接受者,当接收到发的广播,就启动service:
public class KeepAliveBroadCastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
if ("com.example.testBroadCastReceiver".equals(intent.getAction())){
intent = new Intent(context, KeepAliveService.class);
context.startService(intent);
}
}
}
3.在manifest中进行进行注册:
<service android:name=".KeepAliveService"/>
<receiver android:name=".KeepAliveBroadCastReceiver">
<intent-filter>
<action android:name="com.example.testBroadCastReceiver"/>
</intent-filter>
</receiver>
(二)启动前台service:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(new Intent(this, KeepAliveService.class));//start前台Service
}else {
startService(new Intent(this, KeepAliveService.class));
}
Service中的startCommand如下:
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
Notification.Builder builder = new Notification.Builder(this)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.app_icon))
.setSmallIcon(R.mipmap.app_icon)
.setContentTitle("title")
.setContentText("例子")
.setContentIntent(pendingIntent)
.setWhen(System.currentTimeMillis());
/**
* 为了规范通知栏,在26以后必须创建通知渠道
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){//sdk>=26
NotificationChannel notificationChannel = new NotificationChannel("com.example.administrator.anyview", "AnyView", NotificationManager.IMPORTANCE_HIGH);
mNotificationManager.createNotificationChannel(notificationChannel);
builder.setChannelId("com.example.administrator.anyview");
}
startForeground(1, builder.build());//前台通知,通过stopForeground(true)关闭
// mNotificationManager.notify(1, notification);//普通通知
return super.onStartCommand(intent, flags, startId);
}
所以,进程保活其实第二种才是我们最常用的方法,这样一键杀进程就杀不掉我们的应用了(只能手动单独杀)
以上是service的一些基本介绍,如有问题,欢迎指出!!