Android LaunchMode案例篇

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

Android LaunchMode案例篇_第1张图片

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

方案一

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

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

2.在D文件:

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

方案二

在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

  1. standard
  2. singleTop
  3. singleTask
  4. singleInstance

standard(标准模式)

系统默认的启动模式,启动一个activity时,不管这个栈中存不存在这个activity,系统都会为它新建一个activity实例,推入栈中。

singleTop(栈顶复用模式)

启动一个activity时,如果当前的activity在栈顶,那么系统就不会再去创建一个该Activity的实例,而是调用栈顶Activity的onNewIntent()方法。如果当前的activity不在栈顶,那么系统就会为它新建一个activity实例在栈顶,并且不会摧毁已经存在的activity。

singleTask(栈内复用模式)

方案二就是用的singleTask模式,如果在栈顶则调用onNewIntent()方法,如果不在栈顶,则会摧毁该Activity之上的所有栈中元素。

singleInstance(单实例模式)

该Activity会放入到新的栈中,并且该新栈中只会有该Activity,如果该Activity不处于激活状态,出栈的顺序为,先出原始栈中元素,再出新栈中元素。

Intent几种常见的flags

下面的内容可能比较繁琐,有点饶,希望有点耐心看下去。
上面我们讲到Activity的四种启动模式的时候,已经了解到一些关于task(栈,后文我以task呈现)的技术,我再向大家介绍一下。task是一个具有栈结构的容器,可以放置多个Activity实例。启动一个应用,系统就会为之创建一个task,来放置根Activity;默认情况下,一个Activity启动另一个Activity时,两个Activity是放置在同一个task中的,后者被压入前者所在的task栈,当用户按下后退键,后者从task被弹出,前者又显示在屏幕前,特别是启动其他应用中的Activity时,两个Activity对用户来说就好像是属于同一个应用;系统task和task之间是互相独立的,当我们运行一个应用时,按下Home键回到主屏,启动另一个应用,这个过程中,之前的task被转移到后台,新的task被转移到前台,其根Activity也会显示到屏幕前,过了一会之后,在此按下Home键回到主屏,再选择之前的应用,之前的task会被转移到前台,系统仍然保留着task内的所有Activity实例,而那个新的task会被转移到后台,如果这时用户再做后退等动作,就是针对该task内部进行操作了。我们在了解flags之前,来了解下affinity。

affinity

拥有相同affinity的多个Activity理论同属于一个task,task自身的affinity决定于根Activity的affinity值。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最好的宿主。我们来看看下面的这个例子:

录制工具不是很清晰,有些模糊了。
从上图当中很明显的看出,程序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);

Android LaunchMode案例篇_第2张图片

我们看到差别了吧。当我们再次启动程序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效果相同。

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

你可能感兴趣的:(android,task,launchMode,flags,Affinity)