读书简记4-《Android开发艺术探索》第八、第九章

第八章 理解Window 和 WidowMananger

8.1 window 和 windowManager

1.window的基本概念

Window是一个抽象类,它的具体实现是PhoneWindow。创建一个Window是很简单的事,只需要通过WindowManager即可完成。WindowManager是外界访问Window的入口,Window的具体实现位于WindowManagerService中,WindowManager和Window-ManagerService的交互是一个IPC过程。Android中所有的视图都是通过Window来呈现的,不管是Activity、Dialog还是Toast,它们的视图实际上都是附加在Window上的,因此Window实际是View的直接管理者。

2.windowManager的使用

下面是通过windowManager添加window的示例代码:

        mFloatingButton = new Button(this);        
        mFloatingButton.setText("button");        
        mLayoutParams = new WindowManager.LayoutParams(                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, 0, 0,                    PixelFormat.TRANSPARENT);            
        mLayoutParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL                    
            | LayoutParams.FLAG_NOT_FOCUSABLE                    
            | LayoutParams. FLAG_SHOW_WHEN_LOCKED    
        mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;           
        mLayoutParams.x = 100;            
        mLayoutParams.y = 300;            
        mWindowManager.addView(mFloatingButton, mLayoutParams);

上面代码可以将一个Button添加到屏幕坐标为(100,300)的位置

WindowManager. LayoutParams中的flags和type这两个参数比较重要,Flags参数表示Window的属性,它有很多选项,通过这些选项可以控制Window的显示特性。

  • FLAG_NOT_FOCUSABLE 表示Window不需要获取焦点,也不需要接收各种输入事件,此标记会同时启用FLAG_NOT_TOUCH_MODAL,最终事件会直接传递给下层的具有焦点的Window。
  • FLAG_NOT_TOUCH_MODAL 在此模式下,系统会将当前Window区域以外的单击事件传递给底层的Window,当前Window区域以内的单击事件则自己处理。这个标记很重要,一般来说都需要开启此标记,否则其他Window将无法收到单击事件
  • FLAG_SHOW_WHEN_LOCKED 开启此模式可以让Window显示在锁屏的界面上。

Type参数表示Window的类型,Window有三种类型,分别是应用Window、子Window和系统Window。应用类Window对应着一个Activity。子Window不能单独存在,它需要附属在特定的父Window之中,比如常见的一些Dialog就是一个子Window。系统Window是需要声明权限在能创建的Window,比如Toast和系统状态栏这些都是系统Window。

3.WindowManager

WindowManager所提供的功能很简单,常用的只有三个方法,即添加View、更新View和删除View,这三个方法定义在ViewManager中,而WindowManager继承了ViewManage

8.2Window的内部机制

1.window简述

Window是一个抽象的概念,每一个Window都对应着一个View和一个ViewRootImpl, Window和View通过ViewRootImpl来建立联系,因此Window并不是实际存在的,它是以View的形式存在

2.window的添加过程

Window的添加过程需要通过WindowManager的addView来实现,WindowManager是一个接口,它的真正实现是WindowManagerImpl类,WindowManagerImpl并没有直接实现Window的三大操作,而是全部交给了WindowManagerGlobal来处理,WindowManagerGlobal以工厂的形式向外提供自己的实例。

WindowManager-Global的addView方法的实现步骤:

  • 1.检查参数是否合法,如果是子Window那么还需要调整一些布局参数

  • 2.创建ViewRootImpI并将View添加到列表中

private final ArrayList<View> mViews = new ArrayList<View>();              private final ArrayList<ViewRootImpl> mRoots = new ArrayList              <ViewRootImpl>();              
private final ArrayList<WindowManager.LayoutParams> mParams =                    new ArrayList<WindowManager.LayoutParams>();          
private final ArraySet<View> mDyingViews = new ArraySet<View>();

