Activity的结构分析

1. 结构介绍

  大伙儿应该都知道,Activity的结构分为三层,分别是:ActivityWindowView,不同层承担着不同的责任。

  上面的图简单的描述了Activity整个结构的构建流程。这里我再简单的介绍一下这几个部分的作用。

  1. ActivityThread:每个流程调用起始地点,至于ActivityThread的内容不是本文的重点,所以本文不会过多介绍,后续有相关的文章来介绍。
  2. Activity:相当于是一个管理者,负责创建WindowManagerWindow,同时初始化View
  3. Window:承载着View,同时代Activity处理一切View的事务。
  4. WindowManager:从字面意思来理解是Window的管理,其实是管理Window上的View,包括addViewremove

  而这几个角色的对应关系是:ActivityThread全局唯一,整个App进程中只一个实例。ActivityThread可以对应多个Activity,一个Activity对应一个Window(这里不考虑Dialog之类的),一个Window对应一个WindowManager

  本文打算参考上面的流程图,分别分析Activity的attach过程、Activity的onCreateView过程和WindowManager的addView过程。

2. Activity的attach

  Activity的attach过程就是一个初始化的过程,分别对Window进行初始化、WindowManager进行初始化。我们先来看看ActivityThreadhandleLaunchActivity方法:

 public Activity handleLaunchActivity(ActivityClientRecord r,
       // ······

       final Activity a = performLaunchActivity(r, customIntent);
        // ······
   }
复制代码

  handleLaunchActivity方法主要调用performLaunchActivity来启动一个Activity,再来看看performLaunchActivity方法:

 private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
  // ·······
  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);
   // ·······
}
复制代码

  在这里,我们看到了Activityattach方法的调用,我们来看看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 = new PhoneWindow(this, window, activityConfigCallback);
       // ······
       mWindow.setWindowManager(
               (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
               mToken, mComponent.flattenToString(),
               (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
       // ······
   }
复制代码

  attach方法一共做了两件事情:

  1. 初始化Window,其中Window的构造方法还做了一些事,比如说,我们比较关心的LayoutInflater的初始化。
  2. 初始化WindowManager,并且set到Window里面去。

3. Activity的onCreate

  当初,我们刚入门Android的时候就知道,在ActivityonCreate方法调用setContentView可以给当前页面设置一个布局。当时有没有觉得这个非常的神奇,并且对其充满了好奇,可惜的是当时自己对整个Android的设计还很陌生,不敢去探究。今天我们可以正式进入setContentView的内部了,去一探究竟🤪。

  其实ActivityonCreate过程不仅做setContentView操作,其实还做了其他的事情。从源码中我们也可以看到,比如说,初始化AppCompatDelegate(这里以AppCompatActivity为例),设置Theme等。不过这些不是本文的重点,后面我会专门的文章来分析这些过程,本文的重点是setContentView方法。

  ActivitysetContentView没有做啥事,实际做事的是它的Window。我们来看看WindowsetContentView方法:

    @Override
  public void setContentView(int layoutResID) {
      // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window
      // decor, when theme attributes and the like are crystalized. Do not check the feature
      // before this happens.
      if (mContentParent == null) {
          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 {
          mLayoutInflater.inflate(layoutResID, mContentParent);
      }
      mContentParent.requestApplyInsets();
      final Callback cb = getCallback();
      if (cb != null && !isDestroyed()) {
          cb.onContentChanged();
      }
      mContentParentExplicitlySet = true;
  }
复制代码

  setContentView方法里面主要是做了如下几件事:

  1. 通过installDecor方法创建DecorViewmContentParentDecorView的创建在generateDecor方法;mContentParent的创建在generateLayout方法里面,generateLayout方法里面有一个重要地方根据属性的配置设置了Windowflagsfeatures。这里flags的生效是在DecorViewupdateColorViews方法,会根据flags来计算DecorView的大小;features将那种布局加载到DecorView,这将影响到Activity默认的布局样式。不过我有一点疑惑至今无解,从mContentParent的注释了解到,mContentParent也有可能是DecorView,但是我看了installDecor以及内部调用的generateLayout方法,都没有找到相关可能性。
  2. ContentView加载到mContentParent上去。

  针对上面设置flag生效的解释我还想补充一下,大伙儿应该都知道,在Android中,我们可以通过setFlags或者addFlags来改变一些属性,但是有没有想过是怎么生效的呢?其实过程是非常的简单,我们就不一一的追踪源码了,直接来看一下调用流程:

  Window#setFlags -> PhoneWindow#dispatchWindowAttributesChanged -> DecorView#updateColorViews

  最终,会根据设置好的Flag来计算DecorView的位置和大小,例如说,我们设置了FLAG_FULLSCREEN让界面全屏,最终在updateColorViews方法这么来计算DecorView的高度:

        // If we didn't request fullscreen layout, but we still got it because of the
        // mForceWindowDrawsStatusBarBackground flag, also consume top inset.
        boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
                && (sysUiVisibility & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
                && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
                && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
                && mForceWindowDrawsStatusBarBackground
                && mLastTopInset != 0;
复制代码

  上面的代码表达的意思非常简单,就是判断是否消费状态栏的高度。至于其他Flag也是如此,有兴趣的同学可以看一看。

4. WindowManager的addView

  我们知道,DecrorViewViewParentViewRootImpl,而View最重要的三大流程就是由ViewRootImpl触发的。在正式介绍这一块的知识之前,我们先来看一个简单的结构图:

  这个图相信大家已经熟悉的不能再熟悉了,不过这里我还是将它贴出来。参考上面的图,我们可以知道,经过上面的两个流程,我们将DecorView部分创建完成,现在还需要两件事需要做:

  1. 初始化ViewRootImpl,并且将ViewRootImplWindow绑定。
  2. 将之前创建好的DecorView添加到ViewRootImpl里面去。

  我们先来看看ActivityThreadhandleResumeActivity方法:

    public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
            String reason) {
        // ······
        final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
        // ·······
        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;
            // ······
            if (a.mVisibleFromClient) {
                if (!a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }
                // ·······
            }
        }
        // ······

    }
复制代码

  handleResumeActivity方法里面主要做了两件事:

  1. 调用performResumeActivity进而回调ActivityonResume方法。
  2. 调用WindowManageraddView方法,将DecorView添加到ViewRootImpl中去。

  调用的addView方法最终是进入WindowManager的实现类WindowManagerImpl中去,而在WindowManagerImpladdView方法中调用了WindowManagerGlobaladdView方法。   从名字中,我们就可以看出来,WindowManagerGlobal是属于全局的。我们再来看看WindowManagerGlobaladdView方法:

public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        // ······
        ViewRootImpl root;
        View panelParentView = null;

        synchronized (mLock) {
            // ·······
            root = new ViewRootImpl(view.getContext(), display);

            view.setLayoutParams(wparams);

            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;
            }
        }
    }
