本篇内容介绍了“Android Activity View加载与绘制流程源码分析”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!
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); // }
“Android Activity View加载与绘制流程源码分析”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。