Android LaunchMode案例篇

首先感谢小伙伴的关注,然后祝愿广大的情侣们节日快乐!
在开发中有时会遇到这样的场景,用户点击注册,第一步,第二步,完成注册跳转到登录界面,不需要用户一步一步的返回到登录界面。这是怎么实现的呢?
案例:有四个界面 A,B,C,D 从A跳转到B,B跳转到C,C跳转到D,D完成注册跳转到A,点击返回键退出程序。具体过程来看下图:

lm1

这里提供了三种常见的解决方案。

方案一

1.清单文件(AndroidManifest.xml)文件设置A的启动模式

        <activity android:name=".A"
            android:launchMode="singleTask">

2.在D文件:

 startActivity(new Intent(D.this, A.class));

方案二

使用 Intent 标志,在D文件:

        Intent intent =new Intent(D.this, A.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        startActivity(intent);
        finish();

方案三

方案三相对于前面两种方案稍微麻烦一些,但更利于我们理解栈的原理。

1.新建ActivityManager类:

public class ActivityManager {

    private static Stack<Activity> mStack;
    private static ActivityManager mActivityManager;
    public static ActivityManager getActivityManager() {
        if (mActivityManager == null) {
            mActivityManager = new ActivityManager();
        }
        return mActivityManager;
    }
    //将当前Activity推入栈中
    public void pushActivity(Activity activity) {
        if (mStack == null) {
            mStack = new Stack<Activity>();
        }
        mStack.add(activity);
    }
    //退出栈顶Activity
    public void popActivity(Activity activity) {
        if (activity != null) {
            //在从自定义集合中取出当前Activity时,也进行了Activity的关闭操作
            activity.finish();
            mStack.remove(activity);
            activity = null;
        }
    }
    //获得当前栈顶Activity
    public Activity currentActivity() {
        Activity activity = null;
        if (!mStack.empty())
            activity = mStack.lastElement();
        return activity;
    }
    //退出栈中 cls以前的所有Activity
    public void popAllActivityExceptOne(Class cls) {
        while (true) {
            Activity activity = currentActivity();
            if (activity == null) {
                break;
            }
            if (activity.getClass().equals(cls)) {
                break;
            }
            popActivity(activity);
        }
    }
    //获取栈
    public Stack<Activity> getStackActivity() {
        if (mStack != null) {
            return mStack;
        }
        return new Stack<Activity>();
    }
}

2.MyApplication文件(记得在清单文件中声明)

public class MyApplication extends Application {

    public static ActivityManager activityManager = null;

    @Override
    public void onCreate() {
        super.onCreate();
        activityManager = ActivityManager.getActivityManager();
    }

    //获取实例
    public ActivityManager getActivityManager() {
        return activityManager;
    }
}

3.BaseActivity文件(公共Activity)

public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ((MyApplication)getApplication()).getActivityManager().pushActivity(this);
    }

    @Override
    public void onBackPressed() {
        super.onBackPressed();
      ((MyApplication)getApplication()).getActivityManager().popActivity(this);
    }
}

4.D文件

 startActivity(new Intent(D.this, A.class));
        ((MyApplication) getApplication()).getActivityManager().popAllActivityExceptOne(A.class);

LaunchMode

launchMode 属性指明了 activity 启动 task 的方式。 launchMode 属性可设为四种启动模式:

  • standard
  • singleTop
  • singleTask
  • singleInstance

standard

默认值。系统在启动 activity 的 task 中创建一个新的 activity 实例,并把 intent 传送路径指向它。该 activity 可以被实例化多次,各个实例可以属于不同的 task,一个 task 中也可以存在多个实例。

singleTop

如果 activity 已经存在一个实例并位于当前 task 的栈顶,则系统会调用已有实例的onNewIntent()方法把 intent 传递给已有实例,而不是创建一个新的 activity 实例。activity 可以被实例化多次,各个实例可以属于不同的 task,一个 task 中可以存在多个实例(但仅当 back stack 顶的 activity 实例不是该 activity 的)。

比如,假定 task 的 back stack 中包含了根 activity A 和 activities B、C、D(顺序是 A-B-C-D;D 在栈顶)。
这时过来一个启动 D 的 intent。如果 D 的启动模式是默认的”standard”,则会启动一个新的实例,栈内容变为 A-B-C-D-D。
但是,如果 D 的启动模式是”singleTop”,则已有的 D 实例会通过onNewIntent():接收这个 intent,因为该实例位于栈顶——栈中内容仍然维持 A-B-C-D 不变。当然,如果 intent 是要启动 B 的,则 B 的一个新实例还是会加入栈中,即使 B 的启动模式是”singleTop”也是如此。

singleTask

系统将创建一个新的 task,并把 activity 实例作为根放入其中。但是,如果 activity 已经在其它 task 中存在实例,则系统会通过调用其实例的onNewIntent() 方法把 intent 传给已有实例,而不是再创建一个新实例。 此 activity 同一时刻只能存在一个实例。

