博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
死磕Android_Service绑定流程分析(二)
阅读量:6698 次
发布时间:2019-06-25

本文共 10905 字,大约阅读时间需要 36 分钟。

通过startService只能是把Service给启动起来,但是我们无法与其建立联系.通过bindService方式启动Service的话,不仅能启动Service,还能与其建立连接,相互调用比较方便.今天我们来理一理bindService其中的原理.

建议先看一下如下两篇文章,我按照顺序来写的,循序渐进.可能有些东西前面已经介绍了,后面就不再赘述,感谢理解.

1. 使用方式

简单回顾一下使用方式,就是在Activity里面调一下bindService方法,需要传入一个ServiceConnection.

Intent intentService = new Intent(this,MyService.class);bindService(intentService,mServiceConnection,BIND_AUTO_CREATE);复制代码

2. 源码分析

和startService类似,bindService也是调用的ContextImpl里面的方法.

@Overridepublic boolean bindService(Intent service, ServiceConnection conn,        int flags) {    ...    return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), getUser());}private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler        handler, UserHandle user) {        IServiceConnection sd;        //注意  ServiceConnection不能传入null,否则直接抛个异常给你    if (conn == null) {        throw new IllegalArgumentException("connection is null");    }    if (mPackageInfo != null) {        //会走到这里来  生成一个IServiceConnection,它其实是一个ServiceDispatcher.InnerConnection,用来与Service建立连接的,与Service建立连接时可能会需要远程调用,那么ServiceConnection是不得行的.就需要ServiceDispatcher.InnerConnection来远程调用,然后再通知ServiceConnection.        sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);    } else {        throw new RuntimeException("Not supported in system context");    }        validateServiceIntent(service);    try {        //获取当前Activity的token        IBinder token = getActivityToken();        if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null                && mPackageInfo.getApplicationInfo().targetSdkVersion                < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {            flags |= BIND_WAIVE_PRIORITY;        }        service.prepareToLeaveProcess(this);                //好巧  又是我们所熟悉的AMS bindService也需要AMS参与        int res = ActivityManager.getService().bindService(            mMainThread.getApplicationThread(), getActivityToken(), service,            service.resolveTypeIfNeeded(getContentResolver()),            sd, flags, getOpPackageName(), user.getIdentifier());        if (res < 0) {            throw new SecurityException(                    "Not allowed to bind to service " + service);        }        return res != 0;    } catch (RemoteException e) {        throw e.rethrowFromSystemServer();    }}复制代码

bindService是ContextImpl里面的方法,最终会调用到bindServiceCommon方法.bindServiceCommon方法首先将ServiceConnection转换成了一个ServiceDispatcher.InnerConnection(也就是上面的IServiceConnection sd).我们知道,绑定Service可能是跨进程的,所以需要跨进程通信,这里使用的是Binder方式.而这里的IServiceConnection其实是一个ServiceDispatcher.InnerConnection,而这里的ServiceDispatcher.InnerConnection是拿来跨进程通信的,因为ServiceConnection不能直接进行跨进程通信,所以需要ServiceDispatcher.InnerConnection来充当Binder的角色.然后ServiceDispatcher是连接InnerConnection和ServiceConnection的,InnerConnection是ServiceDispatcher的一个内部类.

上面的mPackageInfo是LoadedApk,跟进去看看getServiceDispatcher方法

public final IServiceConnection getServiceDispatcher(ServiceConnection c,        Context context, Handler handler, int flags) {    synchronized (mServices) {        //ServiceDispatcher是LoadedApk的内部类        LoadedApk.ServiceDispatcher sd = null;                //mServices是一个map,定义是private final ArrayMap
> mServices = new ArrayMap<>(); //它里面存放的是当前Activity与ServiceConnection和ServiceDispatcher的映射关系 //如果之前有创建好的ServiceDispatcher,那么直接拿出来用,没有则创建一个 ArrayMap
map = mServices.get(context); if (map != null) { sd = map.get(c); } if (sd == null) { //创建一个ServiceDispatcher sd = new ServiceDispatcher(c, context, handler, flags); if (map == null) { map = new ArrayMap<>(); //放入mServices进行缓存起来 mServices.put(context, map); } map.put(c, sd); } else { sd.validate(context, handler); } //获取ServiceDispatcher中的InnerConnection对象 return sd.getIServiceConnection(); }}复制代码

首先ServiceDispatcher是LoadedApk的内部类.系统将当前Activity与ServiceConnection和ServiceDispatcher的映射关系缓存起来了的,有需要的时候直接拿出来.当前第一次绑定的时候,肯定缓存起来没有,所以需要创建一个ServiceDispatcher.创建ServiceDispatcher的时候就会创建InnerConnection. 相当于ServiceDispatcher内部有ServiceConnection和InnerConnection,那么后面需要调用ServiceConnection里面的方法就比较方便了.

接着我们继续看AMS的绑定Service的过程,是调用的是AMS的bindService方法

public int bindService(IApplicationThread caller, IBinder token, Intent service,        String resolvedType, IServiceConnection connection, int flags, String callingPackage,        int userId) throws TransactionTooLargeException {    return mServices.bindServiceLocked(caller, token, service,            resolvedType, connection, flags, callingPackage, userId);}复制代码

mServices是ActiveServices,在startService源码分析中提到过,ActiveServices类是辅助AMS管理Service的,包括Service的启动、绑定和停止等.

上面是调用了ActiveServices的bindServiceLocked方法,bindServiceLocked再调 用bringUpServiceLocked,bringUpServiceLocked又会调用realStartServiceLocked方法, realStartServiceLocked方法的执行逻辑和中的逻辑类似,最终都是通过 ApplicationThread来完成Service实例的创建并执行其onCreate方法,这里不再重复讲解了.

但是需要注意的是在realStartServiceLocked方法里面,当我们是bindService绑定Service的时候,需要关注一个东西

private final void realStartServiceLocked(ServiceRecord r,        ProcessRecord app, boolean execInFg) throws RemoteException {    boolean created = false;        //启动Service    app.thread.scheduleCreateService(r, r.serviceInfo,            mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),            app.repProcState);    created = true;        //深入    requestServiceBindingsLocked(r, execInFg);    ......}private final void requestServiceBindingsLocked(ServiceRecord r, boolean execInFg)        throws TransactionTooLargeException {    //深入    requestServiceBindingLocked(r, ibr, execInFg, false);}private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i,        boolean execInFg, boolean rebind) throws TransactionTooLargeException {        //远程调用,执行ActivityThread中的scheduleBindService方法    r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind,            r.app.repProcState);    return true;}复制代码

在realStartServiceLocked方法里面调用了requestServiceBindingsLocked方法,requestServiceBindingsLocked方法调用了另一个requestServiceBindingLocked方法,然后远程调用ActivityThread的scheduleBindService方法.scheduleBindService方法从名字看,终于要开始执行绑定了

public final void scheduleBindService(IBinder token, Intent intent,        boolean rebind, int processState) {    updateProcessState(processState, false);    BindServiceData s = new BindServiceData();    //注意  这里将Activity的token传过来了    s.token = token;    s.intent = intent;    s.rebind = rebind;    if (DEBUG_SERVICE)        Slog.v(TAG, "scheduleBindService token=" + token + " intent=" + intent + " uid="                + Binder.getCallingUid() + " pid=" + Binder.getCallingPid());    sendMessage(H.BIND_SERVICE, s);}复制代码

然后又来到了我们熟悉的H这个Handler,在H中BIND_SERVICE消息调用的是ActivityThread中的handleBindService()这个方法

private void handleBindService(BindServiceData data) {    //在handleCreateService方法里面将token和Service映射存入了mServices里面    //mServices定义是final ArrayMap
mServices = new ArrayMap<>(); //所以这里可以根据token取出Service Service s = mServices.get(data.token); if (s != null) { data.intent.setExtrasClassLoader(s.getClassLoader()); data.intent.prepareToEnterProcess(); if (!data.rebind) { //如果不是重新绑定 //注意,这里调用了我们熟悉的Service的onBind方法,,,,就是在这里调用的哦,,,所以这里是UI线程哈,记住了 IBinder binder = s.onBind(data.intent); //调用AMS的publishService方法 ActivityManager.getService().publishService( data.token, data.intent, binder); } else { s.onRebind(data.intent); ActivityManager.getService().serviceDoneExecuting( data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0); } }}复制代码

在handleCreateService方法里面将token和Service映射存入了mServices里面,mServices定义是final ArrayMap<IBinder, Service> mServices = new ArrayMap<>();.所以那里可以根据token取出Service,然后紧接着调用了我们熟悉的Service的onBind方法,这里可以得出onBind方法是在UI线程执行的.还可以得出,重新绑定Service时,onBind方法只会调用一次,除非Service之前被终止了.onBind方法执行之后,说明Service已经成功连接了.

然后又来到了AMS,这次是AMS的publishService方法.

public void publishService(IBinder token, Intent intent, IBinder service) {    mServices.publishServiceLocked((ServiceRecord)token, intent, service);}//ActiveServices.java => publishServiceLockedvoid publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {    try {        if (r != null) {            for (int conni=r.connections.size()-1; conni>=0; conni--) {                ArrayList
clist = r.connections.valueAt(conni); for (int i=0; i

c.conn的类型是ServiceDispatcher.InnerConnection,service就是Service 的onBind方法返回的Binder对象.来看一下ServiceDispatcher.InnerConnection的connected方法

private static class InnerConnection extends IServiceConnection.Stub {    final WeakReference
mDispatcher; InnerConnection(LoadedApk.ServiceDispatcher sd) { mDispatcher = new WeakReference
(sd); } public void connected(ComponentName name, IBinder service, boolean dead) throws RemoteException { LoadedApk.ServiceDispatcher sd = mDispatcher.get(); if (sd != null) { sd.connected(name, service, dead); } }}复制代码

InnerConnection的构造方法里面就传入了ServiceDispatcher,所以可以很轻松拿到ServiceDispatcher,拿到ServiceDispatcher调用其connected方法

public void connected(ComponentName name, IBinder service, boolean dead) {    //mActivityThread其实就是ActivityThread中名为H的Handler    if (mActivityThread != null) {        //主线程中的Handler调用post  说明RunConnection是运行在主线程中        mActivityThread.post(new RunConnection(name, service, 0, dead));    } else {        doConnected(name, service, dead);    }}private final class RunConnection implements Runnable {    RunConnection(ComponentName name, IBinder service, int command, boolean dead) {        mName = name;        mService = service;        mCommand = command;        mDead = dead;    }    public void run() {        if (mCommand == 0) {            //深入            doConnected(mName, mService, mDead);        } else if (mCommand == 1) {            doDeath(mName, mService);        }    }    final ComponentName mName;    final IBinder mService;    final int mCommand;    final boolean mDead;}//ServiceDispatcher => doConnectedpublic void doConnected(ComponentName name, IBinder service, boolean dead) {    ...    //mConnection是ServiceDispatcher中的ServiceConnection,初始化ServiceDispatcher的时候就初始化了ServiceConnection    mConnection.onServiceConnected(name, service);    ...}复制代码

通过ActivityThread中的Handler#post执行一个doConnected方法,而doConnected方法里面就是通过我们熟悉的ServiceConnection对象进行了onServiceConnected方法的回调.

可以看到ServiceDispatcher做了一个转接.当Service连接上之后,通过InnerConnection去远程调用ServiceDispatcher中的ServiceConnection中的onServiceConnected方法,完成绑定成功消息的通知.让客户端知道Service已绑定.

bindService也就分析完成了,其他比如 Service的停止过程和解除绑定的过程,系统的执行过程是类似的,这里留给大家自己去分析咯.

转载于:https://juejin.im/post/5d050c106fb9a07ecb0ba66f

你可能感兴趣的文章
linux——查看系统日志错误并解决
查看>>
cuda+ffmpeg+opengl解码rtsp h264码流多路
查看>>
Android权限大全代码
查看>>
svn:previous operation has not finished
查看>>
PHP Socket 编程进阶指南
查看>>
PHP-CPP开发扩展(一)
查看>>
Git常用命令
查看>>
【html】使用img标签和背景图片之间的区别
查看>>
JDK源码阅读(一) ArrayList
查看>>
Quartz1.8.5例子(六)
查看>>
leetcode524
查看>>
leetcode806
查看>>
(29)odoo的可用小图标
查看>>
MVC ViewBag传值
查看>>
达成目标5步法则——雷达里奥/核聚
查看>>
CentOS虚拟机通过主机网络上网
查看>>
Redis架构设计
查看>>
nio编程
查看>>
【竞赛笔记】飞思卡尔智能车竞赛
查看>>
codevs 1291 火车线路
查看>>