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

本文探讨如何确保Android Service在被杀死后自动重启,重点介绍了Service的生命周期、启动方式,特别是onStartCommand()的返回常量START_STICKY和START_REDELIVER_INTENT在维持服务运行中的作用。

前言:

在我司项目1.0版本的时候消息是使用的环信、用了之后发现各种bug,各种机型不支持导致app崩溃,于是在2.0版本果断去掉环信,使用了公众号用的那套消息系统(老大自己写的)并做了扩展升级。搞了近半个月终于是搞完了,项目也顺利上线......

再见你无法想象,检测未读消息/新消息我写了个线程,每隔30s去请求一个,好low的,app退出后你就拜拜了吧,肯定要改啊!用什么?service呗,于是开始service之旅...



废话连篇,开始我们的Service之旅吧!


1.我们要知道什么是Service?

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 corresponding <service> declaration in its package'sAndroidManifest.xml. Services can be started with Context.startService() and Context.bindService().

呵呵,你看得懂?

废话...

简单解释下就是:Service是一个应用程序组件,它能够在后台执行一些耗时较长的操作,并且不提供用户界面。服务能被其它应用程序的组件启动,即使用户切换到另外的应用时还能保持后台运行。此外,应用程序组件还能与服务绑定,并与服务进行交互,甚至能进行进程间通信(IPC)。 比如,服务可以处理网络传输、音乐播放、执行文件I/O、或者与content provider进行交互,所有这些都是后台进行的(抱歉,我抄的...)

附:Service官方介绍:传送门

  Android中文API:传送门


2.Service生命周期



一会我们通过代码看结果,先了解...


3.Service基本类型(启动方式):

Started :通过应用程序组件(例如Activity)调用startService()启动服务:StartService(intent)系统通通过传入的intent搜索相关符合intent的Service,

依次执行其相关生命周期,service一旦启动就会一直在后台运行,直到调用stopService或stopSelf停止服务。

注:public void onStart(Intent intent, int startId) {}已过时,在2.0之后引入public int onStartCommand(Intent intent, int flags, int startId) {},flags,Service启动函数,后面介绍。

Bind:通过bindService()绑定服务,该提供了一个客户端/服务器接口,允许组建与服务进行交互、发送请求、返回结果,设置可以利用进程间通信夸进程执行这些操作;多个组件

可以同时与一个服务绑定,通过onUnbind()方法解绑服务,当所有组件解绑后,服务也被销毁。


接下来正式进入我们今天的话题:如何保证Service不被杀死或被kill之后自动重启!

1). onStartCommand() 返回常量Flag介绍:

  • START_STICKY 表示你希望系统可用的时候自动重启你的服务,但你不关心是否能获得最后一次的 Intent (例如,你可以重建自己的状态或者控制自己的 start/stop 生命周期)。
  • START_REDELIVER_INTENT 是为那些在被杀死之后重启时重新获得 Intent 的服务的,直到你用传递给 onStartCommand() 方法的 startId 参数调用 stopSelf() 为止。这里你会使用 Intent 和 startId 作为队列完成工作。
  • START_NOT_STICKY 用于那些杀掉也没关系的服务。这适合那些管理周期性任务的服务,它们只是等待下一个时间窗口工作。(摘自掘金
so,在内存不足服务被kill时,我们手动返回flag为START_STICKY / START_REDELIVER_INTENT(取决于重启是否需要重新获得intent),当内存足够时,服务会被重新创建.此方法然并卵,并不能使服务常驻...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.i("TAG", "Services onStartCommand");
    return START_REDELIVER_INTENT;
}

2).配置android:persistent="true"  ,persistent根据字面意思理解是持久...持久要持久!也就是常驻。但是通过测试发现被kill掉之后并不能重启...
3).查看其官方文档,有startForeground这个方法
  startForeground(int id, Notification notification)

Make this service run in the foreground, supplying the ongoing notification to be shown to the user while in this state.

其意思就是使服务在前台运行,发送一个通知给处于此状态的用户,前台必须提供一个状态栏通知
这里就涉及到service的进程优先级:当系统内存不足需要释放时,会按照优先级对进程回收,而android将进程分为六个等级
前台进程( FOREGROUND_APP)、可视进程(VISIBLE_APP )、次要服务进程(SECONDARY_SERVER )
后台进程 (HIDDEN_APP)、内容供应节点(CONTENT_PROVIDER)、空进程(EMPTY_APP)

