1. Service为什么会被杀死?
在我们开发的过程当中,有几种情况会导致我们的应用会被杀死:
1>:应用正在运行,这个时候由于内存不足会杀死进程进行回收内存;
2>:手机用了系统自带的或者一些第三方的清理软件,比如360、腾讯管家清理进程也会杀死我们应用的进程;
3>:各大Rom厂商,在应用退出的时候也会杀死我们应用的进程;
基本就是以上3种情况,作为开发来讲,其实最主要关心的是第一种情况就可以,下边我们就针对于这3中情况进行逐一的解决。
2. 进程优先级
共有5级,第一个最重要,最后一个是最不重要的,会从最后一个开始回收内存资源,以确保新的进程或者更重要的进程。
可以参照我之前的文章:
Android中的进程保活
或者参照大神的文章:
Android进程间的通信 - 耍流氓的方式保活Service
3. 解决第一种 —— 应用正在运行,这个时候由于内存不足会杀死进程进行回收内存
一般有3种解决方式:
1>:在应用退到后台时候,就需要释放资源,达到降低App内存的占用量,因为在oom_adj相同的时候,会优先干掉内存消耗大的进程;
举例说明:
比如:首页的ViewPager轮播图,在应用切换到其他页面或者返回到桌面时一定要处理,不要让它再去轮播,如果页面都切换到其他页面或者返回到桌面,还让它继续轮播,可能会消耗比较大的内存;
比如:百度地图的使用,必须在onResume()、onStop()、onDestroy()中必须调用mapView.onResume()、mapView.onStop()、mapView.onDestroy()方法,来释放内存;
2>:对于一直在后台运行的Service,我们一定要让它轻量,不能太风骚;
处理方式和第一种差不太多;
3>:提高进程的优先级,其实就是减小进程的 oom_adj,越小越好,越大越容易被回收,比如启动Service调用startForeground()方法,就会让你的应用在状态栏出现,从而提高进程优先级;
比如墨迹天气会一直在我们手机状态栏中存在,就是调用startForeground();
下边举例说明:
比如我从MainActivity启动一个MessageService,然后在MessageService中重写onStartCommand()方法,然后调用 startForeground()方法,就会达到下边的效果,在状态栏会一直出现我们的应用,效果如下:
代码如下:
1>:MainActivity代码如下:
/**
* Email: [email protected]
* Created by Novate 2018/4/22 9:25
* Version 1.0
* Params:
* Description:
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this , MessageService.class)) ;
}
}
2>:MessageService代码如下:
/**
* Email: [email protected]
* Created by Novate 2018/4/22 9:08
* Version 1.0
* Params:
* Description: QQ聊天通讯 Service代码中一定要轻量
*/
public class MessageService extends Service {
private int MessageId = 1;
@Override
public void onCreate() {
super.onCreate();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
Log.e("TAG", "等待接收消息");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 提高进程优先级 参数1:id 参数2:通知
startForeground(MessageId , new Notification());
return START_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
3>:在清单文件中配置:
只要在onStartCommand()方法中调用 startForeground()方法,就可以看到,手机状态栏会出现我们应用的图标,可以手动在状态栏中强行停止上边的ServiceALIveDemo.
2. 解决第二种 —— 用了系统自带的或者一些第三方的清理软件,也会杀死我们应用的进程;
解决方式是:
采用双进程守护,使用两个进程,即就是开启两个服务,让其相互唤醒,可以达到杀死其中一个,另一个会立马唤醒这个让其再重新启动然后建立连接,代码如下:
1>:第一个进程MessageService:
/**
* Email: [email protected]
* Created by Novate 2018/4/22 9:08
* Version 1.0
* Params:
* Description: QQ聊天通讯 Service代码中一定要轻量
*/
public class MessageService extends Service {
private int MessageId = 1;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new ProcessConnection.Stub(){} ;
}
@Override
public void onCreate() {
super.onCreate();
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
Log.e("TAG", "等待接收消息");
try {
Thread.sleep(2000);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}).start();
}
/**
* MainActivity中一启动MessageService之后,就会调用 onStartCommand()方法
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 提高进程优先级 ,就会在通知栏中出现自己的应用,如果不想提高优先级,可以把这个注释
// 参数1:id 参数2:通知
startForeground(MessageId , new Notification());
// 让MessageService绑定 GuardService并建立连接
bindService(new Intent(this , GuardService.class) , mServiceConnection , Context.BIND_IMPORTANT) ;
return START_STICKY;
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 连接上
Toast.makeText(MessageService.this , "建立连接" , Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 断开连接,需要重新启动,然后重新绑定
// 重新启动
startService(new Intent(MessageService.this , GuardService.class)) ;
// 重新绑定
bindService(new Intent(MessageService.this , GuardService.class) , mServiceConnection , Context.BIND_IMPORTANT) ;
}
} ;
}
2>:第二个进程GuardService:
/**
* Email: [email protected]
* Created by Novate 2018/4/22 9:32
* Version 1.0
* Params:
* Description: 守护进程,双进程通讯,需要使用aidl(进程间通讯)
* 两个进程之间相互监听,相互唤醒
*/
public class GuardService extends Service {
private int GuardId = 1;
/**
* 返回 IBinder驱动
* Stub: 存根
*/
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new ProcessConnection.Stub(){} ;
}
// private ProcessConnection mBinder = new ProcessConnection.Stub(){} ;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 提高进程优先级 ,就会在通知栏中出现自己的应用,如果不想提高优先级,可以把这个注释
startForeground(GuardId , new Notification());
// 让GuardService绑定MessageService 并建立连接
bindService(new Intent(this , MessageService.class) , mServiceConnection , Context.BIND_IMPORTANT) ;
return START_STICKY;
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 连接上
Toast.makeText(GuardService.this , "建立连接" , Toast.LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 断开连接,就需要重新启动,然后重新绑定
// 重新启动
startService(new Intent(GuardService.this , MessageService.class)) ;
// 重新绑定
bindService(new Intent(GuardService.this , MessageService.class) , mServiceConnection , Context.BIND_IMPORTANT) ;
}
} ;
}
3>:在MainActivity中同时启动两个服务:
/**
* Email: [email protected]
* Created by Novate 2018/4/22 9:25
* Version 1.0
* Params:
* Description:
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this , MessageService.class)) ;
startService(new Intent(this , GuardService.class)) ;
}
}
效果如下:
由上图可以看到:
点击到手机的 设置 -> 应用管理 -> 正在运行,可以看到有两个一样的进程都正在运行,而且你随便杀死一个,另一个会立马唤醒这个,让它再次重新启动并重新绑定服务;
可以点击应用,然后手动停止应用,或者使用第三方停止应用;
第三方在杀进程的时候,是一个一个杀的。
3. 解决第三个 —— 各大Rom厂商在应用退出的时候会清理杀死进程
电量优化 JobScheduler,处理定时任务,5.0以上才有
1>:JobWakeUpService代码如下:
/**
* Email: [email protected]
* Created by JackChen 2018/4/22 10:33
* Version 1.0
* Params:
* Description: JobService -> 5.0以上才有的
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class JobWakeUpService extends JobService {
private final int jobWakeUpId = 1 ;
/**
* 开启服务之后,指定一个轮询时间
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 开启一个轮询
JobInfo.Builder jobBuilder = new JobInfo.Builder(jobWakeUpId , new ComponentName(this , JobWakeUpService.class)) ;
jobBuilder.setPeriodic(2000) ; // 2s
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.schedule(jobBuilder.build()) ;
return super.onStartCommand(intent, flags, startId);
}
@Override
public boolean onStartJob(JobParameters params) {
// 开启一个定时任务,定时轮询 , 判断MessageService有没有被杀死
// 如果杀死,就启动,因为这个方法是每隔一段时间进行轮询调用 onStartJob()方法
// 判断服务有没有被杀死(判断服务有没有正在运行)
boolean messageServiceALive = serviceAlive(JobWakeUpService.class.getName());
// 如果服务没有活,就启动 ,这里只需要启动一个服务就ok,不需要启动另一个 GuardService
if (!messageServiceALive){
startService(new Intent(JobWakeUpService.this , MessageService.class)) ;
}
return false;
}
@Override
public boolean onStopJob(JobParameters params) {
return false;
}
/**
* 判断某个服务是否正在运行的方法
* @param serviceName
* 是包名+服务的类名(例如:net.loonggg.testbackstage.TestService)
* @return true代表正在运行,false代表服务没有正在运行
*/
private boolean serviceAlive(String serviceName) {
boolean isWork = false;
ActivityManager myAM = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
List myList = myAM.getRunningServices(100);
if (myList.size() <= 0) {
return false;
}
for (int i = 0; i < myList.size(); i++) {
String mName = myList.get(i).service.getClassName().toString();
if (mName.equals(serviceName)) {
isWork = true;
break;
}
}
return isWork;
}
}
2>:然后在MainActivity中启动,但是一定要判断是否大于5.0:
/**
* Email: [email protected]
* Created by Novate 2018/4/22 9:25
* Version 1.0
* Params:
* Description:
*/
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startService(new Intent(this , MessageService.class)) ;
startService(new Intent(this , GuardService.class)) ;
// 这里必须判断,否则5.0以下手机肯定崩溃
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP){
startService(new Intent(this , JobWakeUpService.class)) ;
}
}
}
3>:最后在清单文件中配置即可:
// JobService权限
代码已上传至github:
https://github.com/shuai999/ServiceALiveDemo.git
注意
不到万不得已就不要这样子写,否则别人可能会卸载你应用。