Activity

文章参考 Android Activity 完全解析Android【安卓】Activity详解。仅方便个人记忆。

一、正常情况下一个 Acitivity 会经历如下的生命周期:

    1. 生命周期介绍
      1)onCreate
      表示Activity正在被创建,做一些初始化动作,只在创建时调用一次。
      2)onStart
      表示Activity正在被启动,这时Activity已经可见,但不在前台,用户看不到也无法交互。
      3)onResume
      这里 Activity 已经可见了,可以和用户进行交互了,这时 Activity 处于栈顶,正在运行,这里要注意和 onStart 进行区分,可以理解为 onStart 和onResume 都表示 Activity 可见,但 onStart 的时候 Activity 还在后台,用户不可见,onResume 的时候在前台,用户可见
      4)onPause
      这个方法表示 Activity 正在停止,但还没有真正的停止,我们通常在这个方法中将一些消耗资源东西释放,可以存储一些关键的数据,但一定不能做耗时的操作,要不会意向下一个活动的使用
      5)onStop
      表示 Activity 即将停止,在 Activity 完全不可见得时候调用,可以做一些回收工作,也不能太耗时,这里要注意和 onPause 方法的区分,它和 onPause 方法的主要区别在于,如果新activity的方法是一个对话框式的活动,onPause 方法执行,onStop 方法不执行
      6)onDestroy
      表示 Activity 即将被销毁,对应于 onCreate 方法,这是 Acitivty 生命周期方法的最后一个回调方法,活动的状态将变为销毁状态
      7)onRestart
      这个方法在活动由停止状态变为运行状态时调用,这时候活动就是被重新启动了
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
 
    private static final String TAG = "MainActivity";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i(TAG, "MainActivity: onCreate");
    }
    @Override
    protected void onStart() {
        super.onStart();
        Log.i(TAG, "MainActivity: onStart");
    } 
    @Override
    protected void onResume() {
        super.onResume();
        Log.i(TAG, "MainActivity: onResume");
    }
    @Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "MainActivity: onPause");
    }
    @Override
    protected void onStop() {
        super.onStop();
        Log.i(TAG, "MainActivity: onStop");
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "MainActivity: onDestroy");
    }
    @Override
    protected void onRestart() {
        super.onRestart();
        Log.i(TAG, "MainActivity: onRestart");
    }
    @Override
    public void onClick(View v) {
        Intent intent = new Intent(MainActivity.this,SecondActivity.class);
        startActivity(intent);
    }
}

这里我们创建了一个 MainActivity 并且重写了 Activity 生命周期的方法,这时候我们运行程序,在 Log 的控制台可以看到如下打印
MainActivity: MainActivity: onCreate
MainActivity: MainActivity: onStart
MainActivity: MainActivity: onResume
然后我们点击后退按钮,我们再来看一下控制台的打印内容
MainActivity: MainActivity: onPause
MainActivity: MainActivity: onStop
MainActivity: MainActivity: onDestroy
这时候一个 Activity 完整的生命周期就执行完毕了
接下来我们不点击后退键,我们点击 Home 键来看一下生命周期的情况
MainActivity: MainActivity: onCreate
MainActivity: MainActivity: onStart
MainActivity: MainActivity: onResume
MainActivity: MainActivity: onPause
MainActivity: MainActivity: onStop
这里要注意 Activity 并没有去执行 onDestroy,这里 Activity 只是变成了不可见状态,并没有被销毁,我们再点击打开应用
MainActivity: MainActivity: onRestart
MainActivity: MainActivity: onStart
MainActivity: MainActivity: onResume
这里注意到应用并没有调用 onCreate 重新创建,而是只调用了 onRestart,onStart,onResume 方法

这里我们就把在正常情况下的一个 Activity 的生命周期情况介绍完毕,总结如下:


  • Activity的状态
    每个activity在其生命周期中最多可能会有 4 种状态
    1)运行状态
    当一个活动位于返回栈的栈顶时,这时活动就处于运行状态,系统最不愿意回收的就是出于运行状态的活动,因为这样会带来非常差的用户体验
    2)暂停状态
    当一个活动不再处于栈顶位置,但仍然可见时,这时活动就进入了暂停状态,你可能会觉得,既然活动已经不在栈顶了,还怎么会可见呢?这是因为并不是每一个活动都会占满整个屏膜的,比如对话框形式的活动只会占用屏膜一部分的区域,处于暂停状态的活动任然是存活的,系统也不愿意去回收这种活动(因为它还是可见的),只有内存极低的情况下,系统才会去考虑回收这种活动
    3)停止状态
    当一个活动不再处于栈顶位置,并且完全不可见的时候,就进入停止状态,系统任然会为这种活动保存相应的状态和成员变量,但是当其他地方需要内存时,处于停止状态的活动有可能会被系统回收
    4)销毁状态
    当一个活动从返回栈中移除后就变成了销毁状态,系统会最倾向于回收处于这种状态的活动,从而保持手机的内存充足