mViews存储的是所有Window所对应的View,mRoots存储的是所有Window所对应的ViewRootImpl, mParams存储的是所有Window所对应的布局参数,而mDyingViews则存储了那些正在被删除的View对象,或者说是那些已经调用removeView方法但是删除操作还未完成的Window对象。

  • 3.通过ViewRootImpI来更新界面并完成Window的添加过程(这里进行ipc操作)

这个步骤由ViewRootImpl的setView方法来完成,从第4章可以知道,View的绘制过程是由ViewRootImpl来完成的,这里当然也不例外,在setView内部会通过requestLayout来完成异步刷新请求

接着会通过WindowSession最终来完成Window的添加过程。mWindowSession的类型是IWindowSession,它是一个Binder对象,真正的实现类是Session,也就是Window的添加过程是一次IPC调用。在Session内部会通过WindowManagerService来实现Window的添加。

3.window的删除过程

Window的删除过程和添加过程一样,都是先通过WindowManagerImpl后,再进一步通过WindowManagerGlobal来实现的

removeView的逻辑很清晰,首先通过findViewLocked来查找待删除的View的索引,这个查找过程就是建立的数组遍历,然后再调用removeViewLocked来做进一步的删除,removeView-Locked是通过ViewRootImpl来完成删除操作的。

在doDie内部(viewrootimpl的方法)会调用dispatchDetachedFromWindow方法,真正删除View的逻辑在dispatchDetachedFromWindow方法的内部实现。dispatchDetachedFromWindow方法主要做四件事:

(1)垃圾回收相关的工作,比如清除数据和消息、移除回调。

(2)通过Session的remove方法删除Window:mWindowSession.remove(mWindow),这同样是一个IPC过程,最终会调用WindowManagerService的removeWindow方法。

(3)调用View的dispatchDetachedFromWindow方法,在内部会调用View的onDetached-FromWindow()以及onDetachedFromWindowInternal()。对于onDetachedFromWindow()大家一定不陌生,当View从Window中移除时,这个方法就会被调用,可以在这个方法内部做一些资源回收的工作,比如终止动画、停止线程等。

(4)调用WindowManagerGlobal的doRemoveView方法刷新数据,包括mRoots、mParams以及mDyingViews,需要将当前Window所关联的这三类对象从列表中删除。

4.window的更新过程

分析Window的更新过程,还是要看WindowManagerGlobal的updateViewLayout方法。

updateViewLayout方法做的事情就比较简单了,首先它需要更新View的LayoutParams并替换掉老的LayoutParams,接着再更新ViewRootImpl中的LayoutParams,这一步是通过ViewRoot-Impl的setLayoutParams方法来实现的。

。在ViewRootImpl中会通过scheduleTraversals方法来对View重新布局,包括测量、布局、重绘这三个过程。除了View本身的重绘以外,ViewRootImpl还会通过WindowSession来更新Window的视图,这个过程最终是由WindowManagerService的relayoutWindow()来具体实现的,它同样是一个IPC过程。

8.3Window的创建过程

1.Activity中window的创建

首先。在Activity的attach方法里,系统会创建Activity所属的Window对象并为其设置回调接口,Window对象的创建是通过PolicyManager的makeNewWindow方法实现的

2.Activity的视图是怎么附属在Window上的

从Activity的setContentView的实现可以看出,Activity将具体实现交给了Window处理,而Window的具体实现是PhoneWindow,所以只需要看PhoneWindow的相关逻辑即可。PhoneWindow的setContentView方法大致遵循如下几个步骤。

  • 1.如果没有DecorView,那么就创建它
  • 2.将View添加到DecorView的mContentParent中
  • 3.回调Activity的onContentChanged方法通知Activity视图已经发生改变

最后,在ActivityThread的handleResumeActivity方法中,首先会调用Activity的onResume方法,接着会调用Activity的makeVisible(),正是在makeVisible方法中,DecorView真正地完成了添加和显示这两个过程,到这里Activity的视图才能被用户看到

        void makeVisible() {  // 通过windowManager正式将其加入          
            if (! mWindowAdded) {                
                ViewManager wm = getWindowManager();                
                wm.addView(mDecor, getWindow().getAttributes());                			 mWindowAdded = true;            }           
            mDecor.setVisibility(View.VISIBLE);        
        }

