源码分析篇 - Android绘制流程(一)窗口启动流程分析

时间:2022-09-30 11:19:18

Activity、View、Window之间的关系可以用以下的简要UML关系图表示,在这里贴出来,比较能够帮组后面流程分析部分的阅读。

源码分析篇 - Android绘制流程(一)窗口启动流程分析

一、Activity的启动流程

  在startActivity()后,经过一些逻辑流程会通知到ActivityManagerService(后面以AMS简称),AMS接收到启动acitivty的请求后,会通过跨进程通信调用AcitivtyThread.handleLauncherActivity()方法,我们从这里开始分析,首先来看handleLauncherActivity()方法。

   private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
     
...// Initialize before creating the activity
WindowManagerGlobal.initialize(); Activity a = performLaunchActivity(r, customIntent);

      if (a != null) {
        r.createdConfig = new Configuration(mConfiguration);
        reportSizeConfigurations(r);
        Bundle oldState = r.state;

        //该方法会调用到Activity的onResume()方法
        handleResumeActivity(r.token, false, r.isForward,
              !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

        ...

      }

        ...
}

  这里重点关注三个方法(加粗的地方),首先来看WindowManagerGlobal.initialize(),WindowManagerGlobal是单例模式的,一个进程内只有一个,这里调用该类的初始化方法,后续我们再对该类的作用和相关方法进行分析;第三个是在创建好Activity后调用Acitivty的onResume()方法。这里我们来看需关注的第二个方法performLaunchActivity(),代码如下。

    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
     ...

     //通过反射方式创建Activity
Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
} try {
...
       if (activity != null) {
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = 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);

          ...           //调用acitivity的onCreate()方法

           if (r.isPersistable()) {
               mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
           } else {
               mInstrumentation.callActivityOnCreate(activity, r.state);
           }

          ...
       }
            return activity;
}

  这个方法主要是读取Acitivity这里利用反射创建出ActivityClientRecord所要求的Activity对象,然后调用了acitivity.attach()方法。注意attach()传入的参数有很多,在performLacunchActivity()方法流程中,调用attach()方前,我们省略掉的步骤基本都在为这些参数做准备,attach()方法的作用其实就是将这些参数配置到新创建的Activity对象中;而在attach之后则会回调到acitivity的onCreate()方法。我们进入Activity.java类详细来看下attach方法。

  此外,在attach之前会初始化一个Window对象,Window.java是一个抽象类,代表了一个矩形不可见的容器,主要负责加载显示界面,每个Activity都会对应了一个Window对象。如果ActivityClientRecord.mPendingRevomeWindow变量中已经保存了一个Window对象,则会在后面的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) {
attachBaseContext(context); mFragments.attachHost(null /*parent*/);

mWindow = new PhoneWindow(this, window);//(1)
mWindow.setWindowControllerCallback(this);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this
);
mWindow.getLayoutInflater().setPrivateFactory(this);
if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
mWindow.setSoftInputMode(info.softInputMode);
}
if (info.uiOptions != 0) {
mWindow.setUiOptions(info.uiOptions);
}
     ... //初始化Acitity相关属性 mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags
& ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);//(2)
if (mParent != null) {
mWindow.setContainer(mParent.getWindow());
}
mWindowManager = mWindow.getWindowManager();
mCurrentConfig = config;
}

  重点关注初始化window对象的操作,首先创建了PhoneWindow对象为activity的mWindow变量,在创建时传入了上一个activity对应的window对象,之后又将这个acitivity设置为window对象的回调。Activity中很多操作view相关的方法,例如setContentView()、findViewById()、getLayoutInflater()等,实际上都是直接调用到PhoneWindow里面的相关方法。创建完acitivty对应的PhoneWindow之后便会调用setWindowManager()方法。首先来看PhonewWindow构造方法。

  public PhoneWindow(Context context, Window preservedWindow) {
this(context);
// Only main activity windows use decor context, all the other windows depend on whatever
// context that was given to them.
mUseDecorContext = true;if (preservedWindow != null) { //快速重启activity机制
mDecor = (DecorView) preservedWindow.getDecorView();
mElevation = preservedWindow.getElevation();
mLoadElevation = false;
mForceDecorInstall = true;
// If we're preserving window, carry over the app token from the preserved
// window, as we'll be skipping the addView in handleResumeActivity(), and
// the token will not be updated as for a new window.
getAttributes().token = preservedWindow.getAttributes().token;
}
// Even though the device doesn't support picture-in-picture mode,
// an user can force using it through developer options.
boolean forceResizable = Settings.Global.getInt(context.getContentResolver(),
DEVELOPMENT_FORCE_RESIZABLE_ACTIVITIES, 0) != 0;
mSupportsPictureInPicture = forceResizable || context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_PICTURE_IN_PICTURE);
}

  首先要关注的就是preserviedWindow参数,这个参数就是上一段中提到的mPendingRevomeWindow变量,这个参数在什么时候会不为空呢?其实这里的逻辑是用来快速重启acitivity的,比如你的一个activity已经启动了,但是主题换了或者configuration变了,这里只需要重新加载资源和view,没必重新再执行DecorView的创建工作。

  另一个要关注的就是mDecor变量,这个变量是DecorView类型的,如果这里没有初始化的话,则会在调用setContentView方法中new一个DecorView对象出来。DecorView对象继承自FrameLayout,所以他本质上还是一个view,只是对FrameLayout做了一定的包装,例如添加了一些与window需要调用的方法setWindowBackground()、setWindowFrame()等。我们知道,acitivty界面的view是呈树状结构的,而mDecor变量在这里作为activity的界面的根view存在。这三个点关系就比如,PhoneWindow是一块手机电子屏,DecorView就是电子屏要显示的内容,Activity就是手机电子屏安装位置。

  再来看创建PhonewWindow之后调用的setWindowManager()方法的逻辑,这段代码是在PhonewWindow.java的父类Window.java中代码如下。

    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 = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

  对于wWindowManager变量,实际上这里是创建了一个WindowManagerImpl对象。首先是这种首先获取系统服务的代理到wm上,然后强制转换为WindowManagerImpl调用createLocalWindowManager(),在createLocalWindowManager()实际是执行了一个new WindowManagerImpl()到方法来创建。关于这部分代码看了很久很困惑的一个点,就是为啥要弄个这么复杂的逻辑,直接把上面加粗的代码改为new WindowManagerImpl(...),这养会有什么区别吗?如果有大虾看到这里,希望能帮我解答。

  在WindowManager中保存了对于单例对象WindowManagerGloble的引用,即mGlobal变量。此外,WindowManager.java实现了WindowManager又,而WindowManager继承自ViewManager接口,ViewManager接口方法如下方代码。

public interface ViewManager
{
/**
* Assign the passed LayoutParams to the passed View and add the view to the window.
* <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
* errors, such as adding a second view to a window without removing the first view.
* <p>Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a
* secondary {@link Display} and the specified display can't be found
* (see {@link android.app.Presentation}).
* @param view The view to be added to this window.
* @param params The LayoutParams to assign to view.
*/
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
}

  在WindowManager对于addView()、updateViewLayout()和removeView()的实现,都是调用到mGlobal变量对应的addView()、updateViewLayout()和removeView()方法来实现的。这里我们

  

这样我们分析完activity以及对应的window对象的创建,回到performLauncerActivity()方法中Activity a = performLaunchActivity(r, customIntent)这一步骤,之后便回调activity方法的onCreate(),在onCreate()的setContentView方法会初始化DecorView,并根据传入参数加载布局,详细步骤在下一节介绍。

  再回到最初的handlerLaunchActivity()方法中,通过调用performLauncerActivity()创建出一个Acitivty对象后,如果创建成功则执行handleResumeActivity(),便执行到了Acitivity的onResume()方法,即是完成了acitivty的启动。

 

