死磕Android_Service绑定流程分析(二)

栏目: Android · 发布时间: 6年前

内容简介:通过startService只能是把Service给启动起来,但是我们无法与其建立联系.通过bindService方式启动Service的话,不仅能启动Service,还能与其建立连接,相互调用比较方便.今天我们来理一理bindService其中的原理.建议先看一下如下两篇文章,我按照顺序来写的,循序渐进.可能有些东西前面已经介绍了,后面就不再赘述,感谢理解.简单回顾一下使用方式,就是在Activity里面调一下bindService方法,需要传入一个ServiceConnection.

通过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里面的方法.

@Override
public 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<Context, ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher>> mServices
        = new ArrayMap<>();
        //它里面存放的是当前Activity与ServiceConnection和ServiceDispatcher的映射关系
        //如果之前有创建好的ServiceDispatcher,那么直接拿出来用,没有则创建一个
        ArrayMap<ServiceConnection, LoadedApk.ServiceDispatcher> 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方法的执行逻辑和 "死磕Android_Service启动流程分析" 中的逻辑类似,最终都是通过 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<IBinder, Service> 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 => publishServiceLocked
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
    try {
        if (r != null) {
            for (int conni=r.connections.size()-1; conni>=0; conni--) {
                ArrayList<ConnectionRecord> clist = r.connections.valueAt(conni);
                for (int i=0; i<clist.size(); i++) {
                    ConnectionRecord c = clist.get(i);
                    //核心代码
                    c.conn.connected(r.name, service, false);
                }
            }

            serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
        }
    } finally {
        Binder.restoreCallingIdentity(origId);
    }
}

复制代码

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

private static class InnerConnection extends IServiceConnection.Stub {
    final WeakReference<LoadedApk.ServiceDispatcher> mDispatcher;

    InnerConnection(LoadedApk.ServiceDispatcher sd) {
        mDispatcher = new WeakReference<LoadedApk.ServiceDispatcher>(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 => doConnected
public 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的停止过程和解除绑定的过程,系统的执行过程是类似的,这里留给大家自己去分析咯.


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Text Processing in Python

Text Processing in Python

David Mertz / Addison-Wesley Professional / 2003-6-12 / USD 54.99

Text Processing in Python describes techniques for manipulation of text using the Python programming language. At the broadest level, text processing is simply taking textual information and doing som......一起来看看 《Text Processing in Python》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具