标签: Activity

利用Theme自定义Activity间的切换动画

利用Theme自定义Activity间的切换动画

 

大多Android系统默认Activity间的动画切换效果为,右边滑入,左边滑出;有时候我们的需求可能是要求所有Activity的切换为淡入淡出的效果,这时候就可能需要改变一下默认的切换风格。

下面开始实现:

首先在res文件夹下建立anim文件夹,然后在里面建立fade_in.xml和fade_out.xml两个动画资源

fade_in.xml

 

  1. <?xml version=“1.0” encoding=”utf-8″?>  
  2. <alpha xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     android:duration=“300”  
  4.     android:fromAlpha=“0.0”  
  5.     android:interpolator=“@android:anim/accelerate_interpolator”  
  6.     android:toAlpha=“1.0” />  

fade_out.xml

 

 

  1. <?xml version=“1.0” encoding=”utf-8″?>  
  2. <alpha xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     android:duration=“300”  
  4.     android:fromAlpha=“1.0”  
  5.     android:interpolator=“@android:anim/accelerate_interpolator”  
  6.     android:toAlpha=“0.0” />  

然后在values文件夹下的styles.xml中的resources标签内写:

 

 

  1. <style name=“Anim_fade” parent=”android:Theme.NoTitleBar”>  
  2.        <item name=“android:windowAnimationStyle”>@style/fade</item>  
  3.    </style>  
  4.    <style name=“fade” parent=”@android:style/Animation.Activity”>  
  5.        <item name=“android:activityOpenEnterAnimation”>@anim/fade_in</item>  
  6.        <item name=“android:activityOpenExitAnimation”>@anim/fade_out</item>  
  7.        <item name=“android:activityCloseEnterAnimation”>@anim/fade_in</item>  
  8.        <item name=“android:activityCloseExitAnimation”>@anim/fade_out</item>  
  9.    </style>  

*后一步在AndroidManifest.xml中的Activity的声明上加入android:theme=”@style/Anim_fade”

 

 

  1. <?xml version=“1.0” encoding=”utf-8″?>  
  2. <manifest xmlns:android=“http://schemas.android.com/apk/res/android”  
  3.     package=“com.example.customanimationforactivity”  
  4.     android:versionCode=“1”  
  5.     android:versionName=“1.0” >  
  6.     <uses-sdk  
  7.         android:minSdkVersion=“10”  
  8.         android:targetSdkVersion=“10” />  
  9.     <application  
  10.         android:allowBackup=“true”  
  11.         android:icon=“@drawable/ic_launcher”  
  12.         android:label=“@string/app_name”  
  13.         android:theme=“@android:style/Theme.NoTitleBar” >  
  14.         <activity  
  15.             android:name=“com.example.customanimationforactivity.MainActivity”  
  16.             android:label=“@string/app_name”  
  17.             android:theme=“@style/Anim_fade” >  
  18.             <intent-filter>  
  19.                 <action android:name=“android.intent.action.MAIN” />  
  20.                 <category android:name=“android.intent.category.LAUNCHER” />  
  21.             </intent-filter>  
  22.         </activity>  
  23.         <activity android:name=“.AppActivity” android:theme=”@style/Anim_fade” >  
  24.         </activity>  
  25.     </application>  
  26. </manifest>  

贴下Splash Activity的代码:

 

 

  1. package com.example.customanimationforactivity;  
  2. import android.app.Activity;  
  3. import android.content.Intent;  
  4. import android.os.Bundle;  
  5. import android.os.Handler;  
  6. public class MainActivity extends Activity  
  7. {
  8.     private Handler handler = new Handler();  
  9.     @Override  
  10.     protected void onCreate(Bundle savedInstanceState)  
  11.     {
  12.         super.onCreate(savedInstanceState);  
  13.         setContentView(R.layout.activity_main);
  14.         handler.postDelayed(new Runnable()  
  15.         {
  16.             @Override  
  17.             public void run()  
  18.             {
  19.                 Intent intent = new Intent(MainActivity.this, AppActivity.class);  
  20.                 startActivity(intent);
  21.                 finish();
  22.             }
  23.         }, 1000);  
  24.     }
  25. }

 

Activity的启动模式与flag详解

Activity的启动模式与flag详解

Activity有四种加载模式:standard(默认), singleTop, singleTask和 singleInstance。以下逐一举例说明他们的区别:

standard:Activity的默认加载方法,即使某个Activity在 Task栈中已经存在,另一个activity通过Intent跳转到该activity,同样会新创建一个实例压入栈中。例如:现在栈的情况为:A B C D,在D这个Activity中通过Intent跳转到D,那么现在的栈情况为: A B C D D 。此时如果栈顶的D通过Intent跳转到B,则栈情况为:A B C D D B。此时如果依次按返回键,D  D C B A将会依次弹出栈而显示在界面上。

singleTop:如果某个Activity的Launch mode设置成singleTop,那么当该Activity位于栈顶的时候,再通过Intent跳转到本身这个Activity,则将不会创建一个新的实例压入栈中。例如:现在栈的情况为:A B C D。D的Launch mode设置成了singleTop,那么在D中启动Intent跳转到D,那么将不会新创建一个D的实例压入栈中,此时栈的情况依然为:A B C D。但是如果此时B的模式也是singleTop,D跳转到B,那么则会新建一个B的实例压入栈中,因为此时B不是位于栈顶,此时栈的情况就变成了:A B C D B。

singleTask:如果某个Activity是singleTask模式,那么Task栈中将会只有一个该Activity的实例。例如:现在栈的情况为:A B C D。B的Launch mode为singleTask,此时D通过Intent跳转到B,则栈的情况变成了:A B。而C和D被弹出销毁了,也就是说位于B之上的实例都被销毁了。

singleInstance:将Activity压入一个新建的任务栈中。例如:Task栈1的情况为:A B C。C通过Intent跳转到D,而D的Launch mode为singleInstance,则将会新建一个Task栈2。此时Task栈1的情况还是为:A B C。Task栈2的情况为:D。此时屏幕界面显示D的内容,如果这时D又通过Intent跳转到D,则Task栈2中也不会新建一个D的实例,所以两个栈的情况也不会变化。而如果D跳转到C,则栈1的情况变成了:A B C C,因为C的Launch mode为standard,此时如果再按返回键,则栈1变成:A B C。也就是说现在界面还显示C的内容,不是D。
好了,现在有一个问题就是这时这种情况下如果用户点击了Home键,则再也回不到D的即时界面了。如果想解决这个问题,可以为D在Manifest.xml文件中的声明加上<intent-filter>
<action android:name=”android.intent.action.MAIN” />
<category android:name=”android.intent.category.LAUNCHER” />
</intent-filter>

加上这段之后,也就是说该程序中有两个这种声明,另一个就是那个正常的根 activity,在打成apk包安装之后,在程序列表中能看到两个图标,但是如果都运行的话,在任务管理器中其实也只有一个。上面的情况点击D的那个图标就能回到它的即时界面(比如一个EditText,以前输入的内容,现在回到之后依然存在)。

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Intent的常用Flag参数:

FLAG_ACTIVITY_CLEAR_TOP:例如现在的栈情况为:A B C D 。D此时通过intent跳转到B,如果这个intent添加FLAG_ACTIVITY_CLEAR_TOP 标记,则栈情况变为:A B。如果没有添加这个标记,则栈情况将会变成:A B C D B。也就是说,如果添加了FLAG_ACTIVITY_CLEAR_TOP 标记,并且目标Activity在栈中已经存在,则将会把位于该目标activity之上的activity从栈中弹出销毁。这跟上面把B的Launch mode设置成singleTask类似。

FLAG_ACTIVITY_NEW_TASK:例如现在栈1的情况是:A B C。C通过intent跳转到D,并且这个intent添加了FLAG_ACTIVITY_NEW_TASK 标记,如果D这个Activity在Manifest.xml中的声明中添加了Task affinity,并且和栈1的affinity不同,系统首先会查找有没有和D的Task affinity相同的task栈存在,如果有存在,将D压入那个栈,如果不存在则会新建一个D的affinity的栈将其压入。如果D的Task affinity默认没有设置,或者和栈1的affinity相同,则会把其压入栈1,变成:A B C D,这样就和不加FLAG_ACTIVITY_NEW_TASK 标记效果是一样的了。      注意如果试图从非activity的非正常途径启动一个activity,比如从一个service中启动一个activity,则intent比如要添加FLAG_ACTIVITY_NEW_TASK 标记。

FLAG_ACTIVITY_NO_HISTORY:例如现在栈情况为:A B C。C通过intent跳转到D,这个intent添加FLAG_ACTIVITY_NO_HISTORY标志,则此时界面显示D的内容,但是它并不会压入栈中。如果按返回键,返回到C,栈的情况还是:A B C。如果此时D中又跳转到E,栈的情况变为:A B C E,此时按返回键会回到C,因为D根本就没有被压入栈中。

FLAG_ACTIVITY_SINGLE_TOP:和上面Activity的 Launch mode的singleTop类似。如果某个intent添加了这个标志,并且这个intent的目标activity就是栈顶的activity,那么将不会新建一个实例压入栈中。

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Activity的主要属性:

allowTaskReparenting:设置成true时,和Intent的FLAG_ACTIVITY_NEW_TASK 标记类似。

alwaysRetainTaskStat:   如果用户长时间将某个task 移入后台,则系统会将该task的栈内容弹出只剩下栈底的activity,此时用户再返回,则只能看到根activity了。如果栈底的 activity的这个属性设置成true,则将阻止这一行为,从而保留所有的栈内容。

clearTaskOnLaunch:根activity的这个属性设置成true时,和上面的alwaysRetainTaskStat 的属性为true情况搞好相反。

finishOnTaskLaunch:对于任何activity,如果它的这个属性设置成true,则当task被放置到后台,然后重新启动后,该activity将不存在了

使用MVP模式重构代码

之前写了两篇关于MVP模式的文章,主要讲得都是一些概念,这里谈谈自己在Android项目中使用MVP模式的真实感受,并以实例的形式一起尝试来使用MVP模式去重构我们现有的代码。

有兴趣的童鞋可以先去阅读之前的文章,因为这里将不再重复概念的部分了,本文会假设你对MVP有一点了解了:

1. 在谈MVP之前,你真的懂MVC吗?

2. MVP模式是你的救命稻草吗?

臃肿的Activity

大部分谈Android架构的时候,都基本会提到Activity越来越臃肿的问题,这几乎是一个普遍现象,而包括我本人在内的,都会首先将这个罪责推到MVC架构上,但如果你真的花时间去重构activity的时候,你会发现问题其实往往出在自己身上。

一般的MVC里的 Controller 需要做的事情:

  1. 负责获取和处理用户的输入。
  2. 负责将输入传给负责业务逻辑层去做数据上的操作(如增删改查)。
  3. 负责将业务逻辑层对于数据操作的结果,传给View层去做展示。

因此如果完全按照这种定义的话,你应该很难看到一个非常臃肿的Controller,因为Controller在MVC模式中,本来就应该是很轻的,而不是很重的部分,重的应该是M层,甚至在前端交互复杂的时候,V层都应该比C层要重。

我认为对于Controller的理解,就是一个站在M和V两者之间的一个翻译家,M来自地球,V来自火星。而如果站在中间的这个翻译者,话比他两的话还多,老是抢话,自言自语,这样显然是不合适的。

那么我们再来看典型的Activity的代码,处理的业务是常见的登录页面:

public class UserActivity extends Activity {

  private RequestQueue mQueue = Volley.newRequestQueue(this);

  private TextView mUsernameTextView;
  private TextView mPasswordTextView;
  private Button mLoginBtn;

  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
    setContentView(R.layout.login);

    mUsernameTextView = (TextView) findViewById(R.id.username);
    mPasswordTextView = (TextView) findViewById(R.id.password);

    mLoginBtn = (Button) findViewById(R.id.login_btn);
    mLoginBtn.setOnClickListener(new View.OnClickListener() {
          public void onClick(View v)  {
              String username = mUsernameTextView.getText().toString();
               String password = mPasswordTextView.getText().toString();

               String loginUrl = "http://somesite.com/login.php";

               JSONObjectRequest request = new JSONObjectRequest(loginUrl, Method.POST,
                 new Response.Listener<JSONObject>() {
                      public void onResponse(JSONObject json) {
                          if (json != null && json.get("isOk") == true) {
                              Toast.makeToast(getApplicationContext(), "Login Success", Toast.LENGTH_SHORT).show();
                                startActivity(new Intent(LoginActivity.this, MainActivity.class));
                        } else {
                              Toast.makeToast(getApplicationContext(), "Login Fail", Toast.LENGTH_SHORT).show();
                        }
                    }
               },
                new Response.ErrorListener(){
                      public void onError(VolleyError error) {
                          Toast.makeToast(getApplicationContext(), "Login Fail", Toast.LENGTH_SHORT).show();
                    }
               });
              mQueue.add(request);
        } 
    });
  }

  @Override
  protected void onStop() {
      super.onStop();
    if (mQueue != null) {
          mQueue.cancelAll();
    }
  }

}

其实这已经是*度简化过的代码了,真正的一个LoginActivity很容易就会超过几千行,不信你去看自己项目里面的代码就明白我在说什么了。

而一对比概念,我相信大部分人一下子就会发现问题,我们还是来看Activity作为一个 Controller 到底都负责干什么了:

  1. 首先activity必须去操作View的控件,设置它们的回调函数,有时也需要用代码去控制它们如何展示的属性。
  2. 然后activity一定需要去处理用户的输入,例如输入的值,以及点击事件等用户行为。
  3. 而几乎大部分异步网络请求都从activity发起,以及服务器返回数据的处理。
  4. activity一般还需要根据数据的操作结果,负责在页面上将结果告之用户,例如Toast或者其他View的操作。
  5. 除此之外,activity还需要管理其生命周期相关的所有事务,例如在页面退出的时候处理一下View控件和其他与生命期相关的逻辑。

你会发现Activity天生的责任太重,其中确实覆盖了 Controller 的原本的责任,例如处理用户输入,将用户操作转换成传递给业务逻辑层的命令等职责。

但如果你仔细的分析,你会发现activity不仅仅需要承担Controller的责任,还需要处理大量View的逻辑,例如控件的监听的属性,如何展示数据的职责也往往落到了它的肩上。更何况你很容易在activity写操作数据和网络请求的代码,也就是让它又承担了Model的责任,那么请问这样的Activity能不臃肿吗?

当然这是一个坏的例子,其实很多代码是可以封装到独立的层去的,例如网络请求,数据解析等。但就算你怎么封装和重构,你*多能做的事情也就是把本来就不应该放在Controller里的Model层分离出去,这是你原本就应该做的事情。但你很难在activity将controller和view分离开来,怎么写activity作为Controller,都和View的关系太紧密,必须多多少少去控制如何展示数据这个View的责任。

Presenter是来给activity减负的吗?

很多人会认为MVP中引入Presenter的概念,是为了给日益臃肿的activity来减负的,而我不这样认为,我认为Presenter和Controller的责任是差不多的,它们后期承担的目的都其实很简单,就是用来隔离Model和View的,也就是常说的展示层和业务层的解藕。

那么该如何解决activity的问题呢?目前常见的MVP在Android里的实践有两种解决方案:

  1. 直接将Activity看作View,让它只承担View的责任。
  2. 将Activity看作一个MVP三者以外的一个Controller,只控制生命周期。

在Google推出的官方MVP实例里,使用的就是第2种思路,它让每一个Activity都拥有一个Fragment来作为View,然后每个Activity也对应一个Presenter,在Activity里只处理与生命周期有关的内容,并跳出MVP之外,负责实例化Model,View,Presenter,并负责将三者合理的建立联系,承担的就是一个上帝视角。

在实践中,也有很多观点会简化掉Fragment,直接将Activity视为View,这个也是我比较赞同的,更简便一些,而且这样观念上也容易理解一些,你就把activity看作View的一部分,永远只让它处理展示的逻辑,不允许它去处理数据,和拥有业务逻辑。但是这样也有一个缺点,就是V和P的依赖关系不太规范了,理论上你是不应该在View里面去实例化Presenter和Model的,这其实是不合理的,正确的依赖关系,确实是应该在一个独立的更上层去实例化Model,View,Presenter的,这样依赖才是较为合理的关系,这点来看Google的架构模式确实更合理,但实操上也会麻烦一点,必须让每个activity拥有一个独立的fragment,这个我是觉得可以自由取舍,你是要概念上的合理,还是现实中的方便,其实都可以。

因为重点还是在于如何分离展示层和业务层,activity具体承担什么责任都可以,但只能承担一个责任。

例如之前的代码可以被重构成如下结构:

/**
 * View负责展示数据
 */
public interface UserView {

  void showLoginSuccessMsg(User loginedUser);
  void showLoginFailMsg(String errorMsg); 

}
/**
 * Presenter负责做View和Model的中间人
 */
public interface UserPresenter {

  void login(String username, String password);

}
/**
 * Model负责数据的处理和业务逻辑
 */
public interface UserModel {

  void login(String username, String password, Callback callback);

}

这里将Activity被视为View, 仅负责数据的展示,并且将用户的操作事件路由给P去做处理。

public class UserActivity extends Activity implements UserView {

  private UserContract.Presenter mPresenter;

  private TextView mUsernameTextView;
  private TextView mPasswordTextView;
  private Button mLoginBtn;

  @Override
  public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
    setContentView(R.layout.login);

    mPresenter = new AddressListPresenter(this, new UserModelImpl());

    mUsernameTextView = (TextView) findViewById(R.id.username);
    mPasswordTextView = (TextView) findViewById(R.id.password);

    mLoginBtn = (Button) findViewById(R.id.login_btn);
    mLoginBtn.setOnClickListener(new View.OnClickListener() {
          public void onClick(View v)  {
              String username = mUsernameTextView.getText().toString();
              String password = mPasswordTextView.getText().toString();
            // View将用户的点击事件直接路由给Presenter区处理
            mPresenter.login(username, password); 
        } 
    });
  }

  @Override
  public void showLoginSuccessMsg(User loginedUser) {
    // Presenter在处理完毕后, 会通知View更新UI来通知用户数据操作的结果
    Toast.makeToast(getApplicationContext(), "Login Success", Toast.LENGTH_SHORT).show();
  }

  @Override
  public void showLoginFailMsg(String errorMsg) {
    // Presenter在处理完毕后, 会通知View更新UI来通知用户数据操作的结果
    Toast.makeToast(getApplicationContext(), "Login Fail", Toast.LENGTH_SHORT).show();
  }

  @Override
  protected void onResume() {
      super.onResume();
      mPresenter.subscribe();
  }

  @Override
  protected void onPause() {
      super.onPause();
      mPresenter.unSubscribe();
  }

}

Presenter层则负责将在View和Model做中间人:

/**
 * Model负责数据的处理和业务逻辑
 */
public class UserPresenterImpl implements UserPresenter {

  private UserView mUserView;
  private UserModel mUserModel;

  public UserPresenterImpl(UserView view, UserModel model) {
    mUserView = view;
    mUserModel = model;
  }

  public void login(String username, String password) {
    // Presenter处理View路由过来的用户操作,
    // 将其转换成相对的命令,传递给Model来做数据操作
    mUserModel.login(username, password, new Callback(){
      public void onSuccess(User user) {
        // Model层对数据操作后,将结果返回给Presenter,
        // 再由Presenter来通知View去更新UI来通知用户数据操作的结果
        mView.showLoginSuccessMsg(user);
      }
      public void onFail(String errorMsg) {
        mView.showLoginFailMsg(user);
      }
    });
  }

}

而Model层大家则已经可以脑补出来了,只负责对于数据的操作而已了。例如请求服务器获取数据,获取查询本地数据库都可以。

从1个类变为3个类

在MVP的实践中,很明显的结构变化就是很多页面从1个类变成了3个甚至更多的类。

例如,原来只有一个 LoginActivity ,而现在会变成至少3个类:

  1. LoginActivity(View)
  2. LoginPresenterImpl (Presenter)
  3. LoginModelImpl (Model)

而你以为这些就够了,就太天真的,在MVP里,为了解藕三者之间的关系,还需要通过接口来通信,P层是通过接口来和M层通信的,P层和V层之间也是通过接口来互相通信的(但V层对P层的通信被视为被动通信,而非主动通信)

接口列表:

  1. LoginView (interface for View)
  2. LoginPresenter (interface for Presenter)
  3. LoginMode (interface for Model)

这里插一句题外话,在Google官方的MVP实例里的,有一个契约类的概念,这个契约类的概念引入我觉得真的很赞,其实它只是将View和Presenter的接口写到了一个类里面,但这样写则会使得读代码的人一目了然就可以了解这个页面需要展示些什么,有什么操作。

如果你以为MVP各一个接口这样就应该够了,我只能说你还是太年轻太天真。要知道很多消息在MVP三者之间传递,不仅仅是同步消息,还有很多异步消息,例如用户点击了一个按钮,View将该事件传递给Presenter,Presenter异步的向Model请求数据,Model异步的返回数据给Presenter,Presenter再将Model处理的结果异步的传递给View,让其向用户作出回应。

可想而之,这样异步操作,自然少不了一些Callback的接口类,虽然可以用内部类来解决,但如果不用范型的话,这些Callback的接口类数目还是很多的。

这里也插一句提外话,我个人推荐使用rxJava来解决回调恶魔的问题,不过这仅仅是个人偏好而已。

从直来直去变成跳来跳去

上文说了,从1个类的代码,分离到了N个文件,三个层面以后,原本直来直去的代码结构,就会变成跳来跳去,例如之前1000行代码是写在一起的,现在把其中View的部分代码独立到了View的文件里,把其中Model的部分独立到Model的文件中,然后用Presenter放在它们中间,做一个中间人。

而且再加上很多消息的传递是异步的,因为在看代码的时候,或者在调试的时候,你必须从过去线性的思维变成跳跃式的,很多代码过去你开一个文件,顺着看下来就明白的,DEBUG模式下,一顺运行下来的,现在变成了你需要开N个文件,DEBUG模式下就看着从View的一个方法,跳到Presenter的一个方法,然后再跳到Model的一个方法,然后再原路跳回来,友情提示,刚开始用MVP的时候,很容易代码很清晰,但大脑却很混乱,甚至有晕车的感觉。

并且我认为这样的代码结构,甚至加大了调试的困难,过去直来直去,你很容易判断出数据是断在哪里,而现在你很难判断出数据断在哪一个层面,例如用户点击了刷新,需要从服务器拉回数据刷新到列表。但当页面没有正常展示数据的时候,你必须知道在哪个环节出错了,而我告诉你,因为分成了三个层,并且消息和数据在三个层之间传递,那么出错的可能性也变多了:

  1. 可能是View层没有把用户的事件传递给Presenter层。
  2. Presenter层可能接受到View层事件,但没有将操作传递给Model层。
  3. Model层可能接受到Presenter的请求,但没有将数据传回给Presenter层。
  4. Presenter层可能接受到Model的返回值了,但没有正确的将数据传回给View层。
  5. View层可能接受到了Presenter返回值,只是没有正确的将数据显示到页面而已。

在调试的时候,你会发现,跟踪一个问题变复杂了,消息在MVP之间传来传去的,你很难一下定位到问题出在哪个层面。

那么为什么要用MVP?

说了这么MVP带来的麻烦,例如多写了很多类,思维跳来跳去,消息传来传去,层层回调把人转晕,那回头去思考:我们为什么要用MVP,为什么要这样拆分代码,不是说这样代码更清晰,更容易理解了吗,为什么我看不懂我的代码了,为什么调试起来如何麻烦?

其实,这样我要反复说的,如果你只是学会怎么使用MVP,那么你只是换了一个架构而已,这就和你换了一个IDE写代码,却期望换了IDE就可以让代码突然变的更好一样。而你真正需要做的,依然是我之前说过的:

你需要换的是脑子,而不是架构。

如果你还在每次修改一行代码,就整体去测试你的系统,那么你把代码写在一个文件里,还是拆分到几个文件里,其实是没有区别的。你只是把代码拆开在放,而这样的拆注定只是形式的,*终我相信写着写着,你会在View里面写Model的逻辑,在Model里面写View的逻辑,并且和过去一样,Presenter越来越臃肿。

为什么要把架构里的各个层次分得清清楚楚,每个层面负责什么,不应该负责负责,如何组合起来都需要严格的定义起来,你要知道,每一种架构都不是编码规范,也不是组织代码的规范,它们都是一种思维方式。

之前说过,良好的架构都是在解决几个问题:低藕合,高复用,易测试,好维护。

如果你还在你的类和类之间new来new去,你引用我,我引用你,互相依赖,层层依赖,那么你把它们写在一个文件里,和把它们几个文件里有区别吗?

如果你的一个类还承担多个职责,明明这是个叫 Car 的类,却又在承担轮子,又在承担引擎的责任,那么你抽象和不抽像,封装不封装真的有区别吗?

如果你的一个方法还在做两件甚至三件事情,甚至把一整套事情都做完了,动辄超过几屏的函数,那么你真的觉得用不用架构真的有区别吗?

单元测试&MVP

为什么要把代码拆分成不同的文件,为什么要把架构拆分成不同的层面,其实思想都是在将一个复杂的整体拆分成一个个独立的模块,然后再用合理的接口将这些模块组装到一起,成为一个完整而稳定的系统。

很多文章都会提到“易测试”的概念,在编码里面,易测试*对不是易于测试人员去测试的意思,而只有一个意思,那就是易于单元测试,易于将整体拆分成独立的单元进行测试。

但是很多时候,我们都会认为写单元测试是一种浪费时间的事情,但其实这是非常错误的一种观点,单元测试反倒是在节省时间。

就像上文提到的,如果你还是修改了一处代码,然后就跑一遍系统,整体的测试一遍,那么不使用MVP反而比使用MVP调试要轻松。但你反过来想,你如果还是每次都是整体的测试,那么你把代码分开的意义又何在呢?将代码拆分成独立的层次,独立的模块,一来是为了更好的复用,二来就是为了能够独立的测试。

可以说使用MVP,如果只是按照Google的实例去拆分代码,这只做到了*步,而第二步就是去看Google实例中是如何写单元测试的,如何独立的对Model层去做测试,对View层去做测试,以及Presenter层如何测试。你就会发现之所以拆分,带来的*大好处就是测试友好了。你可以独立的去做测试,因为拆分了,所以互相藕合低了,互相藕合低了,所以各自更独立了,各个更独立了就使得单元测试成为了可能性,你可以独立的对MVP里的每一个层面,每一个模块,每一个公开函数进行独立的测试,当你确保了每一个独立的函数,每一个类,每一个包都能都独立的完成自己的逻辑,那么通过接口把它们组合在一起后,整体测试反而变成依然轻松了,你不需要关心代码跳来跳去,消息传来传去,只要每个模块,每个层次的逻辑是正确的,是经过单元测试的,那么整体系统就不会出现太大的问题。

所以说,*终我认为MVP的关键还是在于 单元测试 ,不管你是用MVC,还是MVP,如果你的代码是能够进行良好的单元测试,那么说明你的架构就不可能有太大问题,而使用什么架构只是表象,真正起区别代码高低境界的还是思考问题的方式。

Android任务和返回栈完全解析,那些你不知道的细节

任务和返回栈

一个应用程序当中通常都会包含很多个Activity,每个Activity都应该设计成为一个具有特定的功能,并且可以让用户进行操作的组件。另外,Activity之间还应该是可以相互启动的。比如,一个邮件应用中可能会包含一个用于展示邮件列表的Activity,而当用户点击了其中某一封邮件的时候,就会打开另外一个Activity来显示该封邮件的具体内容。

 

除此之外,一个Activity甚至还可以去启动其它应用程序当中的Activity。打个比方,如果你的应用希望去发送一封邮件,你就可以定义一个具有”send”动作的Intent,并且传入一些数据,如对方邮箱地址、邮件内容等。这样,如果另外一个应用程序中的某个Activity声明自己是可以响应这种Intent的,那么这个Activity就会被打开。在当前场景下,这个Intent是为了要发送邮件的,所以说邮件应用程序当中的编写邮件Activity就应该被打开。当邮件发送出去之后,仍然还是会回到你的应用程序当中,这让用户看起来好像刚才那个编写邮件的Activity就是你的应用程序当中的一部分。所以说,即使有很多个Activity分别都是来自于不同应用程序的,Android系统仍然可以将它们无缝地结合到一起,之所以能实现这一点,就是因为这些Activity都是存在于一个相同的任务(Task)当中的。

 

任务是一个Activity的集合,它使用栈的方式来管理其中的Activity,这个栈又被称为返回栈(back stack),栈中Activity的顺序就是按照它们被打开的顺序依次存放的。

 

手机的Home界面是大多数任务开始的地方,当用户在Home界面上点击了一个应用的图标时,这个应用的任务就会被转移到前台。如果这个应用目前并没有任何一个任务的话(说明这个应用*近没有被启动过),系统就会去创建一个新的任务,并且将该应用的主Activity放入到返回栈当中。

 

当一个Activity启动了另外一个Activity的时候,新的Activity就会被放置到返回栈的栈顶并将获得焦点。前一个Activity仍然保留在返回栈当中,但会处于停止状态。当用户按下Back键的时候,栈中*顶端的Activity会被移除掉,然后前一个Activity则会得重新回到*顶端的位置。返回栈中的Activity的顺序永远都不会发生改变,我们只能向栈顶添加Activity,或者将栈顶的Activity移除掉。因此,返回栈是一个典型的后进先出(last in, first out)的数据结构。下图通过时间线的方式非常清晰地向我们展示了多个Activity在返回栈当中的状态变化:

 

%title插图%num

 

如果用户一直地按Back键,这样返回栈中的Activity会一个个地被移除,直到*终返回到主屏幕。当返回栈中所有的Activity都被移除掉的时候,对应的任务也就不存在了。

 

任务除了可以被转移到前台之外,当然也是可以被转移到后台的。当用户开启了一个新的任务,或者点击Home键回到主屏幕的时候,之前任务就会被转移到后台了。当任务处于后台状态的时候,返回栈中所有的Activity都会进入停止状态,但这些Activity在栈中的顺序都会原封不动地保留着,如下图所示:

 

%title插图%num

 

这个时候,用户还可以将任意后台的任务切换到前台,这样用户应该就会看到之前离开这个任务时处于*顶端的那个Activity。举个例子来说,当前任务A的栈中有三个Activity,现在用户按下Home键,然后点击桌面上的图标启动了另外一个应用程序。当系统回到桌面的时候,其实任务A就已经进入后台了,然后当另外一个应用程序启动的时候,系统会为这个程序开启一个新的任务(任务B)。当用户使用完这个程序之后,再次按下Home键回到桌面,这个时候任务B也进入了后台。然后用户又重新打开了*次使用的程序,这个时候任务A又会回到前台,A任务栈中的三个Activity仍然会保留着刚才的顺序,*顶端的Activity将重新变为运行状态。之后用户仍然可以通过Home键或者多任务键来切换回任务B,或者启动更多的任务,这就是Android中多任务切换的例子。

 

由于返回栈中的Activity的顺序永远都不会发生改变,所以如果你的应用程序中允许有多个入口都可以启动同一个Activity,那么每次启动的时候就都会创建该Activity的一个新的实例,而不是将下面的Activity的移动到栈顶。这样的话就容易导致一个问题的产生,即同一个Activity有可能会被实例化很多次,如下图所示:

 

%title插图%num

 

但是呢,如果你不希望同一个Activity可以被多次实例化,那当然也是可以的,马上我们就将开始讨论如果实现这一功能,现在我们先把默认的任务和Activity的行为简单概括一下:

  • 当Activity A启动Activity B时,Activity A进入停止状态,但系统仍然会将它的所有相关信息保留,比如滚动的位置,还有文本框输入的内容等。如果用户在Activity B中按下Back键,那么Activity A将会重新回到运行状态。
  • 当用户通过Home键离开一个任务时,该任务会进入后台,并且返回栈中所有的Activity都会进入停止状态。系统会将这些Activity的状态进行保留,这样当用户下一次重新打开这个应用程序时,就可以将后台任务直接提取到前台,并将之前*顶端的Activity进行恢复。
  • 当用户按下Back键时,当前*顶端的Activity会被从返回栈中移除掉,移除掉的Activity将被销毁,然后前面一个Activity将处于栈顶位置并进入活动状态。当一个Activity被销毁了之后,系统不会再为它保留任何的状态信息。
  • 每个Activity都可以被实例化很多次,即使是在不同的任务当中。

 

管理任务

 

Android系统管理任务和返回栈的方式,正如上面所描述的一样,就是把所有启动的Activity都放入到一个相同的任务当中,通过一个“后进先出”的栈来进行管理的。这种方式在*大多数情况下都是没问题的,开发者也无须去关心任务中的Activity到底是怎么样存放在返回栈当中的。但是呢,如果你想打破这种默认的行为,比如说当启动一个新的Activity时,你希望它可以存在于一个独立的任务当中,而不是现有的任务当中。或者说,当启动一个Activity时,如果这个Activity已经存在于返回栈中了,你希望能把这个Activity直接移动到栈顶,而不是再创建一个它的实例。再或者,你希望可以将返回栈中除了*底层的那个Activity之外的其它所有Activity全部清除掉。这些功能甚至更多功能,都是可以通过在manifest文件中设置<activity>元素的属性,或者是在启动Activity时配置Intent的flag来实现的。

 