二、setContentView()流程

  首先,我们一般在onCreate()里调用setContentView()的方法。

    @override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
}

  这里实际调用到到地方是Acitivity.java类中的setContentView()方法,如下。

    public void setContentView(@LayoutRes int layoutResID) {
  getWindow().setContentView(layoutResID);
  initWindowDecorActionBar();
}

  这里getWindow()返回的是Acitivity.java类中的mWindow变量,就是Activity创建时一起创建的PhoneWindow对象,进入到

    @Override
public void setContentView(int layoutResID) {
if (mContentParent == null) {
installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();//如果多次调用setContentView则会执行removeAllView操作
} if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) { //过渡动画机制相关
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}

  代码里涉及到FEATURE_CONTENT_TRANSITIONS的属性,这里是Android的过渡动画相关机制,这里我们不再展开详述。一般的Acitivty启动时,会进入mContentParent为null的逻辑,首先调用的是installDecor()方法,完成DecorView的创建工作;之后调用mLayoutInflater.inflate()方法将我们传入的资源文件转换为view树,装载到mContentParent中。首先来看installDecor()代码。

    private void installDecor() {
mForceDecorInstall = false;
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) {
mContentParent = generateLayout(mDecor); ...
}

  在这个方法又两个主要步骤,首先是使用generateDecor()方法创建了DecorView对象,generateDecor()方法比较简单,主要就是执行new DecorView(context, featureId, this, getAttributes())方法,这里不再贴出代码;重点来看generateLayout()方法,这个方法生成的mContentParent是作为来我们后续加载加载的用户的布局的父布局存在的。

   protected ViewGroup generateLayout(DecorView decor) {
// Apply data from current theme.
     //获取当前主题的相关属性
TypedArray a = getWindowStyle(); ... //一大段的根据获取到到主题属性,解析保存到PhonwWindow的相关参数的变量中 int layoutResource;
int features = getLocalFeatures();

     ... //一大段根据PhoneWindow的设定好的属性(features和mIsFloating)的判断,为layoutResource进行赋值,
//值可以为R.layout.screen_custom_title、R.layout.screen_action_bar等 mDecor.startChanging();
     //将layoutRsourece值对应的布局文件加载到DecorView中
mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);

     //在加载给DecorView的布局文件中有一块id为ID_ANDROID_CONTENT的区域是用于用户显示自己布局的,也是setContextView传入的布局显示的地方
     //这块区域会以ViewGroup的形式赋值给mContentParent变量,这个ViewGroup即是用户布局的父布局节点
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);

     ... //继续一大段的属性配置 mDecor.finishChanging(); return contentParent;
}

  generateLayout方法实际就是解析出主题的相关属性,根据不同的主题样式的属性值选择不同的布局文件设置到DecorView中(DecorView本事就是FrameLayout)。在view的树状结构下,DecorView即是整个Window显示的视图的根节点,在DecorView的子节点中又有一块id为ID_ANDROID_CONTENT的区域有一块区域作为mContentParent变量用于加载用户的布局,与mContentParent平级的视图有ActionBar视图和Title的视图。总结来说,installDecor()方法实质就是产生mDecor和mContentParent对象。在installDecor之后,会执行到mLayoutInflater.inflate(layoutResID, mContentParent)方法将用户传入的布局转化为view再加入到mContentParent上。这样就完成了setContentView()流程。