二、多个 Activity 交互的生命周期:

这里我们新建一个 SecondActivity 类,我们在 MainActivity 中设置一个点击按钮,用来跳到 SecondActivity,并且和 MainActivity 一样复写生命周期的方法如下:

/**
 * 生命周期测试SecondActivity
 */
 
public class SecondActivity extends AppCompatActivity {
 
    private static final String TAG = "SecondActivity";
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        Log.i(TAG, "SecondActivity: onCreate");
    } 
    @Override
    protected void onStart() {
        super.onStart();
        Log.i(TAG, "SecondActivity: onStart");
    } 
    @Override
    protected void onResume() {
        super.onResume();
        Log.i(TAG, "SecondActivity: onResume");
    } 
    @Override
    protected void onPause() {
        super.onPause();
        Log.i(TAG, "SecondActivity: onPause");
    }
    @Override
    protected void onStop() {
        super.onStop();
        Log.i(TAG, "SecondActivity: onStop");
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "SecondActivity: onDestroy");
    }
    @Override
    protected void onRestart() {
        super.onRestart();
        Log.i(TAG, "SecondActivity: onRestart");
    }
}

现在我们来看分析生命周期的运行情况:首先打开 MainActivity,控制台打印生命周期运行如下:

MainActivity: MainActivity: onCreate
MainActivity: MainActivity: onStart
MainActivity: MainActivity: onResume

这时候我们再点击跳转按钮,控制台打印生命周期如下:

MainActivity: MainActivity: onPause
SecondActivity: SecondActivity: onCreate
SecondActivity: SecondActivity: onStart
SecondActivity: SecondActivity: onResume
MainActivity: MainActivity: onStop

这里我们思考如下内容
1)为什么要先调用 onPause 方法暂停当前显示的 MainActivity
2)为什么要等 SecondActivity 的 onCreate,onStart,onResume 方法执行完才执行 MainActivity的 onStop 方法,为什么不先执行当前 MainActivity的 onPause,onStop 方法呢?

在 MainActivity 打开 SecondActivity 的时候,会先执行 MainActivity 的 onPause 方法,然后执行 SecondActivity 的 onCreate、onStart、onResume 方法,完了再执行 MainActivity 的 onStop 方法,这里面还是有一种思想的,首先执行 MainActivity 的 onPause 方法是要关闭当前 MainActivity 的一些状态信息,如音频,视频等的状态,以免对新打开的 Activity 造成影响,试想我们正在听歌或看电影,这时候打进来一电话,如果我们不把当前的 Activity 先onPause 掉,会使一种怎样的体验,一边听歌,一遍打电话,不敢想象这样的场景,那么为什么又是在 MainActivity 执行完 onPause 而没有 onStop 的情况下就先执行 SecondActivity 的 onCreate,onStart,onResume 方法然后再执行 onStop 方法呢,这是因为考虑到如果在打开 SecondActivity 开启时发生 crash 的情况,这样如果 MainActivity 执行了 onStop 方法则会变为不可见状态,就会呈现出黑屏状态,用户什么都看不到。

三、横竖屏切换的时候 Acitivity 的生命周期:

  • 不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,切横屏时会执行一次,切竖屏时会执行两次
  • 设置Activity的android:configChanges="orientation"时,切屏还是会重新调用各个生命周期,切横、竖屏时只会执行一次
  • 设置Activity的android:configChanges="orientation|keyboardHidden|screenSize"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法

如果横竖屏切换的时候 Acitivity 的生命周期销毁掉然后会重新创建,所以我们展示的数据要在onSaveInstanceState 方法中去保存数据,在 MainActivity 中添加如下代码就可以降临时数据进行保存:

 @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        String data = "hello";
        outState.putString("key",data);
    }

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState != null) {
            String data = savedInstanceState.getString("key");
        }
        Log.i(TAG, "MainActivity: onCreate");
    }

