Android 开发之Service 探索如何保证Service不被杀死或被kill之后自动重启

xkfm5730 7年前
   <p>前言:</p>    <p>在我司项目1.0版本的时候消息是使用的环信、用了之后发现各种bug,各种机型不支持导致app崩溃,于是在2.0版本果断去掉环信,使用了公众号用的那套消息系统(老大自己写的)并做了扩展升级。搞了近半个月终于是搞完了,项目也顺利上线......</p>    <p>当时检测未读消息/新消息我写了个线程,每隔30s去请求一个,好low的,app退出后你就拜拜了吧,肯定要改啊!用什么?service呗,于是开始service之旅...</p>    <p>废话连篇,开始我们的Service之旅吧!</p>    <p><strong>1.我们要知道什么是Service?</strong></p>    <p>A Service is an application component representing either an application's desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a correspondingdeclaration in its package'sAndroidManifest.xml. Services can be started with Context.startService() and Context.bindService() .</p>    <p>呵呵,你看得懂?</p>    <p>废话...</p>    <p>简单解释下就是: Service 是一个应用程序组件,它能够在后台执行一些耗时较长的操作,并且不提供用户界面。服务能被其它应用程序的组件启动,即使用户切换到另外的应用时还能保持后台运行。此外,应用程序组件还能与服务绑定,并与服务进行交互,甚至能进行进程间通信(IPC)。 比如,服务可以处理网络传输、音乐播放、执行文件I/O、或者与content provider进行交互,所有这些都是后台进行的。</p>    <p><strong>2.Service生命周期</strong></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/d87e42721eeeb44e2f5a268757e5d88e.png"></p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/bc02427eb7d5a1168d72247d9ba38842.png"></p>    <p>一会我们通过代码看结果,先了解...</p>    <p><strong>3.Service基本类型(启动方式):</strong></p>    <p>Started :通过应用程序组件(例如Activity)调用 startService() 启动服务:StartService(intent)系统通通过传入的intent搜索相关符合intent的Service,</p>    <p>依次执行其相关生命周期,service一旦启动就会一直在后台运行,直到调用stopService或stopSelf停止服务。</p>    <p>注:public void onStart(Intent intent, int startId) {}已过时,在2.0之后引入public int onStartCommand(Intent intent, int flags, int startId) {},flags,Service启动函数,后面介绍。</p>    <p>Bind:通过bindService()绑定服务,该提供了一个客户端/服务器接口,允许组建与服务进行交互、发送请求、返回结果,设置可以利用进程间通信夸进程执行这些操作;多个组件</p>    <p>可以同时与一个服务绑定,通过onUnbind()方法解绑服务,当所有组件解绑后,服务也被销毁。</p>    <p>接下来正式进入我们今天的话题:如何保证Service不被杀死或被kill之后自动重启!</p>    <p>1).onStartCommand()</p>    <p>返回常量Flag介绍:</p>    <p>START_STICKY 表示你希望系统可用的时候自动重启你的服务,但你不关心是否能获得最后一次的 Intent (例如,你可以重建自己的状态或者控制自己的 start/stop 生命周期)。</p>    <p>START_REDELIVER_INTENT 是为那些在被杀死之后重启时重新获得 Intent 的服务的,直到你用传递给 onStartCommand() 方法的 startId 参数调用 stopSelf() 为止。这里你会使用 Intent 和 startId 作为队列完成工作。</p>    <p>START_NOT_STICKY 用于那些杀掉也没关系的服务。这适合那些管理周期性任务的服务,它们只是等待下一个时间窗口工作。(摘自掘金</p>    <p>so,在内存不足服务被kill时,我们手动返回flag为START_STICKY / START_REDELIVER_INTENT(取决于重启是否需要重新获得intent),当内存足够时,服务会被重新创建.此方法然并卵,并不能使服务常驻...</p>    <p>@Overridepublic intonStartCommand(Intent intent, intflags, intstartId) {    Log.i("TAG","Services onStartCommand");return <em>START_REDELIVER_INTENT</em> ;}</p>    <p>2).配置android:persistent="true"  ,persistent根据字面意思理解是持久...持久要持久!也就是常驻。但是通过测试发现被kill掉之后并不能重启...</p>    <p>3).查看其官方文档,有 startForeground 这个方法</p>    <p>startForeground (int id, Notification notification)</p>    <p>Make this service run in the foreground, supplying the ongoing notification to be shown to the user while in this state.</p>    <p>其意思就是使服务在前台运行,发送一个通知给处于此状态的用户,前台必须提供一个状态栏通知</p>    <p>这里就涉及到service的进程优先级:当系统内存不足需要释放时,会按照优先级对进程回收,而android将进程分为六个等级</p>    <p>前台进程(</p>    <p>FOREGROUND_APP)、可视进程(VISIBLE_APP )、次要服务进程(SECONDARY_SERVER )</p>    <p>后台进程</p>    <p>(HIDDEN_APP)、内容供应节点(CONTENT_PROVIDER)、空进程(EMPTY_APP)</p>    <p>可在service onStartCommand()方法中如此操作:</p>    <p>NotificationCompat.Builder builder =newNotificationCompat.Builder(G. <em>applicationContext</em> );Notification notification = builder.build();notification.flags= Notification. <em>FLAG_FOREGROUND_SERVICE</em> ;startForeground(0,notification);Log.i("Service","UnreadMessageServices onStartCommand");return <em>START_STICKY</em> ;</p>    <p>写一个状态通知栏大家都会吧,这里就不详说了,不会的自行google...</p>    <p>运行后效果为 如图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b91b6f9e4b9d73fd48c15c9b5a89ce7e.png"></p>    <p>我们将service置为前台服务时,一般情况还是不想让用户看到对吧。 startForeground() 方法。此方法有两个参数:唯一标识通知的整数值、状态栏通知 Notification 对象。当我传id不为0时, Notification 会显示,当id=0时则不显示到通知栏。</p>    <p>这样做只是在低内存是降低该service被kill掉的几率,并不能真正使service常驻。</p>    <p>4).还有方法是说让其成为系统应用...  好吧这个我确实没测试,也不想..听说apk无法卸载;</p>    <p>设置该service为独立进程,貌似提升了优先级,但是照样被kill...  pass ;</p>    <p>在onDestory()方法中重启service,能不能不用这么low的方法...我没做测试</p>    <p>这几种方法只能提升service优先级/存活率,但是还不能解决其被杀毒软件强行kill的命运...</p>    <p>我的解决方案:</p>    <p>1.首先设置服务为开机自启</p>    <p>public classBootBroadcastReceiverextendsBroadcastReceiver {</p>    <p>@Override</p>    <p>public voidonReceive(Context context,Intent intent) {</p>    <p>Intent unreadCountService =newIntent(context,UnreadCountService.class);</p>    <p>context.startService(unreadCountService);</p>    <p>Log.d("MessageService","开机服务自启...");</p>    <p>}}</p>    <p>同时需要配置其权限AndroidManifest.xml</p>    <p>2.利用系统广播Intent.ACTION_TIME_TICK 每隔一分钟检测一次Service的运行状态</p>    <p>public classCheckBroadCastReceiverextendsBroadcastReceiver { <em> <em> <em> </em> </em> </em></p>    <p>@Override</p>    <p>public voidonReceive(Context context,Intent intent) {</p>    <p>if(intent.getAction().equals(Intent.ACTION_TIME_TICK)) {</p>    <p>//检查Service状态</p>    <p>Log.e("MessageService","onReceive: "+"检测MessageService是否运行 :"+</p>    <p>UtilsHelper.isServiceRunning(context,"com.dailylifeapp.app.and.dailylife.helper.UnreadCountService"));</p>    <p>if(UtilsHelper.isServiceRunning(context,"com.dailylifeapp.app.and.dailylife.helper.UnreadCountService") ==false) {</p>    <p>//重启服务</p>    <p>Intent i =newIntent(context,UnreadCountService.class);</p>    <p>context.startService(i);</p>    <p>}}}}</p>    <p>公司项目该功能做到这一步就已经足够了,暂时也不打算深入研究了。作为Android开发者,我们本身是有必要去维护Android的生态环境而不是一昧的去破坏... 到此为止!菜鸟写博客,诸多不合之处欢迎指出,还望无喷...</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/e7be29d6cd46</p>    <p> </p>