Andoid Activity四大启动模式

众所周知当我们多次启动同一个Activity时,系统会创建多个实例,并把它们按照先进后出的原则一一放入任务栈中,当我们按back键时,就会有一个activity从任务栈顶移除,重复下去,直到任务栈为空,系统就会回收这个任务栈。但是这样以来,系统多次启动同一个Activity时就会重复创建多个实例,这种做法显然不合理,为了能够优化这个问题,Android提供四种启动模式来修改系统这一默认行为。

Activity的四种启动模式:

standard、singleTop、singleTask、singleInstance

接下来,我们一边讲理论一边结合案例来全面学习这四种启动模式。
为了方便打印,定义一个BaseActivity,在其onCreate方法和onNewIntent方法中打印出当前Activity的日志信息,主要包括所属的task,当前类的hashcode,以及taskAffinity的值。之后我们进行测试的Activity都直接继承该BaseActivity

public class BaseActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.i("owen", "onCreate method " + getClass().getSimpleName() + " taskId: " + getTaskId() + " hasCode: " + this.hashCode());
        dumpTaskAffinity();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Log.i("owen", "onCreate method " + getClass().getSimpleName() + " taskId: " + getTaskId() + " hasCode: " + this.hashCode());
        dumpTaskAffinity();
    }

    protected void dumpTaskAffinity() {
        try {
            ActivityInfo info = this.getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
            Log.i("owen", "taskAffinity " + info.taskAffinity);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Standard-默认模式

这个模式是默认的启动模式,即标准模式,在不指定启动模式的前提下,系统默认使用该模式启动Activity,每次启动一个Activity都会重写创建一个新的实例,不管这个实例存不存在,这种模式下,谁启动了该模式的Activity,该Activity就属于启动它的Activity的任务栈中。这个Activity它的onCreate(),onStart(),onResume()方法都会被调用。

Manifest 中的配置形式:
<activity android:name=".standard.StandardActivity" android:launchMode="standard" >
使用案例:

对于standard模式,android:launchMode可以不进行声明,因为默认就是standard。
StandardActivity 的代码如下,入口MainActivity启动会直接启动该Activity,这个Activity中又有一个按钮启动StandardActivity。

public class ActivityStandard extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_standard);

        TextView jumpButton = (TextView) findViewById(R.id.standard);
        jumpButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent =  new Intent(ActivityStandard.this, ActivityStandard.class);
                startActivity(intent);
            }
        });
    }
}

我们首先进入StandardActivity,进入后再点击进入Standard的按钮,再按四次返回键不断返回。

打印日志如下:

02-08 11:11:58.192 11356-11356/com.ownchan.activitystartmodel I/owen: onCreate method MainActivity taskId: 358 hasCode: 531149574
02-08 11:11:58.193 11356-11356/com.ownchan.activitystartmodel I/owen: taskAffinitycom.ownchan.activitystartmodel
02-08 11:11:58.217 11356-11356/com.ownchan.activitystartmodel I/owen: onCreate method ActivityStandard taskId: 358 hasCode: 663550354
02-08 11:11:58.218 11356-11356/com.ownchan.activitystartmodel I/owen: taskAffinitycom.ownchan.activitystartmodel
02-08 11:12:04.402 11356-11356/com.ownchan.activitystartmodel I/owen: onCreate method ActivityStandard taskId: 358 hasCode: 247765416
02-08 11:12:04.402 11356-11356/com.ownchan.activitystartmodel I/owen: taskAffinitycom.ownchan.activitystartmodel
02-08 11:12:05.026 11356-11356/com.ownchan.activitystartmodel I/owen: onCreate method ActivityStandard taskId: 358 hasCode: 1020873879
02-08 11:12:05.027 11356-11356/com.ownchan.activitystartmodel I/owen: taskAffinitycom.ownchan.activitystartmodel
02-08 11:12:08.113 11356-11356/com.ownchan.activitystartmodel I/owen: onCreate method ActivityStandard taskId: 358 hasCode: 522160210
02-08 11:12:08.113 11356-11356/com.ownchan.activitystartmodel I/owen: taskAffinitycom.ownchan.activitystartmodel
02-08 11:12:11.323 11356-11356/com.ownchan.activitystartmodel I/owen: onCreate method ActivityStandard taskId: 358 hasCode: 213766985
02-08 11:12:11.324 11356-11356/com.ownchan.activitystartmodel I/owen: taskAffinitycom.ownchan.activitystartmodel

