Android-Activity所应该了解的大概就这样。(下)

本文出自 “阿敏其人” 简书博客,转载或引用请注明出处。

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.png
2.png
3.png
4.png

就是上面那个样子。

准备工作至此完成

情景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}

情况1再验证.gif

我们发现,这是在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);
            }
        });

    }
}

情景3.gif

这里我们就不贴终端了。一个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图。

诡异的FLAG_ACTIVITY_CLEAR_TOP.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,那么这个时候会经历一次建新毁旧的过程,没有复用,任务栈里面从结果的来说没什么变化,但是生命周期的着眼于过程的角度来却经历非常大的变化。

本篇完。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容