目录
- 零、参考
- 一、Activity定义
- 二、任务栈(Task)
- 三、生命周期
- (一)Activity的四种状态
- (二)正常情况下的回调方法
- (三)异常情况下的回调方法
- 四、启动模式
- (一)Standard(标准模式)
- (二)SingleTop(栈顶复用)
- (三)SingleTask(栈内复用)
- (四)SingleInstance(单例模式)
- 五、用到的填坑方式
- 六、注释
零、参考
- https://coding.imooc.com/lesson/101.html#mid=17541
- 《第一行代码》郭霖 第二章第5节
一、Activity定义
Android系统提供给我们的,与用于交互的接口(界面)。
二、任务栈(Task)
- 任务栈是一个栈结构,具有先进先出的特性,用于存放所有的Activity组件。
- 栈中Activity的顺序就是按照它们被打开的顺序依次存放的。
- Android系统使用任务栈的方式来有序地管理其中的Activity,并决定哪个Activity与用户进行交互——只有在任务栈栈顶的Activity才可以跟用户进行交互。
- 触发操作和背后原理:
- 打开一个新的Activity或者退出当前Activity,将在一个称为任务栈的结构中添加或者减少一个Activity组件。
- 退出应用程序时,必须把所有的任务栈中所有的activity清除出栈时,任务栈才会被销毁。当然任务栈也可以移动到后台, 并且保留了每一个activity的状态. 可以有序的给用户列出它们的任务, 同时也不会丢失Activity的状态信息。
- APP、Activity、Task的关系
- 一个App中可能不止一个Task。
- 某些特殊情况下,单独一个Activity可以独享一个Task。
- 一个Task中的Activity可以来自不同的App。
- 同一个App的Activity可能不在一个Task中。
- 任务栈结构示意图:
三、生命周期
(一)Activity的四种状态
- Active:Activity处于栈顶。
注意:只是失去了与用户交互的能力。内存充足的情况下不会回收数据、成员变量等资源,除非内存不足。 - Paued:Activity可见但不可交互(失去焦点)。
- Stopped:当前Activity不可见(被另一个Activity完全覆盖)。
注意:内存充足的情况下不会回收数据、成员变量等资源,除非内存不足。大概率资源会被收回。 - Killed:Activity被系统回收。
(二)正常情况下的回调方法
- Activity启动
- onCreate():初始化工作,布局资源,图片预加载等。
- onStart():用户可见,但是还处在后台,无法与用户交互。
- onResume():前台可见,可以与用户交互交互。
- 被其他Activity覆盖
- onPause():Activity处于正在停止状态。
- onStop():Activity处于完全覆盖状态,不可见,只能在后台运行。
- 再次回到原Activity
- onRestart():由不可见变为可见。
- onStart()
- onResume()
- 点击“BACK”回退键,退出当前Activity。
- onPause()
- onStop()
- onDestroy():表示正在被销毁,回收,资源释放
(三)异常情况下的回调方法
1.onSaveInstanceState():保存一些临时性的数据到Bundle中
- 由系统主动销毁一个Activity时会被自动调用。
- 如果调用,调用将发生在onPause()或onStop()方法之前(多数在onPause()前)。
- 调用时机(系统调用)
- 内存不足时。
- 用户直接按“HOME”键时。
- 长按“HOME“”键,选择运行其他的程序时。
- 从Activity A中启动一个新的Activity B时。
- 屏幕方向切换时,例如从竖屏切换到横屏。
- 是否重写?
- 如果需要保存额外的数据时, 就需要覆写此方法,但是只适合保存瞬态数据, 比如UI控件的状态,成员变量的值等,而不应该用来保存持久化数据。
- onRestoreInstanceState(Bundle):获得传过来的Bundle
- Activity重新创建时调用,通过这个方法可以恢复之前界面的状态。
- 如果调用,调用将发生在onStart() 和 onPostCreate(Bundle)之间。
- onRetainNonConfigurationInstance():主要用于屏幕旋转操作,主要保存一些不适合保存在Bundle中的数据,像AsyncTask和SQLiteDatabse等,当然也可以保存任何类型数据。
如果调用,调用将发生在onSaveInstanceState()之前。
配合使用getLastNonConfigurationInstance()获取切换屏幕的状态或其他数据。
代码演示:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
String token = savedInstanceState.getString("key");
Log.d(TAG, "onCreate: token" + token);
}
Log.d(TAG, "onCreate: execute");
Button btnDialog = findViewById(R.id.start_dialog);
btnDialog.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, DialogActivity.class);
startActivity(intent);
}
});
Button startNormal = findViewById(R.id.start_normal);
startNormal.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, NormalActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
String token = "22334455";
outState.putString("key", token);
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart");
}
-logcat结果:
-说明:启动MainActivity,点击按钮1进入NormalActivity(完全覆盖),返回MainActivity,点击按钮2进入DialogActivity(部分覆盖),再按返回键退出。
(一)Standard(标准模式)
- 特点:
- 在不指定启动模式的前提下,系统默认使用此模式。
- 每次启动activity,都会新建一个实例,并将它放在栈的顶部。
- 出现完整生命周期onCreate(),onStart(),onResume(),内存消耗大。
- 代码演示:
<activity android:name=".launchmode.FirstActivity"/>
public class FirstActivity extends AppCompatActivity {
private static final String TAG = "FirstActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
//打印当前活动的实例
Log.d(TAG, this.toString());
Button btn = findViewById(R.id.btn_first);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, FirstActivity.class);
startActivity(intent);
}
});
}
}
- logcat 结果:
- 说明:每次点击一次按钮就会常见出一个新的FirstActivity实例,这里new出了5个FirstActivity实例,如果要退出程序需要连续按5次。
- 示意图(启动和返回):
(二)SingleTop(栈顶复用)
- 情况一:当前栈中已有该Activity实例,并且该实例位于栈顶时,这个Activity不会被重新创建,而是复用栈顶的实例,并且会将Intent对象传入,回调onNewIntent()方法。
- 注意:此Activity它的onCreate(),onStart()方法不会被调用,因为它并没有发生改变。
- 情况二:当前栈中已有该Activity实例,但是该实例不在栈顶时,会新建一个实例。
- 情况三:当前栈中不存在该Activity实例时,会新建一个实例。
- 使用场景:
- IM对话框(即时通讯)
- 新闻客户端推送
- 代码演示:
<activity
android:name=".launchmode.FirstActivity"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".launchmode.SecondActivity"/>
public class FirstActivity extends AppCompatActivity {
private static final String TAG = "launch_FirstActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
//打印当前活动的实例
Log.d(TAG, this.toString());
Button btn = findViewById(R.id.btn_first);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
}
public class SecondActivity extends AppCompatActivity {
private static final String TAG = "launch_SecondActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Log.d(TAG, this.toString());
Button btn = findViewById(R.id.button1);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SecondActivity.this, FirstActivity.class);
startActivity(intent);
}
});
}
}
- logcat结果:
- 说明:点击FirstActivity的按钮启动SecondActivity,再点击SecondActivity的按钮重新进入到FirstActivity,但是系统创建了两个不同的FirstActivity实例,符合情况二。
- 示意图(启动和返回):
(三)SingleTask(栈内复用)
- 特点:在同一个任务栈中只存在一个实例。
- 流程:
- 首先会根据taskAffinity1去寻找当前是否存在一个对应名字的任务栈。
- 如果任务栈不存在,则会创建一个新的Task。
- 如果任务栈存在,则得到该任务栈,查找该任务栈中是否存在该Activity实例,如果存在,那么这个Activity不会被重新创建,而是移除(Destory)任务栈中在该实例之上的所有Activity,把目标实例置于栈顶并直接调用它的onNewIntent()方法。
- 使用场景:
- 应用的主界面
- 代码演示:
<activity
android:name=".launchmode.FirstActivity"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".launchmode.SecondActivity"/>
public class FirstActivity extends AppCompatActivity {
private static final String TAG = "launch_FirstActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
//打印当前活动的实例
Log.d(TAG, this.toString());
Button btn = findViewById(R.id.btn_first);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(TAG, "onRestart");
}
}
public class SecondActivity extends AppCompatActivity {
private static final String TAG = "launch_SecondActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Log.d(TAG, this.toString());
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
}
}
- logcat结果:
- 说明:在FirstActivity界面点击按钮进入到SecondActivity,然后点击后退按钮,又会重新进入FirstActivity,可以发现,再回到FirstActivity时,返回栈已经存在一个FirstActivity的实例,所以FirstActivity重新成为了栈顶Activity,并将SecondActivity的实例移除。
- 示意图(启动和返回):
(四)SingleInstance(单例模式)
- 特点:
- 以这种模式启动的activity具有全局唯一性,即整个系统中就这么一个实例。
- 如果在启动这样的Activity时,已经存在了一个实例,那么就会把所在的任务调度到前台,直接重用这个实例。
- 以这种模式启动的Activity具有独占性,这个实例独享一个任务栈。
- 使用场景
- 呼叫来电
- 代码演示:
<activity
android:name=".launchmode.FirstActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".launchmode.SecondActivity"
android:launchMode="singleInstance"/>
<activity android:name=".launchmode.ThirdActivity"
public class FirstActivity extends AppCompatActivity {
private static final String TAG = "launch_FirstActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
//打印当前活动的实例
Log.d(TAG, "Task id is"+ getTaskId());
Button btn = findViewById(R.id.btn_first);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(FirstActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
}
public class SecondActivity extends AppCompatActivity {
private static final String TAG = "launch_SecondActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Log.d(TAG, "Task id is" + getTaskId());
Button btn = findViewById(R.id.button1);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(SecondActivity.this, ThirdActivity.class);
startActivity(intent);
}
});
}
}
public class ThirdActivity extends AppCompatActivity {
private static final String TAG = "launch_ThirdActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third);
Log.d(TAG, "Task id is" + getTaskId());
}
}
- logcat结果:
说明:在FirstActivity中点击按钮进入SecondActivity中,再从SecondActivity中点击按钮进入ThirdActivity,可以看到打印出来的Task id的信息,FirstActivity和ThirdActivity的Task id一样,是存放在同一个返回栈中,SecondActivity的Task id不同,是存放在另一个返回栈中。
- 示意图(启动和返回):
taskAffinity:控制Activity所属的任务栈。
方式一:配合launchMode=singleTask或者singleInstance使用。
方式二:首先在activity中设置TaskAffinity属性,然后在启动Activity时设置flag为FLAG_ACTIVITY_NEW_TASK。这样可以让某些Activity会在taskAffinity指定的栈中启动。 ↩︎