bindService的流程,入口同样在ContextImpl中。

ContextImpl.java

public boolean bindService(Intent service, ServiceConnection conn, int flags) {warnIfCallingFromSystemProcess();return bindServiceCommon(service, conn, flags, mMainThread.getHandler(), Process.myUserHandle());
}private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler handler, UserHandle user) {IServiceConnection sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);validateServiceIntent(service);int res = ActivityManager.getService().bindService(mMainThread.getApplicationThread(), getActivityToken(), service, service.resolveTypeIfNeeded(getContentResolver()), sd, flags, getOpPackageName(), user.getIdentifier());return res != 0;
}

类似startService,bindService也是经由bindServiceCommon交由AMS处理。
要注意的是bind连接回调ServiceConnection是引用类型,因为bindService可能是跨进程的,需要先将其转换为bind接口IServiceConnection,具体实现为ServiceDispatcher的内部类InnerConnection,充当了Binder的角色。

ActivityManagerService.java

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

AMS中同样直接交给了ActiveService.bindServiceLocked处理。

这个方法的处理步骤如下:

  1. 从ServiceMap中查找ServiceRecord,并且在查的时进行了exported属性、权限等校验。
  2. 将Service调用方和被调用方的信息保存到AMS中;
  3. 创建连接信息;
  4. 将连接信息保存到mServiceConnections中;
  5. 如bind的flag包含BIND_AUTO_CREATE,则先尝试启动Service;
  6. 如此intent未被连接过,直接建立连接,回调onServiceConnected;
  7. Service未被bind过,需先调用onBind后,再回调onServiceConnected。

连接的过程分为2种场景:已经bind过 和 未被bind过,这2种场景如何区分呢?

s.app != null && b.intent.received,回到前面创建AppBindRecord的代码:

// 3.创建连接信息
AppBindRecord b = s.retrieveAppBindingLocked(service, callerApp);
ConnectionRecord c = new ConnectionRecord(b, activity, connection, flags, clientLabel, clientIntent);

具体看ServiceRecord的retrieveAppBindingLocked方法实现:

public AppBindRecord retrieveAppBindingLocked(Intent intent, ProcessRecord app) {Intent.FilterComparison filter = new Intent.FilterComparison(intent);// bindings是一个mapIntentBindRecord i = bindings.get(filter);if (i == null) {i = new IntentBindRecord(this, filter);bindings.put(filter, i);}AppBindRecord a = i.apps.get(app);if (a != null) {return a;}a = new AppBindRecord(this, i, app);i.apps.put(app, a);return a;
}

从这个方法中可以看出,如果fileter不同,即intent数据不同,将返回一个新的AppBindRecord对象。再去看Intent.FilterComparison equals()的实现,其比较的是Intent的数据。即,同一个app bind同一个Service,如果bind时传递的Intent数据一致,将共享同一个AppBindRecord。也就是说,bindServiceLocked中认为Service已经连接,需要满足2个条件:

  1. Service已启动
  2. 调用bindServce时传递的Intent没有被连接过,即intent数据不一样,会再次触发onBind。

这个设计给我们提供了一种新的思路:在一个Service中,可以根据需要为不同的启动参数,提供不同的binder服务,从而使Service内部逻辑更加清晰。

继续看bind的过程:

private final boolean requestServiceBindingLocked(ServiceRecord r, IntentBindRecord i, boolean execInFg, boolean rebind) throws TransactionTooLargeException {if ((!i.requested || rebind) && i.apps.size() > 0) {bumpServiceExecutingLocked(r, execInFg, "bind");r.app.forceProcessStateUpTo(ActivityManager.PROCESS_STATE_SERVICE);r.app.thread.scheduleBindService(r, i.intent.getIntent(), rebind, r.app.repProcState);if (!rebind) {i.requested = true;}i.hasBound = true;i.doRebind = false;}return true;
}

调用ActivityThread进行bind,这里不关注ActivityThread内部消息派发过程,直接看bind的实现。

ActivityThread.java

private void handleBindService(BindServiceData data) {Service s = mServices.get(data.token);data.intent.setExtrasClassLoader(s.getClassLoader());data.intent.prepareToEnterProcess();if (!data.rebind) {IBinder binder = s.onBind(data.intent);ActivityManager.getService().publishService(data.token, data.intent, binder);} else {s.onRebind(data.intent);ActivityManager.getService().serviceDoneExecuting(data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);}ensureJitEnabled();......
}

调用Service.onBind后,继续调用AMS.publishService发布Service。

public void publishService(IBinder token, Intent intent, IBinder service) {mServices.publishServiceLocked((ServiceRecord)token, intent, service);
}