可以看到日志输出了四次StandardActivity的和一次MainActivity的,从MainActivity进入StandardActivity一次,后来我们又按了三次按钮,总共四次StandardActivity的日志,并且所属的任务栈的id都是358,这也验证了谁启动了该模式的Activity,该Activity就属于启动它的Activity的任务栈中这句话,因为启动StandardActivity的是MainActivity,而MainActivity的taskId是358,因此启动的StandardActivity也应该属于id为358的这个task,后续的3个StandardActivity是被StandardActivity这个对象启动的,因此也应该还是358,所以taskId都是2087。并且每一个Activity的hashcode都是不一样的,说明他们是不同的实例,即“每次启动一个Activity都会重写创建一个新的实例”

singleTop-栈顶复用模式

这个模式下,如果新的activity已经位于栈顶,那么这个Activity不会被重写创建,同时它的onNewIntent方法会被调用,通过此方法的参数我们可以去除当前请求的信息。如果栈顶不存在该Activity的实例,则情况与standard模式相同。需要注意的是这个Activity它的onCreate(),onStart()方法不会被调用,因为它并没有发生改变。

配置形式:
<activity android:name=".singletop.SingleTopActivity" android:launchMode="singleTop">
使用案例:
public class SingleTopActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_single_top);
        TextView singleTop = (TextView) findViewById(R.id.single_top);
        TextView other = (TextView) findViewById(R.id.single_other);

        singleTop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(SingleTopActivity.this, SingleTopActivity.class);
                startActivity(intent);
            }
        });

        other.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(SingleTopActivity.this, OtherTopActivity.class);
                startActivity(intent);
            }
        });
    }
}

public class OtherTopActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_other_top);
        TextView jump = (TextView) findViewById(R.id.jump);
        jump.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(OtherTopActivity.this, SingleTopActivity.class);
                startActivity(intent);
            }
        });
    }
}
输出日志:
02-08 15:07:57.576 7635-7635/? I/owen: onCreate method MainActivity taskId: 388 hasCode: 531149574
02-08 15:07:57.576 7635-7635/? I/owen: taskAffinity com.ownchan.activitystartmodel
02-08 15:07:57.607 7635-7635/? I/owen: onCreate method SingleTopActivity taskId: 388 hasCode: 985065435
02-08 15:07:57.607 7635-7635/? I/owen: taskAffinity com.ownchan.activitystartmodel
02-08 15:08:48.270 7635-7635/com.ownchan.activitystartmodel I/owen: onNewIntent method SingleTopActivity taskId: 388 hasCode: 985065435
02-08 15:08:48.271 7635-7635/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel
02-08 15:09:07.616 7635-7635/com.ownchan.activitystartmodel I/owen: onNewIntent method SingleTopActivity taskId: 388 hasCode: 985065435
02-08 15:09:07.617 7635-7635/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel
02-08 15:09:28.372 7635-7635/com.ownchan.activitystartmodel I/owen: onNewIntent method SingleTopActivity taskId: 388 hasCode: 985065435
02-08 15:09:28.373 7635-7635/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel
   我们看到,除了第一次进入SingleTopActivity这个Activity时,输出的是onCreate方法中的日志,后续的都是调用了onNewIntent方法,并没有调用onCreate方法,并且四个日志的hashcode都是一样的,说明栈中只有一个实例。这是因为第一次进入的时候,栈中没有该实例,则创建,后续的三次发现栈顶有这个实例,则直接复用,并且调用onNewIntent方法。那么假设栈中有该实例,但是该实例不在栈顶情况又如何呢? 
   我们先从MainActivity中进入到SingleTopActivity,然后再跳转到OtherActivity中,再从OtherActivity中跳回SingleTopActivity,再从SingleTopActivity跳到SingleTopActivity中,看看整个过程的日志。