2.dialog中window的创建

Dialog的Window的创建过程和Activity类似,有如下几个步骤:

  • 1.创建Window
  • 2.初始化DecorView并将DiaIog的视图添加到DecorView中
  • 3.将DecorView添加到Window中并显示

补充:

  • 当Dialog被关闭时,它会通过WindowManager来移除DecorView:mWindowManager. removeViewImmediate(mDecor)。

  • 普通的Dialog有一个特殊之处,那就是必须采用Activity的Context,如果采用Application的Context,那么就会报错。(报错显示缺失token)

  • 系统Window比较特殊,它可以不需要token

3.Toast中window的创建

oast属于系统Window,它内部的视图由两种方式指定,一种是系统默认的样式,另一种是通过setView方法来指定一个自定义View。不管如何,它们都对应Toast的一个View类型的内部成员mNextView。Toast提供了show和cancel分别用于显示和隐藏Toast,它们的内部是一个IPC过程

先看Toast的show方法:

        INotificationManager service = getService();        
        String pkg = mContext.getOpPackageName();        
        TN tn = mTN;   //   binder对象,实现远程回调   
        tn.mNextView = mNextView;  // toast的视图    
        try {            
        service.enqueueToast(pkg, tn, mDuration);        
        } catch (RemoteException e) {            
        // Empty        
        }

NMS的enqueueToast方法的第一个参数表示当前应用的包名,第二个参数tn表示远程回调,第三个参数表示Toast的时长。enqueueToast首先将Toast请求封装为ToastRecord对象并将其添加到一个名为mToastQueue的队列中。mToastQueue其实是一个ArrayList。对于非系统应用来说,mToastQueue中最多能同时存在50个ToastRecord

当ToastRecord被添加到mToastQueue中后,NMS就会通过showNextToastLocked方法来显示当前的Toast。此时被调用的TN中的方法会运行在发起Toast请求的应用的Binder线程池中

Toast显示以后,NMS还会通过scheduleTimeoutLocked方法来发送一个延时消息,具体的延时取决于Toast的时长,ONG_DELAY是3.5s,而SHORT_DELAY是2s。延迟相应的时间后,NMS会通过cancelToastLocked方法来隐藏Toast并将其从mToastQueue中移除,这个时候如果mToastQueue中还有其他Toast,那么NMS就继续显示其他Toast。

总结:

Toast的显示和影响过程实际上是通过Toast中的TN这个类来实现的,它有两个方法show和hide,分别对应Toast的显示和隐藏。由于这两个方法是被NMS以跨进程的方式调用的,因此它们运行在Binder线程池中。为了将执行环境切换到Toast请求所在的线程,在它们的内部使用了Handler。

mShow和mHide是两个Runnable,它们内部分别调用了handleShow和handleHide方法。由此可见,handleShow和handleHide才是真正完成显示和隐藏Toast的地方。TN的handleShow中会将Toast的视图添加到Window中,

        mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); 
	// 获取windowManager
        mWM.addView(mView, mParams)

而NT的handleHide中会将Toast的视图从Window中移除。

        if (mView.getParent() ! = null) {            
        if (localLOGV) 
         Log.v(TAG, "REMOVE! " + mView + " in " + this);            
         mWM.removeView(mView);        
         }

第九章 四大组件的工作过程

9.1 四大组件的运行转态