在<activity>元素中,有以下几个属性是可以使用的:

  • taskAffinity
  • launchMode
  • allowTaskReparenting
  • clearTaskOnLaunch
  • alwaysRetainTaskState
  • finishOnTaskLaunch

 

而在Intent当中,有以下几个flag是比较常用的:

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

 

下面我们就将开始讨论,如何通过manifest参数,以及Intent flag来改变Activity在任务中的默认行为。

 

定义启动模式

启动模式允许你去定义如何将一个Activity的实例和当前的任务进行关联,你可以通过以下两种不同的方式来定义启动模式:

 

1.使用manifest文件

当你在manifest文件中声明一个Activity的时候,你可以指定这个Activity在启动的时候该如何与任务进行关联。

 

2.使用Intent flag

当你调用startActivity()方法时,你可以在Intent中加入一个flag,从而指定新启动的Activity该如何与当前任务进行关联。

 

也就是说,如果Activity A启动了Activity B,Activity B可以定义自己该如何与当前任务进行关联,而Activity A也可以要求Activity B该如何与当前任务进行关联。如果Activity B在manifest中已经定义了该如何与任务进行关联,而Activity A同时也在Intent中要求了Activity B该怎么样与当前任务进行关联,那么此时Intent中的定义将覆盖manifest中的定义。

需要注意的是,有些启动模式在manifest中可以指定,但在Intent中是指定不了的。同样,也有些启动模式在Intent中可以指定,但在manifest中是指定不了的,下面我们就来具体讨论一下。

 

使用manifest文件

当在manifest文件中定义Activity的时候,你可以通过<activity>元素的launchMode属性来指定这个Activity应该如何与任务进行关联。launchMode属性一共有以下四种可选参数:

 

“standard”(默认启动模式)

standard是默认的启动模式,即如果不指定launchMode属性,则自动就会使用这种启动模式。这种启动模式表示每次启动该Activity时系统都会为创建一个新的实例,并且总会把它放入到当前的任务当中。声明成这种启动模式的Activity可以被实例化多次,一个任务当中也可以包含多个这种Activity的实例。

 

“singleTop”

这种启动模式表示,如果要启动的这个Activity在当前任务中已经存在了,并且还处于栈顶的位置,那么系统就不会再去创建一个该Activity的实例,而是调用栈顶Activity的onNewIntent()方法。声明成这种启动模式的Activity也可以被实例化多次,一个任务当中也可以包含多个这种Activity的实例。

 

举个例子来讲,一个任务的返回栈中有A、B、C、D四个Activity,其中A在*底端,D在*顶端。这个时候如果我们要求再启动一次D,并且D的启动模式是”standard”,那么系统就会再创建一个D的实例放入到返回栈中,此时栈内元素为:A-B-C-D-D。而如果D的启动模式是”singleTop”的话,由于D已经是在栈顶了,那么系统就不会再创建一个D的实例,而是直接调用D Activity的onNewIntent()方法,此时栈内元素仍然为:A-B-C-D。

 

“singleTask”

这种启动模式表示,系统会创建一个新的任务,并将启动的Activity放入这个新任务的栈底位置。但是,如果现有任务当中已经存在一个该Activity的实例了,那么系统就不会再创建一次它的实例,而是会直接调用它的onNewIntent()方法。声明成这种启动模式的Activity,在同一个任务当中只会存在一个实例。注意这里我们所说的启动Activity,都指的是启动其它应用程序中的Activity,因为”singleTask”模式在默认情况下只有启动其它程序的Activity才会创建一个新的任务,启动自己程序中的Activity还是会使用相同的任务,具体原因会在下面 处理affinity 部分进行解释。

 

“singleInstance”

这种启动模式和”singleTask”有点相似,只不过系统不会向声明成”singleInstance”的Activity所在的任务当中再添加其它Activity。也就是说,这种Activity所在的任务中始终只会有一个Activity,通过这个Activity再打开的其它Activity也会被放入到别的任务当中。

 

再举一个例子,Android系统内置的浏览器程序声明自己浏览网页的Activity始终应该在一个独立的任务当中打开,也就是通过在<activity>元素中设置”singleTask”启动模式来实现的。这意味着,当你的程序准备去打开Android内置浏览器的时候,新打开的Activity并不会放入到你当前的任务中,而是会启动一个新的任务。而如果浏览器程序在后台已经存在一个任务了,则会把这个任务切换到前台。

 

其实不管是Activity在一个新任务当中启动,还是在当前任务中启动,返回键永远都会把我们带回到之前的一个Activity中的。但是有一种情况是比较特殊的,就是如果Activity指定了启动模式是”singleTask”,并且启动的是另外一个应用程序中的Activity,这个时候当发现该Activity正好处于一个后台任务当中的话,就会直接将这整个后台任务一起切换到前台。此时按下返回键会优先将目前*前台的任务(刚刚从后台切换到*前台)进行回退,下图比较形象地展示了这种情况:

 

%title插图%num

 

 

使用Intent flags

除了使用manifest文件之外,你也可以在调用startActivity()方法的时候,为Intent加入一个flag来改变Activity与任务的关联方式,下面我们来一一讲解一下每种flag的作用:

 

FLAG_ACTIVITY_NEW_TASK

 

设置了这个flag,新启动Activity就会被放置到一个新的任务当中(与”singleTask”有点类似,但不完全一样),当然这里讨论的仍然还是启动其它程序中的Activity。这个flag的作用通常是模拟一种Launcher的行为,即列出一推可以启动的东西,但启动的每一个Activity都是在运行在自己独立的任务当中的。

 

FLAG_ACTIVITY_SINGLE_TOP

 

设置了这个flag,如果要启动的Activity在当前任务中已经存在了,并且还处于栈顶的位置,那么就不会再次创建这个Activity的实例,而是直接调用它的onNewIntent()方法。这种flag和在launchMode中指定”singleTop”模式所实现的效果是一样的。

 

FLAG_ACTIVITY_CLEAR_TOP

 

设置了这个flag,如果要启动的Activity在当前任务中已经存在了,就不会再次创建这个Activity的实例,而是会把这个Activity之上的所有Activity全部关闭掉。比如说,一个任务当中有A、B、C、D四个Activity,然后D调用了startActivity()方法来启动B,并将flag指定成FLAG_ACTIVITY_CLEAR_TOP,那么此时C和D就会被关闭掉,现在返回栈中就只剩下A和B了。

 

那么此时Activity B会接收到这个启动它的Intent,你可以决定是让Activity B调用onNewIntent()方法(不会创建新的实例),还是将Activity B销毁掉并重新创建实例。如果Activity B没有在manifest中指定任何启动模式(也就是”standard”模式),并且Intent中也没有加入一个FLAG_ACTIVITY_SINGLE_TOP flag,那么此时Activity B就会销毁掉,然后重新创建实例。而如果Activity B在manifest中指定了任何一种启动模式,或者是在Intent中加入了一个FLAG_ACTIVITY_SINGLE_TOP flag,那么就会调用Activity B的onNewIntent()方法。

 

FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_NEW_TASK结合在一起使用也会有比较好的效果,比如可以将一个后台运行的任务切换到前台,并把目标Activity之上的其它Activity全部关闭掉。这个功能在某些情况下非常有用,比如说从通知栏启动Activity的时候。

 

处理affinity

affinity可以用于指定一个Activity更加愿意依附于哪一个任务,在默认情况下,同一个应用程序中的所有Activity都具有相同的affinity,所以,这些Activity都更加倾向于运行在相同的任务当中。当然了,你也可以去改变每个Activity的affinity值,通过<activity>元素的taskAffinity属性就可以实现了。

 

taskAffinity属性接收一个字符串参数,你可以指定成任意的值(经我测试字符串中至少要包含一个.),但必须不能和应用程序的包名相同,因为系统会使用包名来作为默认的affinity值。

 

affinity主要有以下两种应用场景:

  • 当调用startActivity()方法来启动一个Activity时,默认是将它放入到当前的任务当中。但是,如果在Intent中加入了一个FLAG_ACTIVITY_NEW_TASK flag的话(或者该Activity在manifest文件中声明的启动模式是”singleTask”),系统就会尝试为这个Activity单独创建一个任务。但是规则并不是只有这么简单,系统会去检测要启动的这个Activity的affinity和当前任务的affinity是否相同,如果相同的话就会把它放入到现有任务当中,如果不同则会去创建一个新的任务。而同一个程序中所有Activity的affinity默认都是相同的,这也是前面为什么说,同一个应用程序中即使声明成”singleTask”,也不会为这个Activity再去创建一个新的任务了。
  • 当把Activity的allowTaskReparenting属性设置成true时,Activity就拥有了一个转移所在任务的能力。具体点来说,就是一个Activity现在是处于某个任务当中的,但是它与另外一个任务具有相同的affinity值,那么当另外这个任务切换到前台的时候,该Activity就可以转移到现在的这个任务当中。
    那还是举一个形象点的例子吧,比如有一个天气预报程序,它有一个Activity是专门用于显示天气信息的,这个Activity和该天气预报程序的所有其它Activity具体相同的affinity值,并且还将allowTaskReparenting属性设置成true了。这个时候,你自己的应用程序通过Intent去启动了这个用于显示天气信息的Activity,那么此时这个Activity应该是和你的应用程序是在同一个任务当中的。但是当把天气预报程序切换到前台的时候,这个Activity又会被转移到天气预报程序的任务当中,并显示出来,因为它们拥有相同的affinity值,并且将allowTaskReparenting属性设置成了true。

清空返回栈

 

如何用户将任务切换到后台之后过了很长一段时间,系统会将这个任务中除了*底层的那个Activity之外的其它所有Activity全部清除掉。当用户重新回到这个任务的时候,*底层的那个Activity将得到恢复。这个是系统默认的行为,因为既然过了这么长的一段时间,用户很有可能早就忘记了当时正在做什么,那么重新回到这个任务的时候,基本上应该是要去做点新的事情了。

 

当然,既然说是默认的行为,那就说明我们肯定是有办法来改变的,在<activity>元素中设置以下几种属性就可以改变系统这一默认行为:

 

alwaysRetainTaskState

如果将*底层的那个Activity的这个属性设置为true,那么上面所描述的默认行为就将不会发生,任务中所有的Activity即使过了很长一段时间之后仍然会被继续保留。

 

clearTaskOnLaunch

如果将*底层的那个Activity的这个属性设置为true,那么只要用户离开了当前任务,再次返回的时候就会将*底层Activity之上的所有其它Activity全部清除掉。简单来讲,就是一种和alwaysRetainTaskState完全相反的工作模式,它保证每次返回任务的时候都会是一种初始化状态,即使用户仅仅离开了很短的一段时间。

 

finishOnTaskLaunch

这个属性和clearTaskOnLaunch是比较类似的,不过它不是作用于整个任务上的,而是作用于单个Activity上。如果某个Activity将这个属性设置成true,那么用户一旦离开了当前任务,再次返回时这个Activity就会被清除掉。

Activity启动流程–Activity启动的概要流程

概述

Android中启动某个Activity,将先启动Activity所在的应用。应用启动时会启动一个以应用包名为进程名的进程,该进程有一个主线程,叫ActivityThread,也叫做UI线程。

本系列博客将详细阐述Activity的启动流程,这些博客基于Cm 10.1源码研究。

  • 深入理解Activity启动流程(二)–Activity启动相关类的类图
  • 深入理解Activity启动流程(三)–Activity启动的详细流程1
  • 深入理解Activity启动流程(三)–Activity启动的详细流程2
  • 深入理解Activity启动流程(四)–Activity Task的调度算法

Activity启动时的概要交互流程

用户从Launcher程序点击应用图标可启动应用的入口Activity,Activity启动时需要多个进程之间的交互,Android系统中有一个zygote进程专用于孵化Android框架层和应用层程序的进程。还有一个system_server进程,该进程里运行了很多binder service,例如ActivityManagerService,PackageManagerService,WindowManagerService,这些binder service分别运行在不同的线程中,其中ActivityManagerService负责管理Activity栈,应用进程,task。

Activity启动时的概要交互流程如下图如下所示(点击图片可看大图):

activity_start_flow

用户在Launcher程序里点击应用图标时,会通知ActivityManagerService启动应用的入口Activity,ActivityManagerService发现这个应用还未启动,则会通知Zygote进程孵化出应用进程,然后在这个dalvik应用进程里执行ActivityThread的main方法。应用进程接下来通知ActivityManagerService应用进程已启动,ActivityManagerService保存应用进程的一个代理对象,这样ActivityManagerService可以通过这个代理对象控制应用进程,然后ActivityManagerService通知应用进程创建入口Activity的实例,并执行它的生命周期方法。

后续博客将介绍Activity的详细启动流程。

Activity启动流程–Activity启动相关类的类图

在介绍Activity的详细启动流程之前,先为大家介绍Activity启动时涉及到的类,这样大家可以有大概的了解,不至于在细节中迷失。

  • 深入理解Activity启动流程(一)–Activity启动的概要流程
  • 深入理解Activity启动流程(三)–Activity启动的详细流程1
  • 深入理解Activity启动流程(三)–Activity启动的详细流程2
  • 深入理解Activity启动流程(四)–Activity Task的调度算法

Activity启动时涉及到的类有IActivityManager相关类, IApplicationThread相关类, ActivityManagerService相关类。

IActivityManager相关类

点击图片可看大图

IActivityManager

Activity的管理采用binder机制,管理Activity的接口是IActivityManager. ActivityManagerService实现了Activity管理功能,位于system_server进程,ActivityManagerProxy对象是ActivityManagerService在普通应用进程的一个代理对象,应用进程通过ActivityManagerProxy对象调用ActivityManagerService提供的功能。应用进程并不会直接创建ActivityManagerProxy对象,而是通过调用ActiviyManagerNative类的工具方法getDefault方法得到ActivityManagerProxy对象。所以在应用进程里通常这样启动Activty:

1
ActivityManagerNative.getDefault().startActivity()

IApplicationThread相关类

点击图片可看大图

IApplicationThread

应用进程需要调用ActivityManagerService提供的功能,而ActivityManagerService也需要主动调用应用进程以控制应用进程并完成指定操作。这样ActivityManagerService也需要应用进程的一个Binder代理对象,而这个代理对象就是ApplicationThreadProxy对象。

ActivityManagerService通过IApplicationThread接口管理应用进程,ApplicationThread类实现了IApplicationThread接口,实现了管理应用的操作,ApplicationThread对象运行在应用进程里。ApplicationThreadProxy对象是ApplicationThread对象在ActivityManagerService线程 (ActivityManagerService线程运行在system_server进程)内的代理对象,ActivityManagerService通过ApplicationThreadProxy对象调用ApplicationThread提供的功能,比如让应用进程启动某个Activity。

ActivityManagerService相关类

点击图片可看大图

ActivityManagerService

ActivityManagerService管理Activity时,主要涉及以下几个类:

  • 1)\tActivityManagerService,它是管理activity的入口类,聚合了ProcessRecord对象和ActivityStack对象
  • 2)\tProcessRecord,表示应用进程记录,每个应用进程都有对应的ProcessRecord对象
  • 3)\tActivityStack,该类主要管理回退栈
  • 4)\tActivityRecord,每次启动一个Actvity会有一个对应的ActivityRecord对象,表示Activity的一个记录
  • 5)\tActivityInfo,Activity的信息,比如启动模式,taskAffinity,flag信息(这些信息在AndroidManifest.xml里声明Activity时填写)
  • 6)\tTaskRecord,Task记录信息,一个Task可能有多个ActivityRecord,但是一个ActivityRecord只能属于一个TaskRecord

注意:

ActivityManagerService里只有一个ActivityStack对象,并不会像Android官方文档描述的一样,每个Task都有一个activity stack对象。ActivityStack管理ActivityRecord时,不是下面这样组织ActivityRecord的:

1
2
List<TaskRecord> taskList; //ActivityStack类
List<ActivityRecord> recordList;// TaskRecord类

而是像下面这样组织ActivityRecord:

1
2
ArrayList<ActivityRecord> mHistory = new ArrayList<ActivityRecord>(); //ActivityStack类里
TaskRecord task; // ActivityRecord类里

也就是说ActivityManagerService组织回退栈时以ActivityRecord为基本单位,所有的ActivityRecord放在同一个ArrayList里,可以将mHistory看作一个栈对象,索引0所指的对象位于栈底,索引mHistory.size()-1所指的对象位于栈顶。

