Android之理解Activity的启动模式

目的:

1.Activity的启动模式有哪几种,分别用于什么场景?

2.清晰地描述下onNewIntent和onConfigurationChanged这两个生命周期方法的场景


从事Android的朋友都知道activity是什么,activity的启动方式。

activity的启动方式有四种:

1.standard

2.singleTop

3.singleTask

4.singlestance



下面一一介绍这四种启动模式的特点和应用场景:

那怎么配置其启动模式呢?

就是直接在AndroidManifest.xml配置的activity节点里配置:android:launchMode="xxxxx"即可,如下图所示:



1.standard

首先在布局界面的添加一个按钮和一个textview,按钮用来做点击跳转事件,textview用来显示当前activity实例的序号

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.nova.startupdemo.MainActivity">

    <TextView
        android:id="@+id/tv_taskid"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_centerInParent="true"
        android:gravity="center"
        />

    <Button
        android:id="@+id/btn_gomain"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="跳转MainActivity"
        android:layout_below="@id/tv_taskid"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="10dp"
        android:background="@drawable/bg_button"
        android:textColor="#FFFFFF"
         />

</RelativeLayout>

MainActivity类的代码:


public class MainActivity extends Activity implements View.OnClickListener {
    private Button btn_gomain;
    private TextView tv_taskid;
    private String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"-----进入onCreate");
        setContentView(R.layout.activity_main);
        btn_gomain = (Button)findViewById(R.id.btn_gomain);
        tv_taskid = (TextView)findViewById(R.id.tv_taskid);
        tv_taskid.setText(this.toString());
        Log.d(TAG,this.toString());
        btn_gomain.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_gomain:
                startActivity(new Intent(MainActivity.this,MainActivity.class));
                break;
        }
    }
    @Override
    protected void onResume(){
        super.onResume();
        Log.d(TAG,"-----进入onResume");
    }

    @Override
    protected void onStart(){
        super.onStart();
        Log.d(TAG,"-----进入onStart");
    }

}
并在AndroidManifest.xml配置启动模式。
下面点击按钮,并看打印结果:



发现虽然是跳转同一个Activity,但是他们的序列号不一样,也发现新跳转的Mainactivity的onCreate,onStart,onResume生命周期方法都会被调用,我们知道,android是通过栈来管理activity,通过上面例子可以初步确定,当启动模式设为standard,不管这个实例是否存在,都会重新去创建这个实例。当按back键返回时,会跳到原来之前的界面,也就是创建新的实例会放到栈顶。


2.singleTop

这次将布局文件修改一下,增加一个按钮,并且增多一个activity,叫SecondActivity,因为要验证

1.当activity在栈顶的情况下跳转自己的情况
2.当activity不在栈顶的情况

activity_main布局:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.nova.startupdemo.MainActivity">

    <TextView
        android:id="@+id/tv_taskid"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_centerInParent="true"
        android:gravity="center"
        />

    <Button
        android:id="@+id/btn_gomain"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:text="跳转MainActivity"
        android:layout_below="@id/tv_taskid"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="10dp"
        android:background="@drawable/bg_button"
        android:textColor="#FFFFFF"
         />

    <Button
        android:id="@+id/btn_second"
        android:layout_below="@id/btn_gomain"
        android:layout_centerHorizontal="true"
        android:layout_width="200dp"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="跳转到SecondActivity"
        android:textColor="#FFFFFF"
        android:background="@drawable/bg_button"/>

</RelativeLayout>

MainActivity:
public class MainActivity extends Activity implements View.OnClickListener {
    private Button btn_gomain;//跳自己
    private Button btn_second;//跳其他界面
    private TextView tv_taskid;
    private String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"-----进入onCreate");
        setContentView(R.layout.activity_main);
        btn_gomain = (Button)findViewById(R.id.btn_gomain);
        btn_second = (Button)findViewById(R.id.btn_second);
        tv_taskid = (TextView)findViewById(R.id.tv_taskid);
        tv_taskid.setText(this.toString());
        Log.d(TAG,this.toString());
        btn_gomain.setOnClickListener(this);
        btn_second.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_gomain:
                startActivity(new Intent(MainActivity.this,MainActivity.class));
                break;
            case R.id.btn_second:
                startActivity(new Intent(MainActivity.this,SecondActivity.class));
        }
    }
    @Override
    protected void onResume(){
        super.onResume();
        Log.d(TAG,"-----进入onResume");
    }

    @Override
    protected void onStart(){
        super.onStart();
        Log.d(TAG,"-----进入onStart");
    }

}


activity_second.xml布局:
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/tv_second"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:layout_centerVertical="true"/>

    <Button
        android:id="@+id/btn_second"
        android:layout_width="200dp"
        android:layout_height="40dp"
        android:background="@drawable/bg_button"
        android:layout_centerHorizontal="true"
        android:text="跳转到MainActivity"
        android:textColor="#FFFFFF"
        android:layout_marginTop="10dp"
        android:layout_below="@id/tv_second"/>

</RelativeLayout>

SecondActivity:

public class SecondActivity extends Activity implements View.OnClickListener{

