原作者:[在一颗大大大榕树下
原链接:[ https://www.jianshu.com/p/902aa6a8f604 ]
概念:Android四大组件之一,没有可视化界面,是一个运行于后台的服务程序。
目录
- Service和线程的区别和场景
- 管理Service的生命周期
- service和IntentService有什么区别
- 启动服务绑定服务先后次序问题
- 远程服务AIDL
- Binder
-
Service和线程的区别和场景
我们都知道,Service和线程呢,都是在后台运行的,那么google为什么要为android系统创造出Service这么一个组件呢?我们先来看下线程和Service的特点。
Thread
程序最小的执行单元,他是分配给CPU的基本单位
生命周期
- 新建(new Thread)
- 就绪 (线程已经启动,等待CPU资源)
- 运行(run已经获得资源)
- 死亡(执行完成或被杀死)
- 阻塞(因为某种原因线程让出cpu资源,就会进入阻塞状态)
这里可以看出来一个致命的问题
线程一但run了起来,就和Activity失去关联了。
即使Activity被干掉了,Thread依旧能快乐的执行下去。
Thread是无法控制的。
google:这问题很大。
OK,于是我们的Service就诞生了。
-
管理Service的生命周期
Service的生命周期
- onCreate
- onStart
- onDestroy
- onBind
- onUnBind
我们来看一下,Activity是如何管理Service的,他的生命周期又是如何变化的。
- 启动服务:startService
23:42:02.003 1173-1173/? I/MyService: onCreate
23:42:02.003 1173-1173/? I/MyService: onStartCommand
注意,多次调用startService()
方法,onCreate
只会调用一次,但是onStartCommand
会调用多次
- 停止服务:stopService
23:45:17.443 1292-1292/com.yirong.androidpractice I/MyService: onDestroy
注意,在调用bindService之后,就算调用stopService也无法停止服务,必须先解绑
- 绑定服务:bindService
bindService(intent,this, Context.BIND_AUTO_CREATE)
stopService(intent)
00:09:38.653 1361-1361/com.yirong.androidpractice I/MyService: onCreate
00:09:38.653 1361-1361/com.yirong.androidpractice I/MyService: onBind
划重点:bindService不调用onStart方法
- 解绑服务:unBindService
00:10:40.882 1411-1411/com.yirong.androidpractice I/MyService: onUnbind
00:10:40.882 1411-1411/com.yirong.androidpractice I/MyService: onDestroy
- service和IntentService有什么区别
首先得划个重点:Service是在主线程中运行的,所以不能做耗时操作,会报ANR异常
不能做耗时操作,就意味着无法替代Thread的功能,那这Service啥意义啊?
这就是IntentService和Service区别了。
打开源码,按照惯例看下脑袋这块儿的小灰字。
总结下,大概就这么点意思:
- IntentService是一个继承并处理异步请求的类
- 他的内部有一个工作线程来处理耗时操作
扒一段源码看看
@Override
public void onCreate() {
// TODO: It would be nice to have an option to hold a partial wakelock
// during processing, and to have a static startService(Context, Intent)
// method that would launch the service & hand off a wakelock.
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
一看就明白了,底层还是Thread写的,但是对Thread做了处理。
点开HandlerThread
,直接看重点:
public class HandlerThread extends Thread {
...
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();//为当前线程创建一个Looper对象,一个线程只能有一个Looper对象
synchronized (this) {
mLooper = Looper.myLooper();//获取当前线程的looper对象
notifyAll();//唤醒线程
}
Process.setThreadPriority(mPriority);
onLooperPrepared();//空方法,可重写,在队列循环之前执行
Looper.loop();//消息队列开始循环
mTid = -1;
}
...
}
thread.start()
代表着线程开启,随后通过mServiceLooper = thread.getLooper()
获取Looper,mServiceHandler = new ServiceHandler(mServiceLooper)
并将looper放到Handler的构造函数中。
我们都知道Handler,Looper都是属于Android消息机制中特有的类。
IntentService的本质是自动新建一个线程,并为之创建一个Looper(Android只有主线程自带Looper,其他线程不带Looper,必须自己创建)。并通过Looper向Handler传递消息,完成异步操作。
最后当Looper队列中的消息全部传达完之后,IntentService会自动销毁。
方便的不要不要的啊,把异步操作扔给IntentService,这位老哥把活干完向主线程报告了以后还会自杀,简直是根本不要操心,一条龙给你服务到位。
再看一眼onStart方法:
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);//一般override这个方法,并将
stopSelf(msg.arg1);
}
}
我们可以看到,IntentService是在onStart方法里向Handler发送消息的。
咱都清楚bindService
是不跑onStart
方法的。所以注意,IntentService必须使用startService
。若使用bindService
,onHandleIntent
方法就会失效,那就和普通Service没区别了。
- 启动服务绑定服务先后次序问题
无非分为两种情况:
先绑定服务后开启服务
此时就算Activity已经被销毁了,服务依旧会继续执行。先开启服务后绑定服务
此时解绑服务也不会使服务销毁,服务会继续运行,直到停止服务。
由此可见,启动服务的优先级一直是高于绑定服务的。
这个感兴趣可以用我的demo试下:https://github.com/CocoYuki/AndroidPractice/tree/master/servicetest
- 远程服务AIDL
好了,讲远程服务之前我们先看下序列化。
为什么突然就扯到序列化了呢?因为接下来要总结下远程服务,用远程服务传递序列化消息也是个很常见的场景。
Q1:什么是序列化?
表示将一个对象转换成可存储或可传输的状态。
Q2:做什么用?
为了数据的持久化和可传递化。简而言之,就是对象不好操作 ,我们就把他转成流。反序列化,就是将这个流程颠倒过来,将流转成对象。
看下两种序列化的区别:
- Parcelable
- google专门为安卓写的序列化接口
- 性能好,内存开销小,效率高,写起来复杂
- 缺点是各个机型可能有差异,parcelable使用会产生差异,所以通讯组件之间(AIDL,INTENT)的数据传递,可以使用Parcelable,写入存储设备推荐使用Serializable
- Serializable
- java自带的序列化接口
- 他其实是一个空接口,使用简单,是一个标识,会给类一个序列化UID
- 缺点,因为使用反射,所以性能差,内存开销大
Serializable没什么好说的,非常简单,是一个空接口。主要还是看下Parcelable,他咋用。
打开源码一看,哇塞这也太贴心了吧,加点小注释顺便学习下:
* <pre>
* public class MyParcelable implements Parcelable {
* private int mData;//参数
*
* public int describeContents() {//类描述,一般不管他
* return 0;
* }
*
* public void writeToParcel(Parcel out, int flags) {//序列化 :out写入参数,要注明参数类型 flag一般默认是0,1代表对象需要返回,不回收
* out.writeInt(mData);
* }
*
* public static final Parcelable.Creator<MyParcelable> CREATOR//反序列化操作
* = new Parcelable.Creator<MyParcelable>() {
* public MyParcelable createFromParcel(Parcel in) {
* return new MyParcelable(in);
* }
*
* public MyParcelable[] newArray(int size) {
* return new MyParcelable[size];
* }
* };
*
* private MyParcelable(Parcel in) {
* mData = in.readInt();
* }
* }</pre>
google官方也是贴心的小棉袄啊,直接把怎么用放在源码的注释里了。
但是笔者最近在学习Kotlin的啊,有更加high的办法。至于怎么high,可以看下我的kotlin小笔记,方法还是很简单的!Ctrl+f关键词 序列化(虽然文章很短,但是万一以后很长呢!)https://www.jianshu.com/p/9efe3f7a1771
OKOK,知道怎么用了,我们来看一下什么是远程服务。
他是一门接口定义语言,是 Android 提供的一种进程间通信 (IPC) 机制
其实就是用来跨进程通信的,一般都会有一个服务端提供一个服务,而让客户端去bind这个服务,而为了实现这个过程,必须使用aidl接口。