1.四大组件简述

  • Activity是一种展示型组件,用于向用户直接地展示一个界面,并且可以接收用户的输入信息从而进行交互。Activity的启动由Intent触发,其中Intent可以分为显式Intent和隐式Intent,显式Intent可以明确地指向一个Activity组件,隐式Intent则指向一个或多个目标Activity组件。Activity组件是可以停止的,在实际开发中可以通过Activity的finish方法来结束一个Activity组件的运行。由此来看,Activity组件的主要作用是展示一个界面并和用户交互,它扮演的是一种前台界面的角色。
  • Service是一种计算型组件,用于在后台执行一系列计算任务。Service组件却有两种状态:启动状态和绑定状态。当Service组件处于启动状态时,这个时候Service内部可以做一些后台计算,并且不需要和外界有直接的交互。尽管Service组件是用于执行后台计算的,但是它本身是运行在主线程中的,因此耗时的后台计算仍然需要在单独的线程中去完成。当Service组件处于绑定状态时,这个时候Service内部同样可以进行后台计算,但是处于这种状态时外界可以很方便地和Service组件进行通信。Service组件也是可以停止的,停止一个Service组件稍显复杂,需要灵活采用stopService和unBindService这两个方法才能完全停止一个Service组件。
  • BroadcastReceiver是一种消息型组件,用于在不同的组件乃至不同的应用之间传递消息。BroadcastReceiver也叫广播,广播的注册有两种方式:静态注册和动态注册。静态注册是指在AndroidManifest中注册广播,这种广播在应用安装时会被系统解析,此种形式的广播不需要应用启动就可以收到相应的广播。动态注册广播需要通过Context.registerReceiver()来实现,并且在不需要的时候要通过Context.unRegisterReceiver()来解除广播,此种形态的广播必须要应用启动才能注册并接收广播,因为应用不启动就无法注册广播,无法注册广播就无法收到相应的广播。
  • ContentProvider是一种数据共享型组件,用于向其他组件乃至其他应用共享数据。对于一个ContentProvider组件来说,它的内部需要实现增删改查这四种操作,在它的内部维持着一份数据集合,这个数据集合既可以通过数据库来实现,也可以采用其他任何类型来实现,比如List和Map, ContentProvider对数据集合的具体实现并没有任何要求。需要注意的是,ContentProvider内部的insert、delete、update和query方法需要处理好线程同步,因为这几个方法是在Binder线程池中被调用的,另外ContentProvider组件也不需要手动停止。

9.2 Activity的工作过程

1.Activity的启动过程

从Activity的startActivity方法,startActivity方法有好几种重载方式,但它们最终都会调用startActivityForResult方法。在该方法中接着会调用Instrumentation的execStartActivity方法。

在execStartActivity方法中,启动Activity真正的实现由ActivityManagerNative.getDefault()的startActivity方法来完。(在这里,启动Activity的过程转移到AMS中)。在完成这个过程后,在exec方法中,还会调用checkStart ActivityResult(result,intent),检查启动Activity的结果。当无法正确地启动一个Activity时,这个方法会抛出异常信息。

AMS的startActivity实际调用的又是startActivityAsUser这个方法。在system_server进程中会做很多工作,例如启动判断所在进程是否创建。在ApplicationThreadProxy.java这个类中的scheduleLaunchActivity方法中,通过transact方法,流程回到了目标进程(从system_server到了目标进程)

绕了一大圈后,Activity的启动过程最终回到了ApplicationThread中,Application Thread通过schedule LaunchActivity方法来启动Activity。在这里的schedule LaunchActivity方法会向Handler对象H发送消息。(这里从binder线程池到了主线程)

handlerH对象的handleMessage方法中会调用ActivityThread的handleLaunchActivity方法,在这里完成Activity启动相关的方法的回调

