范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文

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

  作者:fishforest前言
  我们知道Android 四大组件:Activity/Service/Broadcast/ContentProvider 能够进行跨进程通信,它们均是借助Binder实现跨进程通信的能力。四者之间又有千丝万缕的联系,本篇将从宏观角度分析四者通信核心以及联系与区别。
  通过本篇文章,你将了解到:
  1、四大组件通信基础
  2、Activity 与AMS 交互
  3、Service 与AMS 交互
  4、Broadcast 与AMS 交互
  5、ContentProvider 与AMS 交互一、四大组件通信基础
  四大组件基本功能
  先来看看四大组件的基本功能:
  四者如何进行交互的呢?
  由图可知,只要拿到了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 启动ServiceIntent 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();     } }
  在向AMS注册前,构造了 IIntentReceiver    对象,该接口是AIDL声明的,也就是说向AMS注册了个回调接口,当AMS 接收到发送广播的请求后,发现是动态注册的,于是通过回调IIntentReceiver    接口的performReceive(xx)   方法,进而调用BroadcastReceiver   里的onReceive(xx)   方法,貌似没有看到切换到主线程执行呢?看看IIntentReceiver performReceive(xx)   的处理:#LoadedApk.java public 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 {             ...             //注册时传入的BroadcastReceiver             receiver.onReceive(mContext, intent);         } catch (Exception e) {             ...         }     }; }
  很明显,此处是切换到主线程执行了。
  可以看出,不管是静态注册,抑或是动态注册,最终都是在主线程回调 onReceive(xx)   方法。
  Broadcast 与AMS 交互图如下:
  由上还可以总结出: 1、每发送一次广播,都需要走两次IPC(请求AMS/AMS回调),因此若是广播只在同一进程里发送/接收,没必要使用广播,推荐使用本地广播:  LocalBroadcastManager   。
  2、若广播是静态注册的,AMS每次回调时都会反射重新创建  BroadcastReceiver    实例,因此在广播发送/接收很频繁的情况下不建议使用静态注册,推荐使用动态注册。五、ContentProvider 与AMS 交互
  ContentProvider    顾名思义:内容提供者。
  以典型的手机通讯录为例,通讯录如何提供给其它进程使用其数据呢?根据前面的经验,通讯录需要暴露一个对外的接口,外部程序想使用通讯录那得拿到这个暴露出来的接口。
  接下来看看如何实现自定义的 ContentProvider   :
  声明ContentProvider #AndroidManifest.xml          der>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。#AMS  public final void publishContentProviders(IApplicationThread caller,             List 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的交互:
  ContentProvider 数据变更
  当 ContentProvider   数据变更时,需要通知给监听者,而其它进程想要监听变化,则需要注册观察者。
  注册观察者如下: Handler handler = new Handler() {     @Override     public void handleMessage(@NonNull Message msg) {         super.handleMessage(msg);     } }; Uri uri = Uri.parse("content://" + AMSTargetProvider.AUTHORITY + "/ams"); getContentResolver().registerContentObserver(uri, false, new ContentObserver(handler) {     @Override     public void onChange(boolean selfChange) {         super.onChange(selfChange);     } });
  registerContentObserver    调用栈如下:ContentResolver.registerContentObserver -->ContentResolver.registerContentObserver -->getContentService().registerContentObserver(xx)
  ContentObserver    本身没有跨进程的能力,因此需要将它包装起来:#ContentObserver.java public IContentObserver getContentObserver() {     synchronized (mLock) {         if (mTransport == null) {             mTransport = new Transport(this);         }         return mTransport;     } }  private static final class Transport extends IContentObserver.Stub {     //持有ContentObserver     private ContentObserver mContentObserver;     ... }
  封装好观察者之后,需要将它传递出去。
  重点在 getContentService()   里:#ContentResolver.java public 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.java public final class ContentService extends IContentService.Stub {     ... }
  因此调用 ContentService.registerContentObserver(xx)   ,将ContentObserver.Transport    传递给ContentService   。
  当 ContentProvider   数据发生变更的时候调用如下代码:#ContentResolver.java     private 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   有关系,用图表示如下:
  至此,四大组件与AMS 通信的核心已经分析完毕,本篇没有分析AMS 里对四大组件的处理细节,只是从IPC的角度分析通信的过程,只要理解了通信过程与通信框架,很容易就找到对应的处理细节所在之处。
  测试代码包含了 startService/bindService   、动态/静态 注册广播、ContentProvider    跨进程编写等示例:
  本文基于Android 10.0/8.0/7.0
  完整代码演示
  https://github.com/fishforest/AndroidDemo/blob/main/app/src/main/java/com/example/androiddemo/ams/AMSActivity.java  最后
  在这里就还分享一份由大佬亲自收录整理的Android学习PDF+架构视频+面试文档+源码笔记,高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料
  这些都是我现在闲暇时还会反复翻阅的精品资料。里面对近几年的大厂面试高频知识点都有详细的讲解。相信可以有效地帮助大家掌握知识、理解原理,帮助大家在未来取得一份不错的答卷。
  当然,你也可以拿去查漏补缺,提升自身的竞争力。
  真心希望可以帮助到大家,Android路漫漫,共勉!
  如果你有需要的话,只需私信我【进阶】即可获取

