Android LaunchMode案例篇

在开发中有时会遇到这样的场景,用户点击注册,第一步,第二步,完成注册跳转到登录界面,不需要用户一步一步的返回到登录界面。这是怎么实现的呢?
案例:有四个界面 A,B,C,D 从A跳转到B,B跳转到C,C跳转到D,D完成注册跳转到A,点击返回键退出程序。具体过程来看下图:

lm1

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

方案一

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

<code class="hljs xml has-numbering">        <span class="hljs-tag"><<span class="hljs-title">activity</span> <span class="hljs-attribute">android:name</span>=<span class="hljs-value">".A"</span>
            <span class="hljs-attribute">android:launchMode</span>=<span class="hljs-value">"singleTask"</span>></span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li></ul><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li></ul>

2.在D文件:

<code class="hljs actionscript has-numbering"> startActivity(<span class="hljs-keyword">new</span> Intent(D.<span class="hljs-keyword">this</span>, A.<span class="hljs-keyword">class</span>));</code><ul style="display: block;" class="pre-numbering"><li>1</li></ul><ul style="display: block;" class="pre-numbering"><li>1</li></ul>

方案二

在D文件:

<code class="hljs avrasm has-numbering">        Intent intent =new Intent(D<span class="hljs-preprocessor">.this</span>, A<span class="hljs-preprocessor">.class</span>)<span class="hljs-comment">;</span>
        intent<span class="hljs-preprocessor">.setFlags</span>(Intent<span class="hljs-preprocessor">.FLAG</span>_ACTIVITY_CLEAR_TOP)<span class="hljs-comment">;</span>
        startActivity(intent)<span class="hljs-comment">;</span>
        finish()<span class="hljs-comment">;</span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul>

方案三

方案三相对于前面两种方案稍微麻烦一些,但更利于我们理解栈的原理。

1.新建ActivityManager类:

<code class="hljs cs has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> ActivityManager {

    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> Stack<Activity> mStack;
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> ActivityManager mActivityManager;
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ActivityManager <span class="hljs-title">getActivityManager</span>() {
        <span class="hljs-keyword">if</span> (mActivityManager == <span class="hljs-keyword">null</span>) {
            mActivityManager = <span class="hljs-keyword">new</span> ActivityManager();
        }
        <span class="hljs-keyword">return</span> mActivityManager;
    }
    <span class="hljs-comment">//将当前Activity推入栈中</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">pushActivity</span>(Activity activity) {
        <span class="hljs-keyword">if</span> (mStack == <span class="hljs-keyword">null</span>) {
            mStack = <span class="hljs-keyword">new</span> Stack<Activity>();
        }
        mStack.add(activity);
    }
    <span class="hljs-comment">//退出栈顶Activity</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">popActivity</span>(Activity activity) {
        <span class="hljs-keyword">if</span> (activity != <span class="hljs-keyword">null</span>) {
            <span class="hljs-comment">//在从自定义集合中取出当前Activity时,也进行了Activity的关闭操作</span>
            activity.finish();
            mStack.remove(activity);
            activity = <span class="hljs-keyword">null</span>;
        }
    }
    <span class="hljs-comment">//获得当前栈顶Activity</span>
    <span class="hljs-keyword">public</span> Activity <span class="hljs-title">currentActivity</span>() {
        Activity activity = <span class="hljs-keyword">null</span>;
        <span class="hljs-keyword">if</span> (!mStack.empty())
            activity = mStack.lastElement();
        <span class="hljs-keyword">return</span> activity;
    }
    <span class="hljs-comment">//退出栈中 cls以前的所有Activity</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">popAllActivityExceptOne</span>(Class cls) {
        <span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>) {
            Activity activity = currentActivity();
            <span class="hljs-keyword">if</span> (activity == <span class="hljs-keyword">null</span>) {
                <span class="hljs-keyword">break</span>;
            }
            <span class="hljs-keyword">if</span> (activity.getClass().equals(cls)) {
                <span class="hljs-keyword">break</span>;
            }
            popActivity(activity);
        }
    }
    <span class="hljs-comment">//获取栈</span>
    <span class="hljs-keyword">public</span> Stack<Activity> <span class="hljs-title">getStackActivity</span>() {
        <span class="hljs-keyword">if</span> (mStack != <span class="hljs-keyword">null</span>) {
            <span class="hljs-keyword">return</span> mStack;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Stack<Activity>();
    }
}</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li></ul><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li><li>17</li><li>18</li><li>19</li><li>20</li><li>21</li><li>22</li><li>23</li><li>24</li><li>25</li><li>26</li><li>27</li><li>28</li><li>29</li><li>30</li><li>31</li><li>32</li><li>33</li><li>34</li><li>35</li><li>36</li><li>37</li><li>38</li><li>39</li><li>40</li><li>41</li><li>42</li><li>43</li><li>44</li><li>45</li><li>46</li><li>47</li><li>48</li><li>49</li><li>50</li><li>51</li><li>52</li><li>53</li><li>54</li></ul>