四、启动方式

Activity的启动分为显式和隐式启动,原则上二者不该同时存在,同时存在以显式为主。显式需要指定被启动对象的组件信息(包名、类名),隐式不需要指定目标组件信息,只要Intent可以匹配上目标Activity的IntentFilter设置的过滤信息。

  • 1、使用显示Intent
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);

private final int ACTIVITY_BACK = 1001;
Intent intent = new Intent(MainActivity.this,SecondActivity.class);
startActivityForResult(intent,ACTIVITY_BACK);
后面传递数据详解
  • 2、使用隐式Intent
    IntentFilter的过滤信息有action、category、data三种。一个Activity里可以有多组IntentFilter,每组IntentFilter里可以包含多个action、category、data,每组IntentFilter的任意三个及以上action、category、data形成一个匹配约束,只要Java代码里能够匹配一个匹配约束,即可启动目标Activity
 <activity android:name=".SecondActivity">
           <intent-filter>
                <action android:name="android.intent.action.VIEW"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <category android:name="com.weihy"/>
                <category android:name="android.intent.category.BROWSABLE"/>
                <data android:scheme="http"/>
           </intent-filter>
           <intent-filter>
                <action android:name="android.intent.action.SEND"/>
                <category android:name="android.intent.category.DEFAULT"/>
                <data android:mimeType="image/*"/>
           </intent-filter>
 </activity>

Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);