补充:

  • mMainThread.getApplicationThread()这个参数,它的类型是ApplicationThread,ApplicationThread是ActivityThread的一个内部类 (在startActivityForResult方法)

    Instrumentation.ActivityResult ar =                     		              mInstrumentation.execStartActivity ( this, mMainThread.getApplicationThread(), mToken, this, intent, requestCode, options);  
    
  • ActivityManagerService(下面简称为AMS)继承自ActivityManagerNative,而ActivityManagerNative继承自Binder并实现了IActivityManager这个Binder接口,因此AMS也是一个Binder,它是IActivityManager的具体实现。由于ActivityManagerNative.getDefault()其实是一个IActivityManager类型的Binder对象,因此它的具体实现是AMS。

    可以发现,在ActivityManagerNative中,AMS这个Binder对象采用单例模式对外提供,Singleton是一个单例的封装类,第一次调用它的get方法时它会通过create方法来初始化AMS这个Binder对象,在后续的调用中则直接返回之前创建的对象,这个过程的源码如下所示。

     int result = ActivityManagerNative.getDefault()                           
     .startActivity(whoThread, who.getBasePackageName(), intent,                                    intent.resolveTypeIfNeeded(who.getContentResolver()),                                    token, target ! = null ? target.mEmbeddedID : null,                                    requestCode, 0, null, options);            
    
  • ActivityThread的handleLuanchActivity方法,在这里会调用很多与Activity初始化相关的方法

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;
    
        //最终回调目标Activity的onConfigurationChanged()
        handleConfigurationChanged(null, null);
        //初始化wms
        WindowManagerGlobal.initialize();
        //最终回调目标Activity的onCreate[见流程2.23]
        Activity a = performLaunchActivity(r, customIntent);
        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            //最终回调目标Activity的onStart,onResume.
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed);
    
            if (!r.activity.mFinished && r.startsNotResumed) {
                r.activity.mCalled = false;
                mInstrumentation.callActivityOnPause(r.activity);
                r.paused = true;
            }
        } else {
            //存在error则停止该Activity
            ActivityManagerNative.getDefault()
                .finishActivity(r.token, Activity.RESULT_CANCELED, null, false);
        }
    }
    
  • performLaunchActivity完成的工作:

    1.从ActivityCIientRecord中获取待启动的Activity的组件信息

    2.通过Instrumentation的newActivity方法使用类加载器创建Activity对象

    3.通过LoadedApk的makeAppIication方法来尝试创建AppIication对象

    4.创建ContextImpI对象并通过Activity的attach方法来完成一些重要数据的初始化

    5.调用Activity的onCreate方法

9.3 Service的工作流程

1.Service的两种启动方式

Service分为两种工作状态,一种是启动状态,主要用于执行后台计算;另一种是绑定状态,主要用于其他组件和Service的交互。需要注意的是,Service的这两种状态是可以共存的,即Service既可以处于启动状态也可以同时处于绑定状态

// 启动service
Intent intentService = new Intent(this, MyService.class);        startService(intentService);

// 绑定service
Intent intentService = new Intent(this, MyService.class);        bindService(intentService, mServiceConnection, BIND_AUTO_CREATE);

2.Service的启动过程

Service的启动过程从ContextWrapper的startActivity开始,实质调用的是ContextImpl对象的startService方法。

ContextImpl中,startService方法会调用startServiceCommon方法,而startService-Common方法又会通过ActivityManagerNative.getDefault()这个对象的startService方法来启动一个服务(转移到AMS中)

AMS会通过mServices这个对象来完成Service后续的启动过程,mServices对象的类型是ActiveServices, ActiveServices是一个辅助AMS进行Service管理的类,包括Service的启动、绑定和停止等。在ActiveServices的startServiceLocked方法的尾部会调用startServiceInnerLocked方法,这里有层层调用,最后会调用realStartServiceLocked这个方法。在这个方法中会调用ATP.scheduleCreateService这个方法,这里就会通过binder通信,回到目标进程端。(会调用oncreate和onstartcommon)

在ApplicationThreadNative.java这个类中,会调用ApplicationThread的scheduleCreateService,在这个方法里会创建service,并发送消息给handler H(转移到主线程)

H接收到消息,会通过ActivityThread的handleCreateService方法来完成Service的最终启动

