Service使用场景解读

在之前的一篇文章《基于场景解读Android四大组件》中谈到Service是Android提供给开发者的一个组件,主要用于后台一些耗时任务的处理。其实Android系统中已经存在了很多这样在后台执行一些特定任务的系统级Service,比方说与我们开发中打交道最多的ActivityManager,WindowManager,PackageManager和InputManager等等。今天我们依然从具体使用场景来对Android中Service的具体功能进行分析。

Service生命周期

Service生命周期回调

从图中可以看出Service的生命周期会根据启动方式的不同有不同的生命周期回调。其实startService和bindService的区别就是该service是否可以和启动它的组件(比如activity)通信,因为bindService可以拿到Service的binder,binder就是用来实现IPC的嘛。下面我们具体分析下每个生命周期回调:

onCreate

该接口是在Service实例被创建时调用,这里的Service实例跟Activity实例不一样,我们知道Activity实例根据不同的启动模式可以有一个或者多个实例,但是,Service虽然也有两种启动方式,在整个系统中却只会有一个Service实例。为什么呢?换个角度看,这就好比PC端的C/S模式,使用一个服务端去处理多个客户端的请求,这里就对应一个Service去处理来自多个Activity的请求嘛,没必要搞多个,浪费资源,而且你会发现系统级Service其实也都只有一个实例。那么在onCreate里面我们可以做些什么呢?当然是初始化,比如创建数据缓存,线程池等等。Android系统给我们提供了一个IntentService,我们可以参考它的实现方式来做一些初始化操作,IntentService的onCreate源码如下:

@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);
}

这里为什么要创建新的线程或者线程池呢?因为Service默认是在主线程中执行的,所以我不建议你把一个后台任务放在Service中的主线程执行,因为那样就失去了Service存在的初衷,还不如直接放在Activity里面做,除非你想要提升App进程的优先级,防止App退到后台被杀掉。

onStart

该接口是在调用startService方法时调用的,我们的后台任务一般都会放在这里执行,你可以通过intent获取startService方法传递的参数,这里依然以IntentService为例看下它的实现方式:

@Override
public void onStart(@Nullable Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

onBind

该方法是在调用bindService方法时调用,如果使用bindService方式来启动服务的话,一般发生在Activity需要与Service进行通信的场景(比如说音乐播放器app里面就会用到),而Android的IPC主要是通过binder来实现的(也可以通过socket,在系统服务用的比较多),所以这里方法的返回值就需要一个binder实例。这里简单说下binder的实现机制(后续讲Broadcast的时候我们在具体分析Android的IPC机制具体实现),其实就是一套PC上的C/S模式,用户通过bindService接口获取到Service的代理,然后通过这个代理来跟Service通信。我们这里用一段代码来详细说明下:

public class MyService extends Service {
    Binder mService = new IMyAidlInterface.Stub() {

        @Override
        public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {

        }
    };

    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mService;
    }
}
// call in activity
bindService(new Intent(this, MyService.class), new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mServiceProxy = IMyAidlInterface.Stub.asInterface(service);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
}, 0);

这里的代理分两种:本地代理和远程代理,它们是按照service和activity是否在同一个进程中来区分的(这里仅以activity和service通信为例来说明,其他场景原理类似),如果是在同一进程,那么这个代理对象mServiceProxy其实就是mService对象。而当它们不在同一个进程中时,mServiceProxy和mService就属于不同进程空间中的对象,由于不同进程之间的数据不能直接访问,所以这个时候binder driver就来充当一个中间桥梁的作用,来完成参数和返回结果等数据的传递(其实也就是在Linux内核空间开辟了一段共享内存),从而实现通信的目的。当然为了方便开发者使用binder,Android对binder的使用进行了一定的封装,提供了一个AIDL。通过AIDL我们就只需关心service提供的功能接口,而不用去关心这些接口调用的具体细节。所以从这里也可以看出,对于一个好的产品,不管它的用户群是普通用户还是程序员,使用的便捷性都是一个很重要的指标。就好比现在市面上很多做SDK的,往往那些接口简单,文档清晰的SDK,用的人也会多一些。

onRebind

该方法是在多次调用bindService和unbindService时会调用到该接口。该方法使用场景不多,一般我们不会在这里面做一些事情,不过可能会有一些数据统计放在这里以观察用户的某一操作行为。

onUnbind

该方法是在调用unbindService方法时调用,一般发生在activity中需要断开与service的连接的场景。注意该接口有个返回值,默认为false。如果你想要在onRebind里面做一些事情的话,那么这里需要返回true。

onDestroy

该方法会在Service销毁时调用,一般在这里我们会释放一些在onCreate中进行初始化时所申请的资源,可以参考IntentService的实现方式:

@Override
public void onDestroy() {
    mServiceLooper.quit();
}

一般可以通过stopService或者unbindService方式来销毁不再需要的Service。而unbindService这种方式必须是没有通过startService启动Service的情况,否则不会销毁Service。

Service使用场景

为了满足开发者处理后台任务的需要,Android提供了Service这个组件,同时为了方便开发者使用Service,又封装了一个IntentService。当然,现在很多App在处理后台任务的时候并没有优先使用Service,而是自己实现了一套线程池机制或者使用Android提供的AsyncTask来执行后台任务,这里我们来分析下他们各自的优劣:

  • Service的优点是系统原生支持,使用方便;创建进程方便;可以提供给系统内其他App使用;优先级高,当App退到后台后不宜被杀死。缺点是由于启动Service涉及到多次IPC,运行效率不高,而且受限于系统接口,使用不够灵活。
  • 线程池的优点是运行效率高,配置和使用灵活。缺点是多进程实现不方便, 由于Android实现了一套进程托管机制,我们不能直接创建一个新的进程,而只能通过四大组件的形式创建新的进程。

基于以上分析,我们可以看出,一般普通的异步任务,比如网络请求,数据库或者文件相关操作,我们都会使用线程池的方式来做,因为这样使用的系统开销小,运行效率高,而且随着业务逻辑的复杂度增加,扩展性也更强。然而,对于一些特殊场景,比如进程保活,使用第三方SDK服务比如地图,IM等,就需要使用Service来实现,因为这些服务一般与App主进程隔离开,需要运行在新进程中以防止App主进程发生异常崩溃时,牵连第三方服务也挂掉。

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,831评论 25 707
  • 本文出自 Eddy Wiki ,转载请注明出处:http://eddy.wiki/interview-androi...
    eddy_wiki阅读 3,256评论 0 20
  • pastel intrusive seasoned
    Shaw233阅读 162评论 0 0
  • 我今天晚上我在画画,我的梦想是成为一个画家,妈妈说,这周带我我去一个有画画老师的地方,我喜欢各种动物,晚上我画了些...
    苏畅宝阅读 155评论 0 0
  • 前段时间在投资界风靡全球的一段话: 纽约时间比加州时间早三个小时,New York is 3 hours ahea...
    梓青彦阅读 375评论 0 3