美文网首页
android 源码分析(三Activity启动流程)

android 源码分析(三Activity启动流程)

作者: 小浩_w | 来源:发表于2018-10-16 11:16 被阅读0次

Activity启动流程简述

  1. 执行startActivity

  2. 通过IBinder远程调用AMS.startActivity。AMS通过ActivityStack来启动Activity,会处理任务栈的切换,同时通知对应Activity所在ApplicationThread进行生命周期调度。

  3. 调度的时候如果不是自己的Activity,并且如果之前没有运行过,则会创建ActivityThread进程,执行Application的初始化

  4. 然后再启动Activity

应用启动简化时序图


image.png

Activity与PhoneWindow及View之间的关系

attach()方法主要做了什么

对Activity中的成员变量进行初始化等相关操作。
必须在onCreate()方法执行之前调用,否则onCreate方法会报错。
比如onCreate()方法中调用setContentView方法设置要显示的View时,需要依赖一个Window对象,这个Window对象就是在attach()方法中来进行初始化的。

Activity的View的显示过程梳理

首先,Activity的attach()方法被调用了,在attach()方法中,完成了Window的初始化相关工作:

frameworks\base\core\java\android\app\Activity.java

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,
            Object lastNonConfigurationInstance,
            HashMap<String,Object> lastNonConfigurationChildInstances,
            Configuration config) {
 
        ……
        
        // 初始化Window对象
        mWindow = PolicyManager.makeNewWindow(this);
        mWindow.setCallback(this);
 
        // 其他成员变量的一些初始化操作……
 }

创建Window对象的方法如下:
frameworks\base\core\java\com\android\internal\policy\PolicyManager.java

private static final IPolicy sPolicy;
public static Window makeNewWindow(Context context) {
    return sPolicy.makeNewWindow(context);
}

sPolicy是一个IPolicy对象,其实现类为:Policy.java

frameworks\base\policy\src\com\android\internal\policy\impl\Policy.java

    public PhoneWindow makeNewWindow(Context context) {
        return new PhoneWindow(context);
    }

由这里我们可以看到,我们的makeNewWindow方法实际上返回的是一个PhoneWindow的对象。
简单的看一下PhoneWindow的继承关系:

frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindow.java

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    ……
}

PhoneWindow是Window类的一个子类,我们在Activity里用到的Window对象,实际上一个PhoneWindow对象。相应的,我们调用的Window对象的方法,实际上也就是在调用PhoneWindow对象中相应的实现方法。

在attach()方法调用完毕后,onCreate方法将被调用,在onCreate方法中,我们往往会先调用setContentView方法,来将View展示到Activity上面:

frameworks\base\core\java\android\app\Activity.java

    public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID);
    }
 
    public void setContentView(View view) {
        getWindow().setContentView(view);
    }
 
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        getWindow().setContentView(view, params);
    }

由上方代码我们可以看到,我们在Activity的onCreate方法中调用setContentView,实际上是调用了Window的setContentView,也就是PhoneWindow对象的setContentView方法:

frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindow.java

private ViewGroup mContentParent;
 
@Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null) {
            cb.onContentChanged();
        }
    }
 
    @Override
    public void setContentView(View view) {
        setContentView(view, 
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    }
 
    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mContentParent.addView(view, params);
        final Callback cb = getCallback();
        if (cb != null) {
            cb.onContentChanged();
        }
    }

上方代码可得到结论:

  1. 如果mContentParent是null的,则通过调用installDecor()方法初始化mContentParent;如果mContentParent不是null的,则调用removeAllViews()将里面其他的子View先清空;

  2. setContentView中,无论传递的是一个layout id,还是一个View,他都将会以addView的形式被添加到mContentParent这个容器中去;

    注意:mLayoutInflater.inflate(layoutResID, mContentParent); 实际上底层也是调用的addView方法
    

installDecor()方法在初始化mContentParent时进行了如下操作:

frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindow.java

private DecorView mDecor;
 
