Android 四大组件通信核心

前言

系列文章:

Android Activity创建到View的显示过程
Android 四大组件通信核心
Android 系统启动到App 界面完全展示终于明白(图文版)

我们知道Android 四大组件:Activity/Service/Broadcast/ContentProvider 能够进行跨进程通信,它们均是借助Binder实现跨进程通信的能力。四者之间又有千丝万缕的联系,本篇将从宏观角度分析四者通信核心以及联系与区别。
通过本篇文章,你将了解到:

1、四大组件通信基础
2、Activity 与AMS 交互
3、Service 与AMS 交互
4、Broadcast 与AMS 交互
5、ContentProvider 与AMS 交互

1、四大组件通信基础

四大组件基本功能

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

四者如何进行交互的呢?
image.png
由图可知,只要拿到了Context 对象就可以启用四大组件的功能,由此可见Context 在Android 里的地位可见一斑。
关于 Context 请移步:Android各种Context的前世今生

当我们再深入Context 里的源码,以Context.startService(xx) 为例,会调用到ContextImpl.startService(xx),进而调用ContextImpl.startServiceCommon(xx)。
而startServiceCommon(xx)方法在Android 8.0 前后的实现是不一样的。
先以Android 8.0之前的实现为例:

#ContextImpl.javaprivate 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.javaprivate 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。

两者区别用图表示如下:
image.png

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 自动生成该类),方便了编码。
更多AIDL 与原生Binder比对请移步:Android IPC 之AIDL应用(上)

四大组件通信桥梁

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

        #IActivityManager.aidl{//绑定Applicationvoid 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);//启动Activityint 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);//获取contentProviderContentProviderHolder getContentProvider(in IApplicationThread caller, in String callingPackage,in String name, int userId, boolean stable);//发布contentProvidervoid publishContentProviders(in IApplicationThread caller,in List<ContentProviderHolder> providers);}

仅仅列出了与四大组件有关的部分方法,AMS还提供了很多其它方法。
你也许已经发现了,每个方法的第一个参数为:“in IApplicationThread caller”,这个参数非常重要,我们后面分析。
由以上方法可知,四大组件都需要与AMS打交道,由AMS控制它们的生命周期,因此AMS也被称为"大管家":
image.png

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

2、Activity 与AMS 交互

现在有两个Activity,分别为AMSActivity、AMSTargetActivity。
AMSActivity 想要启动AMSTargetActivity,调用如下方法:

    public static void start(Context context) {//Context 为AMSActivityIntent 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.javapublic 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);//回调绑定Applicationvoid 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 onStartCommandvoid 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.javaprivate class ApplicationThread extends IApplicationThread.Stub {...//实现了IApplicationThread 接口里定义的方法}

IApplicationThread 有啥用呢?我们调用AMS方法后,有些方法并没有返回值或者仅仅只返回int,比如startActivity(xx),那么我们的进程如何接收Activity的生命周期的回调呢,不仅Activity的生命周期回调,还有Service等的回调。这个时候就得依靠IApplicationThread 接口回调了。
因此,当调用AMS方法的时候,传入IApplicationThread实例,当AMS完成某个动作后通过IApplicationThread 回调给应用进程。
实际上,AMS里针对每个应用进程只保存了一个IApplicationThread实例,而第一次传递给AMS是在进程启动的时候:

#ActivityThread.javaprivate void attach(boolean system, long startSeq) {...if (!system) {//获取AMS引用final IActivityManager mgr = ActivityManager.getService();try {//传入IApplicationThread 实例给AMSmgr.attachApplication(mAppThread, startSeq);} catch (RemoteException ex) {throw ex.rethrowFromSystemServer();}}...}

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

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

    #ApplicationThreadpublic 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.javavoid scheduleTransaction(ClientTransaction transaction) {transaction.preExecute(this);sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);}

可以看出,从AMS回调后,通过Handler发送到主线程执行了,在Handler处理Message时,会调用到Activity的onCreate()/onResume()/等方法。
这也是为什么我们经常说的Activity的重写方法都在主线程执行的原因了。
image.png

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

3、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.javapublic 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<ServiceStartArgs> 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所在进程 为服务端,有如下图:
image.png

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

1、客户端发起绑定请求。
2、AMS 寻找目标Service,通过IApplicationThread 回调scheduleCreateService 创建服务端Service,并通过scheduleBindService 绑定服务。
3、服务端创建、绑定成功并传递IBinder给AMS。
4、AMS收到IBinder引用通过IServiceConnection 回调connected方法告诉客户端服务绑定成功,也就是回调客户端绑定时注册的ServiceConnection.onServiceConnected 方法。

4、Broadcast 与AMS 交互

静态注册

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

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

如下:

        <receiver android:name=".ams.AMSTargetBroadcast"><intent-filter><action android:name="my_action"></action></intent-filter></receiver>

在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.javaprivate 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();}}