    private TextView tv_second;
    private Button btn_second;
    private  String  TAG ="SecondActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d(TAG,"-----进入onCreate");
        setContentView(R.layout.activity_second);
        btn_second = (Button)findViewById(R.id.btn_second);
        tv_second = (TextView)findViewById(R.id.tv_second);
        tv_second.setText(this.toString());
        Log.d(TAG,this.toString());
        btn_second.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_second:
                startActivity(new Intent(SecondActivity.this,MainActivity.class));
                break;
        }
    }
    @Override
    protected void onResume(){
        super.onResume();
        Log.d(TAG,"-----进入onResume");
    }

    @Override
    protected void onStart(){
        super.onStart();
        Log.d(TAG,"-----进入onStart");
    }
}

最后在AndroidManifest.xml配置启动模式:MainActivity和SecondActivity节点下:android:launchMode="singleTop"

step1:自己跳转自己,也就是当所需要跳转的activiity已经在栈顶的情况
发现:



可发现,当 如果新的Activity已经位于任务栈的栈顶,那么Activity不会重建,onCreate和onStart方法不会被调用,但是onResume会被调用

step 2:当要跳转的activity不再栈顶,步骤:先跳转到SecondActivity,然后SecondActivity在跳转到MainActivity,这时候MainActivity不在栈顶,看看打印的结果:



发现,如果所要跳转的activity不在栈顶,系统会重新创建实例并于栈顶。按back键,先返回SecondActivity,再返回MainActivity
因此可总结:
1.当如果启动模式是singleTop,当所要跳转的activity已经在栈顶,那么系统不会创建新的实例,并回调onResume方法
2.当如果启动模式singleTop,当所要跳转的activity不在栈顶,系统还是重新创建实例放到栈顶


3.singleTask

这次只改启动模式:android:launchMode=singleTask
这里只讨论新activity的任务栈已经存在:
1.step:跳自己


发现如果在栈顶不会创建新的实例

1.step:跳转SecondActivity再跳转MainActivity
发现:


按back键,直接跳回手机界面。
发现:如果所要跳转的activity在栈内,就不会创建新的实例,但是会回调onStart和onResume方法,注意,并且将所要跳转activity上面的activity全部出栈,最终自己置于栈顶。

总结:
1.当如果启动模式是singleTask,当所要跳转的activity已经在栈顶,那么系统不会创建新的实例,并回调onResume方法
2.当如果启动模式singleTask,当所要跳转的activity不在栈顶,系统会将栈内所有在这activity上面的activity全部出栈



4.singleInstance


这种情况比较特殊:

首先在AndroidMainifest下改变启动模式:
MainActivity的launchMode=”standard”,SecondActivity的launchMode=”singleInstance
然后再每一个activity中打印栈信息id
Log.d(TAG+"MainActivity",this.getTaskId()+"");
Log.d(TAG+"SecondActivity",this.getTaskId()+"");

发现:



两个Activity的实例放到两个不同的栈中、

原理图如下:


MainActivity跳转到SecondActivity时,重新启用了一个新的栈结构,来放置SecondActivity实例,然后按下后退键,再次回到原始栈结构;图中下半部分显示的在SecondActivity中再次跳转到MainActivity,这个时候系统会在原始栈结构中生成一个MainActivity实例,然后回退两次,注意,并没有退出,而是回到了SecondActivity,为什么呢?是因为从SecondActivity跳转到MainActivity的时候,我们的起点变成了SecondActivity实例所在的栈结构,这样一来,我们需要“回归”到这个栈结构



四种模式的应用场景:

1.singleTop适合接收通知启动的内容显示页面。例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很烦人的。聊天的对话窗口,再例如QQ接受到消息后弹出Activity,如果一次来10条消息,总不能一次弹10个Activity。 

2.singleTask适合作为程序入口点。例如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。之前打开过的页面,打开之前的页面就ok,不再新建。 singleTask:a界面购物,b界面确认订单,c界面付款,如果付款成功会跳到a,如果不付款则返回b,这时候重启a就会用到singleTask. 

3.singleInstance适合需要与程序分离开的页面。例如闹铃提醒,将闹铃提醒与闹铃设置分离。singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,在此启动,首先打开的是B。 

4.standard 标准的启动模式,也是默认的启动模式。



2.清晰地描述下onNewIntent和onConfigurationChanged这两个生命周期方法的场景

1.启动模式是singleTop栈顶复用的情况下:如果新Activity已经位于任务栈的栈顶,那么此Activity不会被重新创建,它的onNewIntent会被调用

2.启动模式是singleTask栈内复用的情况下:只要Activity在一个栈中存在,那么多次启动此Activity都不会重新创建实例,和singleTop一样,会调用onNewIntent.

3.启动模式是singleInstance单实例的模式下:只要Activity存在就会回调onNewIntent这个方法

因此如果Activity的启动模式是:singleTop, singleTask, singleInstance,在复用这些Acitivity时就会在调用onStart方法前调用onNewIntent方法

当一些系统配置发生改变时,但是不想重新创建activity,通过给activity设定configChanges属性,onConfigurationChanged就会调用。





猜你喜欢

转载自blog.csdn.net/qq_33453910/article/details/78698360