Android Activity View加载与绘制流程深入刨析源码
作者:niuyongzhi 发布时间:2023-10-02 18:03:48
1.App的启动流程,从startActivity到Activity被创建。
这个流程主要是ActivityThread和ActivityManagerService之间通过binder进行通信来完成。
ActivityThread可以拿到AMS 的BinderProxy。AMS可以拿到ActivityThread的BinderProxy ApplicationThread。这样双方就可以互相通讯了。
当ApplicationThread 接收到AMS的Binder调用后,会通过handler机制来执行对应的操作。
可以这样说handler和binder是android framework重要的两块基石。
而Activity的创建就是在ActivityThread中调用handleLaunchActivity方法实现。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//创建一个Activity,并调用生命周期onCreate方法
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
//如果Activity成功创建,则会调用生命周期onResume方法。
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
}
}
2.接下来看performLaunchActivity。创建一个Activity。
如果Activity创建成功,先调用activity.attach(),在这个方法中,创建了Activity的PhoneWindow实例。
然后mInstrumentation.callActivityOnCreate,调用了Activity的onCreate生命周期方法。
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
Activity activity = null;
try {
// Instrumentation mInstrumentation;
//拿到类加载器,最后会通过反射的方式创建Activity对象
//(Activity) cl.loadClass(className).newInstance();
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
if(activity!=null){
//调用Activity.attach,
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.voiceInteractor);
}
//通过这个方法调用了Activity的onCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
// activity.performCreate(icicle, persistentState);
// onCreate(icicle, persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
//activity.performCreate(icicle);
// onCreate(icicle);
}
return activity;
}
final void attach(...){
//初始化了window的子类 初始化了PhoneWindow,PhoneWindow中有一个Decoview对象
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
//mWindowManager 是WindowManagerImpl的实例。
mWindowManager = mWindow.getWindowManager();
}
3.setContentView,布局加载流程:
在Activity.onCreate方法中,我们通过setContentView(R.layout.activity_main);就能在界面显示,那android是怎么做到的?
1)调用installDecor(),初始化Decorview和mContentParent。
2)mLayoutInflater.inflate(),将布局文件,解析成android中对应的View。
//getWindow拿到的是PhoneWindow
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
}
@Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
//初始化Decorview和mContentParent
installDecor();
}
//加载并解析传进来的布局文件,并add到mContentParent上。
//这个操作比较耗时,因为要从xml文件中解析,然后再通过反射的方式生成Java中的View对象。
//所以在recyclerView和listView中要对这一步进行缓存优化,
mLayoutInflater.inflate(layoutResID, mContentParent);
}
先看installDecor(),通过generateDecor() new了一个DecoView实例并赋值给了mDecor,
mDecor是PhoneView的一个变量。
//mDecorView是PhoneWindow的一个成员变量
private DecorView mDecor;
private void installDecor() {
//创建mDecor
if (mDecor == null) {
mDecor = generateDecor(-1);
//new DecorView(context, featureId, this, getAttributes());
}
//创建mContentParent,将创建的DecorView作为参数传递
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}
通过generateLayout(decor) 加载了一个系统的layout文件,在android.jar--res--layout目录下。
在mDecor.onResourcesLoaded方法中加载了这个布局,并添加到了mDecor中。
DecorView继承自FrameLayout,是一个真正的view。
然后通过findViewbyid,找到了一个ViewGoup,可以看下面的布局文件。
ID_ANDROID_CONTENT = com.android.internal.R.id.content; ,并把这个返回出去了。
这个view 就installDecor()方法中的mContentParent()
protected ViewGroup generateLayout(DecorView decor) {
int layoutResource;
//这个布局文件就在android.jar--res--layout目录下。
layoutResource = R.layout.screen_simple;
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);
// int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
//这个R.id.content就是定义在screen_simple中的一个FrameLayout
// android:id="@android:id/content"
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
....
return contentParent;
}
//将布局R.layout.screen_simple 加载成view,并添加到DecorView中
void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
......
final View root = inflater.inflate(layoutResource, null);
......
// Put it below the color views.
addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
}
//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>
总结:至此 installDecor();已经完成。主要是创建了mDecorView,并加载了一个系统的布局,R.layout.screen_simple,
将加载得到的View添加到了mDecorView中,并findViewById(R.id.content)的到的View赋值给了mParentContent。
回到setContentView中看第二行代码:
layoutResID 就是传入的布局文件id,mContentParent就是加载的系统的布局文件中id为“content”的view
mLayoutInflater.inflate(layoutResID, mContentParent);
//加载并解析传进来的布局文件,并add到mContentParent上。
mLayoutInflater.inflate(layoutResID, mContentParent);
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root) {
return inflate(resource, root, root != null);
}
public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) {
final Resources res = getContext().getResources();
//通过 Resource 得到了一个XML解析器。
final XmlResourceParser parser = res.getLayout(resource);
try {
//解析我们自定义的layout,并添加到mParentContent上。
//将xml中定义的ViewGroup和View解析成Java对象。
//这块代码会单独写文章讲解
return inflate(parser, root, attachToRoot);
} finally {
parser.close();
}
}
至此上面的关系可以总结为:
Activity-->PhoneWindow-->mDecorView-->addView(R.layout.screen.simple)-->
R.id.content-->mParentContent-->addView(R.layout.activity.main)
我们自己写的layout已经添加到了系统的DecorView中。
4.我们知道View有三个重要的方法onMeasure,onLayout,onDraw,
那这些方法是在哪里调用的?我们创建的View是如何添加到屏幕上的呢?
回到handleLaunchActivity方法中,还有一个handleResumeActivity,通过performResumeActivity 会执行Activity的onResume生命周期方法。
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//创建一个Activity,并调用生命周期onCreate方法
Activity a = performLaunchActivity(r, customIntent);
if (a != null) {
//如果Activity成功创建,则会调用生命周期onResume方法。
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed);
}
}
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;
//通过上面代码我们知道 window 是PhoneWindow
r.window = r.activity.getWindow();
//拿到DecorView
View decor = r.window.getDecorView();
//wm 是 WindowManagerImpl
ViewManager wm = a.getWindowManager();
if (a.mVisibleFromClient) {
a.mWindowAdded = true;
//关键代码,将decorView 添加到wm中
wm.addView(decor, l);
}
}
}
public final ActivityClientRecord performResumeActivity(IBinder token,
boolean clearHide) {
//执行Activity onStart onResume方法
ActivityClientRecord r = mActivities.get(token);
r.activity.performResume();
return r;
}
//Activity中 onStart onResume 生命周期方法
final void performResume(boolean followedByPause, String reason) {
performRestart(true /* start */, reason);
mInstrumentation.callActivityOnResume(this);
}
final void performRestart(boolean start, String reason) {
if (start) {
performStart(reason);
}
}
public void callActivityOnResume(Activity activity) {
activity.mResumed = true;
activity.onResume();
}
执行完onResume方法后:
wm.addView(decor, l);
将Activity的DecorView添加到了wm中。
ViewManager wm = a.getWindowManager();
ViewManager 是一个抽象类,实例是WindowManagerImpl。
在WindowManagerImpl中通过单例模式获取了一个WindowManagerGlobal对象。
既然是单例模式获取的对象,也就在一个进程,ActivityThread 主进程中 只有一个实例。
//WindowManagerImpl.java addView 方法
//WindowManagerGlobal 是一个单例模式,在ActivityThread 主进程中 只有一个实例。
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(View view, ViewGroup.LayoutParams params) {
mGlobal.addView(view, params, mDisplay, mParentWindow);
}
WindowManagerGlobal.java
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;
ViewRootImpl root;
........
root = new ViewRootImpl(view.getContext(), display);
.........
view.setLayoutParams(wparams);
//将decorView ViewRootImpl 存到集合中
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
root.setView(view, wparams, panelParentView);
}
在WindowManagerGlobal中创建了一个ViewRootImpl对象。这是很重要的一个对象。
将传进来的DecorView设置在了root中,root.setView(view, wparams, panelParentView);
ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
mWindowSession = WindowManagerGlobal.getWindowSession();
mThread = Thread.currentThread();
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
// Schedule the first layout -before- adding to the windowmanager,
//to make sure we do the relayout before receiving
// any other events from the system.
//在添加到windowmanager之前进行布局,确保在收到系统的event之前进行relayout
// 出发布局的绘制流程,measure,layout,view 的绘制流程,就是从这来的。
//这个方法保证了,在添加的屏幕前已经完成了测量、绘制。
requestLayout();
try {
//通过binder和WindowManagerService进行通信,将view添加到屏幕上。
// mWindow = new W(this);
// static class W extends IWindow.Stub {}
//添加到屏幕的逻辑,稍后写文章详细分析。
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mInputChannel);
}catch (RemoteException e) {
throw new RuntimeException("Adding window failed", e);
}
}
在setView()方法中有两句很重要的代码。
requestLayout();
res = mWindowSession.addToDisplay()
1) requestLayout()请求布局,调用者行代码会执行view的measue,layou,draw方法。
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
checkThread这有一个很重要的知识点,为啥子线程不能修改主线程创建的view?
//mThread是在ViewRootImp初始初始化是所在的线程。
//在requestLayout时,会获取当前请求布局的线程。
//如果两个线程不一致就会抛异常,只有原始创建的线程,可以修改views
void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
在scheduleTraversals方法中。通过mChoreographer编舞者对象,最后执行了mTraversalRunnable中的方法。这块代码在消息屏障文章中,详细分解。
//开始遍历
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
//发送一个同步消息,在handler机制分析中提过一下,同步屏障。关于这个知识点还会做详细的分析。
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
//mChoreographer 编舞者类。
//通过编舞者,执行 runnable中 doTraversal 方法
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}
在TraversalRunnable中执行了doTraversal()方法。在这里调用了三个重要的方法
performMeasure(),performLayout(),performDraw()。
这也就是为什么View的绘制流程是先调用onMeasure,onLayout,后调用onDraw的原因。
并且这些写方法都是在onResume执行才调用的。所以,这就是我们想拿到View的宽高,在onResume之前拿不到的原因。
private void performTraversals() {
//mView DecorView
final View host = mView;
// Ask host how big it wants to be
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
if (didLayout) {
performLayout(lp, desiredWindowWidth, desiredWindowHeight);
}
performDraw();
}
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
final View host = mView;
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
}
private void performDraw() {
draw(fullRedrawNeeded);
//
}
来源:https://blog.csdn.net/niuyongzhi/article/details/125922833