补充:

  • 这里是startServiceComon方法

       ComponentName cn = 
       ActivityManagerNative.getDefault().startService(                    
       mMainThread.getApplicationThread(), service,                    
       service.resolveTypeIfNeeded(getContentResolver()),  user.getIdentifier());                   
    
  • 在realStartServiceLocked中,跳转到目标进程的方法

    app.thread.scheduleCreateService(r, r.serviceInfo,
                    mAm.compatibilityInfoForPackageLocked(r.serviceInfo.applicationInfo),
                    app.repProcState);
    
  • handleCreateService的主要工作

    首先通过类加载器创建Service的实例。

    然后创建Application对象并调用其onCreate,当然Application的创建过程只会有一次。

    接着创建ConTextImpl对象并通过Service的attach方法建立二者之间的关系,这个过程和Activity实际上是类似的,毕竟Service和Activity都是一个Context。

    最后调用Service的onCreate方法并将Service对象存储到ActivityThread中的一个列表中

3.绑定service的过程

Service的绑定过程也是从ContextWrapper开始的,实际调用的也是ContextImpl对象的bindService方法。

ContextImpl中,bindService方法最终会调用自己的bindServiceCommon方法。这时先需要把ServiceConnection对象转化为ServiceDispatcher.InnerConnection对象。接着bindServiceCommon方法会通过AMS来完成Service的具体的绑定过程。(转移到System_server进程)

后面的过程就和startSeervice类似,最终都是通过ApplicationThread来完成Service实例的创建并执行其onCreate方法(和启动Service不同的是,Service的绑定过程会调用app.thread的scheduleBindService方法,而不是scheduleCreateServic)

在H接收到消息后,会交给ActivityThread的handleBind-Service方法来处理。在这里还会通过ActivityManagerNative.getDefault()的publishService方法,实现onService Connected的回调。

补充:

  • ServiceDispatcher起着连接ServiceConnection和InnerConnection的作用。这个过程由LoadedApk的getServiceDispatcher方法来完成,方法里有一个ArrayMap,它存储了一个应用当前活动的ServiceConnection和ServiceDispatcher的映射关系。

  • 由于ServiceDispatcher内部保存了客户端的ServiceConnection对象,因此它可以很方便地调用ServiceConnection对象的onServiceConnected方法。同时在ServiceDispatcher内部还保存了handler对象,因此回调能正确的运行在主线程

  • handleBind-Service的主要工作

    首先根据Service的token取出Service对象,然后调用Service的onBind方法,Service的onBind方法会返回一个Binder对象给客户端使用。service的onBind方法被调用以后,Service就处于绑定状态了,但是onBind方法是Service的方法,这个时候客户端并不知道已经成功连接Service了,所以还必须调用客户端的ServiceConnection中的onService Connected,这个过程是由ActivityManagerNative.getDefault()的publishService方法来完成的。

  • Service有一个特性,当多次绑定同一个Service时,Service的onBind方法只会执行一次,除非Service被终止了。当Service的onBind执行以后,系统还需要告知客户端已经成功连接Service了

  • 进程层面的过程总结:

    1. Client进程: 通过getServiceDispatcher获取Client进程的匿名Binder服务端,即LoadedApk.ServiceDispatcher.InnerConnection,该对象继承于IServiceConnection.Stub; 再通过bindService调用到system_server进程;
    2. system_server进程: 依次通过scheduleCreateService和scheduleBindService方法, 远程调用到target进程; 4: target进程: 依次执行onCreate()和onBind()方法; 将onBind()方法的返回值IBinder(作为target进程的binder服务端)通过publishService传递到system_server进程;
    3. system_server进程: 利用IServiceConnection代理对象向Client进程发起connected()调用, 并把target进程的onBind返回Binder对象的代理端传递到Client进程;
    4. Client进程: 回调到onServiceConnection()方法, 该方法的第二个参数便是target进程的binder代理端. 到此便成功地拿到了target进程的代理, 可以畅通无阻地进行交互.

4.自我总结,bindservice和startservice启动流程的回调

主要的不同就是与ServiceDispatcher相关的操作。因为要调用onService Connected方法,所以要对serviceconnetection对象进行处理。保存一些信息,以便执行回调

9.4 BrodadcastReceiver的工作过程

1.BrodadcastReceiver的使用