也可以这么来理解, 如果 task 中不存在 activity 实例,则新建实例并压入栈顶;如果存在 activity 实例并位于栈顶,则复用;如果存在 activity 实例并不位于栈顶,则清空 task 中 activity 实例之上的元素,使 activity 实例位于栈顶。

singleInstance

除了系统不会把其它 activity 放入当前实例所在的 task 之外,其它均与”singleTask”相同。activity 总是它所在 task 的唯一成员;它所启动的任何 activity 都会放入其它 task 中。

举个例子:假定 task 的 back stack 中包含了根 activity A 和 activities B、C、D(顺序是 A-B-C-D;D 在栈顶),并且 activities B 的启动默认设置为 singleInstance ,那么他们的出栈顺序为 D-C-A-B 。

Intent几种常见的flags

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

注意:如果 activity 同时设置了 launchMode 和 flags ,那么 flags 优先级大于 launchMode 。

我们这里需要先来了解一下 属性中的 taskAffinity:

taskAffinity

affinity 表示 activity 预期所处的 task 。 缺省情况下,同一个应用中的所有 activity 都拥有同一个 affinity 值。 因此,同一个应用中的所有 activity 默认都期望位于同一个 task 中。 不过,你可以修改 activity 默认的 affinity 值。 不同应用中的 activity 可以共享同一个 affinity 值,同一个应用中的 activity 也可以赋予不同的 task affinity 值。

你可以用 元素的 taskAffinity 属性修改 activity 的 affinity,taskAffinity 属性是一个字符串值,必须与 元素定义的包名称保证唯一性,因为系统把这个包名称用于标识应用的默认 task affinity 值。

  1. 根据affinity重新为 activity 选择宿主task(与allowTaskReparenting属性配合工作)。
  2. 启动一个 activity 过程中 Intent 使用了 FLAG_ACTIVITY_NEW_TASK 标记,根据 affinity 查找或创建一个新的具有对应affinity的task。

默认情况下,一个应用内的所有 activity 都具有相同的 affinity,都是从 application 继承而来,而 application 默认的 affinity 是 manifest 文件中的包名。我们也可以在 manifest 文件设置 taskAffinity 属性值:

    package="com.github.ws.launchmodedemo">

    <application
        android:taskAffinity="com.grasp.demo"

也可以单独为某个 activity 设置 taskAffinity 属性值。

FLAG_ACTIVITY_NEW_TASK

当Intent对象包含这个标记时,系统会寻找或创建一个新的 task 来放置目标 activity ,寻找时依据目标 activity 的taskAffinity 属性进行匹配,如果找到一个 task 的 taskAffinity 与之相同,就将目标 activity 压入此 task 中,如果查找无果,则创建一个新的 task,并将该 task 的 taskaffinity 设置为目标 activity 的 taskactivity,将目标 activity 放置于此 task。注意,如果同一个应用中 activity 的 taskaffinity 都使用默认值或都设置相同值时,应用内的 activity 之间的跳转使用这个标记是没有意义的,因为当前应用 task 就是目标 activity 最好的宿主。我们来看看下面的这个例子:
lm2

录制工具不是很清晰,有些模糊了。
从上图当中很明显的看出,程序A中有(a,b界面),程序B中也有(a,b界面)。在程序A的b界面跳转到程序B的b界面:

        Intent intent=new Intent("com.github.ws.flagdemo.B");
        startActivity(intent);

按Home键回到主屏,在主选单中启动程序B;按Home键回到主屏,在主选单中启动程序A。

我们发现在从程序A跳转到程序B的b之后,b实例好像是嵌入到了程序A中,但是不影响程序B的正常运行。

然后我们修改一下跳转的代码:

  Intent intent=new Intent("com.github.ws.flagdemo.B");
  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  startActivity(intent);

lm4

我们看到差别了吧。当我们再次启动程序B时,并没有显示程序B的a界面,而是显示了b界面,当我们程序B返回到a界面,再次打开程序A,显示的是A的b界面。

由此可见, FLAG_ACTIVITY_NEW_TASK应该这样去理解:根据 activity affinity 判断是否需要创建新的Task,然后再创建新的Activity实例放进去。

FLAG_ACTIVITY_CLEAR_TOP

FLAG_ACTIVITY_CLEAR_TOP表示启动的 activity 会将 task 中位于其上的 activity 都强制出栈,使其自身位于栈顶。与Activity启动模式中的 singleTask 相同。

FLAG_ACTIVITY_SINGLE_TOP

当task中存在目标Activity实例并且位于栈的顶端时,不再创建一个新的,直接利用这个实例。与Activity启动模式中的singleTop效果相同。

由于篇幅原因,文章到这里就结束了,希望看了上文,对你有所帮助。

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值