[转] Android开发之如何保证Service不被杀掉(broadcast+system/app)

转发:原文连接http://blog.csdn.net/mad1989/article/details/22492519java

序言

 

最近项目要实现这样一个效果:运行后,要有一个service始终保持在后台运行,无论用户做出什么操做,都要保证service不被kill,这可真是一个难题。参考了现今各类定制版的系统和安全厂商牛虻软件,如何能保证本身的Service不被杀死呢?android

其实除了常规的手段,咱们能够参考一下微信和360,设置-程序-正在运行,能够看到微信是同时开启了两个进程和服务:shell

【有兴趣能够研究一下 守护进程 和 AIDL 】缓存

我猜测它应该是相互监听,若是有一方被kill掉,另外一个捕获到当即启动,以达到service永远都在运行的状态,貌似360也是这个原理,具体是否是这个样子,还有待参考,目前我尚未参透它们是如何实现的,先简单说一下我本身的防控措施吧,首先介绍一下Service概念,记性很差,重复造一下车轮,高手能够直接看最后。安全

 

 

Service简介

Service是在一段不定的时间运行在后台,不和用户交互应用组件。每一个Service必须在manifest中 经过<service>来声明。能够经过contect.startservice和contect.bindserverice来启动。和其余的应用组件同样,运行在进程的主线程中。这就是说若是service须要不少耗时或者阻塞的操做,须要在其子线程中实现(或者用系统提供的IntentService,它继承了Service,它处理数据是用自身新开的线程)。【固然你也能够在新的线程中startService,这样Service就不是在MainThread了微信

 

本地服务 Local Service 用于应用程序内部微信开发

它能够启动并运行,直至有人中止了它或它本身中止。在这种方式下,它以调用Context.startService()启动,而以调用Context.stopService()结束。它能够调用Service.stopSelf() 或 Service.stopSelfResult()来本身中止。不论调用了多少次startService()方法,你只须要调用一次stopService()来中止服务。app

【用于实现应用程序本身的一些耗时任务,好比查询升级信息,并不占用应用程序好比Activity所属线程,而是单开线程后台执行,这样用户体验比较好】eclipse

 

远程服务 Remote Service 用于Android系统内部的应用程序之间ide

它能够经过本身定义并暴露出来的接口进行程序操做。客户端创建一个到服务对象的链接,并经过那个链接来调用服务。链接以调用Context.bindService()方法创建,以调用 Context.unbindService()关闭。多个客户端能够绑定至同一个服务。若是服务此时尚未加载,bindService()会先加载它。

【可被其余应用程序复用,好比天气预报服务,其余应用程序不须要再写这样的服务,调用已有的便可】

 

1,Service的生命周期

 

2,Service运行方式

以startService()启动服务,系统将经过传入的Intent在底层搜索相关符合Intent里面信息的service。若是服务没有启动则先运行onCreate,而后运行onStartCommand (可在里面处理启动时传过来的Intent和其余参数),直到明显调用stopService或者stopSelf才将中止Service。不管运行startService多少次,只要调用一次stopService或者stopSelf,Service都会中止。使用stopSelf(int)方法能够保证在处理好intent后再中止。onStartCommand ,在2.0后被引入用于service的启动函数,2.0以前为public void onStart(Intent intent, int startId) 。

以bindService()方法启用服务,调用者与服务绑定在了一块儿,调用者一旦退出,服务也就终止。onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,屡次调用Context.bindService()方法并不会致使该方法被屡次调用。采用Context.bindService()方法启动服务时只能调用onUnbind()方法解除调用者与服务解除,服务结束时会调用onDestroy()方法。

 

3,拥有service的进程具备较高的优先级

官方文档告诉咱们,android系统会尽可能保持拥有service的进程运行,只要在该service已经被启动(start)或者客户端链接(bindService)到它。当内存不足时,须要保持,拥有service的进程具备较高的优先级。

