Android 组件通信中有哪些不为人知的细节?

四大组件基本功能

先来看看四大组件的基本功能:

四者如何进行交互的呢?

由图可知,只要拿到了Context 对象就可以启用四大组件的功能,由此可见Context 在Android 里的地位可见一斑。

当我们再深入Context 里的源码,以Context.startService(xx) 为例,会调用到ContextImpl.startService(xx),进而调用ContextImpl.startServiceCommon(xx)。

而startServiceCommon(xx)方法在Android 8.0 前后的实现是不一样的。 先以Android 8.0之前的实现为例:

#ContextImpl.java

private ComponentName startServiceCommon(Intent service, UserHandle user) {

try {

validateServiceIntent(service);

service.prepareToLeaveProcess(this);

//调用的是ActivityManagerNative 里的方法

ComponentName cn = ActivityManagerNative.getDefault().startService(

mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(

getContentResolver()), getOpPackageName(), user.getIdentifier());

return cn;

} catch (RemoteException e) {

throw e.rethrowFromSystemServer();

}

}

ActivityManagerNative.getDefault() 从单例里取数据,该方法返回的是IActivityManager 接口,对外暴露的方法都声明在IActivityManager接口里。实际拿到的对象是ActivityManagerProxy 实例,而ActivityManagerProxy 实现了IActivityManager 接口,当调用ActivityManagerProxy 里的方法时,实际上是通过BinderProxy.transact(xx)进而调用Binder驱动进行跨进程通信,而接收该调用类,也就是实现了onTransact(xx)的类是ActivityManagerNative,而它是抽象类,其子类实现为:ActivityManagerService。

再说Android 8.0(含)之后的实现为例:

#ContextImpl.java

private ComponentName startServiceCommon(Intent service, boolean requireForeground,

UserHandle user) {

try {

validateServiceIntent(service);

service.prepareToLeaveProcess(this);

//调用的是ActivityManagerService 里的方法

ComponentName cn = ActivityManager.getService().startService(

mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(

getContentResolver()), requireForeground,

getOpPackageName(), user.getIdentifier());

return cn;

} catch (RemoteException e) {

throw e.rethrowFromSystemServer();

}

}

同样的,ActivityManager.getService() 也是从单例里获取数据,该方法返回的是IActivityManager 接口。实际拿到的对象是IActivityManager.Proxy,而IActivityManager.Proxy 实现了IActivityManager接口,当调用IActivityManager.Proxy 里的方法时,实际上是通过BinderProxy.transact(xx)进而调用Binder驱动进行跨进程通信,而接收该调用类,也就是实现了onTransact(xx)的类是IActivityManager.Stub,而它是抽象类,其子类实现为:ActivityManagerService。

两者区别用图表示如下:

ActivityManagerService 运行在system_server 进程里;ServiceManager运行在单独的进程里;应用程序运行在另一个进程里。因此,上图涉及到了三个进程。

1、ActivityManagerService 将IBinder引用注册到ServiceManager里,这是第1步,此过程是个IPC。

2、当应用进程想要获取ActivityManagerService 提供的功能时,需要向ServiceManager进行查询,这是第2步,此过程是个IPC(拿过一次后,缓存下来,下次再来拿就不用再IPC了)。

3、应用进程拿到了IBinder引用后,寻找转换为对应的接口IActivityManager,进而调用ActivityManagerService 提供的方法,这是第3步,此过程是个IPC。

可以看出,尽管Android 8.0前后获取IActivityManager 接口的方式不同,然而都要经历上面3个步骤。只是Android 8.0 之后使用AIDL 的自动生成代码功能替换了ActivityManagerProxy(IActivityManager.Proxy)、ActivityManagerNative(IActivityManager.Stub)、IActivityManager(AIDL 自动生成该类),方便了编码。

四大组件通信桥梁

上面以启动Service为例阐述了应用进程与ServiceManager、ActivityManagerService(AMS)的关系,实际最终的目的是为了调用AMS功能,简单看看AMS 提供了哪些功能:

#IActivityManager.aidl