02-08 15:23:43.676 17325-17325/com.ownchan.activitystartmodel I/owen: onCreate method MainActivity taskId: 392 hasCode: 531149574
02-08 15:23:43.676 17325-17325/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel
02-08 15:23:43.807 17325-17325/com.ownchan.activitystartmodel I/owen: onCreate method SingleTopActivity taskId: 392 hasCode: 686269048
02-08 15:23:43.808 17325-17325/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel
02-08 15:23:53.352 17325-17325/com.ownchan.activitystartmodel I/owen: onCreate method OtherTopActivity taskId: 392 hasCode: 915779052
02-08 15:23:53.353 17325-17325/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel
02-08 15:24:02.005 17325-17325/com.ownchan.activitystartmodel I/owen: onCreate method SingleTopActivity taskId: 392 hasCode: 129351083
02-08 15:24:02.006 17325-17325/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel
02-08 15:24:10.284 17325-17325/com.ownchan.activitystartmodel I/owen: onNewIntent method SingleTopActivity taskId: 392 hasCode: 129351083
02-08 15:24:10.285 17325-17325/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel

我们看到从MainActivity进入到SingleTopActivity时,新建了一个SingleTopActivity对象,并且task id与MainActivity是一样的,然后从SingleTopActivity跳到OtherActivity时,新建了一个OtherActivity,此时task中存在三个Activity,从栈底到栈顶依次是MainActivity,SingleTopActivity,OtherActivity,此时如果再跳到SingleTopActivity,即使栈中已经有SingleTopActivity实例了,但是依然会创建一个新的SingleTopActivity实例,这一点从上面的日志的hashCode可以看出,此时栈顶是SingleTopActivity,如果再跳到SingleTopActivity,就会复用栈顶的SingleTopActivity,即会调用SingleTopActivity的onNewIntent方法。这就是上述日志的全过程。

singleTop模式分3种情况:

当前栈中已有该Activity的实例并且该实例位于栈顶时,不会新建实例,而是复用栈顶的实例,并且会将Intent对象传入,回调onNewIntent方法
当前栈中已有该Activity的实例但是该实例不在栈顶时,其行为和standard启动模式一样,依然会创建一个新的实例
当前栈中不存在该Activity的实例时,其行为同standard启动模式

standard和singleTop启动模式都是在原任务栈中新建Activity实例,不会启动新的Task,即使你指定了taskAffinity属性。
那么什么是taskAffinity属性呢,可以简单的理解为任务相关性。

这个参数标识了一个Activity所需任务栈的名字,默认情况下,所有Activity所需的任务栈的名字为应用的包名
我们可以单独指定每一个Activity的taskAffinity属性覆盖默认值
一个任务的affinity决定于这个任务的根activity(root activity)的taskAffinity
在概念上,具有相同的affinity的activity(即设置了相同taskAffinity属性的activity)属于同一个任务
为一个activity的taskAffinity设置一个空字符串,表明这个activity不属于任何task
很重要的一点taskAffinity属性不对standard和singleTop模式有任何影响,即时你指定了该属性为其他不同的值,这两种启动模式下不会创建新的task(如果不指定即默认值,即包名)

singleTask-栈内复用模式

   这个模式十分复杂,有各式各样的组合。在这个模式下,如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,并且会回调该实例的onNewIntent方法。其实这个过程还存在一个任务栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,这个任务栈就是通过taskAffinity属性指定。如果这个任务栈不存在,则会创建这个任务栈。 

配置形式:

<activity android:name=".singleTask.SingleTaskActivity" android:launchMode="singleTask" >
public class SingleTaskActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_single_top);
        TextView singleTop = (TextView) findViewById(R.id.single_top);
        TextView other = (TextView) findViewById(R.id.single_other);

        singleTop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(SingleTaskActivity.this, SingleTaskActivity.class);
                startActivity(intent);
            }
        });

        other.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(SingleTaskActivity.this, OtherTaskActivity.class);
                startActivity(intent);
            }
        });
    }
}

public class OtherTaskActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_other_top);
        TextView jump = (TextView) findViewById(R.id.jump);
        jump.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(OtherTaskActivity.this, SingleTaskActivity.class);
                startActivity(intent);
            }
        });
    }
}

现在我们先不指定任何taskAffinity属性,对它做类似singleTop的操作,即从入口MainActivity进入SingleTaskActivity,然后跳到OtherActivity,再跳回到SingleTaskActivity。看看整个过程的日志。

02-08 15:56:07.446 4787-4787/com.ownchan.activitystartmodel I/owen: onCreate method MainActivity taskId: 397 hasCode: 531149574
02-08 15:56:07.446 4787-4787/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel
02-08 15:56:07.568 4787-4787/com.ownchan.activitystartmodel I/owen: onCreate method SingleTaskActivity taskId: 397 hasCode: 985065435
02-08 15:56:07.568 4787-4787/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel
02-08 15:56:22.997 4787-4787/com.ownchan.activitystartmodel I/owen: onNewIntent method SingleTaskActivity taskId: 397 hasCode: 985065435
02-08 15:56:22.997 4787-4787/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel
02-08 15:56:28.876 4787-4787/com.ownchan.activitystartmodel I/owen: onCreate method OtherTaskActivity taskId: 397 hasCode: 62449823
02-08 15:56:28.876 4787-4787/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel
02-08 15:56:32.439 4787-4787/com.ownchan.activitystartmodel I/owen: onNewIntent method SingleTaskActivity taskId: 397 hasCode: 985065435
02-08 15:56:32.439 4787-4787/com.ownchan.activitystartmodel I/owen: taskAffinity com.ownchan.activitystartmodel

当我们从MainActiviyty进入到SingleTaskActivity,再进入到OtherActivity后,此时栈中有3个Activity实例,并且SingleTaskActivity不在栈顶,而在OtherActivity跳到SingleTaskActivity时,并没有创建一个新的SingleTaskActivity,而是复用了该实例,并且回调了onNewIntent方法。并且原来的OtherActivity出栈了,具体见下面的信息,使用命令adb shell dumpsys activity activities可进行查看

如果不存在,则会创建一个新的Task,并创建新的Activity实例入栈到新创建的Task中去
如果存在,则得到该任务栈,查找该任务栈中是否存在该Activity实例
如果存在实例,则将它上面的Activity实例都出栈,然后回调启动的Activity实例的onNewIntent方法
如果不存在该实例,则新建Activity,并入栈

singleInstance-全局唯一模式

   该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性,即整个系统中就这么一个实例,由于栈内复用的特性,后续的请求均不会创建新的Activity实例,除非这个特殊的任务栈被销毁了。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。
<activity android:name=".singleinstance.SingleInstanceActivity" android:launchMode="singleInstance" >
public class ActivitySingleInstance extends BaseActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_singleinstance);
    }
}
配置属性如下:
<activity
    android:name=".ActivitySingleInstance"
    android:launchMode="singleInstance">

    <intent-filter>
        <action android:name="com.castiel.demo.singleinstance" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

使用下面的方式分别在两个应用中启动它

Intent intent = new Intent();
intent.setAction("com.castiel.demo.singleinstance");
startActivity(intent);

我们看到,第一个应用启动SingleInstanceActivity时,由于系统中不存在该实例,所以新建了一个Task,按home键后,使用另一个App进入该Activity,由于系统中已经存在了一个实例,不会再创建新的Task,直接复用该实例,并且回调onNewIntent方法。可以从他们的hashcode中可以看出这是同一个实例。因此我们可以理解为:SingleInstance模式启动的Activity在系统中具有全局唯一性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值