1. 若是service正在调用onCreate,onStartCommand或者onDestory方法,那么用于当前service的进程则变为前台进程以免被killed。
2. 若是当前service已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,可是比那些不可见的进程更重要,这就意味着service通常不会被killed.
3. 若是客户端已经链接到service (bindService),那么拥有Service的进程则拥有最高的优先级,能够认为service是可见的。
4. 若是service可使用startForeground(int, Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时killed。
5. 若是有其余的应用组件做为Service,Activity等运行在相同的进程中,那么将会增长该进程的重要性。

 

保证service不被杀掉

 

onStartCommand方法,返回START_STICKY

 

StartCommond几个常量参数简介:

一、START_STICKY

在运行onStartCommand后service进程被kill后,那将保留在开始状态,可是不保留那些传入的intent。不久后service就会再次尝试从新建立,由于保留在开始状态,在建立     service后将保证调用onstartCommand。若是没有传递任何开始命令给service,那将获取到null的intent。

二、START_NOT_STICKY

在运行onStartCommand后service进程被kill后,而且没有新的intent传递给它。Service将移出开始状态,而且直到新的明显的方法(startService)调用才从新建立。由于若是没有传递任何未决定的intent那么service是不会启动,也就是期间onstartCommand不会接收到任何null的intent。

三、START_REDELIVER_INTENT

在运行onStartCommand后service进程被kill后,系统将会再次启动service,并传入最后一个intent给onstartCommand。直到调用stopSelf(int)才中止传递intent。若是在被kill后还有未处理好的intent,那被kill后服务仍是会自动启动。所以onstartCommand不会接收到任何null的intent。

 

[java]  view plain  copy
 
  1. @Override  
  2. public int onStartCommand(Intent intent, int flags, int startId) {  
  3.     flags = START_STICKY;  
  4.     return super.onStartCommand(intent, flags, startId);  
  5. }  

 

【结论】 手动返回START_STICKY,亲测当service因内存不足被kill,当内存又有的时候,service又被从新建立,比较不错,可是不能保证任何状况下都被重建,好比进程被干掉了....

 

 

提高service优先级

 

在AndroidManifest.xml文件中对于intent-filter能够经过android:priority = "1000"这个属性设置最高优先级,1000是最高值,若是数字越小则优先级越低,同时适用于广播。

 

[java]  view plain  copy
 
  1. <service  
  2.     android:name="com.dbjtech.acbxt.waiqin.UploadService"  
  3.     android:enabled="true" >  
  4.     <intent-filter android:priority="1000" >  
  5.         <action android:name="com.dbjtech.myservice" />  
  6.     </intent-filter>  
  7. </service>  

 

【结论】目前看来,priority这个属性貌似只适用于broadcast,对于Service来讲可能无效

 

提高service进程优先级

 

Android中的进程是托管的,当系统进程空间紧张的时候,会依照优先级自动进行进程的回收。Android将进程分为6个等级,它们按优先级顺序由高到低依次是:

   1.前台进程( FOREGROUND_APP)
   2.可视进程(VISIBLE_APP )
   3. 次要服务进程(SECONDARY_SERVER )
   4.后台进程 (HIDDEN_APP)
   5.内容供应节点(CONTENT_PROVIDER)
   6.空进程(EMPTY_APP)

当service运行在低内存的环境时,将会kill掉一些存在的进程。所以进程的优先级将会很重要,可使用startForeground 将service放到前台状态。这样在低内存时被kill的概率会低一些。

 

在onStartCommand方法内添加以下代码:

 

[java]  view plain  copy
 
  1.  Notification notification = new Notification(R.drawable.ic_launcher,  
  2.  getString(R.string.app_name), System.currentTimeMillis());  
  3.   
  4.  PendingIntent pendingintent = PendingIntent.getActivity(this, 0,  
  5.  new Intent(this, AppMain.class), 0);  
  6.  notification.setLatestEventInfo(this, "uploadservice", "请保持程序在后台运行",  
  7.  pendingintent);  
  8. <span style="color:#ff0000;"> startForeground(0x111, notification);</span>  

注意在onDestroy里还须要stopForeground(true),运行时在下拉列表会看到本身的APP在:

 

【结论】若是在极度极度低内存的压力下,该service仍是会被kill掉,而且不必定会restart

 

 

onDestroy方法里重启service

 

service +broadcast  方式,就是当service走ondestory的时候,发送一个自定义的广播,当收到广播的时候,从新启动service;

 

[java]  view plain  copy
 
  1. <receiver android:name="com.dbjtech.acbxt.waiqin.BootReceiver" >  
  2.     <intent-filter>  
  3.         <action android:name="android.intent.action.BOOT_COMPLETED" />  
  4.         <action android:name="android.intent.action.USER_PRESENT" />  
  5.         <action android:name="com.dbjtech.waiqin.destroy" />//这个就是自定义的action  
  6.     </intent-filter>  
  7. </receiver>  

在onDestroy时:

 

[java]  view plain  copy
 
  1. @Override  
  2. public void onDestroy() {  
  3.     stopForeground(true);  
  4.     Intent intent = new Intent("com.dbjtech.waiqin.destroy");  
  5.     sendBroadcast(intent);  
  6.     super.onDestroy();  
  7. }  

在BootReceiver里

 

[java]  view plain  copy
 
  1. public class BootReceiver extends BroadcastReceiver {  
  2.   
  3.     @Override  
  4.     public void onReceive(Context context, Intent intent) {  
  5.         if (intent.getAction().equals("com.dbjtech.waiqin.destroy")) {  
  6.             //TODO  
  7.             //在这里写从新启动service的相关操做  
  8.                 startUploadService(context);  
  9.         }  
  10.   
  11.     }  
  12.   
  13. }  

也能够直接在onDestroy()里startService

 

[java]  view plain  copy
 
  1. @Override  
  2. public void onDestroy() {  
  3.   
  4.      Intent sevice = new Intent(this, MainService.class);  
  5.      this.startService(sevice);  
  6.   
  7.     super.onDestroy();  
  8. }  

 

 

【结论】当使用相似口口管家等第三方应用或是在setting里-应用-强制中止时,APP进程可能就直接被干掉了,onDestroy方法都进不来,因此仍是没法保证~.~

 


Application加上Persistent属性

 

看Android的文档知道,当进程长期不活动,或系统须要资源时,会自动清理门户,杀死一些Service,和不可见的Activity等所在的进程。可是若是某个进程不想被杀死(如数据缓存进程,或状态监控进程,或远程服务进程),能够这么作:

 

[java]  view plain  copy
 
  1. <application  
  2.     android:name="com.test.Application"  
  3.     android:allowBackup="true"  
  4.     android:icon="@drawable/ic_launcher"  
  5.     android:label="@string/app_name"  
  6.    <span style="color:#ff0000;"> android:persistent="true"</span>  
  7.     android:theme="@style/AppTheme" >  
  8. </application>  

 

【结论】听说这个属性不能乱设置,不过设置后,的确发现优先级提升很多,或许是至关于系统级的进程,可是仍是没法保证存活

 

 

监听系统广播判断Service状态

 

经过系统的一些广播,好比:手机重启、界面唤醒、应用状态改变等等监听并捕获到,而后判断咱们的Service是否还存活,别忘记加权限啊。

 

[java]  view plain  copy
 
  1. <receiver android:name="com.dbjtech.acbxt.waiqin.BootReceiver" >  
  2.     <intent-filter>  
  3.         <action android:name="android.intent.action.BOOT_COMPLETED" />  
  4.         <action android:name="android.intent.action.USER_PRESENT" />  
  5.         <action android:name="android.intent.action.PACKAGE_RESTARTED" />  
  6.         <action android:name="com.dbjtech.waiqin.destroy" />  
  7.     </intent-filter>  
  8. </receiver>  

BroadcastReceiver中:

 

[java]  view plain  copy
 
  1. @Override  
  2. public void onReceive(Context context, Intent intent) {  
  3.     if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {  
  4.         System.out.println("手机开机了....");  
  5.         startUploadService(context);  
  6.     }  
  7.     if (Intent.ACTION_USER_PRESENT.equals(intent.getAction())) {  
  8.             startUploadService(context);  
  9.     }  
  10. }  

 

【结论】这也能算是一种措施,不过感受监听多了会致使Service很混乱,带来诸多不便

将APK安装到/system/app,变身系统级应用
 
这个办法不推荐使用,由于若是你的APP若是是给用户使用的,那就不合适了,我是为了给测试的妹子来用,这个APP的目的也是很简单,打开后开启Service而且能保证一直在后台驻留,开机自启动。可是昨天发现若是她的HuaWei手机长时间关闭, 再从新打开时,咱们应用的Service不会自启动,貌似广播收不到了~一怒之下,打算搞成系统应用。
 
前提:
ROOT过的手机
1,把代码编写好后,打包导出apk,copy到手机SD卡根目录下。
2,手机链接eclipse,cmd: adb shell
3,切换root模式,输入:su     (若是root过就不会有错误)
4,设置System为读写权限:mount –o remount rw /system (System默认为只读,没法写入,这一步很关键)
5,cd到sd卡跟目录下,确认是否有咱们拷贝到sd卡根目录下的apk(通常都是 storage/sdcard0)
shell@android:/ # cd storage/sdcard0
6,最关键的一步,咱们要把apk拷贝到 /System/app中:
发现copy命令无效~那么咱们就用push把:
若是有错误:device not found,那么手机下载一个Root Explorer把,找到apk,copy到System/app下,经过这个APP要更容易一些。
7,system/app 肯定拥有咱们的APK后,重启手机把:
设置-应用程序管理,查看一下:
能够看到咱们的APP已经没法卸载了,只能停用
这个时候,就算强制中止,或是关闭Service,重启手机后照样能够起来Service~!
 
系统级的APP,这样一些第三方的管家软件,就没法杀掉咱们,除非本身把APP停用掉,或是强制中止(可是个人APP能够开机自启动)。
 
【结论】这种方式适合调试来用,并不算是一种解决办法,你们能够尝试在正在运行的界面:强制关闭搜狐视频的两个进程,重启手机,发现他又能够自启动,可是若是换成咱们的APP,强制中止,进程挂了,再重启手机,没法自启动了~
 
 
 
你们一块儿研究,怎么样才能像上图搜狐视频同样,开启两个进程,相互监听,作到最大程度的存活,若是这个能实现了,那就和微信、360等同样的效果了。
相关文章
相关标签/搜索