可在service onStartCommand()方法中如此操作:
NotificationCompat.Builder builder = new NotificationCompat.Builder(G.applicationContext);
Notification notification = builder.build();
notification.flags = Notification.FLAG_FOREGROUND_SERVICE;
startForeground(0, notification);
Log.i("Service", "UnreadMessageServices onStartCommand");
return START_STICKY;
写一个状态通知栏大家都会吧,这里就不详说了,不会的自行google...
运行后效果为 如图:

我们将service置为前台服务时,一般情况还是不想让用户看到对吧。startForeground()方法。此方法有两个参数:唯一标识通知的整数值、状态栏通知Notification对象。当我传id不为0时,Notification会显示,当id=0时则不显示到通知栏,原因嘛...送上传送门Android Service startForeground() 不显示Notification问题)。
这样做只是在低内存是降低该service被kill掉的几率,并不能真正使service常驻。
4).还有方法是说让其成为系统应用...  好吧这个我确实没测试,也不想..听说apk无法卸载;
设置该service为独立进程,貌似提升了优先级,但是照样被kill...  pass ;
在onDestory()方法中重启service,能不能不用这么low的方法...我没做测试


这几种方法只能提升service优先级/存活率,但是还不能解决其被杀毒软件强行kill的命运...

我的解决方案:
1.首先设置服务为开机自启
public class BootBroadcastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent unreadCountService = new Intent(context, UnreadCountService.class);
        Intent checkService = new Intent(context, CheckService.class);
        context.startService(unreadCountService);
        context.startService(checkService);
        Log.v("TAG", "开机自动服务自动启动.....");
    }
}
同时需要配置其权限AndroidManifest.xml
<!-- 开机自启动 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- 开机自启动 -->
<receiver
    android:name=".help.BootBroadcastReceiver"
    android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</receiver>

2.利用系统广播  Intent.ACTION_TIME_TICK 每隔一分钟检测一次Service的运行状态
private class CheckBroadCastReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("Intent.ACTION_TIME_TICK")) {
            //检查Service状态
            if (isServiceRunning(context, "rain.myapp.help.UnreadCountService") == false) {
                //重启服务
                Intent i = new Intent(context,UnCountService.class);
                context.startService(i);
            }
        }
    }
}

/**
 * 判断服务是否正在运行中
 *
 * @param context     Context对象
 * @param serviceName Service全名
 * @return
 */
private boolean isServiceRunning(Context context, String serviceName) {
    if (!TextUtils.isEmpty(serviceName) && context != null) {
        ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        ArrayList<ActivityManager.RunningServiceInfo> runningServiceInfoList
                = (ArrayList<ActivityManager.RunningServiceInfo>) activityManager.getRunningServices(100);
        for (Iterator<ActivityManager.RunningServiceInfo> iterator = runningServiceInfoList.iterator(); iterator.hasNext(); ) {
            ActivityManager.RunningServiceInfo runningServiceInfo = iterator.next();
            if (serviceName.equals(runningServiceInfo.service.getClassName().toString()))
                return true;
        }
    } else return false;
    return false;
}

公司项目该功能做到这一步就已经足够了,暂时也不打算深入研究了。作为Android开发者,我们本身是有必要去维护Android的生态环境而不是一昧的去破坏... 到此为止!菜鸟写博客,诸多不合之处欢迎指出,还望无喷...
内容概要:本文系统介绍了RPA(机器人流程自动化)的核心概念、工作原理及其在现代办公场景中的广泛应用。通过真实案例展示了RPA如何自动处理数据录入、订单管理、财务报表生成、人力资源招聘和客服响应等重复性任务,显著提升效率并降低错误率。文章详细对比了主流RPA工具(如UiPath、Blue Prism、影刀RPA、八爪鱼RPA)的功能特点、适用场景及优缺点,并以影刀RPA为例演示了从安装配置到创建自动化流程的完整操作步骤。进一步深入讲解了流程设计中的循环、条件判断等控制结构,以及RPA与AI、OCR、机器学习等技术融合的高级应用,如智能客服、文档识别、数据预测与风险预警。最后通过财务、人力、电商、客服四大领域的实践案例,验证了RPA的实际价值,并提供了常见问题的解决方案和学习支持渠道。; 适合人群:希望提升工作效率的职场新人、中小企业管理者、对自动化技术感兴趣的非技术人员,以及有意从事RPA开发的初级程序员;; 使用场景及目标:①解决日常工作中重复繁琐的任务(如数据整理、文件转换、报表生成);②实现跨系统数据自动流转与集成;③结合AI技术实现智能决策与流程优化;④为企业降本增效,推动数字化转型; 阅读建议:建议读者边学边练,结合文中实例在选定RPA工具中动手实践,重点关注流程设计逻辑与异常处理机制,并积极利用官方文档、社区论坛和技术支持资源解决实际问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一条苍老的小鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值