首先需要定义广播接收者,只需要继承BroadcastReceiver并重写onReceive方法即可,定义好了广播接收者,接着还需要注册广播接收者,注册分为两种方式,既可以在AndroidManifest文件中静态注册,也可以通过代码动态注册。

动态注册的代码如下,需要注意的是,动态注册的广播需要在合适的时机进行解注册,解注册采用unregisterReceiver方法。

        IntentFilter filter = new IntentFilter();    // 创建filter    
        filter.addAction("com.ryg.receiver.LAUNCH");    // 添加action    
        registerReceiver(new MyReceiver(), filter);  // 注册广播

静态注册的示例如下:

        <receiver android:name=".MyReceiver" >            
        <intent-filter>                
        <action android:name="com.ryg.receiver.LAUNCH" />            
        </intent-filter>        
        </receiver>

2.广播的注册过程简述

广播的注册分为静态注册和动态注册,其中静态注册的广播在应用安装时由系统自动完成注册,具体来说是由PMS(PackageManagerService)来完成整个注册过程的,除了广播以外,其他三大组件也都是在应用安装时由PMS解析并注册的。

3.广播动态注册过程

动态注册的过程是从ContextWrapper的registerReceiver方法开始的,和Activity以及Service一样,实际调用是通过ContextImpl对象的registerReceiver方法。而ContextImpl的registerReceiver方法调用了自己的register ReceiverInternal方法。

系统首先通过getReceiverDispatcher方法,从mPackageInfo获取IIntentReceiver对象(一个binder接口,方便进行跨进程请求),然后再采用跨进程的方式向AMS发送广播注册的请求。(ActivityManagerNative的getDefault的registerReceiver方法)

由于注册广播的真正实现过程是在AMS中,因此我们需要看一下AMS的具体实现。AMS的registerReceiver方法看起来很长,其实关键点就只有下面一部分,最终会把远程的InnerReceiver对象以及IntentFilter对象存储起来,这样整个广播的注册过程就完成了

补充:

getReceiverDispatcher方法重新创建了一个ReceiverDispatcher对象并将其保存的InnerReceiver对象作为返回值返回,其中InnerReceiver对象和BroadcastReceiver都是在ReceiverDispatcher的构造方法中被保存起来的。

4.广播发送和接收简述

当通过send方法来发送广播时,AMS会查找出匹配的广播接收者并将广播发送给它们处理。广播的发送有几种类型:普通广播、有序广播和粘性广播,有序广播和粘性广播与普通广播相比具有不同的特性

5.普通广播的发送和接收过程

广播的发送仍然开始于ContextWrapper的sendBroadcast方法。广播的注册过程一样,ContextWrapper的sendBroadcast方法仍然什么都不做,只是把事情交给ContextImpl去处理。

在ContextImpl的sendBroadcast方法中,会通过broadcastIntent方法,向AMS发起请求。

在AMS的broadcastInten方法中,调用了broadcastIntentLocked方法。在broadcastIntentLocked的内部,会根据intent-filter查找出匹配的广播接收者并经过一系列的条件过滤,最终会将满足条件的广播接收者添加到BroadcastQueue中,接着BroadcastQueue就会将广播发送给相应的广播接收者(通过BroadcastQueue的scheduleBroadcastsLocked方法)

经过层层调用,具体的发送过程是由deliverToRegistered-ReceiverLocked方法来实现的。

接下来就进入到接收的过程。ApplicationThread的scheduleRegisteredReceiver的实现比较简单(转移到目标进程),它通过InnerReceiver来实现广播的接收

InnerReceiver的performReceive方法会调用LoadedApk.ReceiverDispatcher的perform-Receive方法,LoadedApk.ReceiverDispatcher的performReceive方法。最终通过handler H,调用onReceive方法

9.5 contentProvider的工作流程

1.contextProvider启动简述

ContentProvider的onCreate要先于Application的onCreate而执行,这在四大组件中是一个少有的现象。