OPPOF21Pro将在4月12日发布多层纹理涂层玻璃纤维皮革设计据gsmarena报道,OPPO在去年3月推出了F19系列,该公司将在下个月推出F21Pro系列,即4月12日。这一消息来自Oppo印度官方,F21Pro系列将于当地时间4月12日砍一刀下,2022年家居大屏是液晶还是激光2022年正在成为居家大屏巨幕的爆发时刻!大屏君注意到,无论是液晶显示技术,还是激光显示技术,在进一步充实产品线的同时,都在大力加大产品价格攻势。砍一刀下,市场必然加速成长,已经成86英寸巨幕电视都跌破7000元了,还要什么自行车?现在很多人买电视都有一种说法,就是买多大都不觉得大,特别对喜欢看电影的伙伴而言,大屏观影带来的沉浸式体验是小屏比不了的,就拿85英寸对比55英寸来说,85英寸的长和宽是是188cm购买电视竟然要靠抢,TCLX11也太火了吧自从邻居家换了新电视,每次串门回来后,就加加剧了我想换电视的念头!虽然我是一个数码作者,但是想要买到自己喜欢的电视,还是要下点功夫去研究一下知乎的蓝大仙人和B站知电晓春哥等专业大佬潍柴CEO谭旭光无人驾驶就是忽悠汽车工业发展到今天,对于未来的探索成为了众多厂家的必修课,大家不约而同将目光投向了无人驾驶,这其中特斯拉等又走在了最前列。不过也有不少人对无人驾驶持深深的怀疑态度,据老板联播报道,冰箱塞不下了?试试西门子家电KX50NA20TI冰箱家里如果是开放式厨房,那一定要选择一款大容量冰箱了!另外考虑到保鲜效果外观设计等等,综合了各种需求,推荐大家选择西门子家电KX50NA20TI冰箱。风冷无霜设计,大容量储鲜,各方面抖音抖碎了谁的玻璃心?(两岸观察)2018年初,大陆抖音短视频(台湾称为TikTok)正式进入台湾地区,迅速成为岛内下载量排名第一的短视频APP。除了TikTok,许多来自大陆的APP也得到了岛内年轻人喜爱。据台媒顺丰同城上市首日破发,何时能摆脱亏损?记者戈振伟12月14日,顺丰控股旗下的即时配送平台顺丰同城(09699。HK)在港上市,发行价为16。42港元。上市首日破发跌7。43,总市值142亿港元。顺丰同城此次IPO初步发区块链版权记区块链技术法律会认可吗?互联网产品本来是技术上的实现,与简单的页面截图等证据手段相比,区块链的证信和可靠性是显而易见的,但是涉及到版权,有人质疑区块链技术是否被认可。对此,可以从两加快完善数据产权制度来源经济日报发展数字经济是把握新一轮科技革命和产业变革新机遇的战略选择。习近平总书记在中共中央政治局第三十四次集体学习时强调,数字经济事关国家发展大局,要做好我国数字经济发展顶层设乞丐版iPhoneXR恢复少量现货,128G降了3100!对比国产怎么选?因为使用体验上还是无可替代,到现在,依旧有很多人选择苹果手机。虽然我们看到安卓阵营和鸿蒙阵营都在提高流畅度,用两三年不卡的旗舰也到处是,但要说三年以上还一点都不卡的机型,那基本上都
比特币1900万枚里程碑前两天,比特币总供应量跨过了1900万枚的里程碑。众所周知,比特币的总供应量被设计为约2100万枚。这意味着,在过去13年里(20092022),约90。5以上的比特币已经被开采出淘宝改名自由后,上百万人连夜告别了前任划重点1淘宝宣布支持修改账号名,仅一天时间,超300万用户修改了自己的账号名,修改后的淘宝账号名中含爱量降低了57。2改名一直是淘宝用户多年来的强烈诉求。2019年,淘宝曾在官微上鸿蒙开发3华为鸿蒙应用开发的低代码(SuperVisual)开发方式开发前可以了解下鸿蒙鸿蒙开发序华为鸿蒙操作系统(HarmonyOS)简介及开发环境搭建开发前的准备鸿蒙开发1华为鸿蒙应用集成开发环境DevEcoStudio安装和设置了解鸿蒙应用开ESClickHouseLoki三种核心日志分析软件比较与思考背景与历史ES和ClickHouse都是开源火得不行的软件,日志分析究竟该用哪个一直争论不休。这篇文章我们不比较哪个更牛,应该选哪个,而是从软件背后去分析用户需求是什么?有没有更好环保大趋势下,电池的环保之路现阶段环保是主题,绿水青山就是金山银山,爱护环境人人有责。现代人们的日常生活中电器使用少不了,手机电脑数码相机无人机可携带的用电设备等,要使用这些电器,电池必不可少。人们苦恼的往往小米11Ultra降的威力有多大,大数据告诉你现在是4月10日,2022年米粉节的最后一天。纵观今年米粉节雷军主打的机型竟然不是小米12系列,也不是RedmiK50系列,而是小米11Ultra这款影像旗舰。从米粉节预热到整体进果蔬净化器真的比手洗得更干净?三款热门果蔬净化器对比测评一引言自从疫情以来,楼主对家人的健康明显更为关注,小到酒精洗手液消毒剂,大到空气净化器衣物消毒盒洗地机各种消杀清洁用品都置办上了,只希望为家人营造更为舒适健康的居住环境。当然,饮食曝非常值得期待小米12Ultra,小米11跌至大米价,上演砸场闹剧曝光将于2022年5月10日正式闪亮登场小米12Ultra发布会信息的截图。曝光将于2022年5月10日正式闪亮登场小米12Ultra将于5月10日1930正式发布。曝光将于202骁龙865小米手机迎来停更,两年的系统维护,官方做到了按照谷歌的要求,手机厂商在使用安卓系统的情况下,需要对智能手机进行两年的系统维护而市面上的骁龙865芯片手机大都发布两年以上的时间,手机厂商也进行了两年的系统维护,所以未来放弃对这小米手机8个隐藏的黑科技功能,让手机更好用,老米粉未必全知道如果我没猜错,相信很多人都在用小米手机。毕竟小米手机除了性价比高之外,也确实好用,每一个功能都能直击我们用户的心灵。今天,我就给大家盘点下小米手机中8个隐藏的黑科技功能,10年老米小米迷你保温瓶在中国亮相,采用316级不锈钢结构等小米在中国市场上架了一款全新的产品,但它不是一款新的智能手机,也不是一款新的智能手表。相反,小米推出了一款普通的500毫升小米品牌迷你保温瓶,售价109元人民币(17美元)。在技术