Android源码之Activity从启动到显示到界面

前言:

在上两篇文章中对setContentView()是如何将我们的内容视图添加到系统提供的根视图中以及PhoneWindow的创建等做了粗略的分析,且这个时候内容视图还完全处于内存中 我们是还看不见的,本篇趁热打铁继续跟进 将要了解Activity是如何将DecorView添加到Window中 以及是如何将DecorView绘制到屏幕上的

分析要点:

老规矩 先来张Activity启动流程图:


都知道系统源码中有一个全局的Handler 不管是Activity的启动还是Service的启动或绑定 亦或广播的注册 都会由它发送消息处理,而这个Handler对消息的处理就在ActivityThread类中 本文也将从ActivityThread中着手去分析Activity启动后生命周期是如何执行的。

源码阅读:

因对于startActivity()和通过Launch启动App的启动流程尚且不熟悉 所以这里直接跳到Activity已经开始启动的流程着手分析 首先来到ActivityThread#handleLaunchActivity() 所有Activity的启动都会调用这里 同时也会只捡要紧的函数看

 private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;
        if (r.profilerInfo != null) {
            mProfiler.setProfiler(r.profilerInfo);
            mProfiler.startProfiling();
        }
        // Make sure we are running with the most recent config.
        handleConfigurationChanged(null, null);

        // WindowManagerGlobal初始化工作 这个WindowManagerGlobal是非常重要的一个类
        WindowManagerGlobal.initialize();
        
        //Activity就是在这里创建的
        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;

            //Activity的onResume() 以及内容视图的绘制就是从这个函数开始的
            handleResumeActivity(r.token, false, r.isForward,!r.activity.mFinished && !r.startsNotResumed);
        }
}

先看Activity是如何创建的

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        if (r.packageInfo == null) {
            r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                    Context.CONTEXT_INCLUDE_CODE);
        }
        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }
        //这里通过反射的方式创建Activity
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
            StrictMode.incrementExpectedActivityCount(activity.getClass());
            r.intent.setExtrasClassLoader(cl);
            r.intent.prepareToEnterProcess();
            if (r.state != null) {
                r.state.setClassLoader(cl);
            }
        } catch (Exception e) {
        }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);
            if (activity != null) {
                Context appContext = createBaseContextForActivity(r, activity);
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);

                //这里调用了activity#attach()进行PhoneWindow的创建以及一些Cllback监听的初始化和非常重要的WindowManagerImpl初始化等操作
                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);

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                activity.mCalled = false;
                
                //Activity的onCreate()这里被调用 区别就是参数不同
                if (r.isPersistable()) {    
                    mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
                } else {
                    mInstrumentation.callActivityOnCreate(activity, r.state);
                }
                r.activity = activity;
                r.stopped = true;
                
                //Activity的onStart()在这里被调用
                if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
                .................................
        return activity;
    }

mInstrumentation.callActivityOnCreate()Activtiy#onCreate()和Activity#onStart()

//这个Instrumentation类似于AppCompatActivity中的AppCompatDelegate一样是一个代理类
public class Instrumentation {

public void callActivityOnCreate(Activity activity, Bundle icicle) {
        prePerformCreate(activity);
        //onCreate()
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }
    //Activity#onStart()
public void callActivityOnStart(Activity activity) {
        activity.onStart();
    }
} 
 .................分割线................
 
// 跟进activity.performCreate(icicle)
 
class Activity{
    //一个参数
    final void performCreate(Bundle icicle) {
        restoreHasCurrentPermissionRequest(icicle);
        //执行OnonCreate()
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }
    //2个参数
    final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        restoreHasCurrentPermissionRequest(icicle);
        //执行OnonCreate()
        onCreate(icicle, persistentState);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }
    
     final void performStart() {
        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
        mFragments.noteStateNotSaved();
        mCalled = false;
        mFragments.execPendingActions();

        //通过调用Instrumentation类的callActivityOnStart执行OnStart()
        mInstrumentation.callActivityOnStart(this);
        mFragments.dispatchStart();
        mFragments.reportLoaderStart();
        ........
    }
}

Activity创建好了 并且也执行到onStart() 再返回去看Activity的onResume()以及内容视图的绘制过程

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // Activity#onResume()在这里执行 且此时内容还未绘制 也验证了onResume()后视图可见 是一个错误的观念 至少需要等一会
        ActivityClientRecord r = performResumeActivity(token, clearHide);
        
        /*****************重点将从这里开始**************************/
        
       if (r.window == null && !a.mFinished && willBeVisible) {
                //获取Activity中创建的Window对象
                r.window = r.activity.getWindow();
                
                //获取Window中的DecorView
                View decor = r.window.getDecorView();
                
                //INVISIBLE虽然不可见 但是却能占据window空间
                decor.setVisibility(View.INVISIBLE);
                
                //这里获取WindowManager的实现类WindowManagerImpl这是一个很重要的类
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;

                    //这一步执行将DecorView添加到Window窗口中和针对DecorView的测量 布局 绘制操作
                    wm.addView(decor, l);
                }
            }
            
            .................... 
  }

先看Activity的onResume()是如何执行的

public final ActivityClientRecord performResumeActivity(IBinder token,
            boolean clearHide) {
        ............
                //onResume()在这里被执行
                r.activity.performResume();
        ............
        
        return r;
    }