但是ActivityManagerService调度ActivityRecord时以task为基本单位,每个ActivityRecord对象都属于某个TaskRecord,一个TaskRecord可能有多个ActivityRecord。

ActivityStack没有TaskRecord列表的入口,只有在ActivityManagerService才有TaskRecord列表的入口:

1
final ArrayList<TaskRecord> mRecentTasks

ActivityStack管理ActivityRecord时,将属于同一个task的ActivityRecord放在一起,如下所示:

backstack

回退栈里可看到两个task,假设上面的task为task1,下面的task为task2,task1包含D,E两个Activity Record,task2包含3个ActivityRecord。task1位于回退栈的栈顶,task2位于task1下面,task1中E位于栈顶,task2中C位于栈顶。需注意两个task的Activity不会混在一起,也就是说task2的B不能放在task1的D和E中间。

因为回退栈是栈结构,所以此时不断按返回键,显示的Activity的顺序为E–>D–>C–>B–>A。

下一篇博客为大家讲述Activity的详细启动流程。

Activity启动流程–Activity启动的详细流程

本系列文章将详细阐述Activity的启动流程,这些基于Cm 10.1源码研究。

  • 深入理解Activity启动流程(一)–Activity启动的概要流程
  • 深入理解Activity启动流程(二)–Activity启动相关类的类图
  • 深入理解Activity启动流程(三)–Activity启动的详细流程1
  • 深入理解Activity启动流程(四)–Activity Task的调度算法

上篇文章介绍了Activity详细启动流程的前半部分:

  • 1. Activity调用ActivityManagerService启动应用
  • 2. ActivityManagerService调用Zygote孵化应用进程
  • 3. Zygote孵化应用进程

本篇文章主要介绍Activity详细启动流程的后半部分:

  • 4. 新进程启动ActivityThread
  • 5. 应用进程绑定到ActivityManagerService
  • 6. ActivityThread的Handler处理启动Activity的消息

4. 新进程启动ActivityThread

点击图片可看大图

zygote_activitythread

Zygote进程孵化出新的应用进程后,会执行ActivityThread类的main方法。在该方法里会先准备好Looper和消息队列,然后调用attach方法将应用进程绑定到ActivityManagerService,然后进入loop循环,不断地读取消息队列里的消息,并分发消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//ActivityThread类
public static void main(String[] args) {
    //... 
    Looper.prepareMainLooper();
    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    AsyncTask.init();
    //...
    Looper.loop();

    //...
}

5. 应用进程绑定到ActivityManagerService

点击图片可看大图

application_amservice

在ActivityThread的main方法里调用thread.attach(false);attach方法的主要代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//ActivityThread类
private void attach(boolean system) {
    sThreadLocal.set(this);
    mSystemThread = system;
    if (!system) {
        //...
        IActivityManager mgr = ActivityManagerNative.getDefault();
        try {
        //调用ActivityManagerService的attachApplication方法
        //将ApplicationThread对象绑定至ActivityManagerService,
        //这样ActivityManagerService就可以
        //通过ApplicationThread代理对象控制应用进程
            mgr.attachApplication(mAppThread);
        } catch (RemoteException ex) {
            // Ignore
        }
    } else {
        //...
    }
    //... 
}

ActivityManagerService的attachApplication方法执行attachApplicationLocked(thread, callingPid)进行绑定。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
//ActivityManagerService类
private final boolean attachApplicationLocked(IApplicationThread thread,
        int pid) { 
    ProcessRecord app;
    //...     
    app.thread = thread; 
    //...  
    try {
        //...
        thread.bindApplication(processName, appInfo, providers,
                app.instrumentationClass, profileFile, profileFd, profileAutoStop,
                app.instrumentationArguments, app.instrumentationWatcher, testMode,
                enableOpenGlTrace, isRestrictedBackupMode || !normalMode, app.persistent,
                new Configuration(mConfiguration), app.compat, getCommonServicesLocked(),
                mCoreSettingsObserver.getCoreSettingsLocked());
        //... 
    } catch (Exception e) {
       //...
    }
    //... 
    ActivityRecord hr = mMainStack.topRunningActivityLocked(null);
    if (hr != null && normalMode) {
        if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                && processName.equals(hr.processName)) {
            try {
                if (mHeadless) {
                    Slog.e(TAG, "Starting activities not supported on headless device: " + hr);
                } else if (mMainStack.realStartActivityLocked(hr, app, true, true)) {
                //mMainStack.realStartActivityLocked真正启动activity
                    didSomething = true;
                }
            } catch (Exception e) {
                //...
            }
        } else {
            //...
        }
    }
    //... 
    return true;
}

attachApplicationLocked方法有两个重要的函数调用thread.bindApplication和mMainStack.realStartActivityLocked。thread.bindApplication将应用进程的ApplicationThread对象绑定到ActivityManagerService,也就是说获得ApplicationThread对象的代理对象。mMainStack.realStartActivityLocked通知应用进程启动Activity。

5.1 thread.bindApplication

thread对象其实是ActivityThread里ApplicationThread对象在ActivityManagerService的代理对象,故此执行thread.bindApplication,*终会调用ApplicationThread的bindApplication方法,该方法的主要代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//ActivityThread类
public final void bindApplication(String processName,
        ApplicationInfo appInfo, List<ProviderInfo> providers,
        ComponentName instrumentationName, String profileFile,
        ParcelFileDescriptor profileFd, boolean autoStopProfiler,
        Bundle instrumentationArgs, IInstrumentationWatcher instrumentationWatcher,
        int debugMode, boolean enableOpenGlTrace, boolean isRestrictedBackupMode,
        boolean persistent, Configuration config, CompatibilityInfo compatInfo,
        Map<String, IBinder> services, Bundle coreSettings) {
    //...  
    AppBindData data = new AppBindData();
    data.processName = processName;
    data.appInfo = appInfo;
    data.providers = providers;
    data.instrumentationName = instrumentationName;
    data.instrumentationArgs = instrumentationArgs;
    data.instrumentationWatcher = instrumentationWatcher;
    data.debugMode = debugMode;
    data.enableOpenGlTrace = enableOpenGlTrace;
    data.restrictedBackupMode = isRestrictedBackupMode;
    data.persistent = persistent;
    data.config = config;
    data.compatInfo = compatInfo;
    data.initProfileFile = profileFile;
    data.initProfileFd = profileFd;
    data.initAutoStopProfiler = false;
    queueOrSendMessage(H.BIND_APPLICATION, data);
}

这样调用queueOrSendMessage会往ActivityThread的消息队列发送消息,消息的用途是BIND_APPLICATION。

这样会在handler里处理BIND_APPLICATION消息,接着调用handleBindApplication方法处理绑定消息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//ActivityThread类
private void handleBindApplication(AppBindData data) {
  //...  
  ApplicationInfo instrApp = new ApplicationInfo();
  instrApp.packageName = ii.packageName;
  instrApp.sourceDir = ii.sourceDir;
  instrApp.publicSourceDir = ii.publicSourceDir;
  instrApp.dataDir = ii.dataDir;
  instrApp.nativeLibraryDir = ii.nativeLibraryDir;
  LoadedApk pi = getPackageInfo(instrApp, data.compatInfo,
        appContext.getClassLoader(), false, true);
  ContextImpl instrContext = new ContextImpl();
  instrContext.init(pi, null, this);
    //... 
   
     

  if (data.instrumentationName != null) {
       //...
  } else {
       //注意Activity的所有生命周期方法都会被Instrumentation对象所监控,
       //也就说执行Activity的生命周期方法前后一定会调用Instrumentation对象的相关方法
       //并不是说只有跑单测用例才会建立Instrumentation对象,
       //即使不跑单测也会建立Instrumentation对象
       mInstrumentation = new Instrumentation();
  }
  //... 
  try {
     //...
     Application app = data.info.makeApplication(data.restrictedBackupMode, null);
     mInitialApplication = app;
     //...         
     try {
          mInstrumentation.onCreate(data.instrumentationArgs);
      }catch (Exception e) {
             //...
      }
      try {
           //这里会调用Application的onCreate方法
           //故此Applcation对象的onCreate方法会比ActivityThread的main方法后调用
           //但是会比这个应用的所有activity先调用
            mInstrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
           //...
        }
    } finally {
        StrictMode.setThreadPolicy(savedPolicy);
    }
}

5.2 mMainStack.realStartActivityLocked

realStartActivity会调用scheduleLaunchActivity启动activity,主要代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//ActivityStack类
final boolean realStartActivityLocked(ActivityRecord r,
        ProcessRecord app, boolean andResume, boolean checkConfig)
        throws RemoteException {

    //...  
    try {
        //...
        app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                System.identityHashCode(r), r.info,
                new Configuration(mService.mConfiguration),
                r.compat, r.icicle, results, newIntents, !andResume,
                mService.isNextTransitionForward(), profileFile, profileFd,
                profileAutoStop);
        
        //...
        
    } catch (RemoteException e) {
        //...
    }
    //...    
    return true;
}

同样app.thread也只是ApplicationThread对象在ActivityManagerService的一个代理对象而已,*终会调用ApplicationThread的scheduleLaunchActivity方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//ActivityThread类
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
        ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
        Bundle state, List<ResultInfo> pendingResults,
        List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
        String profileName, ParcelFileDescriptor profileFd, boolean autoStopProfiler) {
    ActivityClientRecord r = new ActivityClientRecord();
    r.token = token;
    r.ident = ident;
    r.intent = intent;
    r.activityInfo = info;
    r.compatInfo = compatInfo;
    r.state = state;
    r.pendingResults = pendingResults;
    r.pendingIntents = pendingNewIntents;
    r.startsNotResumed = notResumed;
    r.isForward = isForward;
    r.profileFile = profileName;
    r.profileFd = profileFd;
    r.autoStopProfiler = autoStopProfiler;
    updatePendingConfiguration(curConfig);
    queueOrSendMessage(H.LAUNCH_ACTIVITY, r);
}

这里调用了queueOrSendMessage往ActivityThread的消息队列发送了消息,消息的用途是启动Activity,接下来ActivityThread的handler便会处理该消息。

6. ActivityThread的Handler处理启动Activity的消息

点击图片可看大图

activitythread_activity

ActivityThread的handler调用handleLaunchActivity处理启动Activity的消息,handleLaunchActivity的主要代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
//ActivityThread类
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    //... 
    Activity a = performLaunchActivity(r, customIntent);
    if (a != null) {
        //...
        handleResumeActivity(r.token, false, r.isForward,
                !r.activity.mFinished && !r.startsNotResumed);
        //...
    } else {
        //...
    }
}

handleLaunchActivity方法里有有两个重要的函数调用,performLaunchActivity和handleResumeActivity,performLaunchActivity会调用Activity的onCreate,onStart,onResotreInstanceState方法,handleResumeActivity会调用Activity的onResume方法.

6.1 performLaunchActivity

performLaunchActivity的主要代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//ActivityThread类
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    //...
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        //...
    } catch (Exception e) {
        //...
    }
    try {
        //r.packageInfo.makeApplication实际并未创建Application对象,
        //因为bindApplication过程已经创建了Application对象,
        //makeApplication方法会返回已创建的Application对象
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);
        //...         
        if (activity != null) {
            //...
            //将application对象,appContext对象绑定到新建的activity对象
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config);
            //... 
            //会调用Activity的onCreate方法             
            mInstrumentation.callActivityOnCreate(activity, r.state);
            //...
            //...
            //调用Activity的onStart方法
            if (!r.activity.mFinished) {
                activity.performStart();
                r.stopped = false;
            }              
            if (!r.activity.mFinished) {
                if (r.state != null) {
                    //会调用Activity的onRestoreInstanceState方法
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                }
            }
            if (!r.activity.mFinished) {
                activity.mCalled = false;
                mInstrumentation.callActivityOnPostCreate(activity, r.state);
                //...
            }
        }
        //...
    } catch (SuperNotCalledException e) {
        throw e;

    } catch (Exception e) {
        //...
    }
    return activity;
}

6.2 handleResumeActivity

handleResumeActivity的主要代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
//ActivityThread类
final void handleResumeActivity(IBinder token, boolean clearHide, boolean isForward,
        boolean reallyResume) {
    //...
    //performResumeActivity*终会调用Activity的onResume方法 
    ActivityClientRecord r = performResumeActivity(token, clearHide);
    if (r != null) {
        final Activity a = r.activity;
        //... 
        //显示界面
        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;
            if (a.mVisibleFromClient) {
                a.mWindowAdded = true;
                wm.addView(decor, l);
            }
           //...         
        } else if (!willBeVisible) {
             //...
        }
        // Tell the activity manager we have resumed.
        if (reallyResume) {
            try {
                ActivityManagerNative.getDefault().activityResumed(token);
            } catch (RemoteException ex) {
            }
        }

    } else {
         //...
    }
}

performResumeActivity的主要代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//ActivityThread类
public final ActivityClientRecord performResumeActivity(IBinder token,
        boolean clearHide) {
    ActivityClientRecord r = mActivities.get(token);
    //...
    if (r != null && !r.activity.mFinished) {
         //...
        try {
            //... 
            //会调用Activity的onResume方法 
            r.activity.performResume();
            //...
        } catch (Exception e) {
            //...
        }
    }
    return r;
}

总结

Activity的概要启动流程:

用户在Launcher程序里点击应用图标时,会通知ActivityManagerService启动应用的入口Activity,ActivityManagerService发现这个应用还未启动,则会通知Zygote进程孵化出应用进程,然后在这个dalvik应用进程里执行ActivityThread的main方法。应用进程接下来通知ActivityManagerService应用进程已启动,ActivityManagerService保存应用进程的一个代理对象,这样ActivityManagerService可以通过这个代理对象控制应用进程,然后ActivityManagerService通知应用进程创建入口Activity的实例,并执行它的生命周期方法

现在也可以理解:

如果应用的组件(包括所有组件Activity,Service,ContentProvider,Receiver) 被启动,肯定会先启动以应用包名为进程名的进程,这些组件都会运行在应用包名为进程名的进程里,并且是在主线程里。应用进程启动时会先创建Application对象,并执行Application对象的生命周期方法,然后才启动应用的组件。

有一种情况比较特殊,那就是为组件设置了特殊的进程名,也就是说通过android:process设置进程名的情况,此时组件运行在单独的进程内。

Activity启动流程

好吧,终于要开始讲解Activity的启动流程了,Activity的启动流程相对复杂一下,涉及到了Activity中的生命周期方法,涉及到了Android体系的CS模式,涉及到了Android中进程通讯Binder机制等等,

首先介绍一下Activity,这里引用一下Android guide中对Activity的介绍:

An activity represents a single screen with a user interface. For example, an email application might have one activity that shows a list of new emails, another activity to compose an email, and another activity for reading emails. Although the activities work together to form a cohesive user experience in the email application, each one is independent of the others. As such, a different application can start any one of these activities (if the email application allows it). For example, a camera application can start the activity in the email application that composes new mail, in order for the user to share a picture.

英文不太好,这里就不献丑了,这里介绍的Activity的大概意思就是说,activity在Android系统中代表的就是一个屏幕,一个App就是由许多个不同的Acitivty组成的,并且不同进程之间的Activity是可以相互调用的。

在介绍Activity的启动流程之前,我们先介绍几个概念:

  • Activity的生命周期

protected void onCreate(Bundle savedInstanceState);
protected void onRestart();
protected void onStart();
protected void onResume();
protected void onPause();
protected void onStop();
protected void onDestory();
以上为Activity生命周期中的各个时期的回调方法,在不同的方法中我们可以执行不同的逻辑。
关于Activity生命周期的详细介绍可以参考: Android activity的生命周期

  • Activity的启动模式

activity启动时可以设置不同的启动模式,主要是:standrand,singleTop,singleTask,instance等四种启动模式,不同的启动模式在启动Activity时会执行不同的逻辑,系统会按不同的启动模式将Activity存放到不同的activity栈中。
关于Activity启动模式的详细介绍,可以参考: Android任务和返回栈完全解析

  • Activity的启动进程