继续看ActiveServices.publishServiceLocked。

void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {final long origId = Binder.clearCallingIdentity();Intent.FilterComparison filter = new Intent.FilterComparison(intent);IntentBindRecord b = r.bindings.get(filter);if (b != null && !b.received) {// 1.将IntentBindRecord.binder赋值为onBinder返回的Binder对象b.binder = service;b.requested = true;// 2.将IntentBindRecord.binder置为true,表示已调用onBindb.received = true;// 3.遍历此Service的所有连接记录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);if (!filter.equals(c.binding.intent.intent)) {continue;}// 找到匹配的intent进行连接c.conn.connected(r.name, service, false);}}}serviceDoneExecutingLocked(r, mDestroyingServices.contains(r), false);
}

publish过程主要做了3件事:

  1. 将IntentBindRecord.binder赋值为Service.onBinder返回的Binder对象;
  2. 将IntentBindRecord.binder置为true,表示已调用onBind;
    这一步骤后,其他client再通过相同的intent数据进行bind,将直接进行连接,不会再进行onBind的过程。
  3. 遍历此Service的所有连接记录,找到匹配的intent进行连接。
    接下看connect的具体过程:

在前面ContextImp.bindServiceCommon中已经知道,传递到AMS中的ServiceConnection是经过包装的IServiceConnectionBinder对象,所以connect的过程实际上是在启动方进程中进行的。此IServiceConnection是LoadedApk的静态内部类InnerConnection。

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。

static final class ServiceDispatcher {public void connected(ComponentName name, IBinder service, boolean dead) {if (mActivityThread != null) {mActivityThread.post(new RunConnection(name, service, 0, dead));} else {doConnected(name, service, dead);}}public void doConnected(ComponentName name, IBinder service, boolean dead) {ServiceDispatcher.ConnectionInfo old;ServiceDispatcher.ConnectionInfo info;synchronized (this) {// 已经unbind,不做处理if (mForgotten) {return;}// old不为null,表示此ServiceConnection进行了其他连接old = mActiveConnections.get(name);// 同一个Connection已进行过连接,不做处理if (old != null && old.binder == service) {return;}// IBinder对象不为null,表示建立新连接if (service != null) {info = new ConnectionInfo();info.binder = service;info.deathMonitor = new DeathMonitor(name, service);try {// linkToDeathservice.linkToDeath(info.deathMonitor, 0);// 保存连接信息mActiveConnections.put(name, info);} catch (RemoteException e) {mActiveConnections.remove(name);return;}} else {// IBinder对象为null,表示断开连接mActiveConnections.remove(name);}if (old != null) {old.binder.unlinkToDeath(old.deathMonitor, 0);}}// 先移除旧连接if (old != null) {mConnection.onServiceDisconnected(name);}if (dead) {mConnection.onBindingDied(name);}// 新连接,回调onServiceConnectedif (service != null) {mConnection.onServiceConnected(name, service);}}private final class DeathMonitor implements IBinder.DeathRecipient {DeathMonitor(ComponentName name, IBinder service) {mName = name;mService = service;}public void binderDied() {death(mName, mService);}final ComponentName mName;final IBinder mService;}public void doDeath(ComponentName name, IBinder service) {synchronized (this) {ConnectionInfo old = mActiveConnections.get(name);if (old == null || old.binder != service) {return;}// 移除ConnectionInfomActiveConnections.remove(name);// 调用unlinkToDeathold.binder.unlinkToDeath(old.deathMonitor, 0);}// 调用ServiceConnection.onServiceDisconnectedmConnection.onServiceDisconnected(name);}
}

ServiceDispatcher.connected继续调用doConnected,doConnected中做了以下几件事情:

  1. 判断是否为重复bind,如是则直接return;

  2. 如IBinder对象不为null,表示新建连接,保存连接信息,linkToDeath;为null,表示断开连接;
    linkToDeath的目的是在Service被异常kill后,移除连接信息,并且回调ServiceConnection.onServiceDisconnected。

  3. 如此ServiceConnection已进行了其他连接,则先断开原有连接;
    此场景出现在:
    1)unbindService,具体过程下面分析;
    2)用同一个ServiceConnection去bind同一个Service,onBind触发了多次的情况,即bind时intent参数有变化。

  4. 新连接建立完成,回调ServiceConnection.onServiceConnected

至此,一次bind连接已经完成,之后启动方就可以通过连接成功后返回的IBinder对象与Service进行交互了。

一次bind连接的完整流程如下:

bindService流程详解相关推荐

  1. 跨境电商三单对碰三单申报流程详解

    跨境电商三单对碰三单申报流程详解 概要:三单申报是指"电子订单.电子运单.支付凭证". 1.电子订单: 适合申报企业类型"电商企业.电商交易平台.电商境内代理企业&quo ...

  2. Android事件流程详解

    Android事件流程详解 网络上有不少博客讲述了android的事件分发机制和处理流程机制,但是看过千遍,总还是觉得有些迷迷糊糊,因此特地抽出一天事件来亲测下,向像我一样的广大入门程序员详细讲述an ...

  3. 基于spark mllib_Spark高级分析指南 | 机器学习和分析流程详解(下)

    - 点击上方"中国统计网"订阅我吧!- 我们在Spark高级分析指南 | 机器学习和分析流程详解(上)快速介绍了一下不同的高级分析应用和用力,从推荐到回归.但这只是实际高级分析过程 ...

  4. View的绘制-draw流程详解

    目录 作用 根据 measure 测量出的宽高,layout 布局的位置,渲染整个 View 树,将界面呈现出来. 具体分析 以下源码基于版本27 DecorView 的draw 流程 在<Vi ...

  5. 杂志订阅管理系统c++_电池管理系统BMS功能安全开发流程详解

    点击上面 "电动知家"可以订阅哦! BMS功能安全开发流程详解 BMS和ISO26262 - BMS & ISO26262简介 BMS即Battery Management ...

  6. View的绘制-layout流程详解

    目录 作用 根据 measure 测量出来的宽高,确定所有 View 的位置. 具体分析 View 本身的位置是通过它的四个点来控制的: 以下涉及到源码的部分都是版本27的,为方便理解观看,代码有所删 ...

  7. U-Boot启动流程详解

    参考:U-Boot顶层目录链接脚本文件(u-boot.lds)介绍 作者:一只青木呀 发布时间: 2020-10-23 13:52:23 网址:https://blog.csdn.net/weixin ...

  8. java isight zmf_isight集成catia和abaqus,nastran流程详解

    isight集成catia和abaqus,nastran流程详解 CAD软件中参数化建模,导入有限元软件中计算各个工况,isight根据计算结果调整模型参数,反复迭代计算的过程是尺寸优化的典型问题~ ...

  9. java处理请求的流程_Java Spring mvc请求处理流程详解

    Spring mvc请求处理流程详解 前言 spring mvc框架相信很多人都很熟悉了,关于这方面的资料也是一搜一大把.但是感觉讲的都不是很细致,让很多初学者都云里雾里的.本人也是这样,之前研究过, ...

最新文章

  1. Linux ROS与嵌入式的串口通信
  2. python趣味编程10例-Python趣味编程100题
  3. How to Increase the Memory Limit for 32-bit Applications in Windows 64-bit OS
  4. VTK:可视化算法之ColorIsosurface
  5. kylin如何支持flink_日均万亿条数据如何处理?爱奇艺实时计算平台这样做
  6. C++string容器应用举例
  7. javafx ui_调用以验证JavaFX UI的响应能力
  8. CCNA知识总结(一)
  9. python 获取当前目录_如何在Python中获取当前的工作目录?
  10. [HAOI2010]计数(组合数学)(数位DP)
  11. 利用maven的resources、filter和profile实现不同环境使用不同配置文件
  12. wordpress禁止恶意HTTP_USER_AGENT
  13. DevExpress ChartControl 实现多轴
  14. 桌面在计算机哪个文件,计算机中win7系统桌面文件在c盘哪个文件夹
  15. 64位win10专业版下载地址
  16. java计算机毕业设计高考填报信息系统源码+数据库+系统+lw文档+部署
  17. 【知识点】eval() 的用法
  18. 关于微信小程序的开发
  19. git reset, git status, git clean, .gitignore, git diff, git restore 的关系梳理
  20. 多传感器时频信号处理:多通道非平稳数据的分析工具(Matlab代码实现)

热门文章

  1. swift3 设置设备方向支持(supportedInterfaceOrientations)
  2. 庄小威超分辨storm_庄小威15岁进中科大少年班,中国天才却成美国院士,如今又获大奖...
  3. 没有女朋友的程序员 VS 有女朋友的程序员
  4. python实现一个加密的文字处理器
  5. 数据库原理及应用教程(第4版|微课版)陈志泊-第六章习题
  6. 关于MyBatis的Example类详解
  7. python:小乌龟turtle
  8. tp hasWhere
  9. 解决fatal: unable to access ‘https://github.com/NVIDIA/apex.git/‘: Recv failure: Connection was reset
  10. Android 9.0 rom定制化系列讲解导读