Android开发之---Activity启动模式

  在Android开发中,启动一个新的activity我们可以使用startActivity或startActivityForResult,Android系统使用栈的方式来管理一个APP的页面显示与保存顺序,那么,在新启动一个activity时,栈的里面是每次都新增实例还是只有一个实例呢?这个与在清单文件注册一个activity使用的参数:launchMode有关。

  Acvitity启动模式有4种:

  1. 标准模式standard

  2. 栈顶复用模式singleTop

  3. 栈内复用模式singleTask

  4. 单实例模式singleInstance

  为了测试这几种模式,新建了一个测试工程:https://github.com/linjk/TestLaunchMode,可以下载代码进行测试。

  1. 新建测试项目:

  

  2. 新建一个类,用于管理activity栈和打印当前栈的activity名字,代码如下:

package cn.linjk.testlaunchmode.utils;

import android.app.Activity;
import android.util.Log;

import java.util.Stack;

/**
 * Created by LinJK on 05/12/2016.
 *
 * <p>Class to manage activity stack, base activity
 * can use it.</p>
 *
 * @version 1.0
 */

public class ActivityStackManager {

    /**
     * Single Instance
     * @return
     */
    public static ActivityStackManager getINSTANCE() {
        if (null == INSTANCE) {
            INSTANCE = new ActivityStackManager();
        }
        return INSTANCE;
    }

    //region Add and remove activity
    public void addActivity(Activity ac) {
        if (null == stack_activity) {
            stack_activity = new Stack<>();
        }
        stack_activity.add(ac);

        printAllActivityName();
    }

    public void removeActivity(Activity ac) {
        if (ac != null) {
            stack_activity.remove(ac);
        }
printAllActivityName();     } //endregion //region Print Activity Stack Info public void printAllActivityName() { for (int i = stack_activity.size()-1; i >= 0; i--) { Log.d(TAG, "[" + i + "]Activity Name: " + stack_activity.get(i).getClass().getName()); } } //endregion /// private ActivityStackManager() {} private static final String TAG = ActivityStackManager.class.getSimpleName(); private static ActivityStackManager INSTANCE; private static Stack<Activity> stack_activity; } 

  3. MainActivity作为第一个页面,这里再新建第二个activity:

  两个页面的布局xml文件分别如下:

  MainActivity:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="cn.linjk.testlaunchmode.activity.MainActivity">

    <Button
        android:id="@+id/btn_start_new_activity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="启动新的activity_Main"/>
</RelativeLayout>

     ActivitySecond:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <Button
        android:id="@+id/btn_start_new_activity"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="启动新的activity_Second"/>

</LinearLayout>

     MainActivity.java:

    

package cn.linjk.testlaunchmode.activity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import cn.linjk.testlaunchmode.base.activity.BaseActivity;
import cn.linjk.testlaunchmode.R;

public class MainActivity extends BaseActivity {

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

        btnStartNewActivity = (Button)findViewById(R.id.btn_start_new_activity);

        btnStartNewActivity.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View pView) {
                startActivity(new Intent(MainActivity.this, ActivitySecond.class));
            }
        });
    }

    //

    private Button btnStartNewActivity;

}

     ActivitySecond.java:

package cn.linjk.testlaunchmode.activity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import cn.linjk.testlaunchmode.R;
import cn.linjk.testlaunchmode.base.activity.BaseActivity;

/**
 * Created by LinJK on 05/12/2016.
 */

public class ActivitySecond extends BaseActivity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);

        btnStartNewActivity = (Button)findViewById(R.id.btn_start_new_activity);

        btnStartNewActivity.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View pView) {
                startActivity(new Intent(ActivitySecond.this, ActivitySecond.class));
            }
        });
    }

    //

    private Button btnStartNewActivity;
}

     在清单文件中的application节点,activity配置如下,都是默认的启动模式配置:

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".activity.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <activity android:name=".activity.ActivitySecond"/>
    </application>

  4. 启动模式测试:

    APP在启动后,未配置情况下,默认会分配一个任务栈,所有的actiivty默认都会在该栈启动与退出,这是通过任务共用性(Affinities)完成的,任务共用性    

      为这个运行一个或多个Activity的Task提供了一个独特的静态名称,默认的一个活动的任务共用性(Affinity)是实现了该Activity的.apk包的名字,可以在acitivty的onCreate启动后通过getTaskID获取其所在任务栈id,新启动一个页面后,新的页面会在启动它的任务栈打开,如果加了标志:FLAG_ACTIVITY_NEW_TASK,那个这两个activity就会在两个不同的任务栈里面。

    注意,只有actvitivy的上下文才可以启动activity,因为这才存在任务栈,而application级别的,如getApplicationContext().startActivity(new Intent(MainActivity.this, ActivitySecond.class));则无法启动,因为application的上下文并不会分配上下文,需要加新增任务栈标记位:FLAG_ACTIVITY_NEW_TASK。

  4.1 standard

    从第一个页面进入第二个页面后,在第二个页面再启动第二个页面,打印的日志如下:

    可以看到,默认的standard模式,每次都会新建实例来启动activity.

  4.2 singleTop

    配置第二个页面的启动模式:

<activity
            android:launchMode="singleTop"
            android:name=".activity.ActivitySecond"/>

     第一次进入第二个页面日志如下:

    再点击启动新页面:

    这是发现只打印多红色圈住的这一句日志,没调用onCreate() ?

    查看一下activity源码,发现这部分:

    

    原来,配置了singleTop模式后,会调用onNewIntent()重新加载页面,我们修改ActivitySecond代码,增加如下部分:

@Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        Log.d("ActivitySecond", "[Task ID]" + getTaskId());
        ActivityStackManager.getINSTANCE().printAllActivityName();

    }

     重复刚才操作,日志如下:

    可以发现,当栈顶是ActivitySecond,重复打开时只有一个ActivitySecond实例。注意,如果ActivitySecond不是在栈顶,新打开还是会新建的!

  4.3 singleTask

    与singleTop类似,就不重复测试了,大概说明一下差异:

    1. singleTop是在栈顶在复用,不在栈顶则新建,而singleTask是只要在任务栈内就复用,不会新建

    2. singleTask模式中,默认具有clearTop效果,如果当前栈状态ABCD, 此时新建B,会把B页面移到栈顶,并清除原来B以上的栈页面,此时变为AB。

    3. 使用singleTask,可以在清单文件加上taskAffinity属性,其值不能与包名一致,否则会无效,这样,后面以singleTask启动的activity,如果该属性一样,就会分配到同一个新的任务栈中。

    (

    附:如一个activity的taskAffnity与allowTasReparenting属性配合使用,那么该activity可以在两个app的任务栈共用,假设该activity叫C,现有两个应用A、B,名叫activity的C注册在B应用的清单文件中,配置如下:

<activity
            android:name=".activity.ActivitySecond">
            <intent-filter>
                <action android:name="android.intent.action.TESTLAUNCHMODE_SECOND_ACTIVITY"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </activity>

     则应用A中启动该activity可如下启动:

Intent intent = new Intent("android.intent.action.TESTLAUNCHMODE_SECOND_ACTIVITY"); 
startActivity(intent);

     此时,如果应用B打开C,会把C从应用A的任务栈中转移过来。

    )

  4.4 singleInstance

    singleTask的加强版本,以此模式启动的activtiy,系统会新建一个任务栈并把它独立存放,这个activity后面会被重复使用,直到系统把该任务栈kill掉。

转载于:https://www.cnblogs.com/linjk/p/6134264.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值