1)action匹配规则
action是一个字符串,系统预定义了一些action,我们也可以在应用定义自己的action。Java代码里的Intent必须要有action,且这个字符串必须和xml中的action一致,区分大小写。
2)category匹配规则
category是一个字符串,系统预定义了一些category,我们也可以在应用定义自己的category。Java代码里category可以缺省,缺省时系统匹配xml中的android.intent.category.DEFAULT字符串,不缺省,匹配规则与action一致。
3)data匹配规则
如果xml里的IntentFilter定义了data,那么Java代码里的Intent必须要有data。
data由两部分组成mimeType和URI,
mimeType是指媒体类型,比如image/jpeg、audio/mpeg4-generic和video/*等,可以表示图片、文本、视频等不同的媒体格式,
URI表示统一资源标识符,可以定位本地和网络的资源

//如下过滤规则
<intent-filter>
     <action android:name="android.intent.action.SEND"/>
     <category android:name="android.intent.category.DEFAULT"/>
     <data android:mimeType="image/*"/>
</intent-filter>

这种规则指定了媒体类型为所有类型的图片,Intent中mimeType必须为“image/*”才能匹配,xml中的过滤规则虽然没有指定URI但是scheme有默认值为content或file,所以intent中URI的scheme必须为content或file

//如下过滤规则
<intent-filter>
    <action android:name="android.intent.action.SEND"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <data android:mimeType="image/*" android:scheme="file" android:host="asd"/>
</intent-filter>

intent.setDataAndType(Uri.parse("file://asd"),"image/*");

像这种指定了完整的data的xml,Java代码不可以先用setData方法,再用setType方法指定URI和mimeType,因为这两个方法会将对方的值互相清空,应该用setDataAndType方法。

使用隐式Intent不仅可以自己程序内的活动,还可以启动其他程序的活动,这使得Android多个应用程序之间的功能共享称为可能。

启动浏览器:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri url = Uri.parse("https://www.baidu.com");
intent.setData(url);
startActivity(intent);

启动相册:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivity(intent);

启动电话界面:
Intent intent = new Intent();
intent.setAction(Intent.ACTION_VIEW);
Uri url = Uri.parse("tel:123456");
intent.setData(url);
startActivity(intent);

五、数据交互

说到了Intent那就不能不谈Activity之间的数据交互
1)简单的数据传递

//传递
Intent intent = new Intent(MainActivity.this,ThirdActivity.class);
intent.putExtra("name","name");
intent.putExtra("age","age");
startActivity(intent);
//获取
Intent intent = getIntent();
String name =intent.getStringExtra("name");
String age = intent.getStringExtra("age");

2)传递 Bundle 对象

Intent intent = new Intent(MainActivity.this,ThirdActivity.class);
//这里传递Bundle对象
Bundle bundle = new Bundle();
bundle.putString("name","姓名");
bundle.putString("age","0");
intent.putExtras(bundle);
startActivity(intent);

//获取方式同上
Intent intent = getIntent();
String name =intent.getStringExtra("name");
String age = intent.getStringExtra("age");

3)传递 JavaBean
当我们传递的数据比较大的时候,这时候我们可以直接传递一个 JavaBean,这里注意要实现 Serializable 接口

//Bean
public class Person implements Serializable {
 
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
//传递
Person person = new Person("姓名", 0);
Intent intent = new Intent(MainActivity.this, ThirdActivity.class);
intent.putExtras("person", person);
startActivity(intent);
//获取
Intent intent = getIntent();
Person person = (Person) intent.getSerializableExtra("person");

4)返回数据给上个Activity

private final int ACTIVITY_BACK = 1001;
Intent intent = new Intent(MainActivity.this,ThirdActivity.class);
startActivityForResult(intent,ACTIVITY_BACK);

 //接收来自ThirdActivity的数据
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        switch (requestCode){
            case ACTIVITY_BACK:                //一个activity可能启动不同activity,用这个来区分
                if(resultCode == RESULT_OK){   //这个字段区分栈顶activity是单纯的出栈,还是要向下传递消息
                    redBack = data.getIntExtra(RED_KEK,0);
                    greenBack = data.getIntExtra(GREEN_KEY,0);
                    blueBack = data.getIntExtra(BLUE_KEY,0);
                }
                break;
            default:
                break;
        }
    }

ThirdActivity的处理,这里我重写系统返回按键的onBackPressed方法,
因为你把setResult放在finish方法或者onDestroy方法里的话,退出得太快,消息可能没办法传递下去。
@Override
 public void onBackPressed() {
    getColorSendBack(selected);
    super.onBackPressed();
}
  //返回数据给上个activity
private void getColorSendBack(int mode){
    if(redBack == NO_COLOR){
        redBack = 0;
        blueBack = 0;
        greenBack = 0;
     }
     Intent intent = new Intent();
     intent.putExtra(RED_KEK,redBack);
     intent.putExtra(GREEN_KEY,greenBack);
     intent.putExtra(BLUE_KEY,blueBack);
     setResult(RESULT_OK,intent);  //此处告诉上个activity,不是单纯结束,有消息下来了
}

六、启动模式

在说启动模式前,先看一下任务栈(返回栈)

  • Android使用任务(Task)来管理活动。一个任务就是一组存放在栈(也称返回栈 Back Stack)里的活动的集合。栈是一种先进后出的数据结构。
  • 在默认情况下,每当我们启动一个新的活动,它会在任务栈中入栈,并处于栈顶的位置。每当我们销毁一个活动(按Back键或调用finish()方法),处于栈顶的活动会出栈,这时前一个入栈的活动就会重新处于栈顶的位置。
  • 系统总是会显示处于栈顶的活动给用户。
  • 默认情况下所有Activity所需的任务栈的名字为应用的包名。任务栈有前台和后台之分,后台栈中的Activity处于暂停状态,用户可以通过切换,将后台任务栈再次调到前台。


活动一共有四种启动模式:standard、singleTop、singleTask和singleInstance。
在AndroidManifest.xml中给< activity>标签指定android:launchMode属性来选择启动模式,如下:

<activity 
     android:name=".MainActivity"
     android:launchMode="singleTop">
     <intent-filter>
          <action android:name="android.intent.action.MAIN" />
          <category android:name="android.intent.category.LAUNCHER" />
     </intent-filter>
</activity>
  • standard:标准模式 (默认)
    每启动一个Activity就会创建一个新的实例,不管这个实例是否已经存在,并具有典型情况下的生命周期。谁启动了这个Activity,那么这个Activity就运行在启动它的那个Activity所在的栈中。
    使用ApplicationContext去启动standard模式的Activity会报错,非Activity类型的Context没有所谓的任务栈,要给它设立一个FLAG_ACTIVITY_NEW_TASK标记位,这样启动时就会为它建立一个新的任务栈,而不是去找原Context的任务栈。
  • singleTop:栈顶复用模式
    可以说是standard模式的“子类”(就Activity复用的条件和入的栈而言,是一个一般到特殊的过程)。
    新的Activity已经位于任务栈的栈顶,那么此Activity不会重新创建实例,因此它的onCreate、onStart方法不会被系统调用,而是会调用一个onNewIntent方法,通过这个方法,我们可以取得当前请求的信息。
    如果新的Activity不在栈顶,即使已经存在,仍然是会重新创建Activity实例。
  • singleTask:栈内复用模式
    A:新的Activity要求的任务栈S存在。
    检查栈S里是否存在新的Activity A,存在,因为clearTop效果,将A之上的Activity全部出栈,使其到达栈顶。
    B:新的Activity要求的任务栈S不存在。
    创建新的A的实例,并入栈S。
    此外,这种模式也是和singeTop模式类似,会复用Activity实例,所以当重复创建时,不会调用onCreate、onStart方法,而是会调用onNewIntent方法。
    可以说是singleTop模式的“子类”
  • singleInstance:单实例模式
    可以说是singleTask模式的“子类”。它除了具有 singleTask 的全部属性外,那就是这种模式的 Activity 只能单独的位于一个任务栈中
  • 给Activity指定启动模式的两种方法:
    在AndroidManifest.xml中指定
    android:launchMode=“singleTask”
    在Intent中设置标志位
    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    优先级代码里比xml高,要以代码里为准。
    限定范围不同,xml里无法为Activity直接指定FLAG_ACTIVITY_CLEAR_TOP标识,代码里无法指定singleInstance模式。
  • Activity的Flags
    这里的标记位就是上文提到的使用Intent在代码里设置Activity启动方式的标记位。
    常用标记位如下:
FLAG_ACTIVITY_NEW_TASK
为Activity指定启动模式为“singleTask”。

FLAG_ACTIVITY_SINGLE_TOP
为Activity指定启动模式为“singleTop”

FLAG_ACTIVITY_CLEAR_TOP  
在“standard”模式下,使用这个标记位,如果默认任务栈里,已经有了目标ActivityA,那么A和A之上的Activity都要出栈,
然后新建一个A压入栈顶。与“singleTask”配合使用时,使用这个标记位,目标A之上的Activity都要出栈,并且onNewIntent方法会被调用。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有这个标志的Activity不会出现在历史Activity的列表中,
当某些情况下我们不希望用户通过历史列表回到我们的Activity的时候这个标记比较有用。
等同在xml中指定Activity的属性为:
android:excludeFromRecents="true"

七、Activity管理

/**
 * Activity 管理类
 */