三、handleResumeActivity()流程

  在文章开头贴出的第一段AcitityThread.handleLauncherActivity()方法的代码中,执行完performLaunchAcitity()创建好Acitivity后,便会执行到handleResumeActivity()方法,该方法代码如下。

   final void handleResumeActivity(IBinder token,
boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
...// TODO Push resumeArgs into the activity for consideration
// 该方法执行过程中会调用到Acitity的onResume()方法,返回的ActivityClientRecord对象描述的即是创建好的Activity
     r = performResumeActivity(token, clearHide, reason); if (r != null) {
final Activity a = r.activity;//返回之前创建的Acitivty if (localLOGV) Slog.v(
TAG, "Resume " + r + " started activity: " +
a.mStartedActivity + ", hideForNow: " + r.hideForNow
+ ", finished: " + a.mFinished); final int forwardBit = isForward ?
WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0; // If the window hasn't yet been added to the window manager,
// and this guy didn't finish itself or start another activity,
// then go ahead and add the window.
       // 判断该Acitivity是否可见,mStartedAcitity记录的是一个Activity是否还处于启动状态
       // 如果还处于启动状态则mStartedAcitity为true,表示该activity还未启动好,则该Activity还不可见

       boolean willBeVisible = !a.mStartedActivity;
       // 如果启动的组建不是全屏的,mStartedActivity也会是true,此时依然需要willBeVisible为true以下的if逻辑就是针对这种情况的校正
if (!willBeVisible) {
try {
willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
a.getActivityToken());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
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;
l.softInputMode |= forwardBit;
          //PreserverWindow,一般指主题换了或者configuration变了情况下的Acitity快速重启机制
if (r.mPreserveWindow) {
a.mWindowAdded = true;
r.mPreserveWindow = false;
// Normally the ViewRoot sets up callbacks with the Activity
// in addView->ViewRootImpl#setView. If we are instead reusing
// the decor view we have to notify the view root that the
// callbacks may have changed.
ViewRootImpl impl = decor.getViewRootImpl();
if (impl != null) {
impl.notifyChildRebuilt();
}
}
if (a.mVisibleFromClient && !a.mWindowAdded) {
a.mWindowAdded = true;
            //调用了WindowManagerImpl的addView方法
wm.addView(decor, l);

}   ...
}

  重点来看wm.addView()方法,该方法中的decor参数为Acitity对应的Window中的视图DecorView,wm为在创建PhoneWindow是创建的WindowManagerImpl对象,该对象的addView方法实际调用到到是单例对象WindowManagerGlobal的addView方法(前文有提到)。在看addView代码前,我先来看看WindowManagerGlobal对象成员变量。

    private static WindowManagerGlobal sDefaultWindowManager;
private static IWindowManager sWindowManagerService;
private static IWindowSession sWindowSession; private final Object mLock = new Object(); 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>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();

  三个成员变量mViews、mRoots和mParams分别是类型为View、ViewRootImpl和WindowManager.LayoutParams的数组。这里有这样的逻辑关系,每个View都对应着唯一的一个ViewRootImpl和WindowManager.LayoutRarams,即是1:1:1的关系。这三个数组长度始终保持一致,并且在同一个位置上存放的是互相关联的View、ViewRootImpl和WindowManager.LayoutParams对象。此外还有一个成员变量mDyView,保存的则是已经不需要但还未被系统会收到View。

  View与LayoutParams比较好理解,那ViewRootImpl对象的作用是什么呢?首先WindowManagerImpl是作为管理类,就像主管一样,根据Acitity和Window的调用请求,找到合适的做事的人;DecorView本身是FrameworkLayout,本事是一个View,所表示的是一种静态的结构;所以这里就需要一个真正做事的人,那就是ViewRootImpl类的工作。总结来讲ViewRootImpl的功能如下

1. 完成了绘制过程。在ViewRootImpl类中,实现了perfromMeasure()、performDraw()、performLayout()等绘制相关的方法。

2. 与系统服务进行交互,例如与AcitityManagerSerivice,DisplayService、AudioService等进行通信,保证了Acitity相关功能等正常运转。

3. 触屏事件等分发逻辑的实现

  接下来我们进入WindowManagerGlobal.addView()方法的代码。

    public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
... ViewRootImpl root;
View panelParentView = null; synchronized (mLock) {        ...
            // If this is a panel window, then find the window it is being
// attached to for future reference.
       // 如果当前添加的是一个子视图,则还需要找他他的父视图
       //这里我们分析的是添加DecorView的逻辑,没有父视图,故不会走到这里,panelParentView为null
       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);
}
}
}
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

       //保存互相对应的View、ViewRootImpl、WindowManager.LayoutParams到数组中