2.MyApplication文件(记得在清单文件中声明)

<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyApplication</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Application</span> {</span>

    <span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> ActivityManager activityManager = <span class="hljs-keyword">null</span>;

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>() {
        <span class="hljs-keyword">super</span>.onCreate();
        activityManager = ActivityManager.getActivityManager();
    }

    <span class="hljs-comment">//获取实例</span>
    <span class="hljs-keyword">public</span> ActivityManager <span class="hljs-title">getActivityManager</span>() {
        <span class="hljs-keyword">return</span> activityManager;
    }
}
</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li><li>15</li><li>16</li></ul>

3.BaseActivity文件(公共Activity)

<code class="hljs java has-numbering"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BaseActivity</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AppCompatActivity</span> {</span>

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">protected</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onCreate</span>(@Nullable Bundle savedInstanceState) {
        <span class="hljs-keyword">super</span>.onCreate(savedInstanceState);
        ((MyApplication)getApplication()).getActivityManager().pushActivity(<span class="hljs-keyword">this</span>);
    }

    <span class="hljs-annotation">@Override</span>
    <span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">onBackPressed</span>() {
        <span class="hljs-keyword">super</span>.onBackPressed();
      ((MyApplication)getApplication()).getActivityManager().popActivity(<span class="hljs-keyword">this</span>);
    }
}</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li></ul><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li><li>11</li><li>12</li><li>13</li><li>14</li></ul>

4.D文件

<code class="hljs avrasm has-numbering"> startActivity(new Intent(D<span class="hljs-preprocessor">.this</span>, A<span class="hljs-preprocessor">.class</span>))<span class="hljs-comment">;</span>
        ((MyApplication) getApplication())<span class="hljs-preprocessor">.getActivityManager</span>()<span class="hljs-preprocessor">.popAllActivityExceptOne</span>(A<span class="hljs-preprocessor">.class</span>)<span class="hljs-comment">;</span>
</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>

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属性值:

<code class="hljs lua has-numbering">    <span class="hljs-built_in">package</span>=<span class="hljs-string">"com.github.ws.launchmodedemo"</span>>

    <application
        android:taskAffinity=<span class="hljs-string">"com.grasp.demo"</span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li><li>4</li></ul>

也可以单独为某个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最好的宿主。我们来看看下面的这个例子:
lm2

录制工具不是很清晰,有些模糊了。
从上图当中很明显的看出,程序A中有(a,b界面),程序B中也有(a,b界面)。在程序A的b界面跳转到程序B的b界面:

<code class="hljs cs has-numbering">        Intent intent=<span class="hljs-keyword">new</span> Intent(<span class="hljs-string">"com.github.ws.flagdemo.B"</span>);
        startActivity(intent);</code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li></ul><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li></ul>

按Home键回到主屏,在主选单中启动程序B;按Home键回到主屏,在主选单中启动程序A。

我们发现在从程序A跳转到程序B的b之后,b实例好像是嵌入到了程序A中,但是不影响程序B的正常运行。

然后我们修改一下跳转的代码:

<code class="hljs avrasm has-numbering">  Intent intent=new Intent(<span class="hljs-string">"com.github.ws.flagdemo.B"</span>)<span class="hljs-comment">;</span>
  intent<span class="hljs-preprocessor">.setFlags</span>(Intent<span class="hljs-preprocessor">.FLAG</span>_ACTIVITY_NEW_TASK)<span class="hljs-comment">;</span>
  startActivity(intent)<span class="hljs-comment">;</span></code><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul><ul style="display: block;" class="pre-numbering"><li>1</li><li>2</li><li>3</li></ul>

lm4

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

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值