复制代码

  addView方法主要是做了两件事:

  1. 初始化ViewRootImpl,并且将该Window相关信息保存起来,包括:ViewRootImplDecorViewLayoutParams
  2. 调用ViewRootImplsetView方法,将DecorView添加到ViewRooImpl,并且触发View的三大流程。

  我们都知道,每个Window都对应着一个DecorView,而从这里我们可以发现,每个DecorView都对应着一个ViewRootImpl,从而得知,如果是一个Dialog或者其他新Window的界面,必定有一个新的ViewRootImpl来触发View的三大流程,而不是由宿主WindowViwRootImpl触发的。

(1). 为什么在Activity的onResume方法调用Handler的post不能获取View的宽高呢?

  在回答这个问题之前,我们应该需要注意的是,这里是Handler的post方法,而不是View的post方法。ps:View的post方法能拿到View的宽高。

  我们知道ActivityonResume方法的执行是在ViewRootImpl触发测量过程之前,同时ViewRootImpl是通过如下的方式来触发测量过程的:

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }
复制代码

  我们发现这里通过Handler post了一个异步消息来进行测量。可是,尽管post的是异步消息,在onResume方法post的消息也先于它执行,因为它在其后post的。所以,在Activity的onResume方法调用Handler的post不能获取View的宽高。

5. 总结

  到此位置,对Activity的结构分析也差不多,在这里,我们做一个简单的总结。

  1. Activity的结构分为三层,分别是:ActivityWindowView
  2. 结构的创建过程分为三步:1. 创建Activity,并且创建与其相关的WindowManagerWindow,对应着是Activityattach方法的调用;2. 初始化DecorViewContentView,对应着的是ActivityonCreate方法;3. 创建ViewRootImpl,并且将DecorView添加到ViewRootImpl中,同时触发View树的三大流程。
  3. generateLayout方法里面,会根据设置不同的flags来计算DecorView的大小和位置,计算逻辑在updateColorViews方法里面;还是根据设置不同的features方法来选择默认加载到DecorView中,比如说设置了NO_ACTION_BARfeatures,就会加载不带ActionBar的布局。


 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Android 核心分析之一--------分析方法论探讨之设计意.......................................... 1 Android 核心分析之二-------方法论探讨之概念空间篇..............................................3 Android 是什么之三-------手机之硬件形态.....................................................................5 Android 核心分析之四---手机的软件形态...................................................................... 6 Android 核心分析之五-----基本空间划分.................................................................... 7 Android 核心分析之六-----IPC 框架分析Binder,Service,Service manager..... 11 Android 核心分析之七------Service 深入分析..............................................................21 Android 核心分析之八------Android 启动过程详解................................................... 31 Android 核心分析之九-------Zygote Service.................................................................. 36 Android 核心分析之十-------Android GWES 之基本原理篇.......................................40 Android 核心分析之十一-------Android GWES 之消息系统.......................................43 Android 核心分析(12) -----Android GEWS 窗口管理之基本架构原理...................... 48 Android 核心分析(13) -----Android GWES 之Android 窗口管理............................... 50 Android 核心分析(14)------ Android GWES 之输入系统..........................................57 Android 核心分析(15)--------Android 输入系统之输入路径详解.............................. 59 Android 核心分析(16)-----Android 电话系统-概述篇...............................................66 Android 核心分析(17) ------电话系统之rilD............................................................ 69 Android 核心分析(18)-----Android 电话系统之RIL-Java........................................ 76 Android 核心分析(19)----电话系统之GSMCallTacker.............................................84 Android 核心分析(20)----Android 应用程序框架之无边界设计意....................... 87 Android 核心分析(21)----Android 应用框架之AndroidApplication....................... 88 Android 核心分析(22)-----Android 应用框架之Activity..........................................93 Android 核心分析(24)-----Android GDI 之显示缓冲管理.........................................104 Android 核心分析(25)------Android GDI 之共享缓冲区机制................................ 112 Android 核心分析(26)-----Android GDI 之SurfaceFlinger..................................... 116 Android 核心分析(27)-----Android GDI 之SurfaceFlinger 之动态结构示意..123 Android 核心分析(28)-----Android GDI 之Surface&Canvas..................................126