mViews.add(view);
mRoots.add(root);
mParams.add(wparams); // do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}

  关注代码中加粗的两个方法,首先会创建一个ViewRootImpl对象,然后调用ViewRootImpl.setView方法,其中panelParentView在addView参数为DecorView是为null。进入ViewRootImpl.setView()代码。 

   public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
          //初始化成员变量mView、mWindowAttraibutes
          //mAttachInfo是View类的一个内部类AttachInfo类的对象
//该类的主要作用就是储存一组当View attach给它的父Window的时候Activity各种属性的信息
mView = view;
mAttachInfo.mDisplayState = mDisplay.getState();
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler); mViewLayoutDirectionInitial = mView.getRawLayoutDirection();
mFallbackEventHandler.setView(view);
mWindowAttributes.copyFrom(attrs);
... //继续初始化一些变量,包含针对panelParentView不为null时的父窗口的一些处理

          mAdded = true;
// 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.
          // 这里调用异步刷新请求,最终会调用performTraversals方法来完成View的绘制
          requestLayout();
if ((mWindowAttributes.inputFeatures
& WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0) {
mInputChannel = new InputChannel();
}
mForceDecorViewVisibility = (mWindowAttributes.privateFlags
& PRIVATE_FLAG_FORCE_DECOR_VIEW_VISIBILITY) != 0;
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);

} catch (RemoteException e) {
mAdded = false;
mView = null;
mAttachInfo.mRootView = null;
mInputChannel = null;
mFallbackEventHandler.setView(null);
unscheduleTraversals();
setAccessibilityFocus(null, null);
throw new RuntimeException("Adding window failed", e);
} finally {
if (restore) {
attrs.restore();
}
} ...
}
}
}

  相关变量初始化完成后,便会将mAdded设置为true,表示ViewRootImpl与setView传入的View参数已经做好了关联。之后便会调用requestLayout()方法来请求一次异步刷新,该方法后来又会调用到performTraversals()方法来完成view到绘制工作。注意到这里虽然完成了绘制的工作,但是我们创建Activity的源头是AMS中发起的,我们从一开始创建Acitivity到相对应的Window、DecorView这一大套对象时,还并未与AMS进程进行反馈。所以之后便会调用mWindowSession.addToDisplay()方法会执行IPC的跨进程通信,最终调用到AMS中的addWindow方法来在系统进程中执行相关加载Window的操作。

 
														
		

