老样子,灵魂画手给你们绘制的整体关系图…
Window是一个抽象的概念,每一个Window都对应着一个View和一个ViewRootImpl,Window和View通过ViewRootImpl来建立联系,因此Window并不是不存在的,它是以View的形式存在。这点从ViewManager的定义也可以看得出来,它只提供三个接口方法:addView、updateViewLayout、removeView,这些方法都是针对View的。这说明View才是Window存在的实体。
Window的创建过程
首先要分析Window的创建过程,就必须了解Activity的启动过程。
Activity的启动过程很复杂,最终会由ActivityThread中的handleLaunchActivity()来完成整个启动过程。
在这个方法中会通过performLaunchActivity()方法创建Activity,performLaunchActivity()内部通过类加载器创建Activity的实例对象,并调用其attach()方法为其关联运行过程中所依赖的一系列上下文环境变量以及创建与绑定窗口。
具体不在赘述,更多Activity启动细节请移步Activity的启动过程,地址为:
http://blog.csdn.net/qian520ao/article/details/78156214
//ActivityThreadprivate void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
...
//获取WindowManagerService的Binder引用(proxy端)。
WindowManagerGlobal.initialize(); //会调用Activity的onCreate,onStart,onResotreInstanceState方法
Activity a = performLaunchActivity(r, customIntent); if (a != null) {
...
//会调用Activity的onResume方法.
handleResumeActivity(r.token, false, r.isForward,
!r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);
...
}
}//ActivityThread
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) { //通过类加载器创建Activity
Activity activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
...
//通过LoadedApk的makeApplication方法来创建Application对象
Application app = r.packageInfo.makeApplication(false, mInstrumentation); if (activity != null) {
...
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
mInstrumentation.callActivityOnCreate(activity, r.state); //onStart
activity.performStart();
} return activity;
}
在Activity的attach()方法里,系统会创建Activity所属的Window对象并为其设置回调接口,由于Activity实现了Window的Callback接口,因此当Window接收到外界的状态改变时就会回调Activity的方法。Callback接口中的方法很多,下面举几个比较眼熟的方法。
public interface Callback {
public boolean dispatchTouchEvent(MotionEvent event);
public View onCreatePanelView(int featureId);
public boolean onMenuItemSelected(int featureId, MenuItem item);
public void onContentChanged();
public void onWindowFocusChanged(boolean hasFocus);
public void onAttachedToWindow();
public void onDetachedFromWindow();
}
//Activity final void attach(...) {
//绑定上下文
attachBaseContext(context);
//创建Window,PhoneWindow是Window的唯一具体实现类
mWindow = new PhoneWindow(this, window); //此处的window==null,但不影响
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
... //设置WindowManager
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
//创建完后通过getWindowManager就可以得到WindowManager实例
mWindowManager = mWindow.getWindowManager();//其实它是WindowManagerImpl
}
@Override
public Object getSystemService(@ServiceName @NonNull String name) {
... if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
}
return super.getSystemService(name);
}
等,等一下。看到这里是不是有点懵,Activity的getSystemService根本没有创建WindowManager,那么mWindow.setWindowManager()设置的岂不是空的WindowManager,那这样它的下一步mWindowManager = mWindow.getWindowManager()岂不是无线空循环?
因为PhoneWindow中并没有setWindowManager()方法,所以我们打开Window类看看。
//Window public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mAppName = appName;
mHardwareAccelerated = hardwareAccelerated
|| SystemProperties.getBoolean(PROPERTY_HARDWARE_UI, false);
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
//在此处创建mWindowManager
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
//在WindowManagerImpl类中 public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
类似于PhoneWindow和Window的关系,WindowManager是一个接口,具体的实现是WindowManagerImpl。
到了这里Window已经创建完毕,在上面的performLaunchActivity()方法中我们可以看到调用了onCreate()方法:
//ActivityThread --> performLaunchActivity mInstrumentation.callActivityOnCreate(activity, r.state);
在Activity的onCreate方法里,我们通过setContentView()将view添加到DecorView的mContentParent中,也就是将资源布局文件和phoneWindow关联。
所以在PhoneWindow的最后,因为Activity实现了Callback接口,便可以通过下面代码通知Activity视图已经发生改变。
//PhoneWindow --> setContentView callback.onContentChanged();
经过了上面几个过程,Window和DecorView已经被创建并初始化完毕,Activity的布局文件也成功添加到了DecorView的mContentParent中,但这个时候的DecorView还没有被WindowManager正式添加到Window中。
这里需要理解的是,Window更多表示的是一种抽象功能集合,虽然说早在Activity的attach方法中window就已经被创建了,但是这个时候由于DecorView并没有被WindowManager识别,所以这个时候的Window暂时无法提供具体功能。
总的来说,Window可以成功使用有2个标志:
①View绘制完毕,可以呈现给用户。
②View可以接收外界信息(触摸事件等)。
Window的添加过程
PhoneWindow 只是负责处理一些应用窗口通用的逻辑(设置标题栏,导航栏等)。但是真正完成把一个 View,作为窗口添加到 WmS 的过程是由 WindowManager 来完成的。WindowManager 的具体实现是 WindowManagerImpl。
下面我们继续来分析handleLaunchActivity()方法中handleResumeActivity()的执行过程。
//ActivityThread final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
//把activity数据记录更新到ActivityClientRecord
ActivityClientRecord r = mActivities.get(token);
r = performResumeActivity(token, clearHide, reason);
if (r != null) {
if (r.window == null && !a.mFinished && willBeVisible) {
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
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) {
a.mWindowAdded = true;
wm.addView(decor, l);//把decor添加到窗口上(划重点)
}
}
//屏幕参数发生了改变
performConfigurationChanged(r.activity, r.tmpConfig);
WindowManager.LayoutParams l = r.window.getAttributes();
if (r.activity.mVisibleFromClient) {
ViewManager wm = a.getWindowManager();
View decor = r.window.getDecorView();
wm.updateViewLayout(decor, l);//更新窗口状态
}
... if (r.activity.mVisibleFromClient) {
//已经成功添加到窗口上了(绘制和事件接收),设置为可见
r.activity.makeVisible();
}
//通知ActivityManagerService,Activity完成Resumed
ActivityManagerNative.getDefault().activityResumed(token);
}
}
在上面代码中,首先配置ActivityClientRecord,之后将DecorView设置为INVISIBLE,因为View并未绘制完成,当前的DecorView只是一个有结构的空壳。
然后通过WindowManagerImpl将DecorView正式的添加到窗口上wm.addView(decor, l);,这一步非常非常重要,因为它包括了2个比较重要和常见的过程:Window的添加过程和View的绘制流程。
灵魂画手一出手就知有没有~~下面画一张图来看看整体添加流程,留一个大致的概括印象。
窗口的添加过程如上图所示,我们知道 WmS 运行在单独的进程中。这里 IWindowSession 执行的 addtoDisplay 操作应该是 IPC 调用。接下来的Window添加过程,我们会知道每个应用窗口创建时,最终都会创建一个 ViewRootImpl 对象。
ViewRootImpl 是一很重要的类,类似 ApplicationThread 负责跟AmS通信一样,ViewRootImpl 的一个重要职责就是跟 WmS 通信,它通静态变量 sWindowSession(IWindowSession实例)与 WmS 进行通信。
每个应用进程,仅有一个 sWindowSession 对象,它对应了 WmS 中的 Session 子类,WmS 为每一个应用进程分配一个 Session 对象。WindowState 类有一个 IWindow mClient 参数,是由 Session 调用 addToDisplay 传递过来的,对应了 ViewRootImpl 中的 W 类的实例。
简单的总结一下,ViewRootImpl通过IWindowSession远程IPC通知WmS,并且由W类接收WmS的远程IPC通知。(这个W类和ActivityThread的H类同样精悍的命名,并且也是同样的工作职责!)
图解完Window的添加过程,对整个流程有一个印象和思路,那么下面继续分析源码。
在上面的handleResumeActivity()方法中,我们看到源码通过wm.addView(decor, l);操作DecorView和WindowManager.LayoutParams。上面讲解也说过,因为WindowManager是接口,真正具体实现类是windowManagerImpl。如果Window中类的关系还不太清楚的可以再回到上面的「Window的内部机制」看图解。
//WindowManagerImpl
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
我们看到这个WindowManagerImpl原来也是一个吃空饷的家伙!对于Window(或者可以说是View)的操作都是交由WindowManagerGlobal来处理,WindowManagerGlobal以工厂的形式向外提供自己的实例。这种工作模式是桥接模式,将所有的操作全部委托给WindowManagerGlobal来实现。
在WindowManagerImpl的全局变量中通过单例模式初始化了WindowManagerGlobal,也就是说一个进程就只有一个WindowManagerGlobal对象。
//WindowManagerGlobal
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");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
if (parentWindow != null) {
//调整布局参数,并设置token
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
//如果待删除的view中有当前view,删除它
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
}
// The previous removeView() had not completed executing. Now it has.
//之前移除View并没有完成删除操作,现在正式删除该view
}
//如果这是一个子窗口个(popupWindow),找到它的父窗口。
//最本质的作用是使用父窗口的token(viewRootImpl的W类,也就是IWindow)
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,并且将view与之绑定
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);//将当前view添加到mViews集合中
mRoots.add(root);//将当前ViewRootImpl添加到mRoots集合中
mParams.add(wparams);//将当前window的params添加到mParams集合中
}
... //通过ViewRootImpl的setView方法,完成view的绘制流程,并添加到window上。
root.setView(view, wparams, panelParentView);
}
在上面代码中有2个比较重要的知识点
- 在WindowManagerGlobal中有如下几个重要的集合
//存储所有Window对应的View
private final ArrayList mViews = new ArrayList();
//存储所有Window对应的ViewRootImpl
private final ArrayList mRoots = new ArrayList();
//存储所有Window对应的布局参数
private final ArrayList mParams =
new ArrayList();
//存储正被删除的View对象(已经调用removeView但是还未完成删除操作的Window对象)
private final ArraySet mDyingViews = new ArraySet();
- token
在源码中token一般代表的是Binder对象,作用于IPC进程间数据通讯。并且它也包含着此次通讯所需要的信息,在ViewRootImpl里,token用来表示mWindow(W类,即IWindow),并且在WmS中只有符合要求的token才能让Window正常显示。所以上面提出的问题
我们能否在Acitivty的onCreate()中创建popupWindow并显示呢?
就与之有关,我们暂时把token放一边,走完Window的添加过程先。(最后分析token)
言归正传,我们继续看代码。
在WindowManagerGlobal的addView()方法里,最后调用ViewRootImpl的setView方法,处理添加过程。
//WindowManagerGlobal --> addView
//创建ViewRootImpl,并且将view与之绑定
root = new ViewRootImpl(view.getContext(), display);
//通过ViewRootImpl的setView方法,完成view的绘制流程,并添加到window上。
root.setView(view, wparams, panelParentView);
通过上面这个代码可知,WindowManagerGlobal将View的处理操作全权交给ViewRootImpl,而且上面我们也提到了,View成功添加到Window,无非就是展现视图和用户交互。
①View绘制完毕,可以呈现给用户。
②View可以接收外界信息(触摸事件等)。
在ViewRootImpl的setView()方法中,将会完成上面2个艰巨而又伟大的任务。
//ViewRootImpl
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
int res;
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();//View的绘制流程
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
//创建InputChannel
mInputChannel = new InputChannel();
}
try {
//通过WindowSession进行IPC调用,将View添加到Window上
//mWindow即W类,用来接收WmS信息
//同时通过InputChannel接收触摸事件回调
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
}
...
//处理触摸事件回调
mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
Looper.myLooper());
...
}
在ViewRootImpl的setView()方法里,执行requestLayout()方法完成View的绘制流程(下篇文章专门讲解),并且通过WindowSession将View和InputChannel添加到WmS中,从而将View添加到Window上并且接收触摸事件。
通过mWindowSession来完成Window的添加过程 ,mWindowSession的类型是IWindowSession,是一个Bindler对象,真正的实现类是Session,也就是Window的添加是一次IPC调用。(mWindowSession在ViewRootImpl的构造函数中通过WindowManagerGlobal.getWindowSession();创建)
同时将mWindow(即 W extends IWindow.Stub)发送给WmS,用来接收WmS信息。
//Session
final WindowManagerService mService;
@Override
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, Rect outContentInsets, Rect outStableInsets,
Rect outOutsets, InputChannel outInputChannel) {
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId,
outContentInsets, outStableInsets, outOutsets, outInputChannel);
}
如此一来,Window的添加请求就交给WmS去处理了,在WmS内部会为每一个应用保留一个单独的Session。在WmS 端会创建一个WindowState对象用来表示当前添加的窗口。 WmS负责管理这里些 WindowState 对象。至此,Window的添加过程就结束了。
至于Window的删除和更新过程,举一反三,也是使用WindowManagerGlobal对ViewRootImpl的操作,最终也是通过Session的IPC跨进程通信通知到WmS。整个过程的本质都是同出一辙的。
Window中的token
在WindowManager的LayoutParams中,与type同等重要的还有token。
上面说到:在源码中token一般代表的是Binder对象,作用于IPC进程间数据通讯。并且它也包含着此次通讯所需要的信息,在ViewRootImpl里,token用来表示mWindow(W类,即IWindow),并且在WmS中只有符合要求的token才能让Window正常显示。
先剧透一下,在Window中,token(LayoutParams中的token)分为以下2种情况
应用窗口 : token表示的是activity的mToken(ActivityRecord)
子窗口 : token表示的是父窗口的W对象,也就是mWindow(IWindow)
Activity的attach()
我们通过源码分析token的流程,顺带再巩固一遍Window的创建流程。
在Activity的attach()方法中
//Activity
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) {
attachBaseContext(context);
mToken = token;//ActivityRecord
mWindow = new PhoneWindow(this, window);
...
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
}
创建windowManager
在Window中创建windowManager,并且全局变量mAppToken就是上面代码传入的mToken(ActivityRecord)
//Window
private IBinder mAppToken;//Activity的mToken
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,
boolean hardwareAccelerated) {
mAppToken = appToken;
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}
//WindowManagerImpl
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
//parentWindow就是activity的Window
return new WindowManagerImpl(mContext, parentWindow);
}
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
我们拿出上面的关系图,目前已经分析了WindowManagerImpl,接下来就是WindowManagerGlobal的addView()方法。
//WindowManagerGlobal public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
... final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
//parentWindow就是Acitivty的Window
if (parentWindow != null) {
//1.调整布局参数,并设置token
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
//2.如果这是一个子窗口个(popupWindow),找到它的父窗口。
//3.最本质的作用是使用父窗口的token(viewRootImpl的W类,也就是IWindow)
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);
//从mRoots找到相同的W类,再从mViews找到父View
}
}
}
//4.创建ViewRootImpl,并且将view与之绑定
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
}
//5.通过ViewRootImpl的setView方法,完成view的绘制流程,并添加到window上。
root.setView(view, wparams, panelParentView);
}
我们一步一步分析,先看①,parentWindow.adjustLayoutParamsForSubWindow(wparams);
//Window
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
//如果它是子窗口
if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
if (wp.token == null) {
View decor = peekDecorView();
if (decor != null) {
wp.token = decor.getWindowToken();//分析View的getWindowToken
}
}
}
... else {
//应用窗口(dialog的情况)
//此时的wp.token即为mAppToken
if (wp.token == null) {
wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
}
}
}
我们看到如果是应用窗口,wp.token==null的情况,就会给他赋值mAppToken,而这个mAppToken就是我们上面在Activity的attach()方法中传入的mToken!
我们再分析子窗口的token,接上面的decor.getWindowToken()
//View
AttachInfo mAttachInfo;
/**
* Retrieve a unique token identifying the window this view is attached to.
* @return Return the window's token for use in
* {@link WindowManager.LayoutParams#token WindowManager.LayoutParams.token}.
*/
public IBinder getWindowToken() {
return mAttachInfo != null ? mAttachInfo.mWindowToken : null;
}
ViewRootImpl
那么View的mAttachInfo又是如何赋值的呢?
我们接着看第③root = new ViewRootImpl(view.getContext(), display);
//ViewRootImpl类
public ViewRootImpl(Context context, Display display) {
mWindowSession = WindowManagerGlobal.getWindowSession();
mWindow = new W(this);//W是用于接收WmS通知的
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);
...
}
//View类
AttachInfo(IWindowSession session, IWindow window, Display display,
ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) {
mSession = session;
mWindow = window;
mWindowToken = window.asBinder();//即W类
mDisplay = display;
mViewRootImpl = viewRootImpl;
mHandler = handler;
mRootCallbacks = effectPlayer;
}
那么,如果这个View是ViewGroup,如何确保它的所有子View都能调用getWindowToken()获取到mAttachInfo的mWindowToken呢?
这里我们涉及一点View的绘制流程看一下源码。
// ViewRootImpl
private void performTraversals() {
...
if (mFirst) {
//第一次执行performTraversals时,会把自己的mAttachInfo关联到所有的子View
host.dispatchAttachedToWindow(mAttachInfo, 0);
}
}
//ViewGroup.java
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
//关联到所有子View
child.dispatchAttachedToWindow(info,visibility | (child.mViewFlags & VISIBILITY_MASK));
}
}
//View.java
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
mAttachInfo = info;
...
}
这里由于篇幅原因,部分省略,有兴趣的同学可以点击左下角的阅读原文进行内容查看。
所以无论是应用窗口,还是子窗口,token 不能为空。否则会抛出异常,并且应用窗口的token 必须是某个 Activity 的 mToken,子窗口的token 必须是父窗口的 IWindow 对象。而且子窗口还需等父窗口添加成功之后才可添加到Window上。
如果对这部分源码敢兴趣的请查看RxEasyHttp源码,由于此部分涉及的知识点和内容比较多,篇幅问题,准备在下一篇文章中单独介绍网络数据缓存的各大场景实现,敬请期待!
结语
至此,Window的机制已经探索的有些眉目,阅读源码read the fuck source code能够更好的了解到Android的各种机制流程,而且能够熟悉源码编写的码神他们的编写风格,例如performLaunchActivity()就是Activity的整体流程开始,performTraversals()就是开始绘制View,还有performMeasure()、performLayout()、performDraw()等等,并且探索了Window机制后,我们就能比较容易的上手View的绘制流程。
虽然说阅读源码在短时间内可能没办法体现出多大的作用,但是这是一种循序渐进积累和爆发的过程,只要不断的进步,量变终究能引起质变。