private void installDecor() {
        if (mDecor == null) {
            mDecor = generateDecor();
            ……
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
            ……
        }
    }

由上方代码可知,我们是通过一个mDecor来实现的mContentParent对象初始化操作,mDecor实际上是一个DecorView的对象。DecorView定义如下:

private final class DecorView extends FrameLayout implements RootViewSurfaceTaker {
    ……
}

由DecorView类的定义,我们可以知道,DecorView实际上就是一个FrameLayout的子类。

那么,我们在构建mContentParent的时候,又进行了哪些操作,为什么会用到DecorView?

frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindow.java

protected ViewGroup generateLayout(DecorView decor) {
        // 根据当前Activity的Theme,初始化一些页面显示
        ……
      if (a.getBoolean(com.android.internal.R.styleable.Window_windowNoTitle, false)) {
            requestFeature(FEATURE_NO_TITLE);
        }
        ……
        // 填充DecorView……
        int layoutResource; // 将被填充进DecorView的View的layout id
        
……
        // 一系列的if else判断,决定layoutResource应该是哪个layout id
        ……
        layoutResource = com.android.internal.R.layout.screen_title;
        ……
        
        // 填充layoutResource对应的View,并add进DecorView中
        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, 
new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
        
        // 获取其中ID_ANDROID_CONTENT对应的ViewGroup
// 这个ViewGroup其实就是我们的mContentParent
        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
           throw new RuntimeException("Window couldn't find content container view");
        }
 
       ……
        // 返回mContentParent
        return contentParent;
    }

系统的layout:layout.screen_title

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:fitsSystemWindows="true">
    <FrameLayout
        android:layout_width="match_parent" 
        android:layout_height="?android:attr/windowTitleSize"
        style="?android:attr/windowTitleBackgroundStyle">
        <TextView android:id="@android:id/title" 
            style="?android:attr/windowTitleStyle"
            android:background="@null"
            android:fadingEdge="horizontal"
            android:gravity="center_vertical"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </FrameLayout>
    <FrameLayout android:id="@android:id/content"
        android:layout_width="match_parent" 
        android:layout_height="0dip"
        android:layout_weight="1"
        android:foregroundGravity="fill_horizontal|top"
        android:foreground="?android:attr/windowContentOverlay" />
</LinearLayout>

上方可知,我们在setContentView时,实际上是将我们想要展示的View用addView的方式添加到一个mContentParent中,而mContentParent实际上是DecorView里面的一个FrameLayout。

结论:
DecorView为整个Window界面的最顶层View,它包含一个头布局(其实是我们Activity的TitleBar),以及一个FrameLayout布局。我们在Activity中通过setContentView设置的想展示的Layout,实际被add到了这个FrameLayout中。
在handleResumeActivity的时候会将DecorView添加到WindowManager上并显示出来(Activity : makeVisible()方法)。


image.png
Activity、PhoneWindow、View之间的关系

Activity通过调用setContentView将View添加到PhoneWindow中,显示到界面上。

线程与进程

Linux下有真正意义的多线程么

没有,Linux并不是遵循标准的线程设计模型。


image.png

核心级线程设计模型:操作系统内核实现了线程模型,特点:线程的调度着在内核中(windows)


image.png

用户级线程设计模型:操作系统核外实现的线程模式,如在系统类库层、框架层。特点:线程的调度着在核外(Linux)

Linux系统的两个线程库:

  • LinuxThreads线程库

  • RedHat的NPTL(Native POSIX Thread Library )线程库(资料)

Android中进程的5种优先级

Google官方文档给出的介绍

<u>http://developer.android.com/intl/zh-cn/guide/components/processes-and-threads.html</u>

进程生命周期

Android 系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要清除旧进程来回收内存。 为了确定保留或终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。 必要时,系统会首先消除重要性最低的进程,然后是重要性略逊的进程,依此类推,以回收系统资源。

重要性层次结构一共有 5 级。以下列表按照重要程度列出了各类进程(第一个进程最重要,将是最后一个被终止的进程):

  1. 前台进程

