从零开始打卡:第二天


Activity生命周期

从零开始打卡:第二天

活动被回收了怎么办?

场景:应用中有一个活动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");
}
这样就解决了实际开发中的不便。