public class ActivityManager {
 
    //Activity存储实体
    private static Stack<Activity> activityStack;
    private static ActivityManager instance;
 
    private ActivityManager() {}
 
    /**
     * 单一实例
     */
    public static ActivityManager getInstance() {
 
        if (instance == null) {
            synchronized (ActivityManager.class) {
                if (instance == null) {
                    instance = new ActivityManager();
                }
            }
        }
        return instance;
    }
 
 
    /**
     * 添加 Activity 到堆栈
     */
    public void addActivity(Activity activity) {
        if (activityStack == null) {
            activityStack = new Stack<Activity>();
        }
        activityStack.add(activity);
    }
 
    /**
     * 从堆栈移除 Activity
     */
    public void removeActivity(Activity activity) {
        if (activity != null) {
            activityStack.remove(activity);
        }
    }
 
    /**
     * 获取当前 Activity(堆栈中最后一个压入的)
     */
    public Activity currentActivity() {
        Activity activity = activityStack.lastElement();
        return activity;
    }
 
    /**
     * 结束当前 Activity(堆栈中最后一个压入的)
     */
    public void finishActivity() {
        Activity activity = activityStack.lastElement();
        finishActivity(activity);
        removeActivity(activity);
 
    }
 
    /**
     * 结束指定的 Activity
     */
    public void finishActivity(Activity activity) {
        if (activity != null) {
            activityStack.remove(activity);
            activity.finish();
        }
    }
 
    /**
     * 结束指定类名的 Activity
     */
    public void finishActivity(Class<?> cls) {
        for (Activity activity : activityStack) {
            if (activity.getClass().equals(cls)) {
                finishActivity(activity);
            }
        }
    }
 
    /**
     * 结束所有 Activity
     */
    public synchronized void finishAllActivity() {
        for (int i = 0, size = activityStack.size(); i < size; i++) {
            if (null != activityStack.get(i)) {
                activityStack.get(i).finish();
            }
        }
        activityStack.clear();
    }

    /**
     * 退出应用程序
     */
    public void AppExit(Context context) {
        try {
            finishAllActivity();
            ActivityManager activityMgr= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            activityMgr.restartPackage(context.getPackageName());
            System.exit(0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。