用户当前操作所必需的进程。如果一个进程满足以下任一条件,即视为前台进程:

o 托管用户正在交互的 <u>Activity</u>(已调用 <u>Activity</u><u>onResume()</u> 方法)

o 托管某个 <u>Service</u>,后者绑定到用户正在交互的 Activity

o 托管正在“前台”运行的 <u>Service</u>(服务已调用 [<u>startForeground()</u>](#startForeground(int, android.app.Notification)))

o 托管正执行一个生命周期回调的 <u>Service</u><u>onCreate()</u>、[<u>onStart()</u>](#onStart(android.content.Intent, int)) 或 <u>onDestroy()</u>

o 托管正执行其 [<u>onReceive()</u>](#onReceive(android.content.Context, android.content.Intent)) 方法的 <u>BroadcastReceiver</u>

通常,在任意给定时间前台进程都为数不多。只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们。 此时,设备往往已达到内存分页状态,因此需要终止一些前台进程来确保用户界面正常响应。

  1. 可见进程

没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程。 如果一个进程满足以下任一条件,即视为可见进程:

o 托管不在前台、但仍对用户可见的 <u>Activity</u>(已调用其 <u>onPause()</u> 方法)。例如,如果前台 Activity 启动了一个对话框,允许在其后显示上一 Activity,则有可能会发生这种情况

o 托管绑定到可见Activity 的 <u>Service</u>

可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。

  1. 服务进程

正在运行已使用 <u>startService()</u> 方法启动的服务且不属于上述两个更高类别进程的进程。尽管服务进程与用户所见内容没有直接关联,但是它们通常在执行一些用户关心的操作(例如,在后台播放音乐或从网络下载数据)。因此,除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。

  1. 后台进程

包含目前对用户不可见的 Activity 的进程(已调用 Activity 的 <u>onStop()</u> 方法)。这些进程对用户体验没有直接影响,系统可能随时终止它们,以回收内存供前台进程、可见进程或服务进程使用。 通常会有很多后台进程在运行,因此它们会保存在 LRU (最近最少使用)列表中,以确保包含用户最近查看的 Activity 的进程最后一个被终止。如果某个 Activity 正确实现了生命周期方法,并保存了其当前状态,则终止其进程不会对用户体验产生明显影响,因为当用户导航回该 Activity 时,Activity 会恢复其所有可见状态。 有关保存和恢复状态的信息,请参阅<u>Activity</u>文档。

  1. 空进程

不含任何活动应用组件的进程。保留这种进程的的唯一目的是用作缓存,以缩短下次在其中运行组件所需的启动时间。 为使总体系统资源在进程缓存和底层内核缓存之间保持平衡,系统往往会终止这些进程。

根据进程中当前活动组件的重要程度,Android 会将进程评定为它可能达到的最高级别。例如,如果某进程托管着服务和可见 Activity,则会将此进程评定为可见进程,而不是服务进程。

此外,一个进程的级别可能会因其他进程对它的依赖而有所提高,即服务于另一进程的进程其级别永远不会低于其所服务的进程。 例如,如果进程 A 中的内容提供程序为进程 B 中的客户端提供服务,或者如果进程 A 中的服务绑定到进程 B 中的组件,则进程 A 始终被视为至少与进程 B 同样重要。

由于运行服务的进程其级别高于托管后台 Activity 的进程,因此启动长时间运行操作的 Activity 最好为该操作启动<u>服务</u>,而不是简单地创建工作线程,当操作有可能比 Activity 更加持久时尤要如此。例如,正在将图片上传到网站的 Activity 应该启动服务来执行上传,这样一来,即使用户退出 Activity,仍可在后台继续执行上传操作。使用服务可以保证,无论 Activity 发生什么情况,该操作至少具备“服务进程”优先级。 同理,广播接收器也应使用服务,而不是简单地将耗时冗长的操作放入线程中。

相关文章

网友评论

      本文标题:android 源码分析(三Activity启动流程)

      本文链接:https://www.haomeiwen.com/subject/kpnczftx.html