Android Activity被回收的情况分析

手机APP/开发
287
0
0
2023-06-23
标签   Android
目录
  • 1.onSaveInstanceState()方法
  • 2.ViewModel

1.onSaveInstanceState()方法

当一个Activity进入了停止状态,是有可能被系统回收的。想象以下场景:应用中有一个ActivityA,用户在ActivityA的基础上启动了ActivityB,ActivityA就进入了停止状态,这个时候由于系统内存不足,将ActivityA回收掉了,然后用户按下Back键返回ActivityA,会出现什么情况呢?其实还是会正常显示ActivityA的,只不过这时并不会执行onRestart()方法,而是会执行ActivityA的onCreate()方法,因为ActivityA在这种情况下会被重新创建一次。

但是这种情况下可能会出现一个重要的问题:ActivityA中是可能存在临时数据和状态的。打个比方,MainActivity中如果有一个文本输入框,现在你输入了一段文字,然后启动NormalActivity,这时MainActivity由于系统内存不足被回收掉,过了一会你又点击了Back键回到MainActivity,这个时候你会发现刚刚输入的文字都没了,因为MainActivity被重新创建了。

如果我们的应用出现了这种情况是比较影响用户体验的,其实Activity还提供了一个onSaveInstanceState()回调方法,这个方法可以保证在Activity被回收之前一定会被调用,因此我们可以通过这个方法来解决这个问题。

onSaveInstanceState()方法会携带一个Bundle类型的参数,Bundle提供了一系列的方法保存数据,比如可以使用putString()方法保存字符串,使用putInt()方法保存整型数据,以此类推。每个保存方法需要传入两个参数,第一个参数是键,用于后面从Bundle中取值,第二个参数是真正要保存的内容。

在MainActivity中添加如下代码就可以将临时数据进行保存了:

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        val tempData="Something you just typed"
        outState.putString("data_key",tempData)
    }

数据是已经保存下来了,那么我们应该在哪里进行恢复呢?其实我们一直在使用的onCreate()方法其实也有一个Bundle类型的参数。这个参数在一般情况下都是null,但是如果在Activity被系统回收之前,你通过onSaveInstanceState()方法保存数据,这个参数就会带有之前保存的全部数据,我们只需要再通过相应的取值方法将数据取出即可。

修改MainActivity的onCreate()方法,如下所示:

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_first)
        if(savedInstanceState!=null){
            val tempData = savedInstanceState.getString("data_key")
            tempData?.let { Log.d("tag", it) }
        }
}

取出值之后就可以再做相应的恢复操作就可以了,比如将文本内容重新赋值到文本输入框上,这里我只是简单打印一下。

这里使用Bundle保存和取出数据和我们之前使用Intent传递数据的方法很类似,首先我们可以把需要传递的数据都保存在Bundle对象中,然后再将Bundle对象存放在Intent里。到了目标Activity之后,先从Intent中取出Bundle,再从Bundle中一一取出数据。

另外在手机的屏幕发生旋转的时候,Activity也会经历一个重新创建的过程,因而在这种情况下,Activity中的数据也会丢失。这种问题也可以通过onSaveInstanceState()方法来解决,但是对于横竖屏已经有了更好的方案。

2.ViewModel

使用 ViewModel,我们就无需再用这种方法保存,因为 ViewModel 会自动感知生命周期,处理数据的保存与恢复。即数据可在发生屏幕旋转等配置(其它例如分辨率调整、权限变更、系统字体样式、语言变更等)更改后继续留存。

代码如下:

package com.example.viewmodeldemo;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModelProvider;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
    private MyViewModel mMyViewModel;
    private TextView textView;
    private Button mButton1;
    private Button mButton2;
    private final String TAG="MainActivityTest";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate: ");
        //创建一个ViewModel对象
        mMyViewModel = new ViewModelProvider(this, new ViewModelProvider.NewInstanceFactory()).get(MyViewModel.class);
        textView=findViewById(R.id.textView);
        //ViewModel会保存数据,当你重新创建的时候会加载显示出来
        textView.setText(String.valueOf(mMyViewModel.number));
        mButton1=findViewById(R.id.button1);
        mButton2=findViewById(R.id.button2);
        mButton1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mMyViewModel.number++;
                textView.setText(String.valueOf(mMyViewModel.number));
            }
        });
        mButton2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                mMyViewModel.number+=2;
                textView.setText(String.valueOf(mMyViewModel.number));
            }
        });
    }
    @Override
    protected void onStart() {
        super.onStart();
        Log.d(TAG, "onStart: ");
    }
    @Override
    protected void onStop() {
        super.onStop();
        Log.d(TAG, "onStop: ");
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: ");
    }
    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "onPause: ");
    }
    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG, "onResume: ");
    }
}
package com.example.viewmodeldemo;
import androidx.lifecycle.ViewModel;
//这里的ViewModel可以看作全局变量仓库
public class MyViewModel extends ViewModel {
    public int number=0;
}

这样当你旋转屏幕生命周期发生变化,你的数据还在。