源码分析篇 - Android绘制流程(一)窗口启动流程分析的更多相关文章

  1. 源码分析篇 - Android绘制流程(二)measure、layout、draw流程

    performTraversals方法会经过measure.layout和draw三个流程才能将一帧View需要显示的内容绘制到屏幕上,用最简化的方式看ViewRootImpl.performTrav ...

  2. lua源码学习篇三:赋值表达式解析的流程

    上节说到表达式的解析问题,exprstate函数用于解析普通的赋值表达式.lua语言支持多变量赋值.本文先从单变量赋值表达式讲起. a = b = c = a + b 对于简单的两个数的求和过程,lu ...

  3. 源码分析篇 - Android绘制流程(三)requestLayout&lpar;&rpar;与invalidate&lpar;&rpar;流程及Choroegrapher类分析

    本文主要探讨能够触发performTraversals()执行的invalidate().postInvalidate()和requestLayout()方法的流程.在调用这三个方法到最后执行到per ...

  4. android源码解析(十七)--&gt&semi;Activity布局加载流程

    版权声明:本文为博主原创文章,未经博主允许不得转载. 好吧,终于要开始讲讲Activity的布局加载流程了,大家都知道在Android体系中Activity扮演了一个界面展示的角色,这也是它与andr ...

  5. 鸿蒙内核源码分析&lpar;源码注释篇&rpar; &vert; 鸿蒙必定成功,也必然成功 &vert; 百篇博客分析OpenHarmony源码 &vert; v13&period;02

    百篇博客系列篇.本篇为: v13.xx 鸿蒙内核源码分析(源码注释篇) | 鸿蒙必定成功,也必然成功 | 51.c.h .o 几点说明 kernel_liteos_a_note | 中文注解鸿蒙内核 ...

  6. &lbrack;源码解析&rsqb; 并行分布式任务队列 Celery 之 消费动态流程

    [源码解析] 并行分布式任务队列 Celery 之 消费动态流程 目录 [源码解析] 并行分布式任务队列 Celery 之 消费动态流程 0x00 摘要 0x01 来由 0x02 逻辑 in komb ...

  7. &lbrack;源码解析&rsqb; PyTorch 分布式之弹性训练&lpar;2&rpar;---启动&amp&semi;单节点流程

    [源码解析] PyTorch 分布式之弹性训练(2)---启动&单节点流程 目录 [源码解析] PyTorch 分布式之弹性训练(2)---启动&单节点流程 0x00 摘要 0x01 ...

  8. 64位下Hook NtOpenProcess的实现进程保护 &plus; 源码 &lpar;升级篇 &rpar;

    64位下Hook NtOpenProcess的实现进程保护 + 源码 (升级篇 ) [PS: 如果在64位系统下,出现调用测试demo,返回false的情况下,请修改Hook Dll的代码] glhH ...

  9. 转载---- 使用opencv源码自己编制android so库的过程

    http://blog.csdn.net/lantishua/article/details/21182965 工作需要,在Android上使用OpenCV.opencv当前的版本(2.4.8)已经有 ...

随机推荐

  1. http错误代码含义中英文对照

    Http错误代码含义中文 概要当用户试图通过 HTTP 或文件传输协议 (FTP) 访问一台正在运行 Internet 信息服务 (IIS) 的服务器上的内容时,IIS 返回一个表示该请求的状态的数字 ...

  2. Compass的误解与新发现

    最后个人感觉 Compass是 css世界的.NET 很久以前看到 Compass 误以为是css编译器,因为总是在看到如何安装Scss的文章里面看到的 知道最近不小心进入 Compass官网溜达,才 ...

  3. BizTalk发布WS-Security的web services

    最近做个项目,biztalk跟OTM(Oracle Transportation Management)系统做对接,双方通过web services通讯,这部分是BizTalk发布WS-Securit ...

  4. KnockoutJS 3&period;X API 第四章 表单绑定&lpar;6&rpar; click绑定

    目的 click绑定主要作用是用于DOM元素被点击时调用相关JS函数.最常见用于button.input.a元素. 例如: You've clicked timesClick me var viewM ...

  5. windows下vmware10&period;0 安装centos7

    centos7.0-1506, 1511, 是指2015年, 06月份, 11月份. 这是rhel ubuntu发布新版本的时间. centos7.0 只提供了64位的系统下载: x86_64: we ...

  6. hdu 1316 How Many Fibs&quest;

    题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=1316 How Many Fibs? Description Recall the definition ...

  7. 如何把关联性的告警智能添加到 Nagios 上?(2)

    上节回顾 对于许多 IT 和运维团队来说,Nagios 既是一个福音也是一个诅咒.一方面,Naigos 在 IT 应用的工作领域中,给予了你可以实时查看告警数据的可能性:但是另一方面,Nagios 也 ...

  8. android 72 确定取消对话框,单选对话框,多选对话框

    package com.itheima.dialog; import android.os.Bundle; import android.app.Activity; import android.ap ...

  9. 夏普比率&lpar;Sharpe Ratio&rpar;

    投资中有一个常规的特点,即投资标的的预期报酬越高,投资人所能忍受的波动风险越高:反之,预期报酬越低,波动风险也越低.所以理性的投资人选择投资标的与投资组合的主要目的为:在固定所能承受的风险下,追求最大 ...

  10. 数据库分库分表中间件 Sharding-JDBC 源码分析 —— SQL 解析(四)之插入SQL