安卓学习第二弹(终章)活动启动模式及和最佳实践

在谈及终章之前,让我们来了解一下,活动被回收了怎么办?,在之前我们又提到过,当活动进入停止状态的时候,是有可能被回收的,我们可以设想一下,当用户在活动A的基础上启动的活动B,但是由于内存不够,系统回收了活动A,当用户按下Back的时候,活动A还是会正常出现,只是之前存储的数据不见了,这是一种非常差的用户体验,为了避免这种情况出现,Activity中提供了一个,onSaveInstanceState()回调方法,这个方法可以保证活动被回收之前一定会被调用,因此我们可以通过这个方法来解决,活动被回收时,临时数据得不到保存的情况,改方法,会携带一个Bundle类型的参数,Bundle提供了一系列的方法用于保存数据,比如:putString(),putInt()等等;以此类推,每个保存方法需要传入两个参数,第一个是键,用于后面从Bundle中取值,第二个参数是真正要保存的内容。
在mainActivity中 添加如下代码:

@Override
protected void onSaveInstanceState(Bundle outState){
super.onSaveInstanceState(outState);
String tempDate = "Something you just typed";
outState.putString("date_key",tempData);
}

数据已经被保存下来了,那么我们要怎么去提取出来呢,如果你细心观察 你会发现,我们之前一直使用的onCreate()方法其实有一个Bundle类型的参数,这个参数一般情况下,都是null,但是如果在活动被系统回收之前有通过onSaveInstanceState()方法保存数据的话,这个参数就会带上之前所保存的全部数据,我们只需要通过相应的取值方法取出来即可,
下个MainActivity方法的onCreate()方法中的代码,

@Overried
protected void onCreate (Bundle savedInstanceState){
super.onCreate(savedInstanceState);
Log.d(Tag,"onCreate");
setContenview(R.layout.activity_main);
if(savedInstanceState!= null){
String tempData = savedInstanceState.getString("date_key");
Log.d(Tag,tempDate);
}
}

取出值之后,就可以做相应的恢复动作了,比如将文本内存重新赋值到文本输入框上,
此外,值得一提的是,我们可以使用Intent和Bundle结合一起用于传递数据,首先把需要传递的数据保存在Bundle中,然后把Bundle对象放入Intent中,到了目标活动之后,以此一一取出来即可。
2:活动的启动模式
活动的启动模式分4种。分别是standard,singleTop,singleTask,singleInstance。,可以在AndroidManifest.xml中通标签指定android:LaunchMode属性来选择启动模式。接来下我将以此介绍每个模式,
首先;standard启动模式,这个是安卓系统的默认启动模式,如果没有指定我们将使用这个模式启动活动,其安卓系统将默认使用这个模式来启动活动,在standard模式下,每当启动一个新的活动,都就会在返回栈中入栈,并且处于栈顶位置,系统不会在乎栈中是否已经存在这个活动的事例,而是每次都会创建这个活动的实例,我们可以通过实践来体会一哈。在之前的博客中,我有讲到过一个案例。就是ActivityTesk项目,我们在这个项目的基础上来进行修改 让大家更好的理解几个活动的启动模式,
在FirstActivity中的onCreate()方法中添加一条打印消息,然后把通过Intent方法启动的活动改成启动自己本身,即:

Intent intent = new Intent(FirstActivity.this,FirstActivity.class);

我们重新运行程序,然后连续点击按钮二次,
然后我们查看打印日志信息,我们不难发现,日志信息中可以看出,FirstActivity被创建了三次,
在这里插入图片描述
每点击一次按钮就会创建出一个新的FirstActivity实例,此时返回栈中也会存在三个FirstActivity,因此你需要按下三次Back才可以退出程序,
2.singleTop模式。
有些可能会发现这种活动启动模式有一定的不妥,假如活动栈中已经存在一个活动的案例,然后再创建一个同样活动的案列,显得赘于,因此我们可以选择自己的活动启动模式,即:singleTop活动启动模式,该模式发现如果发现返回栈的栈顶已经是该活动,则认为可以直接使用它,不会再创建新的活动实例。
我们可以在AndroidManifest.xml中设计FirstActivity的启动模式,在标签中,加入一行代码。指定活动的启动模式,
android:LaunchMode="singleTop"
然后我们重新运行程序,查看日志信息会发现已经创建了一个FirstActivity的实例,但是之后不管你点击多少次按钮都不会有打印信息,因为目前FirstActivity已经处于返回栈的栈顶,因此只需要按一次Back建就可以推出程序,
不过当FirstActivity并未处于返回栈的栈顶的时候,再次启动FirstActivity,还是会重新创建新的实例
可以通过一个案列来表达一下:
在FirstActivity中我们修改onCreate()方法;代码如下:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        Log.d("1231","Task id is"+ getTaskId());
        Log.d("1231", "FirstActivity 已被创建");
        setContentView(R.layout.first_layout);//加载布局
        Button button1 = (Button) findViewById(R.id.button_1);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//             SecondActivity.actionStart(FirstActivity.this,"date1","date2");
                Intent intent = new Intent(FirstActivity.this,SecondActivity.class);
                startActivity(intent);
            }
        });

这次我们启动的是SecondActivity 然后修改SecondActivity中的onCreate方法,让我们在这个活动中点击,再次回跳到第一个活动,代码如下:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        Log.d("1232","Task id is"+ getTaskId());
        Log.d("1231","SecondActivity 已被创建");
        setContentView(R.layout.second_layout);
        Button button2 = (Button)findViewById(R.id.button_2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
//                Intent intent = new Intent(SecondActivity.this,ThirdActivity.class);
                Intent intent = new Intent(SecondActivity.this,FirstActivity.class);
                startActivity(intent);

            }
        });

