Service的生命周期

原作者:[在一颗大大大榕树下
原链接:[ https://www.jianshu.com/p/902aa6a8f604 ]

概念:Android四大组件之一,没有可视化界面,是一个运行于后台的服务程序。

目录

  • Service和线程的区别和场景
  • 管理Service的生命周期
  • service和IntentService有什么区别
  • 启动服务绑定服务先后次序问题
  • 远程服务AIDL
  • Binder
  1. Service和线程的区别和场景

我们都知道,Service和线程呢,都是在后台运行的,那么google为什么要为android系统创造出Service这么一个组件呢?我们先来看下线程和Service的特点。

Thread

程序最小的执行单元,他是分配给CPU的基本单位

生命周期

  1. 新建(new Thread)
  2. 就绪 (线程已经启动,等待CPU资源)
  3. 运行(run已经获得资源)
  4. 死亡(执行完成或被杀死)
  5. 阻塞(因为某种原因线程让出cpu资源,就会进入阻塞状态)

这里可以看出来一个致命的问题

线程一但run了起来,就和Activity失去关联了。
即使Activity被干掉了,Thread依旧能快乐的执行下去。

Thread是无法控制的。
google:这问题很大。

image

OK,于是我们的Service就诞生了。

  1. 管理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

  1. service和IntentService有什么区别

首先得划个重点:Service是在主线程中运行的,所以不能做耗时操作,会报ANR异常

image

不能做耗时操作,就意味着无法替代Thread的功能,那这Service啥意义啊?

这就是IntentServiceService区别了。

打开源码,按照惯例看下脑袋这块儿的小灰字。

总结下,大概就这么点意思:

  • 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。若使用bindServiceonHandleIntent方法就会失效,那就和普通Service没区别了。

  1. 启动服务绑定服务先后次序问题

无非分为两种情况:

  • 先绑定服务后开启服务
    此时就算Activity已经被销毁了,服务依旧会继续执行。

  • 先开启服务后绑定服务
    此时解绑服务也不会使服务销毁,服务会继续运行,直到停止服务。

由此可见,启动服务的优先级一直是高于绑定服务的。

这个感兴趣可以用我的demo试下:https://github.com/CocoYuki/AndroidPractice/tree/master/servicetest

  1. 远程服务AIDL

好了,讲远程服务之前我们先看下序列化。
为什么突然就扯到序列化了呢?因为接下来要总结下远程服务,用远程服务传递序列化消息也是个很常见的场景。

Q1:什么是序列化?

表示将一个对象转换成可存储或可传输的状态。

Q2:做什么用?

为了数据的持久化和可传递化。简而言之,就是对象不好操作 ,我们就把他转成流。反序列化,就是将这个流程颠倒过来,将流转成对象。

看下两种序列化的区别:

  • Parcelable
  1. google专门为安卓写的序列化接口
  2. 性能好,内存开销小,效率高,写起来复杂
  3. 缺点是各个机型可能有差异,parcelable使用会产生差异,所以通讯组件之间(AIDL,INTENT)的数据传递,可以使用Parcelable,写入存储设备推荐使用Serializable
  • Serializable
  1. java自带的序列化接口
  2. 他其实是一个空接口,使用简单,是一个标识,会给类一个序列化UID
  3. 缺点,因为使用反射,所以性能差,内存开销大

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&lt;MyParcelable&gt; CREATOR//反序列化操作
 *             = new Parcelable.Creator&lt;MyParcelable&gt;() {
 *         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接口。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343