Android 四大组件——Service详解

https://rainmonth.github.io/posts/A191025.html

Service基本概念

Android 四大组件之一,运行于主线程之中。

Service启动分析

startService与bindService分析

Service的销毁

调用startService() 和 bindService()后,要销毁service必须_同时调用_stopService()和unbindService()方法才可以,因为:

  • 点击Stop Service按钮只会让Service停止;
  • 点击Unbind Service按钮只会让Service和Activity解除关联;

一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁;

Service和Thread的关系:没有半毛钱关系

  • Thread,用于开启一个子线程,在里面执行一些耗时操作就不会阻塞主线程的运行;
  • Service,最初的理解是用来处理一些“后台”任务的,就认为一些比较耗时的操作就可以放在这里(后台任务嘛),这就产生了混淆了;其实Service也是运行在主线程中的。

后台和和子线程的概念

Android 的后台指的是它的运行完全不依赖UI;通常Service中需要另外开启一个子线程来执行那些不需要UI的耗时操作的;

为什么不在Activity中直接使用线程而采用Service?

关键在可控制性

  • Activity可以对Service进行很好的控制,所有的Activity都可以与Service进行关联,并且可以很好的操作Service中的方法,即使Activity被销毁了,只要重新创建就可以重新建立与Service的连接;
  • Activity对线程则是十分无奈的,当Activity被销毁之后,就没有其它方法再去获得之前创建的子线程的实例,而且一个Activity创建的子线程,另一个Activity是无法对其进行操作的。

RemoteService

虽然是单独开了一个进程(即和当前应用程序不再同一个进程中)且进行耗时操作也不会出现ANR(但只是不在界面上进行展示,即不会出现ANR弹出框)程序似乎不会继续向下运行。还有RemoteService是有弊端的,由于他和开启它的Activity不再同一个进程当中,就不能采用之前NormalService那样的方式进行bind操作了,这样会导致程序崩溃。

RemoteService供外部调用注意事项

在供给其它应用程序调用的时候,其在AndroidManifest.xml文件中定义时应设置属性:

1
android:exported=true

表示可以供外部访问;如果该RemoteService在声明的时候进行了权限保护,即使用了以下属性:

1
2
3
4
<permission
android:name="com.rainmonth.permission.AIDL_SERVICE"
android:protectionLevel="normal" />

那么调用该Service的外部应用使用该Service的时候必须先声明使用该Service自定义的权限,即:

1
2
<uses-permission 	     	   
android:name="com.rainmonth.permission.AIDL_SERVICE" />

否则提示not allowed to bind to service intent注意,在其他应用调用该AIDL中定义的方法时,要保持包名及类名的一致;

Android 5.0以后的Service使用

Android 5.0以后要求声明使用Service的Intent必须是显式调用,如:

1
Intent intent = new Intent(context, XxxService.class);

而不能采用隐式调用,如:

1
Intent intent = new Intent("ActionString);

Android Studio中编写AIDL文件注意问题

编写完成后记得要先将项目build一下,否则在使用时会找不到AIDL中定义的方法;

IntentService的使用

具体的逻辑处理主要发生在IntentService的回调方法onHandleIntent中,其实用可参见文章末尾的Demo,根据提供的示例可以发现IntentService应该是批量下载的绝佳实现方式了;

Service的开机启动

  1. AndroidManifest.xml文件中获取开机启动的权限;

  2. 定义一个BroadcastReceiver来监听boot_completed这个Action,如:

    1
    2
    3
    <intent-filter>
    <action android:name="android.intent.action.BOOT_COMPLETED"/>
    </intent-filter>

    这样当手机启动后,就会出发该广播了;

  3. 在定义的BroadcastReceiver的onReceiver()方法中调用startService()方法即可;

Service在被杀死后重新启动

  1. 在Service的onDestroy()方法中进行重新启动该Service(简单的调用startService()方法)即可(前提是Service被杀死后会走onDestroy())这个生命周期函数;
  2. 在Service的onDestroy()方法中发送广播重新开启自己,然后自己接受这个广播;
  3. 开启两个服务A和B,A监听B的广播来启动B,B监听A的广播来启动A;
  4. 双进程守护,参考资料:

降低服务因系统资源紧张而被杀死的方法:

  • 提高优先级(修改onStartCommand()方法的返回值);
  • 设置为前台Service(本质还是提高优先级);

IntentService

Service ANR相关

Service和AIDL

Service Demo