在Manifest.xml中定义Activity的时候,Activity默认是属于进程名称为包名的进程的,当然这时候是可以指定Activity的启动进程,所以在Activity启动时首先会检测当前Activity所属的进程是否已经启动,若进程没有启动,则首先会启动该进程,并在该进程启动之后才会执行Activity的启动过程。

  • Intent启动Activity的方式

    Intent启动Activity分为两种,显示启动和隐士启动,显示启动就是在初始化Intent对象的时候直接引用需要启动的Activity的字节码,显示引用的好处就是可以直接告诉Intent对象启动的Activity对象不需要执行intent filter索引需要启动哪一个Activity,但是显示引用不能启动其他进程的Activity对象,因为无法获取其他进程的Activity对象的字节码,而隐式启动则可以通过配置Intent Filter启动其他进程的Activity对象,因此在应用内,我们一般都是使用显示启动的方式启动Activity,而如果需要启动其他应用的Activity时,一般使用隐式启动的方式。

  • Android Framework层的CS模式
    通过前几篇文章的介绍我们知道android系统在启动过程中会执行这样的逻辑:
    Zygote进程 –> SystemServer进程 –> 各种系统服务 –> 应用进程
    在Actvity启动过程中,其实是应用进程与SystemServer进程相互配合启动Activity的过程,其中应用进程主要用于执行具体的Activity的启动过程,回调生命周期方法等操作,而SystemServer进程则主要是调用其中的各种服务,将Activity保存在栈中,协调各种系统资源等操作。
  • Android系统进程间通讯Binder机制
    Android系统存了Zygote进程和SystemServer进程以及各种应用进程等,为了能够实现各种进程之间的通讯,Android系统采用了自己的进程间通讯方式Binder机制。其中主要涉及到了四种角色:Binder Client,Binder Server,Binder Manager, Binder driver。各种角色之间的关系可以参考下面这张图的介绍:
    这里写图片描述

好吧,前面我们介绍了一些Activity启动过程中需要的相关知识点,下面我们开始Activity启动流程的讲解。。。。

还记得前面我们讲过的Launcher启动流程么?可以参考:android源码解析之(十)–>Launcher启动流程
在这篇文章中我们说Launcher启动之后会将各个应用包名和icon与app name保存起来,然后执行icon的点击事件的时候调用startActivity方法:

@Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        Intent intent = intentForPosition(position);
        startActivity(intent);
    }

protected Intent intentForPosition(int position) {
        ActivityAdapter adapter = (ActivityAdapter) mAdapter;
        return adapter.intentForPosition(position);
    }

public Intent intentForPosition(int position) {
            if (mActivitiesList == null) {
                return null;
            }

            Intent intent = new Intent(mIntent);
            ListItem item = mActivitiesList.get(position);
            intent.setClassName(item.packageName, item.className);
            if (item.extras != null) {
                intent.putExtras(item.extras);
            }
            return intent;
        }

 

可以发现,我们在启动Activity的时候,执行的逻辑就是创建一个Intent对象,然后初始化Intent对象,使用隐式启动的方式启动该Acvitity,这里为什么不能使用显示启动的方式呢?

这是因为Launcher程序启动的Activity一般都是启动一个新的应用进程,该进程与Launcher进程不是在同一个进程中,所以也就无法引用到启动的Activity字节码,自然也就无法启动该Activity了。

继续,我们查看startActivity方法的具体实现:

一:开始请求执行启动Activity

MyActivity.startActivity()
Activity.startActivity()
Activity.startActivityForResult
Instrumentation.execStartActivty
ActivityManagerNative.getDefault().startActivityAsUser()

在我们的Activity中调用startActivity方法,会执行Activity中的startActivity

@Override
    public void startActivity(Intent intent) {
        this.startActivity(intent, null);
    }

 

然后在Activity中的startActivity方法体里调用了startActivity的重载方法,这里我们看一下其重载方法的实现:

@Override
    public void startActivity(Intent intent, @Nullable Bundle options) {
        if (options != null) {
            startActivityForResult(intent, -1, options);
        } else {
            // Note we want to go through this call for compatibility with
            // applications that may have overridden the method.
            startActivityForResult(intent, -1);
        }
    }

 

由于在上一步骤中我们传递的Bunde对象为空,所以这里我们执行的是else分支的逻辑,所以这里调用了startActivityForResult方法,并且传递的参数为intent和-1.

注意:通过这里的代码我们可以发现,其实我们在Activity中调用startActivity的内部也是调用的startActivityForResult的。那么为什么调用startActivityForResult可以在Activity中回调onActivityResult而调用startActivity则不可以呢?可以发现其主要的区别是调用startActivity内部调用startActivityForResult传递的传输requestCode值为-1,也就是说我们在Activity调用startActivityForResult的时候传递的requestCode值为-1的话,那么onActivityResult是不起作用的。
实际上,经测试requestCode的值小于0的时候都是不起作用的,所以当我们调用startActivityForResult的时候需要注意这一点。

好吧,我们继续往下看,startActivityForResult方法的具体实现:

public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
        if (mParent == null) {
            Instrumentation.ActivityResult ar =
                mInstrumentation.execStartActivity(
                    this, mMainThread.getApplicationThread(), mToken, this,
                    intent, requestCode, options);
            if (ar != null) {
                mMainThread.sendActivityResult(
                    mToken, mEmbeddedID, requestCode, ar.getResultCode(),
                    ar.getResultData());
            }
            if (requestCode >= 0) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }

            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

 

可以发现由于我们是*次启动Activity,所以这里的mParent为空,所以会执行if分之,然后调用mInstrumentation.execStartActivity方法,并且这里需要注意的是,有一个判断逻辑:

if (requestCode >= 0) {
    mStartedActivity = true;
}

 

通过注释也验证了我们刚刚的说法即,调用startActivityForResult的时候只有requestCode的值大于等于0,onActivityResult才会被回调。

然后我们看一下mInstrumentation.execStartActivity方法的实现。在查看execStartActivity方法之前,我们需要对mInstrumentation对象有一个了解?什么是Instrumentation?Instrumentation是android系统中启动Activity的一个实际操作类,也就是说Activity在应用进程端的启动实际上就是Instrumentation执行的,那么为什么说是在应用进程端的启动呢?实际上acitivty的启动分为应用进程端的启动和SystemServer服务进程端的启动的,多个应用进程相互配合*终完成了Activity在系统中的启动的,而在应用进程端的启动实际的操作类就是Intrumentation来执行的,可能还是有点绕口,没关系,随着我们慢慢的解析大家就会对Instrumentation的认识逐渐加深的。

可以发现execStartActivity方法传递的几个参数:
this,为启动Activity的对象;
contextThread,为Binder对象,是主进程的context对象;
token,也是一个Binder对象,指向了服务端一个ActivityRecord对象;
target,为启动的Activity;
intent,启动的Intent对象;
requestCode,请求码;
options,参数;

这样就调用了Imstrument.execStartActivity方法了:

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        ...
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess();
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

 

我们发现在这个方法中主要调用ActivityManagerNative.getDefault().startActivity方法,那么ActivityManagerNative又是个什么鬼呢?查看一下getDefault()对象的实现:

static public IActivityManager getDefault() {
        return gDefault.get();
    }

 

好吧,相当之简单直接返回的是gDefault.get(),那么gDefault又是什么呢?

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };

 

可以发现启动过asInterface()方法创建,然后我们继续看一下asInterface方法的实现:

static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ActivityManagerProxy(obj);
    }

 

好吧,*后直接返回一个ActivityManagerProxy对象,而ActivityManagerProxy继承与IActivityManager,到了这里就引出了我们android系统中很重要的一个概念:Binder机制。我们知道应用进程与SystemServer进程属于两个不同的进程,进程之间需要通讯,android系统采取了自身设计的Binder机制,这里的ActivityManagerProxy和ActivityManagerNative都是继承与IActivityManager的而SystemServer进程中的ActivityManagerService对象则继承与ActivityManagerNative。简单的表示:
Binder接口 –> ActivityManagerNative/ActivityManagerProxy –> ActivityManagerService;

这样,ActivityManagerNative与ActivityManagerProxy相当于一个Binder的客户端而ActivityManagerService相当于Binder的服务端,这样当ActivityManagerNative调用接口方法的时候底层通过Binder driver就会将请求数据与请求传递给server端,并在server端执行具体的接口逻辑。需要注意的是Binder机制是单向的,是异步的,也就是说只能通过client端向server端传递数据与请求而不同等待服务端的返回,也无法返回,那如果SystemServer进程想向应用进程传递数据怎么办?这时候就需要重新定义一个Binder请求以SystemServer为client端,以应用进程为server端,这样就是实现了两个进程之间的双向通讯。

好了,说了这么多我们知道这里的ActivityManagerNative是ActivityManagerService在应用进程的一个client就好了,通过它就可以滴啊用ActivityManagerService的方法了。

继续往下卡,我们调用的是:

int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);

 

这里通过我们刚刚的分析,ActivityManagerNative.getDefault()方法会返回一个ActivityManagerProxy对象,那么我们看一下ActivityManagerProxy对象的startActivity方法:

public int startActivity(IApplicationThread caller, String callingPackage, Intent intent,
            String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(callingPackage);
        intent.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(resultTo);
        data.writeString(resultWho);
        data.writeInt(requestCode);
        data.writeInt(startFlags);
        if (profilerInfo != null) {
            data.writeInt(1);
            profilerInfo.writeToParcel(data, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
        } else {
            data.writeInt(0);
        }
        if (options != null) {
            data.writeInt(1);
            options.writeToParcel(data, 0);
        } else {
            data.writeInt(0);
        }
        mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
        reply.readException();
        int result = reply.readInt();
        reply.recycle();
        data.recycle();
        return result;
    }

 

这里就涉及到了具体的Binder数据传输机制了,我们不做过多的分析,知道通过数据传输之后就会调用SystemServer进程的ActivityManagerService的startActivity就好了。

以上其实都是发生在应用进程中,下面开始调用的ActivityManagerService的执行时发生在SystemServer进程。

二:ActivityManagerService接收启动Activity的请求

ActivityManagerService.startActivity()
ActvityiManagerService.startActivityAsUser()
ActivityStackSupervisor.startActivityMayWait()
ActivityStackSupervisor.startActivityLocked()
ActivityStackSupervisor.startActivityUncheckedLocked()
ActivityStackSupervisor.startActivityLocked()
ActivityStackSupervisor.resumeTopActivitiesLocked()
ActivityStackSupervisor.resumeTopActivityInnerLocked()

好吧,代码量比较大,慢慢看,首先看一下ActivityManagerService.startActivity的具体实现;

@Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
            resultWho, requestCode, startFlags, profilerInfo, options,
            UserHandle.getCallingUserId());
    }

 

可以看到,该方法并没有实现什么逻辑,直接调用了startActivityAsUser方法,我们继续看一下startActivityAsUser方法的实现:

@Override
    public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
        enforceNotIsolatedCaller("startActivity");
        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
                false, ALLOW_FULL_ONLY, "startActivity", null);
        // TODO: Switch to user app stacks here.
        return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
                resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
                profilerInfo, null, null, options, false, userId, null, null);
    }

 

可以看到这里只是进行了一些关于userid的逻辑判断,然后就调用mStackSupervisor.startActivityMayWait方法,下面我们来看一下这个方法的具体实现:

final int startActivityMayWait(IApplicationThread caller, int callingUid,
            String callingPackage, Intent intent, String resolvedType,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode, int startFlags,
            ProfilerInfo profilerInfo, WaitResult outResult, Configuration config,
            Bundle options, boolean ignoreTargetSecurity, int userId,
            IActivityContainer iContainer, TaskRecord inTask) {
            ...

            int res = startActivityLocked(caller, intent, resolvedType, aInfo,
                    voiceSession, voiceInteractor, resultTo, resultWho,
                    requestCode, callingPid, callingUid, callingPackage,
                    realCallingPid, realCallingUid, startFlags, options, ignoreTargetSecurity,
                    componentSpecified, null, container, inTask);
            ...
            return res;
    }

 

这个方法中执行了启动Activity的一些其他逻辑判断,在经过判断逻辑之后调用startActivityLocked方法:

final int startActivityLocked(IApplicationThread caller,
            Intent intent, String resolvedType, ActivityInfo aInfo,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor,
            IBinder resultTo, String resultWho, int requestCode,
            int callingPid, int callingUid, String callingPackage,
            int realCallingPid, int realCallingUid, int startFlags, Bundle options,
            boolean ignoreTargetSecurity, boolean componentSpecified, ActivityRecord[] outActivity,
            ActivityContainer container, TaskRecord inTask) {
        int err = ActivityManager.START_SUCCESS;

        ...
        err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,
                startFlags, true, options, inTask);

        ...
        return err;
    }

 

这个方法中主要构造了ActivityManagerService端的Activity对象–>ActivityRecord,并根据Activity的启动模式执行了相关逻辑。然后调用了startActivityUncheckedLocked方法:

final int startActivityUncheckedLocked(final ActivityRecord r, ActivityRecord sourceRecord,
            IVoiceInteractionSession voiceSession, IVoiceInteractor voiceInteractor, int startFlags,
            boolean doResume, Bundle options, TaskRecord inTask) {
        ...
        ActivityStack.logStartActivity(EventLogTags.AM_CREATE_ACTIVITY, r, r.task);
        targetStack.mLastPausedActivity = null;
        targetStack.startActivityLocked(r, newTask, doResume, keepCurTransition, options);
        if (!launchTaskBehind) {
            // Don't set focus on an activity that's going to the back.
            mService.setFocusedActivityLocked(r, "startedActivity");
        }
        return ActivityManager.START_SUCCESS;
    }

 

startActivityUncheckedLocked方法中只要执行了不同启动模式不同栈的处理,并*后调用了startActivityLocked的重载方法:

final void startActivityLocked(ActivityRecord r, boolean newTask,
            boolean doResume, boolean keepCurTransition, Bundle options) {
        ...
        if (doResume) {
            mStackSupervisor.resumeTopActivitiesLocked(this, r, options);
        }
    }

 

这个startActivityLocked方法主要执行初始化了windowManager服务,然后调用resumeTopActivitiesLocked方法:

boolean resumeTopActivitiesLocked(ActivityStack targetStack, ActivityRecord target,
            Bundle targetOptions) {
        if (targetStack == null) {
            targetStack = mFocusedStack;
        }
        // Do targetStack first.
        boolean result = false;
        if (isFrontStack(targetStack)) {
            result = targetStack.resumeTopActivityLocked(target, targetOptions);
        }

        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            final ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = stacks.get(stackNdx);
                if (stack == targetStack) {
                    // Already started above.
                    continue;
                }
                if (isFrontStack(stack)) {
                    stack.resumeTopActivityLocked(null);
                }
            }
        }
        return result;
    }

 

可以发现经过循环逻辑判断之后,*终调用了resumeTopActivityLocked方法:

final boolean resumeTopActivityLocked(ActivityRecord prev) {
        return resumeTopActivityLocked(prev, null);
    }

 

然后调用:

final boolean resumeTopActivityLocked(ActivityRecord prev, Bundle options) {
        if (mStackSupervisor.inResumeTopActivity) {
            // Don't even start recursing.
            return false;
        }

        boolean result = false;
        try {
            // Protect against recursion.
            mStackSupervisor.inResumeTopActivity = true;
            if (mService.mLockScreenShown == ActivityManagerService.LOCK_SCREEN_LEAVING) {
                mService.mLockScreenShown = ActivityManagerService.LOCK_SCREEN_HIDDEN;
                mService.updateSleepIfNeededLocked();
            }
            result = resumeTopActivityInnerLocked(prev, options);
        } finally {
            mStackSupervisor.inResumeTopActivity = false;
        }
        return result;
    }

 

继续调用resumeTopActivityInnerLocked方法:

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, Bundle options) {

        ...
        if (mResumedActivity != null) {
            if (DEBUG_STATES) Slog.d(TAG_STATES,
                    "resumeTopActivityLocked: Pausing " + mResumedActivity);
            pausing |= startPausingLocked(userLeaving, false, true, dontWaitForPause);
        }        ...
        return true;
    }

 

经过一系列处理逻辑之后*终调用了startPausingLocked方法,这个方法作用就是让系统中栈中的Activity执行onPause方法。

三:执行栈顶Activity的onPause方法

ActivityStack.startPausingLocked()
IApplicationThread.schudulePauseActivity()
ActivityThread.sendMessage()
ActivityThread.H.sendMessage();
ActivityThread.H.handleMessage()
ActivityThread.handlePauseActivity()
ActivityThread.performPauseActivity()
Activity.performPause()
Activity.onPause()
ActivityManagerNative.getDefault().activityPaused(token)
ActivityManagerService.activityPaused()
ActivityStack.activityPausedLocked()
ActivityStack.completePauseLocked()
ActivityStack.resumeTopActivitiesLocked()
ActivityStack.resumeTopActivityLocked()
ActivityStack.resumeTopActivityInnerLocked()
ActivityStack.startSpecificActivityLocked

好吧,方法比较多也比较乱,首先来看startPausingLocked方法:

final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping, boolean resuming,
            boolean dontWait) {
        ...
        if (prev.app != null && prev.app.thread != null) {
            if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
            try {
                EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY,
                        prev.userId, System.identityHashCode(prev),
                        prev.shortComponentName);
                mService.updateUsageStats(prev, false);
                prev.app.thread.schedulePauseActivity(prev.appToken, prev.finishing,
                        userLeaving, prev.configChangeFlags, dontWait);
            } catch (Exception e) {
                // Ignore exception, if process died other code will cleanup.
                Slog.w(TAG, "Exception thrown during pause", e);
                mPausingActivity = null;
                mLastPausedActivity = null;
                mLastNoHistoryActivity = null;
            }
        } else {
            mPausingActivity = null;
            mLastPausedActivity = null;
            mLastNoHistoryActivity = null;
        }
        ...
    }

 

可以看到这里执行了pre.app.thread.schedulePauseActivity方法,通过分析不难发现这里的thread是一个IApplicationThread类型的对象,而在ActivityThread中也定义了一个ApplicationThread的类,其继承了IApplicationThread,并且都是Binder对象,不难看出这里的IAppcation是一个Binder的client端而ActivityThread中的ApplicationThread是一个Binder对象的server端,所以通过这里的thread.schedulePauseActivity实际上调用的就是ApplicationThread的schedulePauseActivity方法。

这里的ApplicationThread可以和ActivityManagerNative对于一下:
通过ActivityManagerNative –> ActivityManagerService实现了应用进程与SystemServer进程的通讯
通过AppicationThread <– IApplicationThread实现了SystemServer进程与应用进程的通讯

然后我们继续看一下ActivityThread中schedulePauseActivity的具体实现:

public final void schedulePauseActivity(IBinder token, boolean finished,
                boolean userLeaving, int configChanges, boolean dontReport) {
            sendMessage(
                    finished ? H.PAUSE_ACTIVITY_FINISHING : H.PAUSE_ACTIVITY,
                    token,
                    (userLeaving ? 1 : 0) | (dontReport ? 2 : 0),
                    configChanges);
        }

 

发送了PAUSE_ACTIVITY_FINISHING消息,然后看一下sendMessage的实现方法:

private void sendMessage(int what, Object obj, int arg1, int arg2) {
        sendMessage(what, obj, arg1, arg2, false);
    }

 

调用了其重载方法:

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
            + ": " + arg1 + " / " + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }

 

*终调用了mH的sendMessage方法,mH是在ActivityThread中定义的一个Handler对象,主要处理SystemServer进程的消息,我们看一下其handleMessge方法的实现:

public void handleMessage(Message msg) {
            if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
            switch (msg.what) {
                ...
                case PAUSE_ACTIVITY_FINISHING:
                    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityPause");
                    handlePauseActivity((IBinder)msg.obj, true, (msg.arg1&1) != 0, msg.arg2,
                            (msg.arg1&1) != 0);
                    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                    break;
                ...
}

 

可以发现其调用了handlePauseActivity方法:

private void handlePauseActivity(IBinder token, boolean finished,
            boolean userLeaving, int configChanges, boolean dontReport) {
        ActivityClientRecord r = mActivities.get(token);
        if (r != null) {
            //Slog.v(TAG, "userLeaving=" + userLeaving + " handling pause of " + r);
            if (userLeaving) {
                performUserLeavingActivity(r);
            }

            r.activity.mConfigChangeFlags |= configChanges;
            performPauseActivity(token, finished, r.isPreHoneycomb());

            // Make sure any pending writes are now committed.
            if (r.isPreHoneycomb()) {
                QueuedWork.waitToFinish();
            }

            // Tell the activity manager we have paused.
            if (!dontReport) {
                try {
                    ActivityManagerNative.getDefault().activityPaused(token);
                } catch (RemoteException ex) {
                }
            }
            mSomeActivitiesChanged = true;
        }
    }

 

然后在方法体内部通过调用performPauseActivity方法来实现对栈顶Activity的onPause生命周期方法的回调,可以具体看一下他的实现:

final Bundle performPauseActivity(IBinder token, boolean finished,
            boolean saveState) {
        ActivityClientRecord r = mActivities.get(token);
        return r != null ? performPauseActivity(r, finished, saveState) : null;
    }

 

然后调用其重载方法:

final Bundle performPauseActivity(ActivityClientRecord r, boolean finished,
            boolean saveState) {
        ...
        mInstrumentation.callActivityOnPause(r.activity);
        ...

        return !r.activity.mFinished && saveState ? r.state : null;
    }

 

这样回到了mInstrumentation的callActivityOnPuase方法:

public void callActivityOnPause(Activity activity) {
        activity.performPause();
    }

 

呵呵,原来*终回调到了Activity的performPause方法:

final void performPause() {
        mDoReportFullyDrawn = false;
        mFragments.dispatchPause();
        mCalled = false;
        onPause();
        mResumed = false;
        if (!mCalled && getApplicationInfo().targetSdkVersion
                >= android.os.Build.VERSION_CODES.GINGERBREAD) {
            throw new SuperNotCalledException(
                    "Activity " + mComponent.toShortString() +
                    " did not call through to super.onPause()");
        }
        mResumed = false;
    }

 

终于,太不容易了,回调到了Activity的onPause方法,哈哈,Activity生命周期中的*个生命周期方法终于被我们找到了。。。。也就是说我们在启动一个Activity的时候*先被执行的是栈顶的Activity的onPause方法。记住这点吧,面试的时候经常会问到类似的问题。

然后回到我们的handlePauseActivity方法,在该方法的*后面执行了ActivityManagerNative.getDefault().activityPaused(token);方法,这是应用进程告诉服务进程,栈顶Activity已经执行完成onPause方法了,通过前面我们的分析,我们知道这句话*终会被ActivityManagerService的activityPaused方法执行。

@Override
    public final void activityPaused(IBinder token) {
        final long origId = Binder.clearCallingIdentity();
        synchronized(this) {
            ActivityStack stack = ActivityRecord.getStackLocked(token);
            if (stack != null) {
                stack.activityPausedLocked(token, false);
            }
        }
        Binder.restoreCallingIdentity(origId);
    }

 

可以发现,该方法内部会调用ActivityStack的activityPausedLocked方法,好吧,继续看一下activityPausedLocked方法的实现:

final void activityPausedLocked(IBinder token, boolean timeout) {
            ...
                if (DEBUG_STATES) Slog.v(TAG_STATES, "Moving to PAUSED: " + r
                        + (timeout ? " (due to timeout)" : " (pause complete)"));
                completePauseLocked(true);
            ...
    }

 

然后执行了completePauseLocked方法:

private void completePauseLocked(boolean resumeNext) {
        ...

        if (resumeNext) {
            final ActivityStack topStack = mStackSupervisor.getFocusedStack();
            if (!mService.isSleepingOrShuttingDown()) {
                mStackSupervisor.resumeTopActivitiesLocked(topStack, prev, null);
            } else {
                mStackSupervisor.checkReadyForSleepLocked();
                ActivityRecord top = topStack.topRunningActivityLocked(null);
                if (top == null || (prev != null && top != prev)) {
                    // If there are no more activities available to run,
                    // do resume anyway to start something.  Also if the top
                    // activity on the stack is not the just paused activity,
                    // we need to go ahead and resume it to ensure we complete
                    // an in-flight app switch.
                    mStackSupervisor.resumeTopActivitiesLocked(topStack, null, null);
                }
            }
        }
        ...
    }

 

经过了一系列的逻辑之后,又调用了resumeTopActivitiesLocked方法,又回到了第二步中解析的方法中了,这样经过
resumeTopActivitiesLocked –>
ActivityStack.resumeTopActivityLocked() –>
resumeTopActivityInnerLocked –>
startSpecificActivityLocked
好吧,我们看一下startSpecificActivityLocked的具体实现:

void startSpecificActivityLocked(ActivityRecord r,
            boolean andResume, boolean checkConfig) {
        // Is this activity's application already running?
        ProcessRecord app = mService.getProcessRecordLocked(r.processName,
                r.info.applicationInfo.uid, true);

        r.task.stack.setLaunchTime(r);

        if (app != null && app.thread != null) {
            try {
                if ((r.info.flags&ActivityInfo.FLAG_MULTIPROCESS) == 0
                        || !"android".equals(r.info.packageName)) {
                    // Don't add this if it is a platform component that is marked
                    // to run in multiple processes, because this is actually
                    // part of the framework so doesn't make sense to track as a
                    // separate apk in the process.
                    app.addPackage(r.info.packageName, r.info.applicationInfo.versionCode,
                            mService.mProcessStats);
                }
                realStartActivityLocked(r, app, andResume, checkConfig);
                return;
            } catch (RemoteException e) {
                Slog.w(TAG, "Exception when starting activity "
                        + r.intent.getComponent().flattenToShortString(), e);
            }

            // If a dead object exception was thrown -- fall through to
            // restart the application.
        }

        mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
                "activity", r.intent.getComponent(), false, false, true);
    }

 

可以发现在这个方法中,首先会判断一下需要启动的Activity所需要的应用进程是否已经启动,若启动的话,则直接调用realStartAtivityLocked方法,否则调用startProcessLocked方法,用于启动应用进程。
这样关于启动Activity时的第三步骤就已经执行完成了,这里主要是实现了对栈顶Activity执行onPause
方法,而这个方法首先判断需要启动的Activity所属的进程是否已经启动,若已经启动则直接调用启动Activity的方法,否则将先启动Activity的应用进程,然后在启动该Activity。

四:启动Activity所属的应用进程

关于如何启动应用进程,前面的一篇文章已经做了介绍,可参考: android源码解析之(十一)–>应用进程启动流程 这里在简单的介绍一下

ActivityManagerService.startProcessLocked()
Process.start()
ActivityThread.main()
ActivityThread.attach()
ActivityManagerNative.getDefault().attachApplication()
ActivityManagerService.attachApplication()

好吧,首先看一下startProcessLocked()方法的具体实现:

private final void startProcessLocked(ProcessRecord app,
            String hostingType, String hostingNameStr) {
        startProcessLocked(app, hostingType, hostingNameStr, null /* abiOverride */,
                null /* entryPoint */, null /* entryPointArgs */);
    }

 

然后回调了其重载的startProcessLocked方法:

private final void startProcessLocked(ProcessRecord app, String hostingType,
            String hostingNameStr, String abiOverride, String entryPoint, String[] entryPointArgs) {
            ...
            boolean isActivityProcess = (entryPoint == null);
            if (entryPoint == null) entryPoint = "android.app.ActivityThread";
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "Start proc: " +
                    app.processName);
            checkTime(startTime, "startProcess: asking zygote to start proc");
            Process.ProcessStartResult startResult = Process.start(entryPoint,
                    app.processName, uid, uid, gids, debugFlags, mountExternal,
                    app.info.targetSdkVersion, app.info.seinfo, requiredAbi, instructionSet,
                    app.info.dataDir, entryPointArgs);
            checkTime(startTime, "startProcess: returned from zygote!");
            ...
    }

 

可以发现其经过一系列的初始化操作之后调用了Process.start方法,并且传入了启动的类名“android.app.ActivityThread”:

public static final ProcessStartResult start(final String processClass,
                                  final String niceName,
                                  int uid, int gid, int[] gids,
                                  int debugFlags, int mountExternal,
                                  int targetSdkVersion,
                                  String seInfo,
                                  String abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String[] zygoteArgs) {
        try {
            return startViaZygote(processClass, niceName, uid, gid, gids,
                    debugFlags, mountExternal, targetSdkVersion, seInfo,
                    abi, instructionSet, appDataDir, zygoteArgs);
        } catch (ZygoteStartFailedEx ex) {
            Log.e(LOG_TAG,
                    "Starting VM process through Zygote failed");
            throw new RuntimeException(
                    "Starting VM process through Zygote failed", ex);
        }
    }

 

然后调用了startViaZygote方法:

private static ProcessStartResult startViaZygote(final String processClass,
                                  final String niceName,
                                  final int uid, final int gid,
                                  final int[] gids,
                                  int debugFlags, int mountExternal,
                                  int targetSdkVersion,
                                  String seInfo,
                                  String abi,
                                  String instructionSet,
                                  String appDataDir,
                                  String[] extraArgs)
                                  throws ZygoteStartFailedEx {
        synchronized(Process.class) {
            ...

            return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
        }
    }

 

继续查看一下zygoteSendArgsAndGetResult方法的实现:

private static ProcessStartResult zygoteSendArgsAndGetResult(
            ZygoteState zygoteState, ArrayList<String> args)
            throws ZygoteStartFailedEx {
        try {
            /**
             * See com.android.internal.os.ZygoteInit.readArgumentList()
             * Presently the wire format to the zygote process is:
             * a) a count of arguments (argc, in essence)
             * b) a number of newline-separated argument strings equal to count
             *
             * After the zygote process reads these it will write the pid of
             * the child or -1 on failure, followed by boolean to
             * indicate whether a wrapper process was used.
             */
            final BufferedWriter writer = zygoteState.writer;
            final DataInputStream inputStream = zygoteState.inputStream;

            writer.write(Integer.toString(args.size()));
            writer.newLine();

            int sz = args.size();
            for (int i = 0; i < sz; i++) {
                String arg = args.get(i);
                if (arg.indexOf('\n') >= 0) {
                    throw new ZygoteStartFailedEx(
                            "embedded newlines not allowed");
                }
                writer.write(arg);
                writer.newLine();
            }

            writer.flush();

            // Should there be a timeout on this?
            ProcessStartResult result = new ProcessStartResult();
            result.pid = inputStream.readInt();
            if (result.pid < 0) {
                throw new ZygoteStartFailedEx("fork() failed");
            }
            result.usingWrapper = inputStream.readBoolean();
            return result;
        } catch (IOException ex) {
            zygoteState.close();
            throw new ZygoteStartFailedEx(ex);
        }
    }

 

可以发现其*终调用了Zygote并通过socket通信的方式让Zygote进程fork除了一个新的进程,并根据我们刚刚传递的”android.app.ActivityThread”字符串,反射出该对象并执行ActivityThread的main方法。这样我们所要启动的应用进程这时候其实已经启动了,但是还没有执行相应的初始化操作。

为什么我们平时都将ActivityThread称之为ui线程或者是主线程,这里可以看出,应用进程被创建之后首先执行的是ActivityThread的main方法,所以我们将ActivityThread成为主线程。

好了,这时候我们看一下ActivityThread的main方法的实现逻辑。

public static void main(String[] args) {
        ...
        Process.setArgV0("<pre-initialized>");

        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

 

在main方法中主要执行了一些初始化的逻辑,并且创建了一个UI线程消息队列,这也就是为什么我们可以在主线程中随意的创建Handler而不会报错的原因,这里提出一个问题,大家可以思考一下:子线程可以创建Handler么?可以的话应该怎么做?
然后执行了ActivityThread的attach方法,这里我们看一下attach方法执行了那些逻辑操作。

private void attach(boolean system) {
    ...
    final IActivityManager mgr = ActivityManagerNative.getDefault();
            try {
                mgr.attachApplication(mAppThread);
            } catch (RemoteException ex) {
                // Ignore
            }
    ...
}

 

刚刚我们已经分析过ActivityManagerNative是ActivityManagerService的Binder client,所以这里调用了attachApplication实际上就是通过Binder机制调用了ActivityManagerService的attachApplication,具体调用的过程,我们看一下ActivityManagerService是如何实现的:

@Override
    public final void attachApplication(IApplicationThread thread) {
        synchronized (this) {
            int callingPid = Binder.getCallingPid();
            final long origId = Binder.clearCallingIdentity();
            attachApplicationLocked(thread, callingPid);
            Binder.restoreCallingIdentity(origId);
        }
    }

 

可以发现其回调了attachApplicationLocked方法,我们看一下这个方法的实现逻辑。

private final boolean attachApplicationLocked(IApplicationThread thread,
            int pid) {

        ...
        // See if the top visible activity is waiting to run in this process...
        if (normalMode) {
            try {
                if (mStackSupervisor.attachApplicationLocked(app)) {
                    didSomething = true;
                }
            } catch (Exception e) {
                Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
                badApp = true;
            }
        }
        ...

        return true;
    }

 

该方法执行了一系列的初始化操作,这样我们整个应用进程已经启动起来了。终于可以开始activity的启动逻辑了,O(∩_∩)O哈哈~

五:执行启动Acitivity

ActivityStackSupervisor.attachApplicationLocked()
ActivityStackSupervisor.realStartActivityLocked()
IApplicationThread.scheduleLauncherActivity()
ActivityThread.sendMessage()
ActivityThread.H.sendMessage()
ActivityThread.H.handleMessage()
ActivityThread.handleLauncherActivity()
ActivityThread.performLauncherActivity()
Instrumentation.callActivityOnCreate()
Activity.onCreate()
ActivityThread.handleResumeActivity()
ActivityThread.performResumeActivity()
Activity.performResume()
Instrumentation.callActivityOnResume()
Activity.onResume()
ActivityManagerNative.getDefault().activityResumed(token)

首先看一下attachApplicationLocked方法的实现:

boolean attachApplicationLocked(ProcessRecord app) throws RemoteException {
        final String processName = app.processName;
        boolean didSomething = false;
        for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
            ArrayList<ActivityStack> stacks = mActivityDisplays.valueAt(displayNdx).mStacks;
            for (int stackNdx = stacks.size() - 1; stackNdx >= 0; --stackNdx) {
                final ActivityStack stack = stacks.get(stackNdx);
                if (!isFrontStack(stack)) {
                    continue;
                }
                ActivityRecord hr = stack.topRunningActivityLocked(null);
                if (hr != null) {
                    if (hr.app == null && app.uid == hr.info.applicationInfo.uid
                            && processName.equals(hr.processName)) {
                        try {
                            if (realStartActivityLocked(hr, app, true, true)) {
                                didSomething = true;
                            }
                        } catch (RemoteException e) {
                            Slog.w(TAG, "Exception in new application when starting activity "
                                  + hr.intent.getComponent().flattenToShortString(), e);
                            throw e;
                        }
                    }
                }
            }
        }
        if (!didSomething) {
            ensureActivitiesVisibleLocked(null, 0);
        }
        return didSomething;
    }

 

