美文网首页
Activity窗口机制源码解析上(8.0)

Activity窗口机制源码解析上(8.0)

作者: Android_Jian | 来源:发表于2018-09-17 00:40 被阅读27次

要分析Activity的窗口机制,就必须要了解Activity的启动过程,二者的关系是密不可分的。在上小节中,我们分析了Activity的启动流程,今天我们一起看下Activity窗口机制相关的部分。对Activity启动流程还不熟悉的小伙伴可以先移至笔者的上篇文章:Activity启动流程源码解析 https://www.jianshu.com/p/621ae18547b0

我们知道,Activity的启动过程,本质上来讲是通过IPC机制来实现的,最终会由ActivityThread类中的handleLaunchActivity()方法来完成整个启动过程。我们跟进去看下它的源码(为方便查看,源码有所删减):

    private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {

        //1.获得Binder连接池对象  重点!!!
        WindowManagerGlobal.initialize();

         //2.
        Activity a = performLaunchActivity(r, customIntent);
        if (a != null) {

            //3.
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
        } else {
            // If there was an error, for any reason, tell the activity manager to stop us.
            try {
                ActivityManager.getService()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

我们可以看到handleLaunchActivity方法中首先调用到WindowManagerGlobal.initialize();,接着调用到performLaunchActivity方法,后续调用到handleResumeActivity方法。1 处代码主要做初始化工作,目的是获得Binder连接池对象,后续添加窗口时首先会通过这个Binder连接池获取到IWindowSession,这段代码我们暂时先不讲,下个小节我会带领大家详细分析,这里大家先有个印象就好。通过上小节Activity启动流程分析我们知道,在performLaunchActivity方法中完成了Activity的创建以及onCreate、onStart生命周期函数的回调。在handleResumeActivity方法中完成了onResume生命周期函数的回调。今天我们从窗口机制的角度进一步分析下,首先看下performLaunchActivity方法:

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {

        创建Activity对象
   
       ...
   
       activity.attach(appContext, this, getInstrumentation(), r.token,
                        r.ident, app, r.intent, r.activityInfo, title, r.parent,
                        r.embeddedID, r.lastNonConfigurationInstances, config,
                        r.referrer, r.voiceInteractor, window, r.configCallback);

       ...

       Activity onCreate、onStart生命周期方法回调
}

为了方便分析,笔者在这里只留了Activity窗口机制相关的部分,我们可以看到Activity实例对象创建完毕后调用了它的attach方法,我们跟进去看下这个方法到底做了什么:

    # Activity
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor,
            Window window, ActivityConfigCallback activityConfigCallback) {
  
        //1.创建PhoneWindow实例对象
        mWindow = new PhoneWindow(this, window, activityConfigCallback);
        mWindow.setWindowControllerCallback(this);
        //2.设置callback回调
        mWindow.setCallback(this);
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
      
        mUiThread = Thread.currentThread();

        mMainThread = aThread;
        mInstrumentation = instr;
        mToken = token;
        mIdent = ident;
        mApplication = application;
        mIntent = intent;
        mReferrer = referrer;
        mComponent = intent.getComponent();
        mActivityInfo = info;
        mTitle = title;
        mParent = parent;
        mEmbeddedID = id;
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        //3.调用PhoneWindow的setWindowManager方法
        mWindow.setWindowManager(
                (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        if (mParent != null) {
            mWindow.setContainer(mParent.getWindow());
        }
        //4.调用PhoneWindow的getWindowManager方法,返回 WindowManagerImpl实例对象,赋值给mWindowManager 成员变量
        mWindowManager = mWindow.getWindowManager();
        mCurrentConfig = config;

        mWindow.setColorMode(info.colorMode);
    }

我们可以看到,在 1 处创建了PhoneWindow实例对象。PhoneWindow作为Window的唯一实现类,用来表示“窗口”。

public class PhoneWindow extends Window implements MenuBuilder.Callback {

2 处调用了PhoneWindow的setCallback方法,将当前Activity实例作为参数传入,我们点进去看下:

    # Window
    public void setCallback(Callback callback) {
        mCallback = callback;
    }

可以看到,setCallback方法中将当期Activity的引用赋值给PhoneWindow 的成员变量mCallback 。
接着我们看下PhoneWindow 的setWindowManager方法:

    # Window
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
            boolean hardwareAccelerated) {
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated
                || SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
        if (wm == null) {
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }

可以看到在setWindowManager方法的最后调用到WindowManagerImpl的createLocalWindowManager方法,我们跟进去看下:

    #WindowManagerImpl
    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mContext, parentWindow);
    }

可以看到WindowManagerImpl 的 createLocalWindowManager 方法直接 new 了一个WindowManagerImpl实例对象,并 return 掉。也就是说PhoneWindow 的setWindowManager 方法将新创建的 WindowManagerImpl 对象赋值给成员变量mWindowManager 。
最后我们看下 4 处 PhoneWindow的 getWindowManager 方法:

    #PhoneWindow
    public WindowManager getWindowManager() {
        return mWindowManager;
    }

可以看到,方法直接将成员变量mWindowManager return掉,也就是说将新创建的WindowManagerImpl 对象再次赋值给Activity 的成员变量 mWindowManager 。这样就将WindowManagerImpl对象 与当前Activity以及PhoneWindow绑定起来。

好了,我们回过头接着分析,performLaunchActivity 方法中,activity.attach方法调用完毕后,后续回调到Activity的 onCreate生命周期方法,我们在onCreate方法中的操作再熟悉不过了,那就是setContentView,就是这样子:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

大家知道setContentView方法中到底做了什么吗?我们跟进去看下:

    # Activity
    public void setContentView(@LayoutRes int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

可以看到setContentView 方法中调用到 getWindow().setContentView 方法,getWindow()方法获取到的是什么呢?我们跟进去看下:

    public Window getWindow() {
        return mWindow;
    }

getWindow方法中直接将 mWindow 成员变量return掉,mWindow 不就是我们之前在attach方法中创建的PhoneWindow对象嘛!也就是说Activity 的 setContentView 方法直接调用到PhoneWindow 的 setContentView方法,我们跟进去看下:

    #PhoneWindow 
    @Override
    public void setContentView(int layoutResID) {
       
        if (mContentParent == null) {

            //1.创建DecorView以及为mContentParent赋值
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        //判断是否有转场动画
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            //2.将Activity onCreate方法中通过setContentView加载的布局文件装载到mContentParent中
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        mContentParent.requestApplyInsets();
        //3.回调当前Activity的onContentChanged方法
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
        mContentParentExplicitlySet = true;
    }

可以看到,方法中首先对mContentParent 进行null判断,这里我先大概说下,mContentParent 是DecorView对应的布局文件中id为content的子控件,本质上是一个FrameLayout帧布局。我们在Activity的onCreate方法中通过setContentView方法加载的布局文件最终就是装载到这个mContentParent 中。首次进入,mContentParent 为null。
我们首先看下installDecor 方法:

    #PhoneWindow 
    private void installDecor() {
        mForceDecorInstall = false;

        if (mDecor == null) {
            //1.创建DecorView对象并赋值给成员变量mDecor 
            mDecor = generateDecor(-1);
           
        } else {
            mDecor.setWindow(this);
        }

        if (mContentParent == null) {
            //2.获得DecorView对应的布局文件中id为content的子控件,并赋值给mContentParent 
            mContentParent = generateLayout(mDecor);
   
            ...
        }
    }

我们跟进去 1 处generateDecor方法看下:

    #PhoneWindow 
    protected DecorView generateDecor(int featureId) {
        // System process doesn't have application context and in that case we need to directly use
        // the context we have. Otherwise we want the application context, so we don't cling to the
        // activity.
        Context context;
        if (mUseDecorContext) {
            Context applicationContext = getContext().getApplicationContext();
            if (applicationContext == null) {
                context = getContext();
            } else {
                context = new DecorContext(applicationContext, getContext().getResources());
                if (mTheme != -1) {
                    context.setTheme(mTheme);
                }
            }
        } else {
            context = getContext();
        }
        return new DecorView(context, featureId, this, getAttributes());
    }

可以看到generateDecor方法的最后 new 出一个DecorView对象 并 return出去,在DecorView的构造方法中将当前phoneWindow对象作为第三个参数传入,赋值给decorview对象的mWindow成员变量。这个样子decorview和phonewindow就完成了相互注册。
接着我们看下 2 处的generateLayout方法:

    protected ViewGroup generateLayout(DecorView decor) {
      
        ...
      
        // layoutResource表示decorview所对应的根布局
        int layoutResource;
        int features = getLocalFeatures();

        // 根据主题加载相应的根布局
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
            setCloseOnSwipeEnabled(true);
        } 
       
        ...    

        else {
            // 1.如果没有装饰的话,默认加载的布局
            layoutResource = R.layout.screen_simple;
        }

        mDecor.startChanging();
        //2.将layoutResource根布局加载到decorview中
        mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
        //3.通过findViewById方法获取到decorview根布局中id为ID_ANDROID_CONTENT的子控件,并赋值给contentParent 
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }
        //将contentParent return掉
        return contentParent;
    }

我们先来看下R.layout.screen_simple根布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">
    <ViewStub android:id="@+id/action_mode_bar_stub"
              android:inflatedId="@+id/action_mode_bar"
              android:layout="@layout/action_mode_bar"
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:theme="?attr/actionBarTheme" />
    <FrameLayout
         android:id="@android:id/content"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
         android:foregroundInsidePadding="false"
         android:foregroundGravity="fill_horizontal|top"
         android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

可以看到decorview直接加载的子布局实际上就是一个竖直排列的线性布局,包含 title标题栏 和 content内容部分。decorview本质上作为一个FrameLayout帧布局,2 处将layoutResource根布局加载到decorview中,其实就是普普通通的addView而已,我们就不跟进去了。接着我们看下 3 处,ID_ANDROID_CONTENT 这个常量的值是什么呢:

public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;

R.id.content 不就是decorview根布局的内容部分id嘛。所以 3 处通过findViewById方法获取到的就是decorview对应根布局的内容FrameLayout。

到这里installDecor 方法总算分析完毕了,我们回到PhoneWindow 的setContentView 方法中接着往下看:2 处调用到 mLayoutInflater.inflate 方法将我们在Activity onCreate方法中通过setContentView设置的布局文件装载到mContentParent中,也就是decorview对应根布局的内容FrameLayout中。3 处是用来回调当前Activity的onContentChanged方法,不信我们跟进去getCallback方法去瞧一瞧:

    /**
     * Return the current Callback interface for this window.
     */
    public final Callback getCallback() {
        return mCallback;
    }

可以看到PhoneWindow 的 getCallback方法中直接将 成员变量mCallback return掉,成员变量mCallback是在哪里赋值的呢?不知道大家还记不记得,在Activity的attach方法中,我们创建了PhoneWindow实例对象,并调用PhoneWindow的setCallback方法,将当前activity对象作为参数传入,setCallback方法中直接将我们传入的当前Activity的引用赋值给成员变量mCallback。所以这里肯定回调当前Activity的onContentChanged方法啦。

到此为止DecorView已经被创建并初始化完毕,我们在Activity onCreate方法中通过setContentView设置的布局文件也已经成功添加到DecorView对应根布局的内容FrameLayout中。我们回到最初的ActivityThread 中的handleLaunchActivity方法中继续分析,接下来就到了3 处的handleResumeActivity方法。

这个小节就先到这里啦,我们下个小节见。

相关文章

网友评论

      本文标题:Activity窗口机制源码解析上(8.0)

      本文链接:https://www.haomeiwen.com/subject/repfnftx.html