View显示原理剖析


首先我们要知道,Android程序的主入口是 ActivityThread 的 main函数中,所有应用程序 有且仅有一个ActivityThread.


        public static void main(String[] args) {
//            .....
//            .....
            Looper.prepareMainLooper();
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
//            .......
            Looper.loop();
        }


上面省略了一部分代码,只看我们关注的即可.

可以看到,在main函数里, 为我们创建了个Looper. 并且new 了一个ActivityThread, 接着调用 ActivityThread 的attach(false) 方法.


private void attach(boolean system) {
    .......
    if(!system){  //非系统应用
        final IActivityManager mgr = ActivityManagerNative.getDefault();
        try {
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            // Ignore
        }
    }

}

可以看到,上面代码与 AmS(ActivityManagerService) 进行了关联. 这里有人就想了, 为什么要关联呢 ?

实际,由名字我们可以看出,AmS就是管理我们Activity的, 与之对应的还有 WmS(WindowManagerService)负责显示窗口到界面上,这里我们不讨论它..


 当我们启动APP时, AmS会 调用ApplicationThread的 bindApplication() 然后通过H (extends Handler)类  来传递给ActivityThread 的 handleBindApplicaiont().


public final void bindApplication(String processName, ApplicationInfo appInfo,
        List<ProviderInfo> providers, ComponentName instrumentationName,
        ProfilerInfo profilerInfo, Bundle instrumentationArgs,
        IInstrumentationWatcher instrumentationWatcher,
        IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableOpenGlTrace, boolean isRestrictedBackupMode, boolean persistent,
Configuration config, CompatibilityInfo compatInfo, Map<String, IBinder> services,
        Bundle coreSettings) {
    ..........
    ..........
    AppBindData data = new AppBindData();  //构建Application Data对象
    data.processName = processName;
    data.appInfo = appInfo;
    data.providers = providers;
    data.instrumentationName = instrumentationName;
    data.instrumentationArgs = instrumentationArgs;
    data.instrumentationWatcher = instrumentationWatcher;
    data.instrumentationUiAutomationConnection = instrumentationUiConnection;
    data.debugMode = debugMode;
    data.enableOpenGlTrace = enableOpenGlTrace;
    data.restrictedBackupMode = isRestrictedBackupMode;
    data.persistent = persistent;
    data.config = config;
    data.compatInfo = compatInfo;
    data.initProfilerInfo = profilerInfo;
    sendMessage(H.BIND_APPLICATION, data);  //发送消息 至 ActivityThread.
}

   
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);


接下来我们看 handleBindApplication(data)


private void handleBindApplication(AppBindData data) {
    try {
        //在这之前,data.info还是空值
        data.info = getPackageInfoNoCheck(data.appInfo,data.compatInfo);
        .......
        Application app = data.info.makeApplication(data.restrictedBackupMode, null);

    }
}

可以看到, 这里调用到了 data.info.makeApplication(), 其内创建了ContextImpl 和 Application 我们来看代码~

public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
    if (mApplication != null) {
        return mApplication;
    }

    Application app = null;

    try {
        java.lang.ClassLoader cl = getClassLoader();  // 获取classLoader
        .....
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this); //创建ContextImpl
        app = mActivityThread.mInstrumentation.newApplication(   //创建Application
                cl, appClass, appContext);
        appContext.setOuterContext(app);
    } catch (Exception e) {
        .......
    }
    ....
    mApplication = app
    if(instrumentation!=null){
        instrumentation.callApplicationOnCreate(app);  //调用Application onCreate
    }
}



 
以上步骤就是创建Application的过程... 

当我们要启动某个Activity时,  AmS会调用 scheduleLaunchActivity, 之后会通过 H 发送消息 至ActivityThread 调用 handleLaunchActivity.

public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
        String referrer, IVoiceInteractor voiceInteractor, int procState, Bundle state,
        PersistableBundle persistentState, List<ResultInfo> pendingResults,
        List<ReferrerIntent> pendingNewIntents, boolean notResumed, boolean isForward,
ProfilerInfo profilerInfo) {
    .......
    ActivityClientRecord r = new ActivityClientRecord();  // 构建记录Activity 信息类
    //客户端activity,可以通过该token 通知AmS当前运行的Activity的状态.
    r.token = token;
    r.ident = ident;
    r.intent = intent;
    r.referrer = referrer;
    r.voiceInteractor = voiceInteractor;
    r.activityInfo = info;
    r.compatInfo = compatInfo;
    r.state = state;
    r.persistentState = persistentState;

    r.pendingResults = pendingResults;
    r.pendingIntents = pendingNewIntents;

    r.startsNotResumed = notResumed;
    r.isForward = isForward;

    r.profilerInfo = profilerInfo;

    updatePendingConfiguration(curConfig);

    sendMessage(H.LAUNCH_ACTIVITY, r);  //发送消息.
}