可以发现其内部调用了realStartActivityLocked方法,通过名字可以知道这个方法应该就是用来启动Activity的,看一下这个方法的实现逻辑:

final boolean realStartActivityLocked(ActivityRecord r,
            ProcessRecord app, boolean andResume, boolean checkConfig)
            throws RemoteException {

        ...
            app.forceProcessStateUpTo(mService.mTopProcessState);
            app.thread.scheduleLaunchActivity(new Intent(r.intent), r.appToken,
                    System.identityHashCode(r), r.info, new Configuration(mService.mConfiguration),
                    new Configuration(stack.mOverrideConfig), r.compat, r.launchedFromPackage,
                    task.voiceInteractor, app.repProcState, r.icicle, r.persistentState, results,
                    newIntents, !andResume, mService.isNextTransitionForward(), profilerInfo);
        ...
        return true;
    }

 

可以发现与第三步执行栈顶Activity onPause时类似,这里也是通过调用IApplicationThread的方法实现的,这里调用的是scheduleLauncherActivity方法,所以真正执行的是ActivityThread中的scheduleLauncherActivity,所以我们看一下ActivityThread中的scheduleLauncherActivity的实现:

@Override
        public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.referrer = referrer;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profilerInfo = profilerInfo;

            r.overrideConfig = overrideConfig;
            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r);
        }

 

好吧,还是那套逻辑,ActivityThread接收到SystemServer进程的消息之后会通过其内部的Handler对象分发消息,经过一系列的分发之后调用了ActivityThread的handleLaunchActivity方法:

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {

        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed);


        }
        ...
    }

 

可以发现这里调用了performLauncherActivity,看名字应该就是执行Activity的启动操作了。。。

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

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);
            }
        }
        ...

        activity.mCalled = false;
        if (r.isPersistable()) {
           mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
        } else {
           mInstrumentation.callActivityOnCreate(activity, r.state);
        }
        ...
        if (!r.activity.mFinished) {
                    activity.performStart();
                    r.stopped = false;
                }
        ...
        return activity;
    }

 

可以发现这里我们需要的Activity对象终于是创建出来了,而且他是以反射的机制创建的,现在还不太清楚为啥google要以反射的方式创建Activity,先不看这些,然后在代码中其调用Instrumentation的callActivityOnCreate方法。

public void callActivityOnCreate(Activity activity, Bundle icicle,
            PersistableBundle persistentState) {
        prePerformCreate(activity);
        activity.performCreate(icicle);
        postPerformCreate(activity);
    }

 

然后执行activity的performCreate方法。。。。好吧,都转晕了。。。

final void performCreate(Bundle icicle) {
        onCreate(icicle);
        mActivityTransitionState.readState(icicle);
        performCreateCommon();
    }

 

O(∩_∩)O哈哈~,第二个生命周期方法出来了,onCreate方法。。。。

在回到我们的performLaunchActivity方法,其在调用了mInstrumentation.callActivityOnCreate方法之后又调用了activity.performStart();方法,好吧,看一下他的实现方式:

final void performStart() {
        mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
        mFragments.noteStateNotSaved();
        mCalled = false;
        mFragments.execPendingActions();
        mInstrumentation.callActivityOnStart(this);
        if (!mCalled) {
            throw new SuperNotCalledException(
                "Activity " + mComponent.toShortString() +
                " did not call through to super.onStart()");
        }
        mFragments.dispatchStart();
        mFragments.reportLoaderStart();
        mActivityTransitionState.enterReady(this);
    }

 

好吧,还是通过Instrumentation调用callActivityOnStart方法:

public void callActivityOnStart(Activity activity) {
        activity.onStart();
    }

 

然后是直接调用activity的onStart方法,第三个生命周期方法出现了,O(∩_∩)O哈哈~

还是回到我们刚刚的handleLaunchActivity方法,在调用完performLaunchActivity方法之后,其有吊用了handleResumeActivity方法,好吧,看名字应该是回调Activity的onResume方法的。

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // TODO Push resumeArgs into the activity for consideration
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
            final Activity a = r.activity;

            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.
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                }
            }
            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;
                if (a.mVisibleFromClient) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
            } else if (!willBeVisible) {
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }

            // Get rid of anything left hanging around.
            cleanUpPendingRemoveWindows(r);

            // The window is now visible if it has been added, we are not
            // simply finishing, and we are not starting another activity.
            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
                if (r.newConfig != null) {
                    r.tmpConfig.setTo(r.newConfig);
                    if (r.overrideConfig != null) {
                        r.tmpConfig.updateFrom(r.overrideConfig);
                    }
                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
                            + r.activityInfo.name + " with newConfig " + r.tmpConfig);
                    performConfigurationChanged(r.activity, r.tmpConfig);
                    freeTextLayoutCachesIfNeeded(r.activity.mCurrentConfig.diff(r.tmpConfig));
                    r.newConfig = null;
                }
                if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
                        + isForward);
                WindowManager.LayoutParams l = r.window.getAttributes();
                if ((l.softInputMode
                        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
                        != forwardBit) {
                    l.softInputMode = (l.softInputMode
                            & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
                            | forwardBit;
                    if (r.activity.mVisibleFromClient) {
                        ViewManager wm = a.getWindowManager();
                        View decor = r.window.getDecorView();
                        wm.updateViewLayout(decor, l);
                    }
                }
                r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                if (r.activity.mVisibleFromClient) {
                    r.activity.makeVisible();
                }
            }

            if (!r.onlyLocalRequest) {
                r.nextIdle = mNewActivities;
                mNewActivities = r;
                if (localLOGV) Slog.v(
                    TAG, "Scheduling idle handler for " + r);
                Looper.myQueue().addIdleHandler(new Idler());
            }
            r.onlyLocalRequest = false;

            // Tell the activity manager we have resumed.
            if (reallyResume) {
                try {
                    ActivityManagerNative.getDefault().activityResumed(token);
                } catch (RemoteException ex) {
                }
            }

        } else {
            // If an exception was thrown when trying to resume, then
            // just end this activity.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(token, Activity.RESULT_CANCELED, null, false);
            } catch (RemoteException ex) {
            }
        }
    }

 

可以发现其resumeActivity的逻辑调用到了performResumeActivity方法,我们来看一下performResumeActivity是如何实现的。

public final ActivityClientRecord performResumeActivity(IBinder token,
            boolean clearHide) {
        ActivityClientRecord r = mActivities.get(token);
        if (localLOGV) Slog.v(TAG, "Performing resume of " + r
                + " finished=" + r.activity.mFinished);
        if (r != null && !r.activity.mFinished) {
            if (clearHide) {
                r.hideForNow = false;
                r.activity.mStartedActivity = false;
            }
            try {
                r.activity.onStateNotSaved();
                r.activity.mFragments.noteStateNotSaved();
                if (r.pendingIntents != null) {
                    deliverNewIntents(r, r.pendingIntents);
                    r.pendingIntents = null;
                }
                if (r.pendingResults != null) {
                    deliverResults(r, r.pendingResults);
                    r.pendingResults = null;
                }
                r.activity.performResume();

                EventLog.writeEvent(LOG_AM_ON_RESUME_CALLED,
                        UserHandle.myUserId(), r.activity.getComponentName().getClassName());

                r.paused = false;
                r.stopped = false;
                r.state = null;
                r.persistentState = null;
            } catch (Exception e) {
                if (!mInstrumentation.onException(r.activity, e)) {
                    throw new RuntimeException(
                        "Unable to resume activity "
                        + r.intent.getComponent().toShortString()
                        + ": " + e.toString(), e);
                }
            }
        }
        return r;
    }

 

在方法体中,*终调用了r.activity.performResume();方法,好吧,这个方法是Activity中定义的方法,我们需要在Activity中查看这个方法的具体实现:

final void performResume() {
        ...
        mInstrumentation.callActivityOnResume(this);
        ...
    }

 

好吧,又是熟悉的味道,通过Instrumentation来调用了callActivityOnResume方法。。。

public void callActivityOnResume(Activity activity) {
        activity.mResumed = true;
        activity.onResume();

        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    am.match(activity, activity, activity.getIntent());
                }
            }
        }
    }

 

O(∩_∩)O哈哈~,第四个生命周期方法出现了,onResume方法。。。

终于回调onResume方法了,这时候我们的界面应该已经展示出来了,照理来说我们的Activity应该已经启动完成了,但是还没有,哈哈,别着急。

有一个问题,Activity a 启动 Activity b 会触发那些生命周期方法?
你可能会回答?b的onCreate onStart方法,onResume方法 a的onPause方法和onStop方法,咦?对了onStop方法还没回调呢,O(∩_∩)O哈哈~,对了缺少的就是对onStop方法的回调啊。

好吧,具体的逻辑我们下一步再说

六:栈顶Activity执行onStop方法

Looper.myQueue().addIdleHandler(new Idler())
Idler.queueIdle()
ActivityManagerNative.getDefault().activityIdle()
ActivityManagerService.activityIdle()
ActivityStackSupervisor.activityIdleInternalLocked()
ActivityStack.stopActivityLocked()
IApplicationThread.scheduleStopActivity()
ActivityThread.scheduleStopActivity()
ActivityThread.sendMessage()
ActivityThread.H.sendMessage()
ActivityThread.H.handleMessage()
ActivityThread.handleStopActivity()
ActivityThread.performStopActivityInner()
ActivityThread.callCallActivityOnSaveInstanceState()
Instrumentation.callActivityOnSaveInstanceState()
Activity.performSaveInstanceState()
Activity.onSaveInstanceState()
Activity.performStop()
Instrumentation.callActivityOnStop()
Activity.onStop()

回到我们的handleResumeActivity方法,在方法体*后有这样的一代码:

Looper.myQueue().addIdleHandler(new Idler());

这段代码是异步消息机制相关的代码,我们可以看一下Idler对象的具体实现:
private class Idler implements MessageQueue.IdleHandler {
        @Override
        public final boolean queueIdle() {
            ActivityClientRecord a = mNewActivities;
            boolean stopProfiling = false;
            if (mBoundApplication != null && mProfiler.profileFd != null
                    && mProfiler.autoStopProfiler) {
                stopProfiling = true;
            }
            if (a != null) {
                mNewActivities = null;
                IActivityManager am = ActivityManagerNative.getDefault();
                ActivityClientRecord prev;
                do {
                    if (localLOGV) Slog.v(
                        TAG, "Reporting idle of " + a +
                        " finished=" +
                        (a.activity != null && a.activity.mFinished));
                    if (a.activity != null && !a.activity.mFinished) {
                        try {
                            am.activityIdle(a.token, a.createdConfig, stopProfiling);
                            a.createdConfig = null;
                        } catch (RemoteException ex) {
                            // Ignore
                        }
                    }
                    prev = a;
                    a = a.nextIdle;
                    prev.nextIdle = null;
                } while (a != null);
            }
            if (stopProfiling) {
                mProfiler.stopProfiling();
            }
            ensureJitEnabled();
            return false;
        }
    }

 

这样当Messagequeue执行add方法之后就会回调其queueIdle()方法,我们可以看到在方法体中其调用了ActivityManagerNative.getDefault().activityIdle(),好吧,熟悉了Binder机制以后我们知道这段代码会执行到ActivityManagerService的activityIdle方法:

@Override
    public final void activityIdle(IBinder token, Configuration config, boolean stopProfiling) {
        final long origId = Binder.clearCallingIdentity();
        synchronized (this) {
            ActivityStack stack = ActivityRecord.getStackLocked(token);
            if (stack != null) {
                ActivityRecord r =
                        mStackSupervisor.activityIdleInternalLocked(token, false, config);
                if (stopProfiling) {
                    if ((mProfileProc == r.app) && (mProfileFd != null)) {
                        try {
                            mProfileFd.close();
                        } catch (IOException e) {
                        }
                        clearProfilerLocked();
                    }
                }
            }
        }
        Binder.restoreCallingIdentity(origId);
    }

 

然后在activityIdle方法中又调用了ActivityStackSupervisor.activityIdleInternalLocked方法:

final ActivityRecord activityIdleInternalLocked(final IBinder token, boolean fromTimeout,
            Configuration config) {
        ...

        // Stop any activities that are scheduled to do so but have been
        // waiting for the next one to start.
        for (int i = 0; i < NS; i++) {
            r = stops.get(i);
            final ActivityStack stack = r.task.stack;
            if (stack != null) {
                if (r.finishing) {
                    stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false);
                } else {
                    stack.stopActivityLocked(r);
                }
            }
        }

        ...

        return r;
    }

 

可以发现在其中又调用了ActivityStack.stopActivityLocked方法:

final void stopActivityLocked(ActivityRecord r) {
        if (DEBUG_SWITCH) Slog.d(TAG_SWITCH, "Stopping: " + r);
        if ((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY) != 0
                || (r.info.flags&ActivityInfo.FLAG_NO_HISTORY) != 0) {
            ...
                r.app.thread.scheduleStopActivity(r.appToken, r.visible, r.configChangeFlags);
             ...
        }
    }

好吧,又是相同的逻辑通过IApplicationThread.scheduleStopActivity,*终调用了ActivityThread.scheduleStopActivity()方法。。。。
public final void scheduleStopActivity(IBinder token, boolean showWindow,
                int configChanges) {
           sendMessage(
                showWindow ? H.STOP_ACTIVITY_SHOW : H.STOP_ACTIVITY_HIDE,
                token, 0, configChanges);
        }

然后执行sendMessage方法,*终执行H(Handler)的sendMessage方法,并被H的handleMessge方法接收执行handleStopActivity方法。。。
private void handleStopActivity(IBinder token, boolean show, int configChanges) {
        ...
        performStopActivityInner(r, info, show, true);
        ...
    }

然后我们看一下performStopActivityInner的实现逻辑:
private void performStopActivityInner(ActivityClientRecord r,
            StopInfo info, boolean keepShown, boolean saveState) {
            ...
            // Next have the activity save its current state and managed dialogs...
            if (!r.activity.mFinished && saveState) {
                if (r.state == null) {
                    callCallActivityOnSaveInstanceState(r);
                }
            }

            if (!keepShown) {
                try {
                    // Now we are idle.
                    r.activity.performStop();
                } catch (Exception e) {
                    if (!mInstrumentation.onException(r.activity, e)) {
                        throw new RuntimeException(
                                "Unable to stop activity "
                                + r.intent.getComponent().toShortString()
                                + ": " + e.toString(), e);
                    }
                }
                r.stopped = true;
            }
        }
    }

好吧,看样子在这个方法中执行了两个逻辑,一个是执行Activity的onSaveInstance方法一个是执行Activity的onStop方法,我们先看一下callCallActivityOnSaveInstanceState的执行逻辑:

private void callCallActivityOnSaveInstanceState(ActivityClientRecord r) {
        r.state = new Bundle();
        r.state.setAllowFds(false);
        if (r.isPersistable()) {
            r.persistentState = new PersistableBundle();
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state,
                    r.persistentState);
        } else {
            mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
        }
    }

好吧,又是通过Instrumentation来执行。。。