重新运行程序,查看打印日志:
在这里插入图片描述
可以看到系统创建了二次FirstActivity活动的实例,由于我们在SecondActivity再次启动FirstActivity时,栈顶已经变成SecondActivity 因此会重新创建一个FirstActivity实例,按下Back键会返回SecondActivity,在按下则又回去到FirstActivity界面,
三:singleTask模式启动活动
使用singleTop模式可以很好的解决重复创建的问题,但是假如活动不在栈顶的时候,还是会重新创建,那么用什么办法,可以让活动只在整个应用程序中只存在一个实例呢,这就要借助singleTask模式来解决,当活动的启动模式指定为singleTask时候,每个活动启动的时候,系统首先会在返回栈中检查是否存在该活动的实例,如果存在则直接使用该活动的实例。并且把这个活动之上的所有活动统统出栈,
我们在AndroidManifest.xml把FirstActivity的启动模式指定问singleTask,然后在FirstActivity中添加onRestart()方法,并且打印日志

   protected void onRestart() {
        super.onRestart();
        Log.d("1231","FirstActivity重新启动啦");
    }

最后在SecondActivity中添加onDestroy()方法,并打印日志。代码如下:

   @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d("1231", "SecondActivity已经被销毁");
    }

重新运行程序,在FirstActivity中点击按钮进入SecondActivity界面然后在该页面点击按钮返回FirstActivity界面,
查看打印日志;
在这里插入图片描述
四:singleInstance模式
该模式应该是四种模式中最复杂的了,不同于之前的三个启动模式,指定为singleInstance模式的活动会启动一个新的返回栈来管理这个活动,这样做有什么意义呢?我们不妨思考一下,假设我们的的程序中有一个活动是允许其他程序来调用的,我们肯定想让其他程序来和我们这个程序实现共享这个实例,使用其他三种模式肯定是不能实现这个问题的,因为每个程序,都有自己的返回栈,同一个活动在不同的返回栈中入栈时肯定是创建一个新的实例,而使用singleInstance这个模式我们就可以很好的解决这个问题,在这种模式下,会有一个单独的返回栈来管理这个活动,不管是哪个应用程序来访问这个活动, 都共用同一个返回栈,这就实现了共享的问题,说了这么多我们来实践一下吧,将SecondActivity的启动模式设为singleInstance

     <activity android:name=".SecondActivity"
            android:launchMode="singleInstance"
            >

然后修改FirstActivity中onCreate()方法的代码 即将打印日子改为打印当前返回栈的ID

   Log.d("1231","Task id is"+ getTaskId());

然后修改SecondActivity中onCreate()方法的代码,将打印志信息改为同上的信息,并将该按钮点击事件改为,启动第三个活动。代码如下:

  Log.d("1232","Task id is"+ getTaskId());

	//········
	 Intent intent = new Intent(SecondActivity.this,ThirdActivity.class);

同样在ThirdActivity中的代码:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("1233","Task id is"+ getTaskId());
        setContentView(R.layout.third_activity);

然后查看打印日志
在这里插入图片描述
我们可以看到SecondActivity是单独放在一个返回栈的,我们在第三个活动按下Back键,我们会发现返回了第一个活动,然后在Back则又返回到SecondActivity,这是因为第一个和第三个放在同一个返回栈中,然后我们点击Back时ThirdActivity出栈,然后FirstActivity则回到栈顶,然后在Back,第一个出栈,这个栈已经空了。于是就显示另外一个栈的栈顶活动了。在这我并没有贴上四种模式的示意图,大家可以去百度一下,
二:活动的最佳实践
2-1.知道当前是在那一个活动,
这个技巧将会根据当前的界面这是哪一个活动,这在为我们以后接手别人的代码的时候,当你需要去修改当前界面的一些功能,但是你不知道这个界面对应的活动是那个一个的时候,我还是在ActivityTesk上面来做一些演示,首先新建一个BaseActivity类,这里需要的注意的是,这个类和普通的活动创建方式不一样,我们不需要注册,所以只要一个普通的java类就可以了并且重写onCreate()方法,

public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("asd", getClass().getSimpleName());

我们在onCreate()中获取了当前实例的类名,然后打印了出来,接下来让BaseActivity继承AppCompatActivity然后让其他几个活动不再继承AppCompatActivity而是继承BaseActivity这样就不会有什么影响,然后重新运行程序,因为父类重写了onCreate()方法并且打印了当前实例的类名,所以我们观察日志可以清楚看到当前界面对应的哪一个活动。
2-2随时随地的退出程序,
我们在原来的基础上,新建一个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  finshAll(){
        for (Activity activity:activities){
            if(!activity.isFinishing()){
                activity.finish();
            }

        }
        activities.clear();
    }
}

在这里我们用一个List来存放活动,然后书写了三个方法,一个向集合中添加活动,一个从集合中移除活动,一个将集合中所有活动全部销毁
接下来我们修改BaseActivity中的代码,如下:

public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("asd", getClass().getSimpleName());
        ActivityCollector.addActivity(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ActivityCollector.removeActivity(this);
    }
}

在BaseActivity的onCreate()方法中调用ActivityCollector的addActivity()方法,表面将正创建的活动添加到活动管理器里,然后重写onDestory()方法,并调用remove方法。以后不管你想在什么地方退出程序,直接调用ActivityCollector.finishAll()方法就可以了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值