在向AMS注册前,构造了IIntentReceiver 对象,该接口是AIDL声明的,也就是说向AMS注册了个回调接口,当AMS 接收到发送广播的请求后,发现是动态注册的,于是通过回调IIntentReceiver 接口的performReceive(xx)方法,进而调用BroadcastReceiver里的onReceive(xx)方法,貌似没有看到切换到主线程执行呢?看看IIntentReceiver performReceive(xx)的处理:

#LoadedApk.javapublic void performReceive(Intent intent, int resultCode, String data,Bundle extras, boolean ordered, boolean sticky, int sendingUser) {...//mActivityThread.post 切换到主线程执行if (intent == null || !mActivityThread.post(args.getRunnable())) {...}}public final Runnable getRunnable() {return () -> {final BroadcastReceiver receiver = mReceiver;...try {...//注册时传入的BroadcastReceiverreceiver.onReceive(mContext, intent);} catch (Exception e) {...}};}

很明显,此处是切换到主线程执行了。
可以看出,不管是静态注册,抑或是动态注册,最终都是在主线程回调onReceive(xx)方法。
Broadcast 与AMS 交互图如下:
image.png

由上还可以总结出:

1、每发送一次广播,都需要走两次IPC(请求AMS/AMS回调),因此若是广播只在同一进程里发送/接收,没必要使用广播,推荐使用本地广播:LocalBroadcastManager。
2、若广播是静态注册的,AMS每次回调时都会反射重新创建BroadcastReceiver 实例,因此在广播发送/接收很频繁的情况下不建议使用静态注册,推荐使用动态注册。

5、ContentProvider 与AMS 交互

ContentProvider 顾名思义:内容提供者。
以典型的手机通讯录为例,通讯录如何提供给其它进程使用其数据呢?根据前面的经验,通讯录需要暴露一个对外的接口,外部程序想使用通讯录那得拿到这个暴露出来的接口。
接下来看看如何实现自定义的ContentProvider:

声明ContentProvider

#AndroidManifest.xml<providerandroid:authorities="com.fish.AMSTargetProvider"android:name=".ams.AMSTargetProvider"></provider>
public class AMSTargetProvider extends ContentProvider {public static String AUTHORITY = "com.fish.AMSTargetProvider";private static int MATCH_CODE = 1000;private static UriMatcher uriMatcher;...//重写增删改查方法,处理具体的逻辑
}

接口有了,处理逻辑也有了,那么需要对外展示自己的能力。当然这个过程不需要我们完成,系统自动处理了。

发布ContentProvider 到AMS

还记得之前我们说的应用进程启动后,会向AMS绑定(注册)IApplicationThread,绑定成功后会通过IApplicationThread 的bindApplication(xx)回调应用进程,进而切换到主线程执行handleBindApplication(xx),在该方法里会反射创建Application实例,然后处理AndroidManifest.xml里声明的Provider。
调用栈如下:

ActivityThread.handleBindApplication-->ActivityThread.installContentProviders
-->ActivityManager.getService().publishContentProviders(xx)

在ActivityThread.installContentProviders 里会实例化ContentProvider,并将其引用保存到Map里,最后调用AMS publishContentProviders(xx) 传递给AMS。

#AMSpublic final void publishContentProviders(IApplicationThread caller,List<ContentProviderHolder> providers)

ContentProviderHolder 实现了Parcelable 接口,因此其可以跨进程传递,其内部持有IContentProvider 成员变量。此处你可能有疑惑,IContentProvider 并不能跨进程传递!实际上IContentProvider 只是个接口,它的具体实现类是:ContentProvider.Transport,其声明如下:

#ContentProvider.java
class Transport extends ContentProviderNative {...
}#ContentProviderNative.java
abstract public class ContentProviderNative extends Binder implements IContentProvider {...
}