DecorView是如何添加到Window中的? 且WindowManagerImpl是在Activity#attach()时初始化的 就要从WindowManagerImpl类开始分析 上面也是调用的 WindowManagerImpl的addView()

public final class WindowManagerImpl implements WindowManager {

//WindowManagerGlobal
 private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
 
 @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }
}   

 /*******************WindowManagerGlobal************************/
public final class WindowManagerGlobal {
    //这里声明一些集合 来缓存我们加载过的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>();
            
  public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
        
        if (parentWindow != null) { 
    parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } 
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // Start watching for system property changes.
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };
                SystemProperties.addChangeCallback(mSystemPropertyUpdater);
            }

            
            mRoots.get(index).doDie();
           
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);
            //这个view就是DecorView
            mViews.add(view);
            //ViewRootImpl 绘制好了View
            mRoots.add(root);
            //Window#Params
            mParams.add(wparams);
        }
        // do this last because it fires off messages to start doing things
        try {
           /*****************从这里开始 就开启正式的绘制流程*******/
           
            root.setView(view, wparams, panelParentView);
        } catch (RuntimeException e) {
            // BadTokenException or InvalidDisplayException, clean up.
            synchronized (mLock) {
                final int index = findViewLocked(view, false);
                if (index >= 0) {
                    removeViewLocked(index, true);
                }
            }
            throw e;
        }
}

继续深入 ViewRootImpl.setView()

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
             ...........................................
                //执行绘制操作
                requestLayout();
                if ((mWindowAttributes.inputFeatures
                        & WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
                    mInputChannel = new InputChannel();
                }
                try {
                    mOrigWindowType = mWindowAttributes.type;
                    mAttachInfo.mRecomputeGlobalAttributes = true;
                    collectViewAttributes();

                    //这里使用IPC WMS将完成窗口的添加过程 这块我也不太下熟悉就跳过了
                    res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                            getHostVisibility(), mDisplay.getDisplayId(),
                            mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                            mAttachInfo.mOutsets, mInputChannel);
                } 
           //这句代码也很重要 将ViewRootImpl设置为DecorView的 ViewParent                
           view.assignParent(this);

}                

requestLayout()绘制流程

@Override
public void requestLayout() {
     if (!mHandlingLayoutInLayoutRequest) {
          checkThread();
          mLayoutRequested = true;
          scheduleTraversals();
     }
 }
 
void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

            //Choreographer类中有一个Handler通过发送消息执行TraversalRunnable的run()方法
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
} 

final class TraversalRunnable implements Runnable {
        @Override
        public void run() {
            doTraversal();
        }
    } 
    
 void doTraversal() {
            .........      
            performTraversals();
            ..........
    }
//到这函数 针对DecorView的测量 布局 绘制 都将完成    
private void performTraversals() {
    .............
    if (!mStopped || mReportNextDraw) {
      //确定测量模式和测量规格
       boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
       (relayoutResult&WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE) != 0);
       if (focusChangedDueToTouchMode || mWidth != host.getMeasuredWidth()
       || mHeight != host.getMeasuredHeight() || contentInsetsChanged) {
       int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
       int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);

                    
      //将调用DecorView的Measure()
      performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
     //将调用DecorView的Layout()
     performLayout(lp, desiredWindowWidth, desiredWindowHeight);
     
     //将调用DecorView的Draw()
     performDraw();
}                             

测量 布局 绘制完成 再通过WMS完成窗口的添加过程 到此DecorView才真正显示到我们的屏幕上了


最后再多看一眼view.assignParent(this);将ViewRootImpl设置为DecorView的 ViewParent 这样的目的是什么

class View{

    void assignParent(ViewParent parent) {
        if (mParent == null) {
            mParent = parent;
        } else if (parent == null) {
            mParent = null;
    }  
    //当我们调用View的 requestLayout()
    @CallSuper
    public void requestLayout() {
        if (mMeasureCache != null) mMeasureCache.clear();

        if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
            // Only trigger request-during-layout logic if this is the view requesting it,
            // not the views in its parent hierarchy
            ViewRootImpl viewRoot = getViewRootImpl();
            if (viewRoot != null && viewRoot.isInLayout()) {
                if (!viewRoot.requestLayoutDuringLayout(this)) {
                    return;
                }
            }
            mAttachInfo.mViewRequestingLayout = this;
        }

        mPrivateFlags |= PFLAG_FORCE_LAYOUT;
        mPrivateFlags |= PFLAG_INVALIDATED;

        if (mParent != null && !mParent.isLayoutRequested()) {
            //将会一直获取到父容器的 mParent.requestLayout();
            mParent.requestLayout();
        }
    }          
}

到这里就很明白了平时我们在调用View的requestLayout()进行重新测量 布局 绘制时 会一直走到 ViewRootImpl的requestLayout()函数将DecorView重新测量 布局 绘制 由此看出在不是很必要的情况下不能直接调用该函数来刷新视图 因为这个过程是很耗CUP的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,711评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,932评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,770评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,799评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,697评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,069评论 1 276
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,535评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,200评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,353评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,290评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,331评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,020评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,610评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,694评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,927评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,330评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,904评论 2 341

推荐阅读更多精彩内容