发送消息后会调用 handleLaunchActivity,  在其内部, 会调用peformLaunchActivity,然后创建一个Activity.


private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    .........
    ........
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(  // 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);
        }
    }
}

可以看到这里利用了classLoader 来创建了一个Activity, 创建好Activity 之后 会调用到 Activity的attach方法,在attach方法内部进行一些 变量的赋值,并设置baseContext.


if (activity != null) {
    Context appContext = createBaseContextForActivity(r, 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);
}


接下来就会调用 activity的 performCreate方法,最终会调用 onCreate方法,然而我们在自己的 Activity里 重写到了onCreate.  有没有很熟悉的感觉~~~

接下来到我们自己 的onCreate中,一般我们都会在super.oncreate()后 写上setContentView(int layoutId).

public void setContentView(int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}


这里调用了getWindow().setContentView(),   getWindow 返回的是mWindow, 然而mWindow实际是个PhoneWindow,它在activity.attach() 时 赋值. 我们直接去看PhoneWindow的 setContentView()方法.

@Override
public void setContentView(int layoutResID) {
    // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
    // decor, when theme attributes and the like are crystalized. Do not check the feature
    // before this happens.
    if (mContentParent == null) {
        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 {
        mLayoutInflater.inflate(layoutResID, mContentParent);
    }
    final Callback cb = getCallback();
    if (cb != null && !isDestroyed()) {
        cb.onContentChanged();
    }
}

 由上可以看到, 会进入installDecor方法 。


if (mDecor == null) {
    mDecor = generateDecor();
    mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
    mDecor.setIsRootNamespace(true);
    if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
        mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
    }
}
if (mContentParent == null) {
    mContentParent = generateLayout(mDecor);</span>
    .......
}

这里面主要是 generateDecor()方法 和 generateLayout方法.

generateDecor (); 内部创建一个DecorView,该类为 PhoneWindow内部类, 继承至FrameLayout,实际上 它就是我们的根布局. 在generateLayout内部, 会给decorview 加上一个修饰窗口,  修饰窗口可以理解为我们的标题栏, 之后会找到 id为ID_ANDROID_CONTENT 并强转成ViewGroup  该ViewGroup就是 mContentParent.

接下来回到setContentView 方法内,这时 mContentParent 被成功赋值.  接下来会调用mLayout.inflate(layoutResId,mContentParent), 将你自己的布局加入到mContenetParent中.

到此为止我们的窗口就填充完毕了,  接下来我们要去给显示出来.

当Activity准备完成后, 会通知AmS, 之后会进行一系列的调用, 最终调用到 Activity的makeVisible()方法. 添加就是从这里开始的.

void makeVisible() {
    if (!mWindowAdded) {
        ViewManager wm = getWindowManager();
        wm.addView(mDecor, getWindow().getAttributes());
        mWindowAdded = true;
    }
    mDecor.setVisibility(View.VISIBLE);
}



可以看到 这里我们getWindowManager, 它返回的是Activity的 mWindowManager常亮,是在 attach方法内赋值的,而实现了WindowManager的 类是WindowManagerImpl, 但是WindowManagerImpl内部真正操作的类却是 WindowManagerGlobal.  So~  这里wm.addView 实际上是调用了 WindowManagerGlobal的addView, 下面我们来看看该方法.

 

    public void addView(View view, ViewGroup.LayoutParams params,
                        Display display, Window parentWindow) {
        // 检查参数有效性
        if (view == null) {
            throw new IllegalArgumentException("view must not be null");
        }
        if (display == null) {
            throw new IllegalArgumentException("display must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }
        .......省略一些代码
// 查看窗口类型数值 是否在 1000~ 1999之间, 这之间的值 都为子窗口,如果是则遍历找出该窗口的父窗口.</span>
        if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
            final int count = mViews.size();
            for (int i = 0; i < count; i++) {
                if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                    panelParentView = mViews.get(i);
                }
            }
        }

        // 构建ViewRootImpl
        root = new ViewRootImpl(view.getContext(), display);
        // view设置布局参数
        view.setLayoutParams(wparams);

        mViews.add(view);
        mRoots.add(root);
        mParams.add(wparams);</span>
        // do this last because it fires off messages to start doing things
        try {
            //通过 root.setview view显示到手机窗口中.
            root.setView(view, wparams, panelParentView);
        }




可以看到 ,最后创建了ViewRootImpl, 并且给view设置参数, 并且把ViewRootImpl, view, wparams 分别加入三个集合中.  然后调用root,setView, 在setView内 最终会调用 res.mWindowSession.addToDisPlay()从而通知WmS加入该窗口.


以上,就是显示窗口的步骤.   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值