android核心分析,介绍了android的一些特性,共二十几个介绍点,网上word文件转化为pdf,便于阅读 Android核心分析(01)----讨之设计意 Android核心分析(02)----方法论探讨之概念空间篇 Android核心分析(03)----手机之硬件形态 Android核心分析(04)----手机的软件形态 Android核心分析(05)----基本空间划分 Android核心分析(06)----IPC框架分析 Android核心分析(07)----Service深入分析 Android核心分析(08)----Android 启动过程详解 Android核心分析(09)----Zygote Service Android核心分析(10)----Android GWES之基本原理篇 Android核心分析(11)----Android GWES之消息系统 Android核心分析(12)----Android GEWS窗口管理之基本架构原理.. Android核心分析(13)----Android GWES之Android窗口管理 Android核心分析(14)----Android GWES之输入系统 Android核心分析(15)----Android输入系统之输入路径详解 Android核心分析(16)----Android电话系统-概述篇 Android核心分析(17)----电话系统之rilD Android核心分析(18)----Android电话系统之RIL-Java Android核心分析(19)----电话系统之GSMCallTacker Android核心分析(20)----Android应用程序框架之无边界设计意. Android核心分析(21)----Android应用框架之AndroidApplication. Android核心分析(22)----Android应用框架之Activity Android核心分析(23)----Andoird GDI之基本原理及其总体框架 Android核心分析(24)----Android GDI之显示缓冲管理 Android核心分析(25)----Android GDI之共享缓冲区机制 Android核心分析(26)----Android GDI之SurfaceFlinger Android核心分析(27)----Android GDI SurfaceFlinger之动态结构示意 Android核心分析(28)----Android GDI之Surface&Canvas;
Android 中,Activity 的启动过程是由 ActivityManagerService (AMS) 负责的。AMS 接收到启动 Activity 的请求后,会将该请求保存在 Activity 栈中,并通过 Binder 机制将该请求发送给 ActivityThread 进程中的 H 块(即 ActivityThread 中的主线程 Handler)。H 块接收到请求后,会创建要启动的 Activity 的实例,并调用其 onCreate() 方法进行初始化。接下来,AMS 会将该 Activity 的窗口添加到 WindowManagerService (WMS) 中,并调用 Activity 的 onStart()、onResume() 等方法,使其进入运行状态。 具体来说,ActivityRecord 是一个代表 Activity 的数据结构,其中包含了该 Activity 的各种信息,例如包名、类名、启动模式、Intent 等。AMS 在启动 Activity 时,会创建一个新的 ActivityRecord 对象,并将其添加到 Activity 栈中。这个过程通常包括以下几个步骤: 1. AMS 接收到启动 Activity 的请求,解析该请求中的 Intent,并将其封装成一个 ActivityRecord 对象。 2. AMS 根据启动模式和任务栈的情况,决定将该 Activity 添加到哪个任务栈中(或者创建新的任务栈),并将该 ActivityRecord 对象保存到 Activity 栈中。 3. AMS 将该请求发送给 ActivityThread 中的 H 块,请求 H 块创建要启动的 Activity 实例。 4. H 块接收到请求后,在主线程中创建 Activity 实例,并调用其 onCreate() 方法进行初始化。这个过程通常包括加载布局、初始化控件、设置监听器等操作。 5. H 块创建 Activity 实例后,会将其保存到 Activity 栈中,并将其窗口添加到 WMS 中。 6. AMS 接收到 ActivityThread 发送的消息,表示 Activity 已经准备好了,然后 AMS 会调用该 Activity 的 onStart()、onResume() 等方法,使其进入运行状态。 需要注意的是,在启动 Activity 过程中,还会涉及到权限检查、应用启动优化、Activity 生命周期管理等方面的处理,这些都是由 AMS 和系统框架中的其他部分共同完成的。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值