Service整理

前言

Service相关知识和注意点的记录及整理。


目录

目录结构

1. 基础知识

  • 定义:服务,是Android四大组件之一, 属于 计算型组件
  • 作用:提供 需在后台长期运行的服务

    如:复杂计算、音乐播放、下载等

  • 特点:无用户界面、在后台运行、生命周期长

2. 生命周期

官方说明图

注意

  • 若一个Service被startService()多次启动,onCreate()也只会调用一次,只有onStartCommand()可以多次调用,其他只能调用一次。
  • bindService()启动的服务当所有客户端都解绑后,系统会销毁服务。
  • 异常情况下结束Service的生命周期不走OnDestory方法,比如系统清理,或者ANR结束

PS:
服务在被系统杀死后该如何继续运行

onStartCommand()返回一个整数

  • START_NOT_STICKY: 不会重建服务。除非还存在未发送的Intent,当服务不再是必需,并且应用程序能够简单地重启那些未完成的工作时,这是避免服务运行的最安全的选项。
  • START_STICKY: 重建服务 & 调用onStartCommand(),但不会再次送入上一个Intent,而是用null intent来调用onStartCommand(),除非还有启动服务的intent未发送完,那么这些剩下的Intent会继续发送(适用于媒体播放器类似服务,他们不执行命令,但需要一直运行并随时待命)
  • START_REDELIVER_INTENT: 重建服务 & 用上一个已送过的Intent调用onStartCommand()。任何未发送完的intent也都会依次送入。

3. 启动方式

讲到上述生命周期,便涉及到了两种启动方式:startService 和 bindService

3.1 第一种方式:通过startService启动Service

通过startService启动后,service会一直无限期运行下去,只有外部调用了stopService()或stopSelf()方法时,该Service才会停止运行并销毁。

3.2 第二种方式:通过bindService启动Service

  • bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
  • client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。
  • bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。当没有任何client与Service绑定时,Service会自行销毁

4. Service基本使用

服务端继承Service,重写onCreate().onStartCommand().onBind().onDestroy().四个方法。清单文件manifest注册Service

4.1 StartService方式

Service端:

public class PushService extends Service{

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

Activity端:

//启动Service
Intent intent = new Intent(this, PushService.class);
startService(intent);

//停止Service
Intent intent = new Intent(this, PushService.class);
stopService(intent);

4.2 bindService方式

想要Service支持bindService调用:

  • 在Service的onBind()方法中返回IBinder类型的实例。
  • onBInd()方法返回的IBinder的实例需要能够返回Service实例本身

最简单的方法就是在service中创建binder的内部类,加入类似getService()的方法返回Service,这样绑定的client就可以通过getService()方法获得Service实例了。

Service端:

public class PushService extends Service{

    private static final String TAG = "PushService";

    //client 可以通过Binder获取Service实例
    public class MyBinder extends Binder {
        public PushService getService() {
            return PushService.this;
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_NOT_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        //通过binder实现调用者client与Service之间的通信
        return new MyBinder();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    //getServiceName是Service暴露出去供client调用的公共方法
    public String getServiceName() {
        return TAG;
    }
}

Client端:
调用方式与start不同

  • 创建ServiceConnection类型实例,并重写onServiceConnected()方法和onServiceDisconnected()方法。
  • 当执行到onServiceConnected回调时,可通过IBinder实例得到Service实例对象,这样可实现client与Service的连接。
  • onServiceDisconnected回调被执行时,表示client与Service断开连接,在此可以写一些断开连接后需要做的处理。
//创建ServiceConnection类型实例
private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            
        }
    };

//启动绑定
Intent intent = new Intent(this, TestTwoService.class);
intent.putExtra("name", "ABCD");
bindService(intent, conn, BIND_AUTO_CREATE);

//解绑
unbindService(conn);

PS:
保证Service不被杀死?

  1. onStartCommand方式中,返回START_STICKY
  2. 提高Service的优先级
  3. 提升Service进程的优先级
  4. 在onDestroy方法里重启Service
  5. 系统广播监听Service状态
  6. 将APK安装到/system/app,变身为系统级应用

5. Activity和Service通信方式

使用方式自行查阅

5.1 bind启动开放公共方法。

5.2 回调接口的方式

5.3 通过广播(推荐)

5.4 观察者模式


6. Service和IntentService区别

6.1 区别

IntentService包含了Service所有特性。
不同:IntentService可以处理耗时操作。Service是依赖于主线程的,所以不能直接处理耗时操作。而IntentService会将任务依次插入到任务队列中,并逐个发送给onHandleIntent()来处理。省去了手动开线程的麻烦,并且自动停止Service

6.2 IntentService使用场景

很明显在需要按顺序,并且在后台处理请求时,优先IntentService。

6.3 源码分析

@Override
public void onCreate() {
    super.onCreate();
    // HandlerThread继承自Thread,内部封装了 Looper
    // 通过实例化andlerThread新建线程并启动
    // 所以使用IntentService时不需要额外新建线程
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    // 获得工作线程的 Looper,并维护自己的工作队列
    mServiceLooper = thread.getLooper();

    //将上述获得Looper与新建的mServiceHandler进行绑定
    //新建的Handler是属于工作线程的。 ->> 分析1
    mServiceHandler = new ServiceHandler(mServiceLooper);
}

/**
 *  分析1 ServiceHandler绑定的是后台线程的Looper,而在Service停止时,Looper退出了,内存很快会被回收
 **/
private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
        super(looper);
    }

    // IntentService的handleMessage()把接收的消息交给onHandleIntent()处理
    @Override
    public void handleMessage(Message msg) {
        // onHandleIntent 方法在工作线程中执行,->> 分析2
        onHandleIntent((Intent)msg.obj);
        // 执行完调用 stopSelf() 结束服务。
        stopSelf(msg.arg1);
    }
}

/**
 *  分析2 
 *  onHandleIntent()是一个抽象方法,使用时需要重写的方法
 **/
@WorkerThread
protected abstract void onHandleIntent(Intent intent);

6.4 IntentService总结

  • 通过HandlerThread 单独开启1个工作线程:IntentService
  • 创建1个内部 Handler :ServiceHandler
  • 绑定 ServiceHandler 与 IntentService
  • 通过 onStartCommand() 传递服务intent 到ServiceHandler 、依次插入Intent到工作- 队列中 & 逐个发送给 onHandleIntent()
  • 通过onHandleIntent() 依次处理所有Intent对象所对应的任务

PS:

  1. 不适用bindService启动IntentService
  2. 在所有Intent被处理完后,系统自动关闭服务
  3. 复写onHandleIntent() & 在里面 根据Intent的不同进行不同线程操作
  4. IntentService优先级比普通线程高

结束

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

推荐阅读更多精彩内容

  • 前言:本文所写的是博主的个人见解,如有错误或者不恰当之处,欢迎私信博主,加以改正!原文链接,demo链接 Serv...
    PassersHowe阅读 1,408评论 0 5
  • 服务基本上分为两种形式 启动 当应用组件(如 Activity)通过调用 startService() 启动服务时...
    pifoo阅读 1,265评论 0 8
  • [文章内容来自Developers] Service是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。...
    岳小川阅读 863评论 0 7
  • 转载注明出处:http://www.jianshu.com/p/a1d3d9693e91 1. 简介 与前一篇An...
    王三的猫阿德阅读 1,906评论 1 9
  • Service是一种可以在后台执行耗时操作而没有用户界面的应用组件。它默认运行在主线程中,不可以直接进行耗时操作,...
    stevewang阅读 737评论 0 2