做为 Android 四大组件之一的 Activity 个人印象中就是用来展现界面的,在很长一段时间里,只要说起界面、UI、View 我脑子里第一个闪过的就是 Activity ,个人理解中一直认为 界面、UI、View == Activity
。其实呢非也!若是你曾经和我同样,提及 Actvity 就是聊他的生命周期,各个方法背的倒背如流,但也仅限于此而已并无真正的去认识 Activity 的话,那么请跟我一块儿从【 Activity 的组成】、【启动】、【显示】来从新认识一下 Activityhtml
首先明确一下 Activity 的基本概念,咱们先来看下 Google 官方对 Activity 给出的定义:java
Activity 是一个应用组件,用户可与其提供的屏幕进行交互,以执行拨打电话、拍摄照片、发送电子邮件或查看地图等操做。 每一个 Activity 都会得到一个用于绘制其用户界面的窗口。窗口一般会充满屏幕,但也可小于屏幕并浮动在其余窗口之上。android
读完了好像也没整明白。架构
咱们抛开以前对 Activity 的印象,从代码层面上来看他就是一个普通的 Java 类,本质上并非一个 View 或 ViewGroup,他没有继承任何 View 和 ViewGroup 相关类。app
从源码中能够看到 Activity 实现了 Window 、KeyEvent 等一系列的 Callback 以及 Listener 。看到这些大概能够知道 Activity 的职责至关于一个控制器了,接收各类回调以及处理监听事件。ide
基于 Google 官方的定义和 Activity 源码,Activity 的大概职责能够总结成一张图:oop
控制器
角色,负责控制生命周期和事件处理。是时候来揭开 Activity 的庐山真面目了。一图胜千言!先来看一张图。布局
上图描述了 Activity 从外到内大概的一个层级结构post
顶层 View
当前 Activity 全部 View 的祖先, 即当前 Activity 视图树根节点。ui
DecorView 本质上是一个 FrameLayout ( DecorView 类继承自 FrameLayout )。
其内部包含一个竖直布局的 LinearLayout ,分为2部分:
id 为
title_container
的 TitleBar 。
id 为
content
的 Content 。在Activity中经过 setContentView()所设置的布局文件最终就是被添加到此处的 Content 中。
Content 内部的视图树对应的就是 Activity 的布局文件。
视图承载器
承载视图 View 的显示。
PhoneWindow 为 Window 的实现类。每一个 Activity 均会建立一个 Window 用以承载 View ,是视图真正的控制者。
链接器
WindowManager
和 DecorView
的纽带。measure
、layout
、draw
。对应实现类为 ViewRootImpl ,实现了 ViewParent 接口。做为 WindowManagerGlobal 大部份内部实现的实际实现者,需负责与 WindowManagerService 交互通讯,以调整窗口的位置大小,以及对来自 WindowManagerService 的事件(如窗口尺寸改变等)做出相应的处理。
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
......
ViewRootImpl root;
......
root = new ViewRootImpl(view.getContext(), display);
try {
//最终实现,调用 ViewRootImpl setView 方法。
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
......
}
}
复制代码
public ViewRootImpl(Context context, Display display) {
......
mWindowSession = WindowManagerGlobal.getWindowSession();
......
}
//与 WindowManagerService 创建远程链接
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
复制代码
在简单分析了 Activity 各个组成元件以后,接下来就是看这些元件是如何完成完整的 Activity 组装并将 View 呈现到用户面前。
一图胜千言:
下面跟随上图的流程走一遍源码(为避免篇幅过长只展现重点代码)。
这部分主要作的是一些初始化设置的工做,为 Actvity 最终呈如今用户面前作准备。
作为 Android 应用程序的入口类,先来看下 ActivityTread 作了什么工做。
源码路径:ActivityTread 类 main 方法。
public static void main(String[] args) {
.......
Looper.prepareMainLooper();
.......
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
.......
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
复制代码
方法解析:
ActivityTread 的 main 方法初始化了一个 Looper消息循环用以接收主线程的操做消息。
关于Looper消息循环详细介绍能够查看 Android Handler浅析一文,这里就再也不展开。
Application 启动后,主线程会收到 LAUNCH_ACTIVITY
消息,该消息由 ActivityTread 内部类 H (继承自 Handler)
接收。
源码路径:ActivityTread/H 类 handleMessage 方法。
public void handleMessage(Message msg) {
switch (msg.what) {
case LAUNCH_ACTIVITY: {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
//重点看这个方法
handleLaunchActivity(r, null, "LAUNCH_ACTIVITY");
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
} break;
}
复制代码
接收消息后会调用 handleLaunchActivity()
方法,下面重点都在 handleLaunchActivity()
方法里面了。
源码路径:ActivityTread 类 handleLaunchActivity 方法。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
.......
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
......
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
}
......
}
复制代码
方法解析:
次方法内部重点看 performLaunchActivity()
和 handleResumeActivity()
方法的调用。
先来看 performLaunchActivity()
方法,此方法就开始了咱们上图的第 3~9
步的流程,直到流程走完,方法调用栈会回退至 handleLaunchActivity()
中继续往下执行,调用 handleResumeActivity()
。
源码路径:ActivityTread 类 performLaunchActivity 方法。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
.......
Activity activity = null;
try {
//经过 Activity 类名构建 Actvity 对象
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
......
} catch (Exception e) {
.......
}
try {
......
if (activity != null) {
......
//为当前 Activity 初始一个 Window 并设置 WindowManager
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);
......
//经过 Instrumentation 调用 Activity 的 onCreate 方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
......
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
......
}
return activity;
}
复制代码
方法解析:
performLaunchActivity
方法作了三件重要的事:
activity.attach
方法为当前 Activity 初始化一个 Window 对象。performLaunchActivity
方法中调用 activity.attach
方法为当前 Activity 初始化一个 Window 对象,咱们来看下 activity.attach
方法的具体实现。
源码路径:Activity 类 attach 方法。
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) {
......
//为 mWindow 变量赋值,实例化一个 PhoneWindow
mWindow = new PhoneWindow(this, window, activityConfigCallback);
......
//为 mWindow 设置 WindowManager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
......
//为 mWindowManager 变量赋值
mWindowManager = mWindow.getWindowManager();
.......
}
复制代码
attach
方法中为 Activity 的 mWindow 变量实例化了一个 PhoneWindow 对象,PhoneWindow 是 Window 的一个具体实现,这在上文中已经说过,就再也不赘述了。
performLaunchActivity
方法中经过 Instrumentation 调用 Activity 的 onCreate 方法,该方法的具体实如今 Activity 中。 通常来讲咱们都会重写 Activity 的 onCreate 方法并在里面调用 setContentView
。下面来看下咱们熟悉的 setContentView
里面作了些什么。
源码路径:Activity 类 setContentView 方法。
public void setContentView(@LayoutRes int layoutResID) {
//调用 PhoneWindow 的 setContentView
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
复制代码
Activity 的 setContentView
实际的实现来自 PhoneWindow,能够看下 getWindow()
方法返回的其实就是步骤5
里 attach
方法赋值的 mWindow
对象。
源码路径:Activity 类 getWindow 方法。
public Window getWindow() {
return mWindow;
}
复制代码
既然这样咱们不妨跟进 PhoneWindow 中一探究竟:
源码路径:PhoneWindow 类 setContentView 方法。
@Override
public void setContentView(int layoutResID) {
// 如mContentParent为空,建立一个DecroView
if (mContentParent == null) {
//初始化 DecroView
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 {
//为 mContentParent 添加布局文件
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Window.Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
//内容改变回调
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
复制代码
方法解析:
mContentParent
这个变量就是咱们上文在介绍 DecroView
时提到的 FrameLayout 对应的布局部分。installDecor()
方法为 PhoneWindow 初始化一个 DecroView 对象。mContentParent
中。PhoneWindow 的 setContentView 方法会为当前的 Window 初始化一个 DecorView。
源码路径:PhoneWindow 类 installDecor 方法。
private void installDecor() {
if (mDecor == null) {
// 建立 DecorView 对象
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
}
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
// 为 DecorView 的 content 设置布局格式
mContentParent = generateLayout(mDecor);
......
}
}
复制代码
方法解析:
1.调用 generateDecor
方法建立 DecorView 对象,generateDecor
实现很简单直接。
源码路径:PhoneWindow 类 generateDecor 方法。
protected DecorView generateDecor(int featureId) {
......
return new DecorView(context, featureId, this, getAttributes());
}
复制代码
generateLayout
方法为 DecorView 的 content 设置布局格式。源码路径:PhoneWindow 类 generateLayout 方法。
protected ViewGroup generateLayout(DecorView decor) {
//获取窗口样式信息
TypedArray a = getWindowStyle();
......
// Inflate the window decor.
//根据主题样式,加载窗口布局
int layoutResource;
int features = getLocalFeatures();
......
mDecor.startChanging();
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// 获取 Decorview 对应的 content 的 FrameLayout
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
if (contentParent == null) {
throw new RuntimeException("Window couldn't find content container view");
}
......
mDecor.finishChanging();
return contentParent;
}
复制代码
这个步骤的源码步骤7
中已经贴出来了,在 setContentView
方法中:
//为 mContentParent 添加布局文件
mLayoutInflater.inflate(layoutResID, mContentParent);
复制代码
到这里 handleLaunchActivity
方法中的 performLaunchActivity
方法这条线就差很少走完了,如今回到 handleLaunchActivity
方法中继续往下执行,下面的关键方法是 handleResumeActivity
。
先看 handleResumeActivity
方法的重点代码。
源码路径:ActivityThread 类 handleResumeActivity 方法。
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
......
// TODO Push resumeArgs into the activity for consideration
// 调用Activity的onResume()
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
final Activity a = r.activity;
......
if (r.window == null && !a.mFinished && willBeVisible) {
.......
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
//添加 DecorView 到 Window
wm.addView(decor, l);
}
......
}
......
}
}
复制代码
handleResumeActivity
方法中经过调用 performResumeActivity
方法来执行 Activity onResume() 方法。具体实现有兴趣的能够本身跟进源码进去看下,这里就再也不贴源码了,文章篇幅已经长的头皮发麻了。
handleResumeActivity
方法中经过调用 WindowManager 的 addView
方法将以前流程中已赋值的 DecorView 对象添加至当前 Window。
初始化 ViewRootImpl 工做就是在 WindowManager 的 addView
方法中进行的。而 WindowManager 是一个接口,对应的实现类是 WindowManagerImpl
,那就去 WindowManagerImpl 中找 addView
的具体实现咯。
源码路径:WindowManagerImpl 类 addView 方法。
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
复制代码
方法解析:
该方法内部直接调用 WindowManagerGlobal
的 addView
方法,WindowManager 的 addView
方法由 WindowManagerGlobal 代理了。
源码路径:WindowManagerGlobal 类 addView 方法。
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
.......
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
......
//建立ViewRootImpl对象
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
//WindowManager将DecorView实例对象交给ViewRootImpl 绘制View
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
......
throw e;
}
}
}
复制代码
WindowManagerGlobal 类 addView 方法在建立 ViewRootImpl 对象后调用其 setView
方法将将 DecorView 实例对象交给 ViewRootImpl 用以绘制 View 。
全部一切准备就绪以后咱们终于开始对咱们 Activity 的布局进行绘制了。最终将调用 ViewRootImpl 的 performTraversals
方法(此方法贼长)开始 View 绘制的三大流程,measure、layout、draw
。
源码路径:ViewRootImpl 类 performTraversals 方法。
private void performTraversals() {
...
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
...
//执行测量流程
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
//执行布局流程
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
...
//执行绘制流程
performDraw();
}
复制代码
OK到此 Activity 的部件组装以及显示流程就梳理了一个大概,文章篇幅有点长,可是内容不算多,贴了不少代码,有兴趣的能够跟着个人流程本身跟进源码看下加深印象。
参考文献: