首先感谢小伙伴的关注,然后祝愿广大的情侣们节日快乐!
在开发中有时会遇到这样的场景,用户点击注册,第一步,第二步,完成注册跳转到登录界面,不需要用户一步一步的返回到登录界面。这是怎么实现的呢?
案例:有四个界面 A,B,C,D 从A跳转到B,B跳转到C,C跳转到D,D完成注册跳转到A,点击返回键退出程序。具体过程来看下图:
这里提供了三种常见的解决方案。
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);
系统默认的启动模式,启动一个activity时,不管这个栈中存不存在这个activity,系统都会为它新建一个activity实例,推入栈中。
启动一个activity时,如果当前的activity在栈顶,那么系统就不会再去创建一个该Activity的实例,而是调用栈顶Activity的onNewIntent()方法。如果当前的activity不在栈顶,那么系统就会为它新建一个activity实例在栈顶,并且不会摧毁已经存在的activity。
方案二就是用的singleTask模式,如果在栈顶则调用onNewIntent()方法,如果不在栈顶,则会摧毁该Activity之上的所有栈中元素。
该Activity会放入到新的栈中,并且该新栈中只会有该Activity,如果该Activity不处于激活状态,出栈的顺序为,先出原始栈中元素,再出新栈中元素。
下面的内容可能比较繁琐,有点饶,希望有点耐心看下去。
上面我们讲到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在什么场合应用呢?
默认情况下,一个应用内的所有Activity都具有相同的affinity,都是从Application继承而来,而Application默认的affinity是manifest文件中的包名。我们也可以在manifest文件设置taskAffinity属性值:
package="com.github.ws.launchmodedemo">
<application
android:taskAffinity="com.grasp.demo"
也可以单独为某个Activity设置taskAffinity属性值。
当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);
我们看到差别了吧。当我们再次启动程序B时,并没有显示程序B的a界面,而是显示了b界面,当我们程序B返回到a界面,再次打开程序A,显示的是A的b界面。
由此可见, FLAG_ACTIVITY_NEW_TASK应该这样去理解:根据Activity Affinity判断是否需要创建新的Task,然后再创建新的Activity实例放进去。
FLAG_ACTIVITY_CLEAR_TOP表示启动的Activity会将Task中位于其上的Activity都强制出栈,使其自身位于栈顶。与Activity启动模式中的singleTask相同。
当task中存在目标Activity实例并且位于栈的顶端时,不再创建一个新的,直接利用这个实例。与Activity启动模式中的singleTop效果相同。
由于篇幅原因,文章到这里就结束了,希望看了上文,对你有所帮助。