猜你喜欢
- 谷歌官方推出了一种侧滑菜单的实现方式(抽屉效果),即 DrawerLayout,这个类是在Support Library里的,需要加上and
- 本文实例讲述了Java实现的质因数分解操作。分享给大家供大家参考,具体如下:这里演示java通过递归实现质因数分解,代码如下:import
- 概念在Java中,对象的生命周期包括以下几个阶段:创建阶段(Created)应用阶段(In Use)不可见阶段(Invisible)不可达阶
- C# 提供了以下类型的判断语句:语句描述if一个 if 语句 由一个布尔表达式后跟一个或多个语句组成。if...else
- Mybatis采用责任链模式,通过 * 组织多个 * (插件),通过这些 * 可以改变Mybatis的默认行为(诸如SQL重写之类的),由
- 一、select是什么select——>用于选择更快的结果。基于场景理解比如客户端要查询一个商
- 前期准备首先要先明确有个大体的思路,要实现什么样的功能,了解完成整个模块要运用到哪些方面的知识,以及从做的过程中去发现自己的不足。技术方面的
- 一、Close与Dispose这两种方法的区别调用完了对象的Close方法后,此对象有可能被重新进行使用;而Dispose方法来说,此对象所
- 菜单控制:可以用来判断这个用户是不是有这些角色,没有的话就不展示数据控制:由于数据都是从后端查的,在后端控制权限就可以了<!-- &n
- java 中HttpClient传输xml字符串实例详解介绍:我现在有一个对象page,需要将page对象转换为xml格式并以binary方
- 本文实例讲述了WPF中的ListBox实现按块显示元素的方法。分享给大家供大家参考,具体如下:注意:需要设置ListBox的属性 Scrol
- 前言最近在用 MVP + RxJava + Retrofit 写项目,觉得相对于其他的开发框架,这的确是给我们带来了很多方便,但是在网上搜寻
- 前言: 有时候我们需要实现长按选择文字功能,比如阅读器一般都有这个功能,有时候某个自定义控件上可能就有这种需求,如何实现呢?正好最近还算闲,
- 更新了AS 3.1.2之后,发现新建Kotlin类,类注释依然木有,没办法只有自己动手了。方法很简单,编辑File Header就可以啦。只
- 1:首先。创建一个springboot项目,这里我使用以及构建好基本框架的脚手架,打开是这个样子:Result类:已经封装好了三种返回类型的
- 基础概念百度百科是这么描述归并排序的: 归并操作(merge),也叫归并算法,指的是将两个已经排序的序列合并成一个序列的操作。设有数列{6,
- 上一次接触到编码的知识,还是上大学的时候,那时候学的是通信工程专业,有关编码的内容,不记得是在通信原理还是信息论与编码里面学到的了。却依然记
- 每日一笑下班和实习生一起回家,公交站等车,一乞丐把碗推向实习生乞讨。这时,实习生不慌不忙的说了句:“我不要你的钱,你这钱
- Spring框架的关键组件是面向方面编程(AOP)框架。面向方面的编程不仅打破程序逻辑分成不同的部分称为所谓的担忧。跨越多个点的应用程序的功
- 本文实例为大家分享了基于C#实现网页爬虫的详细代码,供大家参考,具体内容如下HTTP请求工具类:功能:1、获取网页html2、下载网络图片u