{

//绑定Application

void attachApplication(in IApplicationThread app, long startSeq);

//启动服务

ComponentName startService(in IApplicationThread caller, in Intent service,

in String resolvedType, boolean requireForeground, in String callingPackage,

in String callingFeatureId, int userId);

//绑定服务

int bindService(in IApplicationThread caller, in IBinder token, in Intent service,

in String resolvedType, in IServiceConnection connection, int flags,

in String callingPackage, int userId);

//发布服务

void publishService(in IBinder token, in Intent intent, in IBinder service);

//启动Activity

int startActivity(in IApplicationThread caller, in String callingPackage, in Intent intent,

in String resolvedType, in IBinder resultTo, in String resultWho, int requestCode,

int flags, in ProfilerInfo profilerInfo, in Bundle options);

//发送广播

int broadcastIntent(in IApplicationThread caller, in Intent intent,

in String resolvedType, in IIntentReceiver resultTo, int resultCode,

in String resultData, in Bundle map, in String[] requiredPermissions,

int appOp, in Bundle options, boolean serialized, boolean sticky, int userId);

//获取contentProvider

ContentProviderHolder getContentProvider(in IApplicationThread caller, in String callingPackage,

in String name, int userId, boolean stable);

//发布contentProvider

void publishContentProviders(in IApplicationThread caller,

in List providers);

}

仅仅列出了与四大组件有关的部分方法,AMS还提供了很多其它方法。

你也许已经发现了,每个方法的第一个参数为:“in IApplicationThread caller”,这个参数非常重要,我们后面分析。

由以上方法可知,四大组件都需要与AMS打交道,由AMS控制它们的生命周期,因此AMS也被称为"大管家":

接下来就分别分析四大组件如何通过AMS 与自身(其它进程)通信的。

二、Activity 与AMS 交互

==============================================================================

现在有两个Activity,分别为AMSActivity、AMSTargetActivity。

AMSActivity 想要启动AMSTargetActivity,调用如下方法:

public static void start(Context context) {

//Context 为AMSActivity

Intent intent = new Intent(context, AMSTargetActivity.class);

context.startActivity(intent);

}

该方法在AMSActivity进程里的调用栈如下:

Activity.startActivity–>Activity.startActivityForResult

–>FragmentActivity.startActivityForResult

–>Instrumentation.execStartActivity

–>ActivityTaskManager.getService().startActivity(xx)

而ActivityTaskManager.getService() 前面已经分析过了,就是获取了AMS对外的接口: IActivityManager,拿到了引用后调用AMS startActivity(xx)方法:

#ActivityManagerService.java

public int startActivity(IApplicationThread caller, String callingPackage,

Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,

int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {

return mActivityTaskManager.startActivity(caller, callingPackage, intent, resolvedType,

resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);

}

重点关注两个参数:IApplicationThread caller和Intent intent。

Intent 我们很熟悉了,指明了要启动哪个Activity。

IApplicationThread 是个接口,里面定义了很多方法,列举部分如:

#IApplicationThread.java

{

//回调静态广播

void scheduleReceiver(in Intent intent, in ActivityInfo info,

in CompatibilityInfo compatInfo,

int resultCode, in String data, in Bundle extras, boolean sync,

int sendingUser, int processState);

@UnsupportedAppUsage

//回调创建服务

void scheduleCreateService(IBinder token, in ServiceInfo info,

in CompatibilityInfo compatInfo, int processState);

@UnsupportedAppUsage

//回调停止服务

void scheduleStopService(IBinder token);

//回调绑定Application

void bindApplication(in String packageName, in ApplicationInfo info,

in ProviderInfoList providerList, in ComponentName testName,

in ProfilerInfo profilerInfo, in Bundle testArguments,

IInstrumentationWatcher testWatcher, IUiAutomationConnection uiAutomationConnection,

int debugMode, boolean enableBinderTracking, boolean trackAllocation,

boolean restrictedBackupMode, boolean persistent, in Configuration config,

in CompatibilityInfo compatInfo, in Map services,

in Bundle coreSettings, in String buildSerial, in AutofillOptions autofillOptions,

in ContentCaptureOptions contentCaptureOptions, in long[] disabledCompatChanges);

//回调Service onStartCommand

void scheduleServiceArgs(IBinder token, in ParceledListSlice args);

//回调绑定服务

void scheduleBindService(IBinder token,

in Intent intent, boolean rebind, int processState);

@UnsupportedAppUsage

//回调解绑服务

void scheduleUnbindService(IBinder token,

in Intent intent);

//回调Activity 生命周期相关

void scheduleTransaction(in ClientTransaction transaction);

}

而ActivityThread.java 里实现了该接口(Android 8.0之后使用了AIDL 定义,此处以此为例分析)。

#ActivityThread.java

private class ApplicationThread extends IApplicationThread.Stub {

//实现了IApplicationThread 接口里定义的方法

}

IApplicationThread 有啥用呢?我们调用AMS方法后,有些方法并没有返回值或者仅仅只返回int,比如startActivity(xx),那么我们的进程如何接收Activity的生命周期的回调呢,不仅Activity的生命周期回调,还有Service等的回调。这个时候就得依靠IApplicationThread 接口回调了。

因此,当调用AMS方法的时候,传入IApplicationThread实例,当AMS完成某个动作后通过IApplicationThread 回调给应用进程。

实际上,AMS里针对每个应用进程只保存了一个IApplicationThread实例,而第一次传递给AMS是在进程启动的时候:

#ActivityThread.java

private void attach(boolean system, long startSeq) {

if (!system) {

//获取AMS引用

final IActivityManager mgr = ActivityManager.getService();

try {

//传入IApplicationThread 实例给AMS

mgr.attachApplication(mAppThread, startSeq);

} catch (RemoteException ex) {

throw ex.rethrowFromSystemServer();

}

}

}

此时AMS将IApplicationThread 实例保存下来,后续有应用进程调用AMS方法时,也会传入IApplicationThread 实例,AMS通过查找是否存在实例,存在就直接拿出来用。

当调用AMS startActivity()后,AMS 将检测目标Activity所在进程是否存活,若没有则启动进程,若有则将Activity移动到栈顶,而后处理之前被移出栈顶的Activity,做完了这些操作之后需要通知涉及到的各个应用进程,而这个通知是通过IApplicationThread 回调的。

假设 AMSActivity、AMSTargetActivity 处在同一个进程里,此时AMS会回调IApplicationThread里的scheduleTransaction()方法:

#ApplicationThread

public void scheduleTransaction(ClientTransaction transaction) throws RemoteException {

ActivityThread.this.scheduleTransaction(transaction);

}

public void handleMessage(Message msg) {

if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));

switch (msg.what) {

case EXECUTE_TRANSACTION:

final ClientTransaction transaction = (ClientTransaction) msg.obj;

//最终调用到ActivityThread里的handleLaunchActivity()等方法

//进而回调目标Activity 的onCreate/onPause/onResume等方法

mTransactionExecutor.execute(transaction);

break;

}

#ClientTransactionHandler.java

void scheduleTransaction(ClientTransaction transaction) {

transaction.preExecute(this);

sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);

}

可以看出,从AMS回调后,通过Handler发送到主线程执行了,在Handler处理Message时,会调用到Activity的onCreate()/onResume()/等方法。

这也是为什么我们经常说的Activity的重写方法都在主线程执行的原因了。

由上可知,当应用进程发起startActivity动作后,AMS 管理了目标Activity的生命周期,我们仅仅只需要在应用进程里重写目标Activity对应方法,并在里面处理相应的逻辑,即可实现一次Activity跳转的功能。

三、Service 与AMS 交互

=============================================================================

start 启动Service

Intent intent = new Intent(AMSActivity.this, AMSTargetService.class);

startService(intent);

调用栈如下:

ContextWrapper.startService–>

ContextImpl.startServiceCommon

–>ActivityManager.getService().startService(xx)

依然是先拿到AMS 接口,进而调用startService(xx):

#ContextImpl.java

public ComponentName startService(IApplicationThread caller, Intent service,

String resolvedType, boolean requireForeground, String callingPackage,

String callingFeatureId, int userId)

假设AMSActivity 与AMSTargetService 在同一进程。

AMS 收到startService请求后,寻找目标Service,若是Service还没有创建,最终则通过回调 IApplicationThread 方法scheduleCreateService(xx)、scheduleServiceArgs(xx),这些方法处理如下:

#ActivityThread.java

public final void scheduleCreateService(IBinder token,

ServiceInfo info, CompatibilityInfo compatInfo, int processState) {

//切换到主线程执行

sendMessage(H.CREATE_SERVICE, s);

}

public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) {

List list = args.getList();

for (int i = 0; i < list.size(); i++) {

//切换到主线程执行

sendMessage(H.SERVICE_ARGS, s);

}

}

public void handleMessage(Message msg) {

switch (msg.what) {

case CREATE_SERVICE:

//1、该方法里创建Service 实例

//2、回调Service onCreate 方法

handleCreateService((CreateServiceData)msg.obj);

break;

case SERVICE_ARGS:

//回调onStartCommand方法

handleServiceArgs((ServiceArgsData)msg.obj);

break;

}

}

与Activity 类似,Service 生命周期回调最终切换到主线程执行。

这也就是我们常说的为什么Service里不能执行耗时任务,否则容易发生ANR,因为回调方法在主线程执行的。

