本文出自 “阿敏其人” 简书博客,转载或引用请注明出处。
Android-Activity所应该了解的大概就这样。(上)
Android-Activity所应该了解的大概就这样。(中)
六、FLAG
我们知道,Android给Activity设定启动模式的方式有有两种。一种是在�manifest里面设置,另一种是在通过Intent的FLAG设置。
FLAG,在安卓里面是 标记位 的意思,Flag有很多种,下面我们记录主要使用的几种。
代码指定Flag(�使用全部FLAG),使得Activity启动模式可变,而且最后一次指定的Flag就是该Activity在任务栈中最终启动模式。
-
Intent.FLAG_ACTIVITY_NEW_TASK
注:Intent.FLAG_ACTIVITY_NEW_TASK单独使用并无任何效果,需要和taskAffinity一起配合着使用。- 当和taskAffinity一起配合着使用时:**Intent.FLAG_ACTIVITY_NEW_TASK + android:taskAffinity 指定非程序包名的任务栈名,�与清单文件里面指定 android:launchMode="singleInstance"效果一致 真心一致 ** (情况2示例)
- 这个Flag常用于在服务中启动Activity,因为服务没有Activity的任务栈,所以只能用一个新的任务栈来启动这个Activity创建实例。
Intent.FLAG_ACTIVITY_SINGLE_TOP
与清单文件里面指定 android:launchMode="singleTop"效果一致。真心一致。
可尝试作用采用FLAG_ACTIVITY_CLEAR_TOP退出整个程序(多activity)
- Intent.FLAG_ACTIVITY_CLEAR_TOP
Intent.FLAG_ACTIVITY_CLEAR_TOP与清单文件里面指定 android:launchMode="singleTask"效果类似,只是类似。因为当位于任务栈顶部的时候和android:launchMode="singleTask"产生很大的差别
设置次标记位的Activity,当它启动的时候,同一个任务栈里面位于他上方的Activity会全部出栈,上方全部清掉。
在一个打开时已经被标记为FLAG_ACTIVITY_CLEAR_TOP的界面(现正处于前台任务栈的栈顶),如果我们在此界面打开自己,并且一样标记为FLAG_ACTIVITY_CLEAR_TOP,那么这个时候会经历一次建新毁旧的过程,任务栈里面从结果的来说没什么变化,但是生命周期的着眼于过程的角度来却经历非常大的变化。(下面的情况5有示例)
mark下,这个标记位通常是和FLAG_ACTIVITY_NEW_TASK配合着使用的。在这种情况下被启动的Activty如果已经存在,那么系统就会调用它的onNewIntent方法。如果被调用的Activity使用默认的standrad模式,那么它连同它之上的Activity都要出栈,系统会创建新的实例并入栈置为栈顶。还未尝试
- Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
具有这个标记的Activity不会出现在Activity的历史列表中.
他等同于在xml里面设定 android:excludeFromRecents="true" 。
情景实测:
准备工作
我们弄一个程序,一共有4个界面,分别是
FirstActivity,这个作为启动页,接下的描述我们用A代替
SecondActivity 页面2,接下的描述我们用B代替
ThirdActivity 页面3 ,接下的描述我们用C代替
FourthActivity 页面4, 接下的描述我们用D代替
界面布局类似,每一个页面都有TextView写清楚当前是哪一个界面,并且每一个界面都会有三个Button,都是可以用于启动页面2,页面3和页面4.
就是上面那个样子。
准备工作至此完成
情景1:我们把所有按钮的打开的Activity都设置为Intent.FLAG_ACTIVITY_NEW_TASK,只有默认的启动页是Standard。
完整过程是:然后启动app,打开B,在B打开C,在C打开D,然后在D打开B。
这个过程我们分两步进行。
结论先上:单独给Activity的Intent设置Intent.FLAG_ACTIVITY_NEW_TASK没有作用。
如果说Intent.FLAG_ACTIVITY_NEW_TASK直接等同于在xml将来启动模式设置为singleTask的话,那么在我们 “在D打开B” 个操作中,应该此时任务栈就应该会清掉B以上的C和D,然后只剩下BA,但是结果是任务栈变成了BDCBA,B只是相当于一个普通的实例入栈。所以这个说法不成立。
注:网上有人直接说Intent.FLAG_ACTIVITY_NEW_TASK就相当于singleTask,这是错误的。
贴一下代码,其实要是这么设置Flag特别傻逼,不用这么每个都设置的,这里为了演示需要,大概怎么设置后面会提到的
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amqr.flagtest" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".FirstActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity"/>
<activity android:name=".ThirdActivity"/>
<activity android:name=".FourthActivity"/>
</application>
</manifest>
接下来是四份代码:
FirstActivity
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
findViewById(R.id.mBtOpen2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openSecond = new Intent(FirstActivity.this, SecondActivity.class);
openSecond.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openSecond);
}
});
findViewById(R.id.mBtOpen3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openThird = new Intent(FirstActivity.this, ThirdActivity.class);
openThird.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openThird);
}
});
findViewById(R.id.mBtOpen4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFourth = new Intent(FirstActivity.this, FourthActivity.class);
openFourth.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openFourth);
}
});
}
}
.
.
SecondActivity
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
findViewById(R.id.mBtOpen2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openSecond = new Intent(SecondActivity.this, SecondActivity.class);
openSecond.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openSecond);
}
});
findViewById(R.id.mBtOpen3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openThird = new Intent(SecondActivity.this,ThirdActivity.class);
openThird.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openThird);
}
});
findViewById(R.id.mBtOpen4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFourth = new Intent(SecondActivity.this,FourthActivity.class);
openFourth.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openFourth);
}
});
}
}
.
.
FourthActivity
public class FourthActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fourth);
findViewById(R.id.mBtOpen2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openSecond = new Intent(FourthActivity.this, SecondActivity.class);
openSecond.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openSecond);
}
});
findViewById(R.id.mBtOpen3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openThird = new Intent(FourthActivity.this, ThirdActivity.class);
openThird.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openThird);
}
});
findViewById(R.id.mBtOpen4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFourth = new Intent(FourthActivity.this, FourthActivity.class);
openFourth.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openFourth);
}
});
}
}
分两个步骤,
第一步:
启动app后A就启动,A打开B,B打开C,C打开D
这是我们来看一下终端的信息:
Running activities (most recent first):
TaskRecord{52a650dc #6 A=com.amqr.flagtest U=0 sz=4}
Run #3: ActivityRecord{529b46e4 u0 com.amqr.flagtest/.FourthActivity t6}
Run #2: ActivityRecord{52983354 u0 com.amqr.flagtest/.ThirdActivity t6}
Run #1: ActivityRecord{5295f8c8 u0 com.amqr.flagtest/.SecondActivity t6}
Run #0: ActivityRecord{52958028 u0 com.amqr.flagtest/.FirstActivity t6}
这是任务栈里面有4个Activity,然后里面分别是DCBA,D为栈顶。
第二步:
我们在D界面打开B
然后重新看一下终端的信息:
Running activities (most recent first):
TaskRecord{52a650dc #6 A=com.amqr.flagtest U=0 sz=5}
Run #4: ActivityRecord{52a3efe8 u0 com.amqr.flagtest/.SecondActivity t6}
Run #3: ActivityRecord{529b46e4 u0 com.amqr.flagtest/.FourthActivity t6}
Run #2: ActivityRecord{52983354 u0 com.amqr.flagtest/.ThirdActivity t6}
Run #1: ActivityRecord{5295f8c8 u0 com.amqr.flagtest/.SecondActivity t6}
Run #0: ActivityRecord{52958028 u0 com.amqr.flagtest/.FirstActivity t6}
我们发现,这是在D界面打开的这个B并没有把复用位于A上方的B并且把自己上方的CD清掉,而是像一个普通的任务栈入栈了。
现在任务栈里面有五个Activity,分别是BDCBA,栈顶是第二次打开的B。
证明结论:单独给Activity的Intent设置Intent.FLAG_ACTIVITY_NEW_TASK没有作用。
.
.
情景2
我们给A打开的B设置为Intent.FLAG_ACTIVITY_NEW_TASK,给B打开的C设置为Intent.FLAG_ACTIVITY_NEW_TASK,给C里面要打开的B和C设定为Intent.FLAG_ACTIVITY_NEW_TASK。
然后重点在这里,在清单文件里面给B和C都属性taskAffinity属性。
我们给B设置了 android:taskAffinity="com.amqr.second222"
给C设置了 android:taskAffinity="com.amqr.third333"
结论先上:**Intent.FLAG_ACTIVITY_NEW_TASK + android:taskAffinity 指定非程序包名的任务栈名,�与清单文件里面指定 android:launchMode="singleInstance"效果一致 **
完整操作:A打开B,B打开C,C再打开C(这里是打不开的),C打开B
附上代码:
manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amqr.flagtest" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".FirstActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity"
android:taskAffinity="com.amqr.second222"
/>
<activity android:name=".ThirdActivity"
android:taskAffinity="com.amqr.third333"
/>
<activity android:name=".FourthActivity"/>
</application>
</manifest>
接下来是代码
FirstActivity
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
findViewById(R.id.mBtOpen2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openSecond = new Intent(FirstActivity.this, SecondActivity.class);
openSecond.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openSecond);
}
});
findViewById(R.id.mBtOpen3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openThird = new Intent(FirstActivity.this, ThirdActivity.class);
startActivity(openThird);
}
});
findViewById(R.id.mBtOpen4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFourth = new Intent(FirstActivity.this, FourthActivity.class);
startActivity(openFourth);
}
});
}
}
.
.
SecondActivity
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
findViewById(R.id.mBtOpen2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openSecond = new Intent(SecondActivity.this, SecondActivity.class);
startActivity(openSecond);
}
});
findViewById(R.id.mBtOpen3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openThird = new Intent(SecondActivity.this, ThirdActivity.class);
openThird.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openThird);
}
});
findViewById(R.id.mBtOpen4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFourth = new Intent(SecondActivity.this, FourthActivity.class);
startActivity(openFourth);
}
});
}
}
.
.
ThirdActivity
public class ThirdActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third);
findViewById(R.id.mBtOpen2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openSecond = new Intent(ThirdActivity.this, SecondActivity.class);
openSecond.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openSecond);
}
});
findViewById(R.id.mBtOpen3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openThird = new Intent(ThirdActivity.this, ThirdActivity.class);
openThird.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openThird);
}
});
findViewById(R.id.mBtOpen4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFourth = new Intent(ThirdActivity.this, FourthActivity.class);
startActivity(openFourth);
}
});
}
}
操作分4步:
提醒:这里B和C被打开时都被指定 Intent.FLAG_ACTIVITY_NEW_TASK,而且各自都指定了 android:taskAffinity 名称为非程序包名的自定义任务栈名称
步骤1:A打开B,
Running activities (most recent first):
TaskRecord{52a6e690 #54 A=com.amqr.second222 U=0 sz=1}
Run #1: ActivityRecord{529b601c u0 com.amqr.flagtest/.SecondActivity t54}
TaskRecord{52928e18 #53 A=com.amqr.flagtest U=0 sz=1}
Run #0: ActivityRecord{5297ded0 u0 com.amqr.flagtest/.FirstActivity t53}
我们发现此时程序里面有2个任务栈,一个是程序自带的名字为包名的任务栈amqr.flagtest,一个是我们自定义名字的任务栈com.amqr.second222。
步骤2:B打开C
Running activities (most recent first):
TaskRecord{52b6a93c #55 A=com.amqr.third333 U=0 sz=1}
Run #2: ActivityRecord{52a8d5f8 u0 com.amqr.flagtest/.ThirdActivity t55}
TaskRecord{52a6e690 #54 A=com.amqr.second222 U=0 sz=1}
Run #1: ActivityRecord{529b601c u0 com.amqr.flagtest/.SecondActivity t54}
TaskRecord{52928e18 #53 A=com.amqr.flagtest U=0 sz=1}
Run #0: ActivityRecord{5297ded0 u0 com.amqr.flagtest/.FirstActivity t53}
此时有三个任务栈,其中自定义名字的任务栈com.amqr.third333是前台任务栈,其他两个都是后台任务栈。
步骤3:C打开C
我们发现,C没有办法打开C。所以任务栈里面没有什么变化
TaskRecord{52b6a93c #55 A=com.amqr.third333 U=0 sz=1}
Run #2: ActivityRecord{52a8d5f8 u0 com.amqr.flagtest/.ThirdActivity t55}
TaskRecord{52a6e690 #54 A=com.amqr.second222 U=0 sz=1}
Run #1: ActivityRecord{529b601c u0 com.amqr.flagtest/.SecondActivity t54}
TaskRecord{52928e18 #53 A=com.amqr.flagtest U=0 sz=1}
Run #0: ActivityRecord{5297ded0 u0 com.amqr.flagtest/.FirstActivity t53}
也就是说,在步骤二的时候我们的页面3,也就是ThirdActivity在这个地方已经是存放com.amqr.third333任务栈里面,而且com.amqr.third333是前台任务栈,在这种情况下我们进行步骤三,在页面3再打开一次页面3,这是没有任何作用的。复用顶部,没有新的实例产生,需要注意的是,这种方式产生的任务栈只能存放一个Activity。
步骤4:C打开B
Running activities (most recent first):
TaskRecord{52a6e690 #54 A=com.amqr.second222 U=0 sz=1}
Run #2: ActivityRecord{529b601c u0 com.amqr.flagtest/.SecondActivity t54}
TaskRecord{52b6a93c #55 A=com.amqr.third333 U=0 sz=1}
Run #1: ActivityRecord{52a8d5f8 u0 com.amqr.flagtest/.ThirdActivity t55}
TaskRecord{52928e18 #53 A=com.amqr.flagtest U=0 sz=1}
Run #0: ActivityRecord{5297ded0 u0 com.amqr.flagtest/.FirstActivity t53}
发现没有产生新的任务栈,也有添加任务Activity。但是前台任务栈改变了,几个任务栈的顺序和前后台关系产生了变化,com.amqr.second222由后台任务栈变成了前台任务栈。
每一个任务栈里面一个Activity,不增不减。
结论:Intent.FLAG_ACTIVITY_NEW_TASK + android:taskAffinity 指定非程序包名的任务栈名,�与清单文件里面指定 android:launchMode="singleInstance"效果一致
.
.
情景3:把所有的按钮点击事件的Intent的全部设定为Intent.FLAG_ACTIVITY_SINGLE_TOP。
没什么区别,这里就只贴一份FirstActivity的代码就好了,其他类似。反正改变的都是四份代码的里面的全部的点击事件的Intent的addFlag从Intent.FLAG_ACTIVITY_NEW_TASK改成Intent.FLAG_ACTIVITY_SINGLE_TOP。
结论先上: ** 设定Intent.FLAG_ACTIVITY_SINGLE_TOP与清单文件里面指定 android:launchMode="singleTop"效果一致。**
操作:A打开B,再打开(复用,无新实例),B打开C,再代开C(复用,无新实例),C打开D,D再开D(复用,无新实例)
FirstActivity代码
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
findViewById(R.id.mBtOpen2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openSecond = new Intent(FirstActivity.this, SecondActivity.class);
openSecond.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(openSecond);
}
});
findViewById(R.id.mBtOpen3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openThird = new Intent(FirstActivity.this, ThirdActivity.class);
openThird.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openThird);
}
});
findViewById(R.id.mBtOpen4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFourth = new Intent(FirstActivity.this, FourthActivity.class);
openFourth.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(openFourth);
}
});
}
}
这里我们就不贴终端了。一个gif说明一切。
从图中我们可以看到,(我们的B,C和D页面都是每次打开的都是Intent.FLAG_ACTIVITY_SINGLE_TOP的),当我们打开页面B,之后,想再次打次B,就没有新的实例产生了,而是复用了顶部的B。
C和D也是这种情况发生。图中也做了演示。
而且在最后,我们发现当我们从A打开B,从B打开C,再从C打开D,最后D打开B的时候,就又可以打开B了,又有一个B的实例产生并且位于栈顶了。所以这一切的一切都是跟xml设定launchMode为singleTop完全一致的。
证明结论:设定Intent.FLAG_ACTIVITY_SINGLE_TOP与清单文件里面指定 android:launchMode="singleTop"效果一致。
情景4 把A、B和C页面的按钮的打开页面的Intent都不设置addFlag,即ABC打开的页面都是standard,然后页面D的打开页面B的Intent的addFlag都设定的Intent.FLAG_ACTIVITY_CLEAR_TOP,其他两个按钮不设置Flag。
先上结论:
结论1:Intent.FLAG_ACTIVITY_CLEAR_TOP与清单文件里面指定 android:launchMode="singleTask"效果类似,只是类似。因为当初顶部的时候和android:launchMode="singleTask"产生很大的差别
(参考情景5我们就知道是区别在哪里了)
结论2: 代码指定Flag(不单指FLAG_ACTIVITY_CLEAR_TOP),使得Activity启动模式可变,而且最后一次指定的Flag就是该Activity在任务栈中最终启动模式。
这个上代码吧,因为后面还要描述一些事情。
FirstActivity
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
findViewById(R.id.mBtOpen2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openSecond = new Intent(FirstActivity.this, SecondActivity.class);
//openSecond.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(openSecond);
}
});
findViewById(R.id.mBtOpen3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openThird = new Intent(FirstActivity.this, ThirdActivity.class);
startActivity(openThird);
}
});
findViewById(R.id.mBtOpen4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFourth = new Intent(FirstActivity.this, FourthActivity.class);
startActivity(openFourth);
}
});
}
}
.
.
SecondActivity
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
findViewById(R.id.mBtOpen2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openSecond = new Intent(SecondActivity.this, SecondActivity.class);
startActivity(openSecond);
}
});
findViewById(R.id.mBtOpen3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openThird = new Intent(SecondActivity.this, ThirdActivity.class);
startActivity(openThird);
}
});
findViewById(R.id.mBtOpen4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFourth = new Intent(SecondActivity.this, FourthActivity.class);
startActivity(openFourth);
}
});
}
}
.
.
ThirdActivity
public class ThirdActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_third);
findViewById(R.id.mBtOpen2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openSecond = new Intent(ThirdActivity.this, SecondActivity.class);
startActivity(openSecond);
}
});
findViewById(R.id.mBtOpen3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openThird = new Intent(ThirdActivity.this, ThirdActivity.class);
startActivity(openThird);
}
});
findViewById(R.id.mBtOpen4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFourth = new Intent(ThirdActivity.this, FourthActivity.class);
startActivity(openFourth);
}
});
}
}
.
.
FourthActivity
public class FourthActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fourth);
findViewById(R.id.mBtOpen2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openSecond = new Intent(FourthActivity.this, SecondActivity.class);
openSecond.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(openSecond);
}
});
findViewById(R.id.mBtOpen3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openThird = new Intent(FourthActivity.this, ThirdActivity.class);
startActivity(openThird);
}
});
findViewById(R.id.mBtOpen4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFourth = new Intent(FourthActivity.this, FourthActivity.class);
startActivity(openFourth);
}
});
}
}
就是这样,其实目前只有在页面D里面的用于打开页面B的按钮设置了Intent.FLAG_ACTIVITY_CLEAR_TOP的Flag
操作:我们启动A,A启动B,B启动B,B再启动C,C启动D,再从D启动B。
分两步走:
第一步:
我们启动A,A启动B,B再启动C,C启动D,此时任务栈的里面的情况是DCBA,其中D为栈顶。
Running activities (most recent first):
TaskRecord{52938abc #36 A=com.amqr.flagtest U=0 sz=4}
Run #3: ActivityRecord{52978c64 u0 com.amqr.flagtest/.FourthActivity t36}
Run #2: ActivityRecord{52946268 u0 com.amqr.flagtest/.ThirdActivity t36}
Run #1: ActivityRecord{52a8fd24 u0 com.amqr.flagtest/.SecondActivity t36}
Run #0: ActivityRecord{52a0a8b4 u0 com.amqr.flagtest/.FirstActivity t36}
第二步:再从D启动B。
这时我们会发现,任务栈变成BA,其中B为栈顶,C和D都被B给干掉了。
Running activities (most recent first):
TaskRecord{52938abc #36 A=com.amqr.flagtest U=0 sz=2}
Run #1: ActivityRecord{52969568 u0 com.amqr.flagtest/.SecondActivity t36}
Run #0: ActivityRecord{52a0a8b4 u0 com.amqr.flagtest/.FirstActivity t36}
证明结论:Intent.FLAG_ACTIVITY_CLEAR_TOP与清单文件里面指定 android:launchMode="singleTask"效果类似。(当位于栈内没什么确实效果一致,当位于栈顶就很不一样了)
我们这里做了个实验。
我们看到代码里我们的FirstActivity打SecondActivity的时候(就是A打开B的时候),并没有设定Intent的Flag,即A打开B的时候B在任务栈里面是属于Standard模式的,但是在D打开B的时候,因为我们指定打开B的时候B需要使用FLAG_ACTIVITY_CLEAR_TOP模式,所以这个时候就从Standard变成了FLAG_ACTIVITY_CLEAR_TOP(singleTask效果)了。
可见,代码设定最大Flag好处就是启动模式可变,而XML则是定死的。
这里如果我们反过来,把上面代码稍作修改,把在A页面打开B的时候把B设为FLAG_ACTIVITY_CLEAR_TOP,然后D打开B不添加任何Flag(即为D打开的B为Standard),发现这样设定之后,我们D打开B,结果是多产生了一个B的实例,而是不是B把D和C都给清掉。这就再一次证明最后设定开启该Activity的模式其最终的启动模式。
结论证明 代码指定Flag使得Activity启动模式可变,而且最后一次指定的Flag就是该Activity在任务栈中最终的模式。
情景5,把A打开B,B打开B的界面都设定为Intent.FLAG_ACTIVITY_CLEAR_TOP,然后A启动B,B启动B。
结论: 在一个打开时已经被标记为FLAG_ACTIVITY_CLEAR_TOP的界面(现正处于前台任务栈的栈顶),如果我们在此界面打开自己,并且一样标记为FLAG_ACTIVITY_CLEAR_TOP,那么这个时候会经历一次建新毁旧的过程,任务栈里面从结果的来说没什么变化,但是从生命周期的着眼于过程的角度来却经历非常大的变化。
步骤一:A打开B:
终端
Running activities (most recent first):
TaskRecord{52ac1a08 #61 A=com.amqr.flagtest U=0 sz=2}
Run #1: ActivityRecord{529e1994 u0 com.amqr.flagtest/.SecondActivity t61}
Run #0: ActivityRecord{529ca8e0 u0 com.amqr.flagtest/.FirstActivity t61}
生命周期
12-06 01:09:22.978 5768-5768/? D/Cur: FirstActivity onCreate
12-06 01:09:22.978 5768-5768/? D/Cur: FirstActivity onStart
12-06 01:09:22.978 5768-5768/? D/Cur: FirstActivity onResume
// A打开B
12-06 01:09:25.694 5768-5768/com.amqr.flagtest D/Cur: FirstActivity onPause
12-06 01:09:25.698 5768-5768/com.amqr.flagtest D/Cur: SecondActivity onCreate
12-06 01:09:25.698 5768-5768/com.amqr.flagtest D/Cur: SecondActivity onStart
12-06 01:09:25.698 5768-5768/com.amqr.flagtest D/Cur: SecondActivity onResume
12-06 01:09:26.090 5768-5768/com.amqr.flagtest D/Cur: FirstActivity onStop
在这里位置一切正常,下面发生的事情比较诡异
步骤二:B打开B,这个是否发生的比较诡异,不是复用,而是建立一个新的,毁灭旧的。
这里贴代码非常有必要:
manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.amqr.flagtest" >
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".FirstActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".SecondActivity"
/>
<activity android:name=".ThirdActivity"
/>
<activity android:name=".FourthActivity"/>
</application>
</manifest>
FirstActivity
public class FirstActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
Log.d("Cur", "FirstActivity onCreate");
findViewById(R.id.mBtOpen2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openSecond = new Intent(FirstActivity.this, SecondActivity.class);
openSecond.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(openSecond);
}
});
findViewById(R.id.mBtOpen3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openThird = new Intent(FirstActivity.this, ThirdActivity.class);
startActivity(openThird);
}
});
findViewById(R.id.mBtOpen4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFourth = new Intent(FirstActivity.this, FourthActivity.class);
startActivity(openFourth);
}
});
}
@Override
protected void onStart() {
super.onStart();
Log.d("Cur", "FirstActivity onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.d("Cur", "FirstActivity onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.d("Cur", "FirstActivity onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.d("Cur", "FirstActivity onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d("Cur", "FirstActivity onDestroy");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d("Cur", "FirstActivity onRestart");
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d("Cur", "FirstActivity onNewIntent");
}
}
SecondActivity
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Log.d("Cur","SecondActivity onCreate");
findViewById(R.id.mBtOpen2).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openSecond = new Intent(SecondActivity.this, SecondActivity.class);
openSecond.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(openSecond);
}
});
findViewById(R.id.mBtOpen3).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openThird = new Intent(SecondActivity.this, ThirdActivity.class);
startActivity(openThird);
}
});
findViewById(R.id.mBtOpen4).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent openFourth = new Intent(SecondActivity.this, FourthActivity.class);
startActivity(openFourth);
}
});
}
@Override
protected void onStart() {
super.onStart();
Log.d("Cur", "SecondActivity onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.d("Cur", "SecondActivity onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.d("Cur", "SecondActivity onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.d("Cur", "SecondActivity onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d("Cur", "SecondActivity onDestroy");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d("Cur", "SecondActivity onRestart");
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d("Cur", "SecondActivity onNewIntent");
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
Log.d("Cur", "SecondActivity onSaveInstanceState");
}
}
.
.
看完了代码,我们先来看一下gif图。
当我们在 B 界面打开B的时候,根据的页面的闪一下的反馈我们感觉这时候一个重新创建了B的一个实例,那么按道理任务这时候的情况就是BBA,B为栈顶,A为栈底,如果照着这种想法那么我们此时按下back键应该是返回到B界面,任务栈变成BA,B为栈顶。
但是实际上发生的却不是这个样子的,实际上发生的事情却是:
按下back键,直接返回A界面。
再按一次back键就退出了程序了。
这意味着什么?这意味我们的眼睛欺骗了我们,屏幕闪的那一下是创建的一个B的实例没有错,但是我们看不到的事情是在新建新的实例入栈的同时那么旧的B就被销毁掉了。所以,当我们屏幕闪一下之后,任务栈存在的是BA,B为栈顶,而不是BBA。
终端显示的是每没打开之前一样,没有变化(因为它只是记录最后的结果,不管过程)
(此时显示的是B打开B之后的状态,还没按下back)
Running activities (most recent first):
TaskRecord{52ac1a08 #61 A=com.amqr.flagtest U=0 sz=2}
Run #1: ActivityRecord{52a0fb5c u0 com.amqr.flagtest/.SecondActivity t61}
Run #0: ActivityRecord{529ca8e0 u0 com.amqr.flagtest/.FirstActivity t61}
从生命周期的变化我们大概明白这一切是怎么发生的了。
12-06 01:09:22.978 5768-5768/? D/Cur: FirstActivity onCreate
12-06 01:09:22.978 5768-5768/? D/Cur: FirstActivity onStart
12-06 01:09:22.978 5768-5768/? D/Cur: FirstActivity onResume
// A打开B
12-06 01:09:25.694 5768-5768/com.amqr.flagtest D/Cur: FirstActivity onPause
12-06 01:09:25.698 5768-5768/com.amqr.flagtest D/Cur: SecondActivity onCreate
12-06 01:09:25.698 5768-5768/com.amqr.flagtest D/Cur: SecondActivity onStart
12-06 01:09:25.698 5768-5768/com.amqr.flagtest D/Cur: SecondActivity onResume
12-06 01:09:26.090 5768-5768/com.amqr.flagtest D/Cur: FirstActivity onStop
// B打开B 从生命周期我们发生了原因,B在这里灭掉了自己,然后重建一个自己
12-06 01:11:50.390 5768-5768/com.amqr.flagtest D/Cur: SecondActivity onPause
12-06 01:11:50.410 5768-5768/com.amqr.flagtest D/Cur: SecondActivity onCreate
12-06 01:11:50.410 5768-5768/com.amqr.flagtest D/Cur: SecondActivity onStart
12-06 01:11:50.410 5768-5768/com.amqr.flagtest D/Cur: SecondActivity onResume
12-06 01:11:50.786 5768-5768/com.amqr.flagtest D/Cur: SecondActivity onStop
12-06 01:11:50.786 5768-5768/com.amqr.flagtest D/Cur: SecondActivity onDestroy
结论:设定为Intent.FLAG_ACTIVITY_CLEAR_TOP的Activity处于前台任务栈栈顶时,如果在当前页面打开自己,经历了一个建新毁旧的过程,算不上复用。
(这个过程会创建一个新的实例,然后把旧的自己给销毁掉,这个结果在任务栈的最终结果看来是没变化的,但是实际上生命周期却发生了很大的变化。)
结论:在一个打开时已经被标记为FLAG_ACTIVITY_CLEAR_TOP的界面(现正处于前台任务栈的栈顶),如果我们在此界面打开自己,并且一样标记为FLAG_ACTIVITY_CLEAR_TOP,那么这个时候会经历一次建新毁旧的过程,没有复用,任务栈里面从结果的来说没什么变化,但是生命周期的着眼于过程的角度来却经历非常大的变化。
本篇完。