public void callActivityOnSaveInstanceState(Activity activity, Bundle outState,
            PersistableBundle outPersistentState) {
        activity.performSaveInstanceState(outState, outPersistentState);
    }

又间接调用了Activity的performSaveInstanceState方法:
final void performSaveInstanceState(Bundle outState) {
        onSaveInstanceState(outState);
        saveManagedDialogs(outState);
        mActivityTransitionState.saveState(outState);
        if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
    }

呵呵,这里调用到了,我们以前经常会重写的onSaveInstanceState方法。

然后我们看一下performStopActivityInner中调用到的Activity方法的performStop方法:

final void performStop() {
        mDoReportFullyDrawn = false;
        mFragments.doLoaderStop(mChangingConfigurations /*retain*/);

        if (!mStopped) {
            if (mWindow != null) {
                mWindow.closeAllPanels();
            }

            if (mToken != null && mParent == null) {
                WindowManagerGlobal.getInstance().setStoppedState(mToken, true);
            }

            mFragments.dispatchStop();

            mCalled = false;
            mInstrumentation.callActivityOnStop(this);
            if (!mCalled) {
                throw new SuperNotCalledException(
                    "Activity " + mComponent.toShortString() +
                    " did not call through to super.onStop()");
            }

            synchronized (mManagedCursors) {
                final int N = mManagedCursors.size();
                for (int i=0; i<N; i++) {
                    ManagedCursor mc = mManagedCursors.get(i);
                    if (!mc.mReleased) {
                        mc.mCursor.deactivate();
                        mc.mReleased = true;
                    }
                }
            }

            mStopped = true;
        }
        mResumed = false;
    }

 

还是通过Instrumentation来实现的,调用了它的callActivityOnStop方法。。

public void callActivityOnStop(Activity activity) {
        activity.onStop();
    }

O(∩_∩)O哈哈~,*后一个生命周期方法终于出来了,onStop()…..

总结:

  • Activity的启动流程一般是通过调用startActivity或者是startActivityForResult来开始的
  • startActivity内部也是通过调用startActivityForResult来启动Activity,只不过传递的requestCode小于0
  • Activity的启动流程涉及到多个进程之间的通讯这里主要是ActivityThread与ActivityManagerService之间的通讯
  • ActivityThread向ActivityManagerService传递进程间消息通过ActivityManagerNative,ActivityManagerService向ActivityThread进程间传递消息通过IApplicationThread。
  • ActivityManagerService接收到应用进程创建Activity的请求之后会执行初始化操作,解析启动模式,保存请求信息等一系列操作。
  • ActivityManagerService保存完请求信息之后会将当前系统栈顶的Activity执行onPause操作,并且IApplication进程间通讯告诉应用程序继承执行当前栈顶的Activity的onPause方法;
  • ActivityThread接收到SystemServer的消息之后会统一交个自身定义的Handler对象处理分发;
  • ActivityThread执行完栈顶的Activity的onPause方法之后会通过ActivityManagerNative执行进程间通讯告诉ActivityManagerService,栈顶Actiity已经执行完成onPause方法,继续执行后续操作;
  • ActivityManagerService会继续执行启动Activity的逻辑,这时候会判断需要启动的Activity所属的应用进程是否已经启动,若没有启动则首先会启动这个Activity的应用程序进程;
  • ActivityManagerService会通过socket与Zygote继承通讯,并告知Zygote进程fork出一个新的应用程序进程,然后执行ActivityThread的mani方法;
  • 在ActivityThead.main方法中执行初始化操作,初始化主线程异步消息,然后通知ActivityManagerService执行进程初始化操作;
  • ActivityManagerService会在执行初始化操作的同时检测当前进程是否有需要创建的Activity对象,若有的话,则执行创建操作;
  • ActivityManagerService将执行创建Activity的通知告知ActivityThread,然后通过反射机制创建出Activity对象,并执行Activity的onCreate方法,onStart方法,onResume方法;
  • ActivityThread执行完成onResume方法之后告知ActivityManagerService onResume执行完成,开始执行栈顶Activity的onStop方法;
  • ActivityManagerService开始执行栈顶的onStop方法并告知ActivityThread;
  • ActivityThread执行真正的onStop方法;

android应用程序中获取view的位置

我们重点在获取view的y坐标,你懂的…

依次介绍以下四个方法:

 

1.getLocationInWindow

 

Java代码  收藏代码
  1. int[] position = new int[2];  
  2. textview.getLocationInWindow(position);
  3. System.out.println(“getLocationInWindow:” + position[0] + “,” + position[1]);  

 

这个方法是将view的左上角坐标存入数组中.此坐标是相对当前activity而言.

若是普通activity,则y坐标为可见的状态栏高度+可见的标题栏高度+view左上角到标题栏底部的距离.

可见的意思是:在隐藏了状态栏/标题栏的情况下,它们的高度以0计算.

若是对话框式的activity,则y坐标为可见的标题栏高度+view到标题栏底部的距离.

此时是无视状态栏的有无的.

 

2.getLocationOnScreen

 

Java代码  收藏代码
  1. int[] position = new int[2];  
  2. textview.getLocationOnScreen(position);
  3. System.out.println(“getLocationOnScreen:” + position[0] + “,” + position[1]);  

这个方法跟上面的差不多,也是将view的左上角坐标存入数组中.但此坐标是相对整个屏幕而言.

 

y坐标为view左上角到屏幕顶部的距离.

 

 

3.getGlobalVisibleRect

 

Java代码  收藏代码
  1. Rect viewRect = new Rect();  
  2. textview.getGlobalVisibleRect(viewRect);
  3. System.out.println(viewRect);

这个方法是构建一个Rect用来”套”这个view.此Rect的坐标是相对当前activity而言.

若是普通activity,则Rect的top为可见的状态栏高度+可见的标题栏高度+Rect左上角到标题栏底部的距离.

若是对话框式的activity,则y坐标为Rect的top为可见的标题栏高度+Rect左上角到标题栏底部的距离.

此时是无视状态栏的有无的.

 

4.getLocalVisibleRect

 

Java代码  收藏代码
  1. Rect globeRect = new Rect();  
  2. button.getLocalVisibleRect(globeRect);

这个方法获得的Rect的top和left都是0,也就是说,仅仅能通过这个Rect得到View的宽度和高度….

 

 

注意:

以上方法在OnCreate方法中调用,都会返回0,这是因为View还未加载完毕.

建议在onWindowFocusChanged方法中进行获取,有些情况下onWindowFocusChanged不好用的时候(比如ActivityGroup),可以这样写:

 

Java代码  收藏代码
  1. mTextView.post(new Runnable() {  
  2.     @Override  
  3.     public void run() {  
  4.         Rect viewRect = new Rect();  
  5.         mTextView.getGlobalVisibleRect(viewRect);
  6.         mTreeScrollView.setRect(viewRect);
  7.     }
  8. });

这样在View加载完毕之后会执行获取位置的方法.

Android允许以画中画 (PIP) 模式启动 Activity

Android 8.0(API 级别 26)允许以画中画 (PIP) 模式启动 Activity。画中画是一种特殊类型的多窗口模式,*常用于视频播放。使用该模式,用户可以通过固定到屏幕一角的小窗口观看视频,同时在应用之间进行导航或浏览主屏幕上的内容。

%title插图%num

画中画利用 Android 7.0 中的多窗口模式 API 来提供固定的视频叠加窗口。如要将画中画添加到您的应用中,您需要注册支持画中画的 Activity,根据需要将 Activity 切换为画中画模式,并确保当 Activity 处于画中画模式时,界面元素处于隐藏状态且视频能够继续播放。

画中画窗口会显示在屏幕的*上层,位于系统选择的一角。您可以将画中画窗口拖动到其他位置。当您点按该窗口时,会看到两个特殊的控件:全屏切换开关(位于窗口的中心)和关闭按钮(右上角的“X”)。

您的应用会控制当前 Activity 在何时进入画中画模式。下面是一些示例:

  • 一个 Activity 可以在用户点按主屏幕或*近使用的应用按钮来选择其他应用时,进入画中画模式。(Google 地图就是通过这种方式,在用户同时运行其他 Activity 时继续显示路线。)
  • 您的应用可以在用户从某个视频返回以浏览其他内容时,将该视频切换到画中画模式。
  • 您的应用可以在用户观看到某集内容结束时将视频切换到画中画模式。主屏幕会显示有关这部电视剧下一集的宣传信息或剧情摘要信息。
  • 您的应用可以提供一种方式,让用户可以在观看视频时将其他内容加入播放队列。当主屏幕显示内容选择 Activity 时,视频会继续以画中画模式播放。

默认情况下,系统不会自动为应用提供画中画支持。如果您想在应用中支持画中画,可以通过将 android:supportsPictureInPicture 设置为 true,在清单中注册视频 Activity。此外,指定您的 Activity 处理布局配置更改,这样一来,在画中画模式转换期间发生布局更改时,您的 Activity 就不会重新启动。

<activity android:name="VideoActivity"
    android:supportsPictureInPicture="true"
    android:configChanges=
        "screenSize|smallestScreenSize|screenLayout|orientation"
    ...

如要进入画中画模式,Activity 必须调用 enterPictureInPictureMode()。例如,以下代码会在用户点击应用界面中的专用按钮时,将 Activity 切换到画中画模式:

KotlinJava
@Override
public void onActionClicked(Action action) {
    if (action.getId() == R.id.lb_control_picture_in_picture) {
        getActivity().enterPictureInPictureMode();
        return;
    }
    ...
}

您可能需要添加将 Activity 切换到画中画模式(而不是进入后台)的逻辑。例如,如果用户在 Google 地图正在导航时按下主屏幕或*近使用的应用按钮,则该应用会切换到画中画模式。您可以通过替换 onUserLeaveHint() 来实现这一目的:

KotlinJava
@Override
public void onUserLeaveHint () {
    if (iWantToBeInPipModeNow()) {
        enterPictureInPictureMode();
    }
}

当 Activity 进入或退出画中画模式时,系统会调用 Activity.onPictureInPictureModeChanged() 或 Fragment.onPictureInPictureModeChanged()

您应替换这些回调以重新绘制 Activity 的界面元素。请注意,在画中画模式下,您的 Activity 会在一个小窗口中显示。在画中画模式下,用户无法与界面元素互动,并且可能很难看清小界面元素的详细信息。界面*简的视频播放 Activity 可提供*佳的用户体验。Activity 应仅显示视频播放控件。在 Activity 进入画中画模式之前移除其他界面元素,并在 Activity 再次变为全屏时恢复这些元素:

KotlinJava
@Override
public void onPictureInPictureModeChanged (boolean isInPictureInPictureMode, Configuration newConfig) {
    if (isInPictureInPictureMode) {
        // Hide the full-screen UI (controls, etc.) while in picture-in-picture mode.
    } else {
        // Restore the full-screen UI.
        ...
    }
}

画中画窗口可以在用户打开窗口菜单(通过点按移动设备上的窗口或使用电视遥控器选择菜单)时显示控件。

如果应用有处于活跃状态的媒体会话,则窗口会显示播放、暂停、下一个和上一个控件。

您还可以通过在进入画中画模式之前构建 PictureInPictureParams(使用 PictureInPictureParams.Builder.setActions())来明确指定自定义操作,并使用 enterPictureInPictureMode(android.app.PictureInPictureParams) 或 setPictureInPictureParams(android.app.PictureInPictureParams) 在进入画中画模式时传递这些参数。请注意,如果您尝试添加的控件数量超过 getMaxNumPictureInPictureActions(),则系统只会添加上限数量的控件。

当您的 Activity 切换到画中画模式时,系统会将该 Activity 置于暂停状态并调用 Activity 的 onPause() 方法。当 Activity 在画中画模式下暂停时,视频播放不得暂停,而应继续播放。

在 Android 7.0 及更高版本中,当系统调用 Activity 的 onStop() 时,您应暂停视频播放;当系统调用 Activity 的 onStart() 时,您应恢复视频播放。这样一来,您就无需在 onPause() 中检查应用是否处于画中画模式,只需继续播放视频即可。

如果您必须在 onPause() 实现中暂停播放,请通过调用 isInPictureInPictureMode() 检查是否处于画中画模式并相应地处理播放情况,例如:

KotlinJava
@Override
public void onPause() {
    // If called while in PIP mode, do not pause playback
    if (isInPictureInPictureMode()) {
        // Continue playback
        ...
    } else {
        // Use existing playback logic for paused Activity behavior.
        ...
    }
}

当您的 Activity 从画中画模式切换回全屏模式时,系统会恢复您的 Activity 并调用 onResume() 方法。

在您的应用中,可能会出现以下情况:有一个视频播放 Activity 正处于画中画模式,用户在主屏幕上浏览内容时选择了新的视频。应以全屏模式在现有的播放 Activity 中播放新的视频,而不是启动可能会令用户感到困惑的新 Activity。

如要确保将单个 Activity 用于视频播放请求并根据需要进入或退出画中画模式,请在清单中将 Activity 的 android:launchMode 设置为 singleTask

<activity android:name="VideoActivity"
    ...
    android:supportsPictureInPicture="true"
    android:launchMode="singleTask"
    ...

在您的 Activity 中,替换 onNewIntent() 并处理新的视频,从而根据需要停止任何现有的视频播放。

低内存设备可能无法使用画中画模式。在应用使用画中画之前,请务必通过调用 hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) 进行检查以确保可以使用画中画。

画中画旨在用于播放全屏视频的 Activity。将 Activity 切换到画中画模式时,请避免显示视频内容以外的任何内容。跟踪您的 Activity 何时进入画中画模式并隐藏界面元素,如处理画中画模式下的界面元素中所述。

由于画中画窗口在屏幕的一角显示为浮动窗口,因此您应避免在主屏幕中可能被画中画窗口遮盖的任何区域内显示重要信息。

当 Activity 进入画中画模式后,默认不会获得输入焦点。要在画中画模式下接收输入事件,请使用 MediaSession.setCallback()。如需详细了解如何使用 setCallback(),请参阅显示“正在播放”卡片。

当您的应用处于画中画模式时,画中画窗口中的视频播放可能会对其他应用(例如,音乐播放器应用或语音搜索应用)造成音频干扰。为避免出现此问题,请在开始播放视频时请求音频焦点,并处理音频焦点更改通知,如管理音频焦点中所述。如果您在处于画中画模式时收到音频焦点丢失通知,请暂停或停止视频播放。

当您的应用即将进入画中画模式时,请注意,只有顶层 Activity 才会进入画中画模式。在某些情况下(例如在多窗口设备上),此时系统可能会显示下层 Activity,在画中画 Activity 旁边,您可能会再次看到下层 Activity。您应根据情况相应地处理这一问题,包括下层 Activity 获取 onResume()或 onPause() 回调。用户也有可能与该 Activity 进行交互。例如,如果您的视频列表 Activity 正在显示,视频播放 Activity 处于画中画模式,用户可能会从列表中选择新视频,画中画 Activity 应相应地进行更新。

友情链接: SITEMAP | 旋风加速器官网 | 旋风软件中心 | textarea | 黑洞加速器 | jiaohess | 老王加速器 | 烧饼哥加速器 | 小蓝鸟 | tiktok加速器 | 旋风加速度器 | 旋风加速 | quickq加速器 | 飞驰加速器 | 飞鸟加速器 | 狗急加速器 | hammer加速器 | trafficace | 原子加速器 | 葫芦加速器 | 麦旋风 | 油管加速器 | anycastly | INS加速器 | INS加速器免费版 | 免费vqn加速外网 | 旋风加速器 | 快橙加速器 | 啊哈加速器 | 迷雾通 | 优途加速器 | 海外播 | 坚果加速器 | 海外vqn加速 | 蘑菇加速器 | 毛豆加速器 | 接码平台 | 接码S | 西柚加速器 | 快柠檬加速器 | 黑洞加速 | falemon | 快橙加速器 | anycast加速器 | ibaidu | moneytreeblog | 坚果加速器 | 派币加速器 | 飞鸟加速器 | 毛豆APP | PIKPAK | 安卓vqn免费 | 一元机场加速器 | 一元机场 | 老王加速器 | 黑洞加速器 | 白石山 | 小牛加速器 | 黑洞加速 | 迷雾通官网 | 迷雾通 | 迷雾通加速器 | 十大免费加速神器 | 猎豹加速器 | 蚂蚁加速器 | 坚果加速器 | 黑洞加速 | 银河加速器 | 猎豹加速器 | 海鸥加速器 | 芒果加速器 | 小牛加速器 | 极光加速器 | 黑洞加速 | movabletype中文网 | 猎豹加速器官网 | 烧饼哥加速器官网 | 旋风加速器度器 | 哔咔漫画 | PicACG | 雷霆加速