从零开始打卡:第二天
活动被回收了怎么办?
场景:应用中有一个活动A,用户在此基础上启动活动B,活动A就进入停止状态。由于系统内存不足,活动A会被回收,用户按下BACK键,返回A,A正常显示,但不会执行onRestart()方法,而是会执行A的onCreate()方法,因为A在此情况下被重新创建了。这时就出现问题了,比如A中有一个文本框,且输入了文字,这时操作如上步骤,A中输入的文字就没了,因为A被重新创建了。
这是非常影响用户体验的。Activity提供了一个onSaveInstanceState()回调方法,这个方法保证在活动被回收前一定会被调用,因此我们可以用此方法解决临时数据的保存问题。
onSaveInstanceState()方法会携带一个Bundle类型的参数,Bundle提供了一系列的方法用于保存数据,列如putString()方法保存字符串,putInt()保存整型数据,以此类推。每个保存方法需要传入两个参数,1st.键,用于后面从Bundle中取值,2nd.真正保存的内容。
代码如下:
@Override
public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {
super.onSaveInstanceState(outState, outPersistentState);
String tempData="Something you just typed";
outState.putString("data_key",tempData);
}
首先我们先通过onSaveInstanceState()方法保存数据,再在onCreate()方法中执行操作来回复数据,
if (savedInstanceState!=null){
String tempData=savedInstanceState.getString("data_key");
Log.d("MainActivity",tempData);
}
此方法类似于Intent传值
public void onClick(View view) {
String data ="Hello SecondActivity";
Intent intent=new Intent(MainActivity.this,SecondActivity.class);
intent.putExtra("extra_data",data);
startActivity(intent);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Intent intent=getIntent();
String data=intent.getStringExtra("extra_data");
Log.d("SecondActivity",data);
}
活动的启动模式
4种启动模式 1.standard 2.singleTop 3.singleTask 4.singleInstance
1.standard
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("MainActivity",this.toString());
setContentView(R.layout.activity_main);
pushBtn = (Button) findViewById(R.id.push_btn);
pushBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent=new Intent(MainActivity.this,MainActivity.class);
startActivity(intent);
}
});
}
连续点击两次按钮,每点击一次就会创建一个新的MainActivity实例,返回栈中也会存在3个MainActivity实例,因此需要按3次BACK键才能退出。
2.singleTop
在Manifest中输入以上代码,运行程序,因为已经创建了一个Mainactivity所以再按按钮都不会再有打印信息,
public void onClick(View view) {
Intent intent=new Intent(MainActivity.this,SecondActivity.class);
startActivity(intent);
}
@Override public void onClick(View view) { Intent intent =new Intent(SecondActivity.this,MainActivity.class); startActivity(intent); }
输入如上代码,再次运行,在MainActivity点击按钮跳转进入SecondActivity然后在SecondActivity中点击
按钮,会跳转到MainActivity,日志打印了两个不同的MainActivity实例,这是因为在SecndActivity中再次启动了MainActivity
栈顶已经变成了SecondActivity因此会创建一个新的MainActivity,现在按下BACK键会回到SecondActivity,再次按下会回到MainActivity
再次按下BACK才会退出。
3.singleTask
在Manifest中更改注册信息
在MainActivity和SecondActivity中修改代码
MainActivity:
@Override
protected void onRestart() {
super.onRestart();
Log.d("MainActivity","onRestart");
}
SecondActivity:
@Override
protected void onDestroy() {
super.onDestroy();
Log.d("SecondActivity","onDestory");
}
重新运行程序,在MainActivity界面点击按钮进入SecondActivity,再在SecondActivity中点击按钮,重新进入MainActivity
打印的日志如下:
从打印的信息可以看出,在SecondActivity中打开MainActivity时,返回栈中已经存在一个MainActivity了,并且在SecondActivity下,于是SecondActivity出栈,MainActivity回到栈顶,因此,onRestart()和onDestroy()方法得到执行,且只需按一次BACK键就可以退出。
4.singleInstance
指定为singleInstance模式的活动会启用一个新的返回栈,
在SecondActivity中写下如下代码:
在FirstActivity,SecondActivity,ThirdActivity中onCreate()方法中修改代码:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("FirstActivity","Task id is"+getTaskId());
setContentView(R.layout.activity_first);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("SecondActivity","Task id is"+getTaskId());
setContentView(R.layout.activity_second);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("ThirdActivity","Task id is"+getTaskId());
setContentView(R.layout.activity_third);
运行程序,打印日志如下:
可以看到只有SecondActivity中的id是不同的,这说明SecondActivity存放在一个单独的返回栈里。我们按下BACK键,直接返回到FirstActivity
再次按下BACK键,返回到SecondActivity,再按一次才会退出。由于FirstActivity和ThirdActivity存放在同一个返回栈里,再Third界面
按下BACK键,Third出栈,First回到栈顶,再次按下BACK键,返回栈清空,于是显示出另一个返回栈,即Second,再次按下就退出程序了。
知晓当前再哪个活动
创建一个BaseActivity类,然后让它继承AppCompatActivity代码如下:
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
}
}
在onCreate()方法中获取当前实例的类名,并打印出来,
让FirstActivity,SecondActivity,ThirdActivity,继承BaseActivity运行程序,打印结果如下:
随时随地退出程序
我们需要一个专门的集合类对所有的活动进行管理。
新建一个ActivityCollector类作为活动管理器,代码如下:
public class ActivityCollector {
public static List<Activity>activities=new ArrayList<>();
public static void addActivity(Activity activity){
activities.add(activity);
}
public static void removeActivity(Activity activity){
activities.remove(activity);
}
public static void finishAll(){
for (Activity activity:activities){
if (!activity.isFinishing()){
activity.finish();
}
}
}
}
在活动管理器中,我们通过一个List来暂存活动,提供一个addActivity()方法用于向List中添加一个活动,
提供一个removeActivity()方法用于从List中移除活动。最后提供一个finishAll()方法将List中存储的活动全部销毁。
public class BaseActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("BaseActivity",getClass().getSimpleName());
ActivityCollector.addActivity(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityCollector.removeActivity(this);
}
}
ThirdActivity中的代码如下:
button_3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
ActivityCollector.finishAll();
}
});
运行程序连续跳转,在ThirdActivity中点击按钮,直接退出程序。
启动活动的最佳写法
假设SecondActivity中需要两个非常重要的字符参数,在启动SecondActivity时必须传递过来,
这是一种办法,但在实际开发中却会有些不方便。
修改代码如下:
public static void actionStart(Context context,String data1,String data2){
Intent intent=new Intent(context,SecondActivity.class);
intent.putExtra("param1",data1);
intent.putExtra("param2",data2);
context.startActivity(intent);
}
FirstActivity中的代码如下:
public void onClick(View view) {
SecondActivity.actionStart(FirstActivity.this,"data1","data2");
}
这样就解决了实际开发中的不便。