bind 启动Service

假设AMSActivity 与AMSTargetService 不在同一进程,以AMSActivity 所在进程为客户端,AMSTargetService所在进程 为服务端,有如下图:

前提是客户端、服务端已经绑定到AMS里了(传递IApplicationThread)。

1、客户端发起绑定请求。

2、AMS 寻找目标Service,通过IApplicationThread 回调scheduleCreateService 创建服务端Service,并通过scheduleBindService 绑定服务。

3、服务端创建、绑定成功并传递IBinder给AMS。

4、AMS收到IBinder引用通过IServiceConnection 回调connected方法告诉客户端服务绑定成功,也就是回调客户端绑定时注册的ServiceConnection.onServiceConnected 方法。

四、Broadcast 与AMS 交互

===============================================================================

静态注册

广播有两种注册方式:静态与动态。

先分析静态注册:

在AndroidManifest.xml 里声明广播,并指定接收的Action和处理该Action的类。

如下:

在AMSActivity里 发送广播,发送广播调用栈如下:

ContextWrapper.sendBroadcast–>ContextImpl.sendBroadcast

–>ActivityManager.getService().broadcastIntent(xx)

依然是先拿到AMS 接口,进而调用broadcastIntent(xx):

#ContextImpl.java

int broadcastIntent(in IApplicationThread caller, in Intent intent,

in String resolvedType, in IIntentReceiver resultTo, int resultCode,

in String resultData, in Bundle map, in String[] requiredPermissions,

int appOp, in Bundle options, boolean serialized, boolean sticky, int userId);

假设AMSActivity 与 AMSTargetBroadcast 在同一进程。

AMS 收到broadcastIntent 请求后,转发给BroadcastQueue 处理,若广播接收器是静态注册,则通过 回调IApplicationThread scheduleReceiver 方法。后面的处理过程与Activity/Service类似。

AMSActivity 所在进程收到scheduleReceiver 回调后,切换到主线程,然后反射构造AMSTargetBroadcast实例,并调用onReceive(xx)方法,而onReceive(xx) 方法里正是我们重写的接收广播后的处理逻辑。

因此,onReceive(xx) 方法也是在主线程执行的,不能执行耗时操作,否则容易发生ANR。

动态注册

动态注册如下:

IntentFilter intentFilter = new IntentFilter();

intentFilter.addAction(AMSTargetBroadcast.MY_ACTION);

registerReceiver(new AMSTargetBroadcast(), intentFilter);

registerReceiver(xx)最终调用到:

#ContextImpl.java

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,

IntentFilter filter, String broadcastPermission,

Handler scheduler, Context context, int flags) {

//AIDL 实现

IIntentReceiver rd = null;

if (receiver != null) {

if (mPackageInfo != null && context != null) {

if (scheduler == null) {

scheduler = mMainThread.getHandler();

}

//构造接收器回调

rd = mPackageInfo.getReceiverDispatcher(

receiver, context, scheduler,

mMainThread.getInstrumentation(), true);

} else {

}

}

try {

//向AMS 注册

final Intent intent = ActivityManager.getService().registerReceiver(

mMainThread.getApplicationThread(), mBasePackageName, rd, filter,

broadcastPermission, userId, flags);

return intent;

} catch (RemoteException e) {

throw e.rethrowFromSystemServer();

}

}

总结

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的Android开发中高级必知必会核心笔记,共计2968页PDF、58w字,囊括Android开发648个知识点,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。

虽然面试失败了,但我也不会放弃入职字节跳动的决心的!建议大家面试之前都要有充分的准备,顺顺利利的拿到自己心仪的offer。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

tent = ActivityManager.getService().registerReceiver(

mMainThread.getApplicationThread(), mBasePackageName, rd, filter,

broadcastPermission, userId, flags);

return intent;

} catch (RemoteException e) {

throw e.rethrowFromSystemServer();

}

}

总结

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的Android开发中高级必知必会核心笔记,共计2968页PDF、58w字,囊括Android开发648个知识点,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。

[外链图片转存中…(img-baY5d5jd-1714327438806)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

2021年虽然路途坎坷,都在说Android要没落,但是,不要慌,做自己的计划,学自己的习,竞争无处不在,每个行业都是如此。相信自己,没有做不到的,只有想不到的。

虽然面试失败了,但我也不会放弃入职字节跳动的决心的!建议大家面试之前都要有充分的准备,顺顺利利的拿到自己心仪的offer。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 4
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值