一、为什么需要启动模式
在Android开发中,我们都知道,在默认的情况下,如果我们启动的是同一个Activity的话,系统会创建多个实例并把它们一一放入任务栈中。当我们点击返回(back)键,这些Activity实例又将从任务栈中一一移除,遵循的原则是“后进先出”(先进后出)。
这里我们考虑一个问题,当我们多次启动同一个Activity,系统也会创建多个实例放入任务栈中,这样岂不是很耗费内存资源?为了解决这一问题,Android为Actiivty提供了启动模式。
Activity的启动模式有四种:standard、singleTop、singleTask和singleInstance。
二,启动模式分类
1. standard:标准模式
这种启动模式为标准模式,也是默认模式。每当我们启动一个Activity,系统就会相应的创建一个实例,不管这个实例是否已经存在。这种模式,一个栈中可以有多个实例,每个实例也都有自己的任务栈。而且是谁启动了此Activity,那么这个Activity就运行在启动它的Activity所在的栈中。
第一次跳转
第二次跳转
从结果可以看出,每一次都会重新创建新的实例,当选择返回键,会逐层按照先进后出的顺序将activity推出栈
2、singleTop:栈顶复用模式
这种启动模式下,如果要启动的Activity已经处于栈的顶部,那么此时系统不会创建新的实例,而是直接打开此页面,同时它的onNewIntent()方法会被执行,我们可以通过Intent进行传值,而且它的onCreate(),onStart()方法不会被调用,因为它并没有发生任何变化。但是如果没有在栈顶,那么依然会再次创建新的实例,这样就会出现重复页面存在的问题。
下面演示的是如果没有在栈顶,即中间跳转到了其他的页面,然后再跳转都自身,会发现系统重新初始化了实例,而不再是简单读取展示了
使用场景:
这种模式应用场景的话,假如一个新闻客户端,在通知栏收到了3条推送,点击每一条推送会打开新闻的详情页,如果为默认的启动模式的话,点击一次打开一个页面,会打开三个详情页,这肯定是不合理的。如果启动模式设置为singleTop,当点击第一条推送后,新闻详情页已经处于栈顶,当我们第二条和第三条推送的时候,只需要通过Intent传入相应的内容即可,并不会重新打开新的页面,这样就可以避免重复打开页面了
3、singleTask:站内复用模式
在这个模式下,如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,因为singleTask本身自带clearTop这种功能。并且会回调该实例的onNewIntent()方法。其实这个过程还存在一个任务栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,这个任务栈就是通过taskAffinity属性指定。如果这个任务栈不存在,则会创建这个任务栈。不设置taskAffinity属性的话,默认为应用的包名。
在复用的时候,首先会根据taskAffinity去找对应的任务栈:
1、如果不存在指定的任务栈,系统会新建对应的任务栈,并新建Activity实例压入栈中。
2、如果存在指定的任务栈,则会查找该任务栈中是否存在该Activity实例
a、如果不存在该实例,则会在该任务栈中新建Activity实例。
b、如果存在该实例,则会直接引用,并且回调该实例的onNewIntent()方法。并且任务栈中该实例之上的Activity会被全部销毁。
使用场景:
SingleTask这种启动模式最常使用的就是一个APP的首页,因为一般为一个APP的第一个页面,且长时间保留在栈中,所以最适合设置singleTask启动模式来复用。
4、singleInstance:单实例模式
单实例模式,顾名思义,只有一个实例。该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性,即整个系统中就这么一个实例,由于栈内复用的特性,后续的请求均不会创建新的Activity实例,除非这个特殊的任务栈被销毁了。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。也就是创建了单独的任务栈,当多个不同的任务栈存在时,选择返回键,就会直接回到上一个任务栈而且展示上一个任务栈顶部的页面,但是顶部页面之前的其他页面再次选择返回键是无法展示的。
启动该模式Activity的时候,会查找系统中是否存在:
1、不存在,首先会新建一个任务栈,其次创建该Activity实例。
2、存在,则会直接引用该实例,并且回调onNewIntent()方法。
特殊情况:该任务栈或该实例被销毁,系统会重新创建。
使用场景:
很常见的是,电话拨号盘页面,通过自己的应用或者其他应用打开拨打电话页面 ,只要系统的栈中存在该实例,那么就会直接调用。
实际开发中出现的问题 (下面的解说请自行忽略,发现在系统5.0以下是有问题的,无法达到目的,系统7.0下是好的。最后还是使用比较笨拙的方式实现了兼容,在文章底部有具体实现)
那么项目想做如下的实现:
支持自动登录,如果用户是第一次登录,就展示登录页面,如果登录成功,下一次杀掉进程后直接进入主页面,而不再展示登录页面。除非用户选择了登出,才会再次回到登录页面。大致的交互流程如下:
第一次登录:图一 ->登录成功 ->图二->图三选择登出->回到图一登录页
已经登录成功:图二->图三选择登出->回到图一登录页
页面结构实现思路
mainActivity 作为主页面,判断是否是第一次登录,如果是第一次登录就加载loginactivity,如果已经登录成功就加载主页面。那么这一过程中本来想设计的思路是这样的:
mainActivity 设置为singleTask类型,login和主页面都设置为singletask类型的,其他的默认是Standard类型的。这样设计的初衷是各个独立的主要页面保证只有一个存在,在用户选择登出的时候就可以将堆栈中mainactivity之上的所有activity都销毁掉了,但是事实证明上来就出现了问题。
问题表现 1:登录页面输入有效的地址等信息,此时选择home键,然后再次resume到前台,内容被清空了。结果又想换成singleinstance试试,一样不行。最后发现应用程序的main页面如果设置为这两种类型的,选择home键,系统就会调用destroy方法,将top页面销毁掉。
解决办法1: 最后只能修改成singleTop类型的。
问题表现2:上面home键的问题解决了,但是新的问题出现了,最简单的表现就是在login页面选择back按钮,竟然出现了空白页,这个空白页其实就是mainactivity,因为设置为了singletop,因此当其在栈顶,系统会又一次复用。
解决办法2: 想办法在login页面选择back键同时销毁掉mainactivity。因此在mainactivity启动login和主页面的时候都使用了方法startActivityForResult,这样在返回到mainactivity的时候就能接收到回调消息并且将自己finish掉。但是这里可以看到resultcode==4,这个是在login成功后也会返回到这里,不能简单finish mainactivity,要继续调用方法进入到主页面
问题表现3: 当用户登录成功后,杀掉进程,再次启动app,此时其实loginactivity是没有被压栈的。此时选择登出按钮后,返回到login页面,但是再次选择back按钮,又会出现了堆栈中的其他页面,原因就是这种情况下login之前没有被压栈,其上也没有其他的activity,而且mainactivity又是singletop类型,因此中间生成的activity无法被销毁。
解决办法3: 在用户选择登出的时候,为intent添加tag FLAG_ACTIVITY_CLEAR_TOP ,这个需要保证要回到的页面在堆栈中要存在,这里就是mainactivity,的确是存在的,然后调用这个方法,会将mainactivity之上的所有的activity都销毁掉,就可以简单达到这个目的了。当已经存在的activity实例处理新的intent时候,会调用onNewIntent()方法,这个需要执行的页面属性如果为singletop,那么这个要跳回的页面就不会被销毁,而只会销毁其上其他的历史页面。这个就很适合做登出功能。
FLAG_ACTIVITY_CLEAR_TOP
清除包含此Activity的Task中位于该Activity实例之上的其他Activity实例。这种行为的 launchMode 属性没有对应的值,只能通过代码设置。
单独使用的情况:ABCD 启动 B ,会销毁B和B以上的实例 变成 AB ,B 重新执行onCreate -> onStart
配合FLAG_ACTIVITY_SINGLE_TOP使用,则 B 不会销毁只销毁B以上实例,然后B 执行onNewIntent -> onStart
login页面登录成功后如下操作即可
Intent的Flag
我们在代码里面通过Intent启动Activity的时候可以添加flag,如 :
intent.addFlags(Intent.FLAG_ACTIVITY_XXX);
FLAG_ACTIVITY_BROUGHT_TO_FRONT
这个标志一般不是由程序代码设置的,如在launchMode中设置singleTask模式时系统帮你设定。
FLAG_ACTIVITY_CLEAR_TASK
此Activity将变成一个新Task中新的最底端的Activity,所有的之前此Activity实例和包含该实例的Task都会被关闭,这个标识仅仅和FLAG_ACTIVITY_NEW_TASK联合起来才能使用。
FLAG_ACTIVITY_NEW_TASK
与launchMode="singleTask"一样的效果。
FLAG_ACTIVITY_CLEAR_TOP
清除包含此Activity的Task中位于该Activity实例之上的其他Activity实例。这种行为的 launchMode 属性没有对应的值,只能通过代码设置。
单独使用的情况:ABCD 启动 B ,会销毁B和B以上的实例 变成 AB ,B 重新执行onCreate -> onStart
配合FLAG_ACTIVITY_SINGLE_TOP使用,则 B 不会销毁只销毁B以上实例,然后B 执行onNewIntent -> onStart
配合FLAG_ACTIVITY_NEW_TASK则是singleTask效果
FLAG_ACTIVITY_SINGLE_TOP
与launchMode="singleTop"一样的效果。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
设置了的话该Activity则不出现在最近使用的列表中。
FLAG_ACTIVITY_FORWARD_RESULT
如果A需要onActivityResult中获取返回结果,startActivityForResult B,而B只是过渡页,启动C之后就finish掉了,需要在 C 中setResult返回给A就可以用到这个标志。
A -> B -> XXXXX(无论多少个过渡页) 设置 FLAG_ACTIVITY_FORWARD_RESULT 来启动 C ,之后该XXX过渡页finish - > C ,那么C的结果返回给A
FLAG_ACTIVITY_NO_HISTORY
如果设置,新的Activity将不在历史stack中保留。用户一离开它,这个Activity就关闭了。
例如A启动B的时候,给B设置了FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY,那么:
A -> B -> C ,启动C 就算 B没有自行finish ,也会变为 AC
FLAG_ACTIVITY_NO_ANIMATION
启动的时候不执行动画。
FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY
当用户点击Home,从历史中选择该Activity,系统会自动加上这个Flag。
FLAG_ACTIVITY_NO_USER_ACTION
在onPause()之前会调用onUserLeaving( )方法,如果使用了该标识,说明目标Activity不和用户交互,所以也就不需要回调onUserLeaving( )方法。
FLAG_ACTIVITY_REORDER_TO_FRONT
如果设置这个标记,新启动的Activity将会被放到它所属task的最前面
例如,假如有一个Task包含4个Activity:A,B,C,D.如果D通过调用startActivity( )来启动B,如果使用了这个标记,B将会排在这个task的最上面,也即现在的顺序变成了A,C,D,B。
如果使用了FLAG_ACTIVITY_CLEAR_TOP,这个标记将会被忽略。
FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
如果设置该属性,并且这个Activity在一个新的Task中正在被启动或者被带到一个已经存在的Task的顶部,这时这个Task会被重置(即该Task中之前的Activity会被关闭),该Activity成为栈底第一个Activity。
FLAG_ACTIVITY_TASK_ON_HOME
把当前新启动的任务置于Home任务之上,也就是按back键从这个任务返回的时候会回到Home,即使这个不是他们最后看见的Activity。
注意这个标记必须和FLAG_ACTIVITY_NEW_TASK加上android:taskAffinity一起使用。
FLAG_DEBUG_LOG_RESOLUTION
用来调试,当设置这个标志的时候,在解析这个intent的时候,将会打出打印信息(queryIntent函数)。
FLAG_INCLUDE_STOPPED_PACKAGES 和 FLAG_DEBUG_LOG_RESOLUTION
从Android 3.1开始,给Intent定义了两个新的Flag,分别为FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES,用来控制Intent是否要对处于停止状态的App起作用,顾名思义:
FLAG_INCLUDE_STOPPED_PACKAGES:表示包含未启动的App
FLAG_EXCLUDE_STOPPED_PACKAGES:表示不包含未启动的App
值得注意的是,Android 3.1开始,系统向所有Intent的广播添加了FLAG_EXCLUDE_STOPPED_PACKAGES标志。这样做是为了防止广播无意或不必要地开启未启动App的后台服务。如果要强制调起未启动的App,后台服务或应用程序可以通过向广播Intent添加FLAG_INCLUDE_STOPPED_PACKAGES标志来唤醒。
FLAG_FROM_BACKGROUND
Intent不光可以在Acitivity里面start,还可以从service里面启动,这个参数就表示这个Intent是从后台服务发起的。
FLAG_GRANT_PERSISTABLE_URI_PERMISSION
区别于 FLAG_GRANT_READ_URI_PERMISSION 跟 FLAG_GRANT_WRITE_URI_PERMISSION, URI权限会持久存在即使重启,直到明确的用 revokeUriPermission(Uri, int) 撤销。 这个flag只提供可能持久授权。但是接收的应用必须调用ContentResolver的takePersistableUriPermission(Uri, int)方法实现 。
FLAG_GRANT_PERSISTABLE_URI_PERMISSION
Uri 权限授予任何原始授权URI前缀匹配的URI。
FLAG_GRANT_PERSISTABLE_URI_PERMISSION
结合FLAG_GRANT_READ_URI_PERMISSION 和 FLAG_GRANT_WRITE_URI_PERMISSION 使用。
Uri 权限授予任何原始授权URI前缀匹配的URI。如果没有这个标志则必须精确匹配Uri了。
FLAG_GRANT_READ_URI_PERMISSION 和 FLAG_GRANT_WRITE_URI_PERMISSION
临时访问读权限和写权限 。Intent的接受者将被授予 INTENT 数据 uri 或者 在ClipData 上的读/写权限。
FLAG_RECEIVER_FOREGROUND
当发送广播时,允许其接受者 在前台运行的拥有更高的优先级,更短的超时间隔。
FLAG_RECEIVER_NO_ABORT
如果是有序广播,不要允许接收者中断广播播。
FLAG_RECEIVER_REGISTERED_ONLY
设置之后就不能通过xml来注册监听这个广播了,必须动态注册。
很多毒病程序为了证保自己被止终后可以再次行运,都会在xml中册注一些系统广播,妄图利用这些系统高频广播来实现自动启。
比如在老版本的android系统中,毒病程序可以通过监听TIME_TICK来动启自己的service后台行运,做一些秘隐的作工,而且就算自己被kill失落了,也能很快重新动启。
而一旦这些系统广播加了flag FLAG_RECEIVER_REGISTERED_ONLY,这些毒病程序就没辙了。
例如系统的TIME_TICK广播,由AlarmManagerService发送,我们看源码可以看到
mTimeTickSender = PendingIntent.getBroadcast(context, 0,
new Intent(Intent.ACTION_TIME_TICK).addFlags(
Intent.FLAG_RECEIVER_REGISTERED_ONLY), 0);
这样就不能监听ACTION_TIME_TICK来自启动了。
FLAG_RECEIVER_REPLACE_PENDING
这个Flag 将会将之前的Intent 替代掉。加了这个Flag,在发送一系列的这样的Intent 之后,中间有些Intent 有可能在你还没有来得及处理的时候,就被替代掉了。
FLAG_ACTIVITY_NEW_DOCUMENT(原FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET)
可以跟FLAG_ACTIVITY_MULTIPLE_TASK结合使用,当只用自己的时候相当于Manifast中 android.R.attr.documentLaunchMode="intoExisting",当跟FLAG_ACTIVITY_MULTIPLE_TASK 结合使用相当于 Manifast中android.R.attr.documentLaunchMode="always"。
FLAG_ACTIVITY_RETAIN_IN_RECENTS
默认情况下通过FLAG_ACTIVITY_NEW_DOCUMENT启动的Activity在关闭之后,Task中的记录会相对应的删除。如果为了能够重新启动这个Activity你想保留它,就可以使用者个flag,最近的记录将会保留在接口中以便用户去重新启动。接受该Flag的Activity可以使用autoRemoveFromRecents去复写这个request或者调用Activity.finishAndRemoveTask( )方法。
FLAG_ACTIVITY_MULTIPLE_TASK
这个标识用来创建一个新的task栈,并且在里面启动新的activity(所有情况,不管系统中存在不存在该activity实例),经常和FLAG_ACTIVITY_NEW_DOCUMENT或者FLAG_ACTIVITY_NEW_TASK一起使用。这上面两种使用场景下,如果没有带上FLAG_ACTIVITY_MULTIPLE_TASK标识,他们都会使系统搜索存在的task栈,去寻找匹配intent的一个activity,如果没有找到就会去新建一个task栈;但是当和FLAG_ACTIVITY_MULTIPLE_TASK一起使用的时候,这两种场景都会跳过搜索这步操作无条件的创建一个新的task。和FLAG_ACTIVITY_NEW_TASK一起使用需要注意,尽量不要使用该组合除非你完成了自己的顶部应用启动器,他们的组合使用会禁用已经存在的task栈回到前台的功能。
taskAffinity 和 allowTaskReparenting
taskAffinity用于指定当前Activity所关联的Task,allowTaskReparenting用于配置是否允许该Activity可以更换从属Task,通常情况二者连在一起使用,用于实现把一个应用程序的Activity移到另一个应用程序的Task中。
allowTaskReparenting用来标记Activity能否从启动的Task移动到taskAffinity指定的Task,默认是继承至application中的allowTaskReparenting=false,如果为true,则表示可以更换;false表示不可以。
例如在A应用中启动了B应用的Activity,如果设置allowTaskReparenting=true,则Activity允许从A的Task移动到B的Task。但如果A被启动之后,Activity就会回到A的Task中。
比较保守的实现方式,简言之就是记录activity堆栈信息,然后实现跳转到登录页面和清除中间的activity。存在的问题在于当第一次安装app,用户直接选择home键,再次启动会kill掉主页面,也就是即便声明了singletop类型依然会被destroy掉!但是如果选择了back键销毁应用然后再次操作,就不会有这个问题了,非常奇怪!这里暂时做一下记录,后面有时间再细纠。
private void registerActivityListener() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle bundle) {
if (activity.getClass().equals(MainActivity.class) &&isActivityExisted(MainActivity.class)){
MainActivity mainActivity =findActivity(MainActivity.class);
// activity.finish();
finishAllActivity();
}
pushActivity(activity);
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
@Override
public void onActivityDestroyed(Activity activity) {
if (null==mActivitys||mActivitys.isEmpty()){
return;
}
if (mActivitys.contains(activity)){
//监听到 Activity销毁事件 将该Activity 从list中移除
popActivity(activity);
}
}
});
}
}
private void registerActivityLifecycleListener(){
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
@Override
public void onActivityStarted(Activity activity) {
Log.d("onActivityStarted", "" + activity.getClass());
activityCount +=1;
if (activityCount >0) {
isActive =true;
}
if (!isAlive){
isAlive =true;
}
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
activityCount -=1;
if (activityCount <=0) {
isActive =false;
}
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
/**
* @param activity 作用说明 :添加一个activity
*/
public void pushActivity(Activity activity) {
mActivitys.add(activity);
Log.d("activityList:size:+" ,"" +mActivitys.size());
}
/**
* @param activity 作用说明 :删除一个activity
*/
public void popActivity(Activity activity) {
mActivitys.remove(activity);
Log.d("activityList:size:-" ,"" +mActivitys.size());
}
/**
* get current Activity 获取当前Activity(栈中最后一个压入的)
*/
public static ActivitycurrentActivity() {
if (mActivitys ==null||mActivitys.isEmpty()) {
return null;
}
Activity activity =mActivitys.get(mActivitys.size()-1);
return activity;
}
/**
* 结束当前Activity(栈中最后一个压入的)
*/
public static void finishCurrentActivity() {
if (mActivitys ==null||mActivitys.isEmpty()) {
return;
}
Activity activity =mActivitys.get(mActivitys.size()-1);
finishActivity(activity);
}
/**
* 结束指定的Activity
*/
public static void finishActivity(Activity activity) {
if (mActivitys ==null||mActivitys.isEmpty()) {
return;
}
if (activity !=null) {
mActivitys.remove(activity);
activity.finish();
activity =null;
}
}
/**
* 结束指定类名的Activity
*/
public static void finishActivity(Class cls) {
if (mActivitys ==null||mActivitys.isEmpty()) {
return;
}
for (Activity activity :mActivitys) {
if (activity.getClass().equals(cls)) {
finishActivity(activity);
}
}
}
/**
* 按照指定类名找到activity
*
* @param cls
* @return
*/
public static T findActivity(Class cls) {
T targetActivity =null;
if (mActivitys !=null) {
for (Activity activity :mActivitys) {
if (activity.getClass().equals(cls)) {
targetActivity = (T)activity;
break;
}
}
}
return targetActivity;
}
public static boolean isActivityExisted(Class cls){
if (mActivitys !=null) {
for (Activity activity :mActivitys) {
if (activity.getClass().equals(cls)) {
return true;
}
}
}
return false;
}
/**
* @return 作用说明 :获取当前最顶部activity的实例
*/
public static ActivitygetTopActivity() {
Activity mBaseActivity =null;
synchronized (mActivitys) {
final int size =mActivitys.size() -1;
if (size <0) {
return null;
}
mBaseActivity =mActivitys.get(size);
}
return mBaseActivity;
}
/**
* @return 作用说明 :获取当前最顶部的acitivity 名字
*/
public StringgetTopActivityName() {
Activity mBaseActivity =null;
synchronized (mActivitys) {
final int size =mActivitys.size() -1;
if (size <0) {
return null;
}
mBaseActivity =mActivitys.get(size);
}
return mBaseActivity.getClass().getName();
}
/**
* 结束所有Activity
*/
public static void finishAllActivity() {
if (mActivitys ==null) {
return;
}
for (Activity activity :mActivitys) {
activity.finish();
}
mActivitys.clear();
}
public static void finishToMainActivity() {
if (mActivitys ==null ||mActivitys.isEmpty()) {
return;
}
Activity temp =null;//= mActivitys.get(mActivitys.size() - 1);
for (Activity activity :mActivitys) {
if (!activity.getClass().equals(MainActivity.class)) {
activity.finish();
}else{
temp = activity;
}
}
mActivitys.clear();
if (temp ==null){
return;
}
mActivitys.add(temp);
}
// public static Activity finishLatestActivity(){
// Activity temp = mActivitys.get(mActivitys.size() - 1);
// finishActivity(temp);
// }
/**
* 退出应用程序
*/
public static void appExit() {
try {
finishAllActivity();
}catch (Exception e) {
}
}
android:name=".uicommonframework.Application"
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
android:name=".uicommonframework.MainActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/WelcomeStyle1">
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
android:name=".uicommonframework.LauncherFullScreen"
android:theme="@style/WelcomeStyle2" />
android:name=".LoginActivity"
android:label="@string/title_activity_login"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.NoActionBar"
android:windowSoftInputMode="adjustPan|stateHidden" />
android:name="RootActivity"
android:launchMode="singleTop"
android:screenOrientation="portrait" />
//选择登出键退出到登录页面
private void returnToMainActivity() {
Application.finishToMainActivity();
Activity context = Application.getTopActivity();
Intent intent =new Intent(context, MainActivity.class);
intent.putExtra(MainActivity.INTENT_KEY_1, MainActivity.INTENT_KEY_1);
context.startActivity(intent);
}