Activity和普通类的重要区别在于其有生命周期的回调方法,本文意在通过其回调方法的调用,揭开其神秘面纱
1.几个重要的类
(1)ActivityThread
应用启动的时候会创建一个独立的进程,在这个进程里面会有一个主线程,主线程首先做的事就是调用ActivityThread的main方法,也就是说ActivityThread的main方法是应用程序的入口,相当于java的main方法.
注意:ActivityThread不是线程!!!
(2)ApplicationThread
是ActivityThread的私有内部类,继承自ApplicationThreadNative,ApplicationThreadNative继承了Binder并实现了IApplicationThread接口,说明了ApplicationThread具有跨进程通信的能力.
IApplicationThread是一个接口,我们看看它的介绍:
/**
* System private API for communicating with the application. This is given to
* the activity manager by an application when it starts up, for the activity
* manager to tell the application about things it needs to do.
* <p>
* {@hide}
*/
翻译:这是一个用于和应用通信的系统私有API,当应用启动的时候会被应用交给ActivityManager,让ActivityManager告诉应用要做什么.
我们看看这个接口里面的一些方法:
这只是一部分方法,通过上面的部分方法我们能看出这个接口是AMS控制Activity,Service等生命周期的一个接口,ApplicationThread实现了该接口,也就是说AMS通过ApplicationThread实现对Activity,Service等的声明周期的控制.在进程间通信中这个时候AMS是Client,ApplicationThread是Server(实际中AMS持有的是ApplicationThread的代理对象ApplicationThreadProxy)
(3)H
H是ActivityThread的内部类,继承自Handler,简单说H就是一个处理消息的类.
我们在上面已经知道AMS通过ApplicationThread来调用Activity的生命周期方法,那ApplicationThread又是怎么调用的呢? ApplicationThread是通过H来发送Message,然后再由H来处理消息
(4)ActivityClientRecord,ActivityRecord
这两者都是用来描述Activity的,比如Activity的window,配置信息等等,二者有不同,但大体上一样.ActivityClientRecord是在我们应用程序端使用的,ActivityRecord是AMS使用的
(5)Instrumentation
是一个辅助类,用于创建application,开启Activity,调用Activity的各个生命周期的方法.
(6)ViewRoot
ViewRoot可能比较陌生,但是其作用非常重大。所有View的绘制以及事件分发等交互都是通过它来执行或传递的。
ViewRoot对应ViewRootImpl类,它是连接WindowManagerService和DecorView的纽带,View的三大流程(测量(measure),布局(layout),绘制(draw))均通过ViewRoot来完成。
ViewRoot并不属于View树的一份子。从源码实现上来看,它既非View的子类,也非View的父类,但是,它实现了ViewParent接口,这让它可以作为View的名义上的父视图。RootView继承了Handler类,可以接收事件并分发,Android的所有触屏事件、按键事件、界面刷新等事件都是通过ViewRoot进行分发的。同时持有WindowSession通过Binder与WMS通信,同时持有IWindow作为WSM的回调接口,用于例如touch事件的回调。
(7)Window
Window是视图的承载器,内部持有一个 DecorView,而这个DecorView才是 view 的根布局。Window是一个抽象类,实际在Activity中持有的是其子类PhoneWindow。PhoneWindow中有个内部类DecorView,通过创建DecorView来加载Activity中设置的布局R.layout.activity_main。Window 通过WindowManager将DecorView加载其中,并将DecorView交给ViewRoot,进行视图绘制以及其他交互。
(8)DecorView
DecorView是FrameLayout的子类,它可以被认为是Android视图树的根节点视图。DecorView作为顶级View,一般情况下它内部包含一个竖直方向的LinearLayout,在这个LinearLayout里面有上下三个部分,上面是个ViewStub,延迟加载的视图(应该是设置ActionBar,根据Theme设置),中间的是标题栏(根据Theme设置,有的布局没有),下面的是内容栏。具体情况和Android版本及主体有关,以其中一个布局为例,如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:fitsSystemWindows="true">
<!-- Popout bar for action modes -->
<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:layout_width="match_parent"
android:layout_height="?android:attr/windowTitleSize"
style="?android:attr/windowTitleBackgroundStyle">
<TextView android:id="@android:id/title"
style="?android:attr/windowTitleStyle"
android:background="@null"
android:fadingEdge="horizontal"
android:gravity="center_vertical"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
<FrameLayout android:id="@android:id/content"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:foregroundGravity="fill_horizontal|top"
android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>
下面通过代码把上面这些类串起来,看看Activity是怎么显示出我们设置的布局来的.
2.代码分析
我们只关注过程,不关注细节
(1)从ActivityThread的main方法开始
//ActivityThread.java
public static void main(String[] args) {
......
//就是为主线程创建一个looper,用于处理消息
Looper.prepareMainLooper();
//这里记住一点,在创建ActivityThread的时候也创建了ApplicationThread
ActivityThread thread = new ActivityThread();
//这里是重点,主要是与ActivityManagerService交互
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//开始轮询,处理消息
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
在这个方法里面涉及到了Handler,具体Handler的原理可以参考:
https://www.jianshu.com/p/d4415033349d
我们这里着重关注thread.attach(false)方法
private void attach(boolean system) {
...
//AMS运行在一个单独的进程中,与我们的app进程不一样,这里我们通过ActivityManagerNative
//获得其远程代理对象
final IActivityManager mgr = ActivityManagerNative.getDefault();
try {
//mAppThread就是ApplicationThread
//通过上面对ApplicationThread的介绍,我们知道ApplicationThread和AMS关系密切,这里就是二者的交互
mgr.attachApplication(mAppThread);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
...
}
(2)第一步中,该关联的都关联了.下面该启动一个Activity了,启动Activity是由AMS调用的,具体反映就是AMS远程调用ApplicationThread的scheduleLaunchActivity方法(实际是调用ApplicationThreadProxy相关的方法).然后该方法通过H 的sendMessage方法发送了一个消息,我们看看H是怎么处理这个消息的.
case LAUNCH_ACTIVITY: {
//这个obj是远程的AMS发送过来的,告诉app要启动哪个Activity
//AMS怎么知道要启动哪个Activity呢,这是在启动该Activity传入的intent参数中获得的
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
} break;
(3)接着看handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");方法
//ActivityThread.java
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
//先看这个方法做了什么,看完这个方法的解析再往下看
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
r.createdConfig = new Configuration(mConfiguration);
reportSizeConfigurations(r);
Bundle oldState = r.state;
//performLaunchActivity看完之后就看这里
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
if (!r.activity.mFinished && r.startsNotResumed) {
performPauseActivityIfNeeded(r, reason);
if (r.isPreHoneycomb()) {
r.state = oldState;
}
}
} else {
// If there was an error, for any reason, tell the activity manager to stop us.
try {
ActivityManagerNative.getDefault()
.finishActivity(r.token, Activity.RESULT_CANCELED, null,
Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
}
}
performLaunchActivity()方法的主要逻辑(这里只贴出部分主要代码):
①通过反射创建Activity
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
//从这里我们看到Activity的创建用的是Instrumentation辅助类,这里用到了反射,我们不去细究
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
②创建application对象
//创建application对象,每个应用都有一个application,原来是在这里创建的.
//具体的创建者也是Instrumentation
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
③创建window对象
//创建Window对象,对Activity内部成员变量进行赋值,把Activity和Window进行关联
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);
④执行onCreate()方法
//从中我们看出来是用到了Instrumentation来调用Activity的onCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
⑤执行onStart()方法
if (!r.activity.mFinished) {
//这个方法点进去,发现是Instrumentation最终调用了Activity的onStart()方法
activity.performStart();
r.stopped = false;
}
我们着重看下第④个方法
这个方法我们点进去看发现就是回调了我们创建Activity时的onCreate()方法.我们在这个方法里面通过setContentView()来设置页面布局,下面我们看看这个布局是怎么显示出来的
//Activity.java
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
原来是调用window的setContentView(),通过本文开头的解释,我们知道这个Window就是PhoneWindow.
//PhoneWindow.java
@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) {
//初始化DecorView,这个view前面已经介绍
//在这个方法里面除了创建decorview,也创建了mContentParent
//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 {
//没有转场动画就直接把我们设置的layout添加到mContentParent
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
注意:执行完PhoneWindow.setContentView()之后,这时view还没有显示,只是添加到DecorView.
上面五个主要方法执行完之后,我们再回到handleLaunchActivity()方法,我们接着看handleResumeActivity()方法
(4)handleResumeActivity()
从这个方法的名字可以看出来,这是要调用onResume方法了,也就是到了要显示我们设置的layout的地方了
我们只看这个方法最核心的部分:
显示view
//ActivityThread.java
final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume) {
//这个时候,Activity.onResume()已经调用了,但是现在界面还是不可见的
ActivityClientRecord r = performResumeActivity(token, clearHide);
if (r != null) {
final Activity a = r.activity;
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
//decor对用户不可见
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//被添加进WindowManager了,但是这个时候,还是不可见的
wm.addView(decor, l);
}
if (!r.activity.mFinished && willBeVisible
&& r.activity.mDecor != null && !r.hideForNow) {
//在这里,执行了重要的操作,使得DecorView可见
if (r.activity.mVisibleFromClient) {
r.activity.makeVisible();
}
}
}
看看Activity的makeVisible()方法
//Activity.java
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
到此DecorView便可见,显示在屏幕中。但是在这其中,wm.addView(mDecor, getWindow().getAttributes());起到了重要的作用,因为其内部创建了一个ViewRootImpl对象,负责绘制显示各个子View。
具体来看addView()方法,因为WindowManager是个接口,具体是交给WindowManagerImpl来实现的。由于这里使用了桥接模式,WindowManagerImpl又交给WindowManagerGlobal 的addView()方法去实现.
//WindowManagerGlobal
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
......
synchronized (mLock) {
ViewRootImpl root;
//实例化一个ViewRootImpl对象
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
......
try {
//将DecorView交给ViewRootImpl
root.setView(view, wparams, panelParentView);
}catch (RuntimeException e) {
}
}
}
看到其中实例化了ViewRootImpl对象,然后调用其setView()方法。其中setView()方法经过一些列折腾,最终调用了performTraversals()方法,然后依照下图流程层层调用,完成绘制,最终界面才显示出来。
至此,结束,长舒一口气~