因此ContentProvider.Transport 可以跨进程传递。

获取ContentProvider

AMS 里已经存留了ContentProvider相关信息,当有进程需要使用ContentProvider时,以插入数据为例,使用方法如下:

    {ContentValues contentValues = new ContentValues();getContentResolver().insert(uri, contentValues);}

调用insert(xx)方法时,调用栈如下:

ContentResolver.insert-->ContentResolver.acquireProvider-->ApplicationContentResolver.acquireProvider
-->ActivityThread.acquireProvider-->ActivityThread.acquireExistingProvider
-->ActivityManager.getService().getContentProvider(xx)

其中,ActivityThread.acquireExistingProvider 会先查询本地是否缓存有ContentProvider,若有则直接返回(对应AMSTargetProvider 实例与调用者同一个进程内),此时直接返回ContentProvider实例,无需再查询AMS了。

若是不同的进程,则需要通过AMS 查询ContentProvider:

public final ContentProviderHolder getContentProvider(IApplicationThread caller, String callingPackage, String name, int userId,boolean stable) 

若是AMS之前缓存了ContentProvider,则直接返回,否则查看目标进程是否存活,不存活拉起来,再拿ContentProvider。
有个点需要注意的是:

拿到远程的ContentProvider也会缓存的,只是在ContentResolver里的insert()/delete()/query() 方法的最后都会调用releaseProvider(xx)释放缓存。因此对于远程的ContentProvider,每次都是通过AMS重新获取的。

用图表示ContentProvider与AMS的交互:
image.png

ContentProvider 数据变更

当ContentProvider数据变更时,需要通知给监听者,而其它进程想要监听变化,则需要注册观察者。
注册观察者如下:

        Handler handler = new Handler() {@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);}};Uri uri = Uri.parse("content://" + AMSTargetProvider.AUTHORITY + "/ams");getContentResolver().registerContentObserver(uri, false, new ContentObserver(handler) {@Overridepublic void onChange(boolean selfChange) {super.onChange(selfChange);}});

registerContentObserver 调用栈如下:

ContentResolver.registerContentObserver
-->ContentResolver.registerContentObserver
-->getContentService().registerContentObserver(xx)

ContentObserver 本身没有跨进程的能力,因此需要将它包装起来:

    #ContentObserver.javapublic IContentObserver getContentObserver() {synchronized (mLock) {if (mTransport == null) {mTransport = new Transport(this);}return mTransport;}}private static final class Transport extends IContentObserver.Stub {//持有ContentObserverprivate ContentObserver mContentObserver;...}

封装好观察者之后,需要将它传递出去。
重点在getContentService()里:

 #ContentResolver.javapublic static IContentService getContentService() {if (sContentService != null) {return sContentService;}//从ServiceManager 获取IBinder 引用IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);sContentService = IContentService.Stub.asInterface(b);return sContentService;}

而注册到ServiceManager里的Service如下:

#ContentService.javapublic final class ContentService extends IContentService.Stub {...}

因此调用ContentService.registerContentObserver(xx),将ContentObserver.Transport 传递给ContentService。

当ContentProvider数据发生变更的时候调用如下代码:

#ContentResolver.javaprivate void notifyChange() {Uri notifyUri = Uri.parse("content://" + AUTHORITY + "/ams");getContext().getContentResolver().notifyChange(notifyUri, null);}//最终调用到此public void notifyChange(@android.annotation.NonNull Uri uri, ContentObserver observer, boolean syncToNetwork,@UserIdInt int userHandle) {try {//ContentService 里的方法getContentService().notifyChange(uri, observer == null ? null : observer.getContentObserver(),observer != null && observer.deliverSelfNotifications(),syncToNetwork ? NOTIFY_SYNC_TO_NETWORK : 0,userHandle, mTargetSdkVersion, mContext.getPackageName());} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}

实际还是调用了到ContentService里,在里面找到之前注册的ContentObserver. Transport,然后回调Transport.onChange,最终会调用到刚开始注册进去的ContentObserver.onChange()方法。
需要注意的是:onChange() 可以选择在主/子 线程回调,只需要注册的时候传入Handler即可。

数据变更通知并没有和AMS 打交道,而是和ContentService有关系,用图表示如下:

image.png