当一个应用启动时,入口方法为ActivityThread的main方法,main方法是一个静态方法,在main方法中会创建ActivityThread的实例并创建主线程的消息队列,然后在ActivityThread的attach方法中会远程调用AMS的attachApplication方法并将ApplicationThread对象提供给AMS。ApplicationThread是一个Binder对象,它的Binder接口是IApplicationThread,它主要用于ActivityThread和AMS之间的通信,这一点在前面多次提到。在AMS的attachApplication方法中,会调用ApplicationThread的bindApplication方法,注意这个过程同样是跨进程完成的,bindApplication的逻辑会经过ActivityThread中的mH Handler切换到ActivityThread中去执行,具体的方法是handleBindApplication。在handleBindApplication方法中,ActivityThread会创建Application对象并加载ContentProvider。需要注意的是,ActivityThread会先加载ContentProvider,然后再调用Application的onCreate方法。

2.单实例contentProvider的启动流程

通过ContentProvider的四个方法的任何一个都可以触发ContentProvider的启动过程,这里选择query方法。

ContentProvider的query方法中,首先会获取IContentProvider对象,不管是通过acquireUnstableProvider方法还是直接通过acquireProvider方法,它们的本质都是一样的,最终都是通过ActivityThread的acquireProvider方法来获取ContentProvider。

在ActivityThread的acquireProvider中,首先会查询是否已存在ContentProvider了,如果存在就直接返回。ActivityThread中通过mProviderMap来存储已经启动的ContentProvider对象(又是一个ArrayMap对象)。

如果目前ContentProvider没有启动,那么就发送一个进程间请求给AMS让其启动目标ContentProvider(通过ActivityManagerNative.getDefault().getContentProvider),最后再通过installProvider方法来修改引用计数,安装provider。

补充:

发布ContentProvider分两种情况:Provider进程尚未启动,Provider进程已启动但未发布。

  1. 场景一(Provider进程尚未启动):system_server进程调用startProcessLocked()创建provider进程且attach到system_server后, 通过binder call到provider进程执行AT.bindApplication()方法;
  2. 场景二(Provider进程已启动但未发布): 获取provider的过程[小节2.7.2], 发现provider进程已存在且attach到system_server,但所对应的provider还没有发布, 通过binder call到provider进程执行AT.scheduleInstallProvider方法。

3.contentProvider启动过程中,AMS是如何启动ContentProvider的呢?

ContentProvider被启动时会伴随着进程的启动,在AMS中,首先会启动ContentProvider所在的进程,然后再启动ContentProvider。启动进程是由AMS的startProcessLocked方法来完成的,其内部主要是通过Process的start方法来完成一个新进程的启动,新进程启动后其入口方法为ActivityThread的main方法。(从AMS到目标进程)

ActivityThread的main方法是一个静态方法,在它内部首先会创建Activity-Thread的实例并调用attach方法来进行一系列初始化,接着就开始进行消息循环了。ActivityThread的attach方法会将ApplicationThread对象通过AMS的attachApplication方法跨进程传递给AMS,(到AMS)最终AMS会完成ContentProvider的创建过程

AMS会调用ApplicationThread的bindApplication(又回到目标进程),bindApplication会发送信息给handler H (切换到主线程)。之后ActivityThread的handleBindApplication则完成了Application的创建以及Content-Provider的创建,

4.启动过程简述

  1. client进程:通过binder(调用AMS.getContentProviderImpl)向system_server进程请求相应的provider;
  2. system进程:如果目标provider所对应的进程尚未启动,system_server会调用startProcessLocked来启动provider进程; 当进程启动完成,此时cpr.provider ==null,则system_server便会进入wait()状态,等待目标provider发布;
  3. provider进程:进程启动后执行完attch到system_server,紧接着执行bindApplication;在这个过程会installProvider以及 publishContentProviders;再binder call到system_server进程;
  4. system进程:再回到system_server,发布provider信息,并且通过notify机制,唤醒前面处于wait状态的binder线程;并将 getContentProvider的结果返回给client进程;
  5. client进程:接着执行installProvider操作,安装provider的(包含对象记录,引用计数维护等工作);
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值