Android面试题:bindService获取代理商是同步还是异步

作者 : 开心源码 本文共4958个字,预计阅读时间需要13分钟 发布时间: 2022-05-12 共166人阅读

ndroid中bindService是一个异步的过程,什么意思呢?使用bindService无非是想取得一个Binder服务的Proxy,但这个代理商获取到的时机并非由bindService发起端控制,而是由Service端来控制,也就是说bindService之后,APP端并不会立刻取得Proxy,而是要等待Service通知APP端,具体流程可简化如下:

  • APP端先通过bindService去AMS登记,说明自己需要绑定这样一个服务,并留下派送地址
  • APP回来,继续做其余事情,可以看做是非阻塞的
  • AMS通知Service端启动这个服务
  • Service启动,并通知AMS启动完毕
  • AMS跟住之前APP端留下的地址通知APP端,并将Proxy代理商传递给APP端

通过代码来看更直接

    void test(){        bindService(intent, new ServiceConnection() {            @Override            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {               iMyAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder);               Log.v(TAG, "onServiceConnected..." );            }           @Override            public void onServiceDisconnected(ComponentName componentName) {           }        }, Context.BIND_AUTO_CREATE);        Log.v(TAG, "end..." );    }

bindService的过程中,上面代码的Log应该是怎样样的呢?假如bindService是一个同步过程,那么Log应该如下:

TAG  onServiceConnected ...TAG  end ...

但是因为是个异步过程,真实的Log如下

TAG  end ...    TAG  onServiceConnected ...

也就是说bindService不会阻塞等待APP端获取Proxy,而是直接返回,这些都可以从源码取得支持,略过,直接去ActivityManagerNative去看

public int bindService(IApplicationThread caller, IBinder token,        Intent service, String resolvedType, IServiceConnection connection,        int flags, int userId) throws RemoteException {    Parcel data = Parcel.obtain();    Parcel reply = Parcel.obtain();    data.writeInterfaceToken(IActivityManager.descriptor);    data.writeStrongBinder(caller != null ? caller.asBinder() : null);    data.writeStrongBinder(token);    service.writeToParcel(data, 0);    data.writeString(resolvedType);    data.writeStrongBinder(connection.asBinder());    data.writeInt(flags);    data.writeInt(userId);    <!--阻塞等待-->    mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);    reply.readException();    int res = reply.readInt();    data.recycle();    reply.recycle();    return res;}

mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0)的确会让APP端调用线程阻塞,等待AMS执行BIND_SERVICE_TRANSACTION请求,不过AMS在执行这个请求的时候并非是唤醒Service才返回,它返回的时机更早,接着看ActivityManagerService,

public int bindService(IApplicationThread caller, IBinder token,        Intent service, String resolvedType,        IServiceConnection connection, int flags, int userId) {    ...    synchronized(this) {        return mServices.bindServiceLocked(caller, token, service, resolvedType,                connection, flags, userId);    }}

ActivityManagerService直接调用ActiveServices的函数bindServiceLocked,请求绑定Service,到这里APP端线程仍旧阻塞,等待AMS端返回,假定Service所处的进程已经启动但是Service没有启动,这时ActiveServices会进一步调用bindServiceLocked->realStartServiceLocked来启动Service,有趣的就在这里:

 private final void realStartServiceLocked(ServiceRecord r,            ProcessRecord app) throws RemoteException {        ...        <!--请求Service端启动Service-->            app.thread.scheduleCreateService(r, r.serviceInfo,                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo));        ...        <!--请求绑定Service-->        requestServiceBindingsLocked(r);

app.thread.scheduleCreateService也是一个Binder通信过程,他其实是请求ActivityThread中的ApplicationThread服务,当然这个时候AMS端也是阻塞的,

// 插入消息,等待主线程执行public final void scheduleCreateService(IBinder token,        ServiceInfo info, CompatibilityInfo compatInfo) {    CreateServiceData s = new CreateServiceData();    s.token = token;    s.info = info;    s.compatInfo = compatInfo;    <!--向Loop的MessagerQueue插入一条消息就返回-->    queueOrSendMessage(H.CREATE_SERVICE, s);}

不过,这个请求直接向Service端的ActivityThread线程中直接插入一个消息就返回了,而并未等到该请求执行,由于AMS使用的非常频繁,不可能老等待用户端完成少量任务,所以AMS端向用户端发送完命令就直接返回,这个时候其实Service还没有被创立,也就是这个请求只是完成了一半,onServiceConnected也并不会执行,onServiceConnected什么时候执行呢?app.thread.scheduleCreateService向APP端插入第一条消息,是用来创立Service的, requestServiceBindingsLocked其实就是第二条消息,用来解决绑定的

 private final boolean requestServiceBindingLocked(ServiceRecord r,            IntentBindRecord i, boolean rebind) {                ...           <!-- 第二个消息,请求解决绑定-->            r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind);

第二条消息是解决少量绑定需求,Android的Hanlder消息解决机制保证了第二条消息肯定是在第一条消息之后执行,

 public final void scheduleBindService(IBinder token, Intent intent,        boolean rebind) {    BindServiceData s = new BindServiceData();    s.token = token;    s.intent = intent;    s.rebind = rebind;    queueOrSendMessage(H.BIND_SERVICE, s);}      

以上两条消息插入后,AMS端被唤醒,进而重新唤醒之前阻塞的bindService端,而这个时候,Service并不肯定被创立,所以说这是个未知的异步过程,Service端解决第一条消息的时会创立Service,

 private void handleCreateService(CreateServiceData data) {    ...    LoadedApk packageInfo = getPackageInfoNoCheck(            data.info.applicationInfo, data.compatInfo);    Service service = null;    try {        java.lang.ClassLoader cl = packageInfo.getClassLoader();        service = (Service) cl.loadClass(data.info.name).newInstance();   ...

执行第二条消息的时候, 会向AMS请求publishService,其实就是告诉AMS,服务启动完毕,可以向之前请求APP端派发代理商了。

 private void handleBindService(BindServiceData data) {    Service s = mServices.get(data.token);    if (s != null) {       try {        data.intent.setExtrasClassLoader(s.getClassLoader());        try {            if (!data.rebind) {                IBinder binder = s.onBind(data.intent);                ActivityManagerNative.getDefault().publishService(                        data.token, data.intent, binder);            ...                       

AMS端收到publishService消息之后,才会向APP端发送通知,进而通过Binder回调APP端onServiceConnected函数,同时传递Proxy Binder服务代理商

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {    ...     try {    <!--通过binder 回到APP端的onServiceConnected-->         c.conn.connected(r.name, service);    } catch (Exception e) {

到这里,onServiceConnected才会被回调,不过,至于Service端那两条消息什么时候执行,谁也不能保证,也许由于特殊起因,那两条消息永远不被执行,那onServiceConnected也就不会被回调,但是这不会影响AMS与APP端解决其余问题,由于这些消息能否被执行已经不能阻塞他们两个了,简单流程如下:

image

bindService的异步流程

最后,其实startService也是异步。
喜欢的话请帮忙转发一下能让更多有需要的人看到吧,有些技术上的问题大家可以多讨论一下。

以上Android资料以及更多Android相关资料及面试经验可在QQ群里获取:936903570。有加群的朋友请记得备注上简书,谢谢

说明
1. 本站所有资源来源于用户上传和网络,如有侵权请邮件联系站长!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系管理员处理!
6. 本站资源售价只是摆设,本站源码仅提供给会员学习使用!
7. 如遇到加密压缩包,请使用360解压,如遇到无法解压的请联系管理员
开心源码网 » Android面试题:bindService获取代理商是同步还是异步

发表回复