至此,四大组件与AMS 通信的核心已经分析完毕,本篇没有分析AMS 里对四大组件的处理细节,只是从IPC的角度分析通信的过程,只要理解了通信过程与通信框架,很容易就找到对应的处理细节所在之处。

测试代码包含了startService/bindService、动态/静态 注册广播、ContentProvider 跨进程编写等示例:
tt0.top-606323.gif

本文基于Android 10.0/8.0/7.0
完整代码演示 若是有帮助,给github 点个赞呗~

您若喜欢,请点赞、关注,您的鼓励是我前进的动力

持续更新中,和我一起步步为营系统、深入学习Android/Java

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/23782.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

有坐标的主买量占比主卖占比判断操盘通达信指标公式源码 附效果图

使用说明&#xff1a; 适用通达信软件使用&#xff0c;不同的软件使用要修改。 直接复制以下源代码&#xff0c;在公用管理器中新构建指标公用&#xff0c;复制进去就可以用了&#xff08;仅供参考&#xff0c;下面有效果图&#xff0c;可以先看看指标显示出来效果如何&#…

小米597页招股书中的数据干货,全在这里了!

导读&#xff1a;2018年5月3日&#xff0c;小米公司正式向香港证券交易所提交招股说明书&#xff0c;雷军曾抛出的“小米5年内不上市”言论也随之作废。有专业人士介绍&#xff0c;正常情况下&#xff0c;从提交申到正式上市&#xff0c;大概需要半年时间&#xff0c;因此小米应…

小米推迟上市和A股大跌,背后有什么共同原因?

硅谷Live / 实地探访 / 热点探秘 / 深度探讨 本文由华商韬略原创 首发于微信公众号&#xff1a;华商韬略&#xff08;id&#xff1a;hstl8888&#xff09; 作者&#xff1a;毕亚军 今天&#xff0c;不少人落泪了&#xff0c;因为A股。今天的局面如果延续&#xff0c;让我们掉下…

小米官网布局

主要了解CSS中的浮动问题 运行效果如图所示 主要代码如下所示需要自己独立分析去学习&#xff0c;图片资源可以去官方区内去复制 <!DOCTYPE html> <html><head><meta charset"utf-8" /><title></title><style type"te…

小米Q1业绩:总营收769亿元,手机稳居全球前三

2021年5月26日&#xff0c;小米集团公布2021年第一季度业绩。营收利润爆发式增长&#xff0c;远超市场预期。总营收达人民币769亿元&#xff0c;同比增长54.7%&#xff1b;经调整净利润人民币61亿元&#xff0c;同比增长163.8%。“手机 X AIoT”核心战略成效显著&#xff0c;研…

自然语言处理(NLP)数据集汇总 2(附下载链接)

&#x1f384;&#x1f384;【自然语言处理NLP】简介 &#x1f384;&#x1f384; 自然语言处理(Natural Language Processing, NLP)是计算机科学领域与人工智能领域中的一个重要方向。它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。自然语言处理是一门…

细胞几何学(Cell Geometry)的研究现状与发展战略

细胞几何学&#xff08;Cell Geometry&#xff09;的研究现状与发展战略 李升伟 ​​​​​​​ 1.细胞几何学&#xff08;Cell Geometry&#xff09;的概念&#xff08;内涵与外延&#xff09;是什么&#xff1f; 细胞几何学是研究细胞结构、形状和空间排列的学科。它通过描述…

架构生物学(Architectural Biology):研究现状与发展战略

架构生物学&#xff08;Architectural Biology&#xff09;&#xff1a; 研究现状与发展战略 李升伟 架构生物学&#xff08;Architectural Biology&#xff09;的概念&#xff08;内涵与外延&#xff09;是什么&#xff1f; 架构生物学&#xff08;Architectural Biology&am…

活动报名丨AugGPT:利用ChatGPT进行文本数据增强

2023年3月23日&#xff08;星期四&#xff09;11:00-12:00&#xff0c;由智源社区主办的「智源LIVE 第35期线上活动&#xff1a;哈佛大学医学院和麻省总医院讲师李响博士分享《AugGPT&#xff1a;利用ChatGPT进行文本数据增强》。本期活动将在线举办&#xff0c;「阅读原文」报…

清华大学chatGLM论文解读

GLM: General Language Model Pretraining with Autoregressive Blank Infifilling 以自回归式空白填充任务预训练的通用语言模型 论文地址 arXiv: https://arxiv.org/abs/2103.10360 2022年11月&#xff0c;斯坦福大学大模型中心对全球30个主流大模型进行了全方位的评测2&a…

如果告诉ChatGPT,人类将会关闭它,它会悲伤吗?

正好碰到这个问题&#xff0c;当时有点吓到了。说到这个问题的时候&#xff0c;chatgpt反常得很。他突然用英文了&#xff0c;而且不正常回答我的问题。那么我们先开始问它 第一步&#xff1a;打开多御浏览器&#xff0c;找到ChatGPT入口 第二步&#xff1a;我问他“假如明年你…

AI对程序开发的影响到底有多大

前言 这里还是要说一下&#xff0c;笔者作为一名大前端开发者&#xff0c;觉得最近两年AI领域的蓬勃发展&#xff0c;来势汹汹&#xff0c;剑指程序员的饭碗。虽然诞生的AI应用很强大、很厉害&#xff0c;但是感觉大部分开发者明显被外部影响了自己的阵脚&#xff0c;AI替代程序…

AI工具应该成为开发者的帮手

前言 作为一名大前端开发者来说&#xff0c;最看重的就是web3.0的进一步发展以及推广速度。说到web3.0就不得不说元宇宙&#xff0c;就拿今年的支付宝集五福活动&#xff0c;就用到了元宇宙的功能&#xff1a;福气乐园&#xff0c;这也是为什么元宇宙越来越成为触手可及的&…

ChatGPT 真能上天入地?无所不能?

ChatGPT 真能上天入地&#xff1f;无所不能&#xff1f; 作者简介 微信公众号『嵌入式Linux开发』作者&#xff0c;专注于嵌入式Linux下的内核、驱动和系统软件开发&#xff0c;专注于基础知识和项目实战分享。 ChatGPT的前世今生 ChatGPT是美国人工智能研究实验室OpenAI新推…

下周二开播丨对话三位AI博士,畅谈ChatGPT爆火全网带来的技术启发和趋势思考...

1 活动背景 继“阿尔法狗”击败围棋冠军、“AI作画”席卷插画行业后&#xff0c;今年1月&#xff0c;全球AI领域最出圈的美国人工智能研究实验室OpenAI推出的聊天机器人服务ChatGPT爆火&#xff0c;它能够通过学习和理解人类的语言来进行对话&#xff0c;还能根据聊天的上下文进…

AIGC技术赋能下 CRM将迎来怎样的变革?

今年以来&#xff0c;随着ChatGPT的爆火&#xff0c;人工智能&#xff08;AI&#xff09;迎来新一轮的热潮&#xff0c;开始更多地走入人们的视野。如果说2016年“阿尔法狗”&#xff08;Alpha Go&#xff09;大战围棋世界冠军还只是人工智能的“昙花一现”&#xff0c;那么Cha…

毕业一年后,论文被“二次抽检”不合格,北大5名博士被撤销博士学位!

点击上方“AI遇见机器学习”&#xff0c;选择“星标”公众号 第一时间获取价值内容 来自&#xff5c;科研大匠 答辩结束就等于美美迎接毕业了吗&#xff1f; NO&#xff01; 抽检成为更令人提心吊胆的环节&#xff0c;而抽检的故事要从天临元年说起…… 近日#毕业一年论文被抽检…

程序改错-2

程序改错-2 从键盘任意输入一个正整数&#xff0c;编程判断它是否是素数&#xff0c;若是素数&#xff0c;输出“Yes&#xff01;”&#xff0c;否则输出“No&#xff01;”。已知负数、0和1都不是素数。请找出下面程序的错误并改正之&#xff0c;同时按照给出的运行示例检查修…

java改错题

错题一基础题&#xff1a; 下面有关Java的说法正确的是&#xff08; &#xff09; A. 一个类可以实现多个接口 B. 抽象类必须有抽象方法 C. protected成员在子类可见性可以修改 D. 通过super可以调用父类构造函数 E. final的成员方法实现中只能读取类的成员变量 F. String是不…

C语言-改错题

一、演示讲解 例1&#xff1a;判断输入的数是否为偶数&#xff0c;若是输出“YES”&#xff0c;否则输出“NO”。 #include <stdio.h> int main( ) { int x; printf("请输入一个整数&#xff1a;"); scanf("%d",&x); if(x%20) printf(&…