android启动模式

  1. 任务
    任务是指执行特定作业时与用户交互的一系列Activity,这些Activity按照各自的打开顺序排列在堆栈
    (即“返回栈”)中。google为了记录用户开启了那些Activity,引入了 任务栈(task stack) 的概念,
    用于记录activity开启的先后顺序,帮助维护好的用户体验。栈的特性是:先进后出
  2. 如何查看当前系统的任务栈
    • 手机中,长按 home或者多任务键 会进入到概览屏幕的一个界面;
    • 命令行中,adb shell dumpsys activity;
  3. Activity启动模式
    Activity启动模式在AndroidMainifest.xml文件里面的activity标签设置,属性名为android:launchMode,示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <activity
    android:name=".FirstActivity"
    android:launchMode="singleTop"
    android:label="This is FirstActivity" >
    <intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    </activity>
    • standard:标准模式,也是系统默认的,每次都会创建新的Activity覆盖在原Activity上。
      标准模式示意图
    • singleTop:栈顶复用模式,首先判断栈顶Activity是否是要启动的Activity,如果是则不创建新的
      Activity而直接引用这个Activity;如果不是则创建新的Activity。
      栈顶复用模式示意图
    • singleTask:栈内复用模式,检测整个Activity栈中是否存在当前需要启动的Activity,如果存在
      则将该Activity置于栈顶,并销毁其上所有Activity,如果不存在则创建新的Activity置于栈顶。
      栈内复用模式示意图
    • singleInstance:单实例模式,创建新的任务栈,且该任务栈仅有一个Activity。
      单实例模式示意图
  4. 其他

    • 为Activity设置新的任务栈
      taskAffinity,任务相关性。xml中的一个属性,标识了一个Activity所需要的任务栈的名字。默认是包名。如果设置了其他的名字如com.test.task1,那启动它的时候就会新建一个名为com.test.task1的任务栈。
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
       <activity
      android:name="com.test.task0.MainActivity"
      android:label="@string/app_name"
      android:launchMode="standard">
      <intent-filter>
      <action android:name="android.intent.action.MAIN" />
      <category andorid:name="android.intent.category.LAUNCHER" />
      </intent-filter>
      </activity>
      <activity
      andorid:name="com.test.SecondActivity"
      android:taskAffinity="com.test.task1"
      android:label="@string/app_name"
      android:launchMode="singleTask"/>
      <activity
      andorid:name="com.test.ThirdActivity"
      android:taskAffinity="com.test.task1"
      android:label="@string/app_name"
      android:launchMode="singleTask"/>
      ```
      如果从MainActivity启动SecondActivity,然后再启动ThirdActivity,那么任务栈如下:

    Blockquotes com.test.task0 MainActivity
    Blockquotes com.test.task1 SecondActivity ThirdActivity
    若再从ThirdActivity启动MainActivity,那么任务栈如下:
    com.test.task0 MainActivity
    com.test.task1 SecondActivity ThirdActivity MainActivit
    总结:通过设置 TaskAffinity 属性更改任务栈属性。

    1
    2
    - 回退栈和通知  
    通过通知进入Activity存在的问题,默认情况下,从通知启动一个Activity,按返回键会回到主屏幕。但某些时候有按返回键仍然留在当前应用的需求。从通知打开的某个深层次Activity。在此Acitivity中点回退,若不做处理,将会直接返回到AndroidLaunch界面。这是因为在Notification中的PendingIntent会默认开启新的任务栈。当回退的时候此任务栈没有其他新的Activity,默认在PendingIntent 的Activity是任务栈中唯一的 Activity.

    举例: HomeActiy=>Step1Activity=>Step2Acitity
    某个通知 默认在PendingIntent 指定了Step2Acitity。通过通知栏进入到Step2Acitity。点回退。正常的顺序为:
    Step2Activity=>Step1Acitivity=>HomeActivity但是如果通过通知栏这样进入Step2Activity 点回退 会直接退回到Android 桌面。就是因为 PendingIntent会默认开启新的任务栈

    1
    解决方法:通过TaskStackBuilder设置具体的回退路径,在 AndroidManifest.xml定义Acitivity从属关系。

Android 4.0.3 及更早版本




Android 4.1 及更高版本



1
2
3
4
5
6
7
8
9
在代码中做如下处理:
```java
Intent intent = new Intent(MainActivity.this, ResultActivity.class);
TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(this);
taskStackBuilder.addParentStack(ResultActivity.class);
taskStackBuilder.addNextIntent(intent);
PendingIntent pendingIntent = taskStackBuilder.getPendingIntent(1, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
notificationBuilder.setContentIntent(pendingIntent);

这样从Notification进入到指定的ResultActivity中。回退到正常的MainActivity中。
TaskStackBuilder 扩展应用
也可以单独使用TaskStackBuilder,让某个二级Acitivity启动的时候启动它的一级Acitity.
在 AndroidManifest.xml定义Acitivity从属关系。

1
2
3
4
5
6
7
8
<activity
android:name="com.example.app.ChildActivity
android:parentActivityName="com.example.app.ParentActivity">
<!-- 下面这段用来兼容API 16之前的版本 -->
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="com.example.app.MainActivity"/>
</activity>

在代码中申明:

1
2
3
4
Intent intent = new Intent(this, ChildActivity.class);
TaskStackBuilder.create(this)
.addNextIntentWithParentStack(intent)
.startActivities();

通过启动一个无任何层级关系的Acitity 激活应用(目前Lianlian3.0应用采用此方法)

1
2
3
4
5
6
7
8
9
public class SimpleLaunchAppActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Now finish, which will drop the user in to the activity that was at the top
// of the task stack
finish();
}
}

原理:若通过Notification打开Acitivity后此Acitivy会位于要启动的App的栈顶。自动finish()后,app恢复到切换到后台之前的状态。SimpleLaunchAppActivity不得设置android:taskAffinity。taskAffinity和默认App的包名一致就不会新建任务栈。若设置了taskAffinity为新的任务栈,点回退后 将会回到android launch页面。
联连wifi的解决方案:

1
2
过启动一个无任何层级关系的TransparentSwitchActivity 激活应用**
目前存在的问题是TransparentSwitchActivity里面会根据参数判断 通过通知进入后还有部分逻辑不太人性化和完善。需要提高用户体验。

  • 关于系统默认的清空返回栈
    如何用户将任务切换到后台之后过了很长一段时间,系统会将这个任务中除了最底层的那个Activity之外的其它所有Activity全部清除掉。当用户重新回到这个任务的时候,最底层的那个Activity将得到恢复。这个是系统默认的行为,因为既然过了这么长的一段时间,用户很有可能早就忘记了当时正在做什么,那么重新回到这个任务的时候,基本上应该是要去做点新的事情了。当然,既然说是默认的行为,那就说明我们肯定是有办法来改变的,在Manifest中 元素中设置以下几种属性就可以改变系统这一默认行为:
    1
    2
    3
    4
    5
    6
    alwaysRetainTaskState   
    如果将最底层的那个Activity的这个属性设置为true,那么上面所描述的默认行为就将不会发生,任务中所有的Activity即使过了很长一段时间之后仍然会被继续保留。
    clearTaskOnLaunch
    如果将最底层的那个Activity的这个属性设置为true,那么只要用户离开了当前任务,再次返回的时候就会将最底层Activity之上的所有其它Activity全部清除掉。简单来讲,就是一种和alwaysRetainTaskState完全相反的工作模式,它保证每次返回任务的时候都会是一种初始化状态,即使用户仅仅离开了很短的一段时间。
    finishOnTaskLaunch
    这个属性和clearTaskOnLaunch是比较类似的,不过它不是作用于整个任务上的,而是作用于单个Activity上。如果某个Activity将这个属性设置成true,那么用户一旦离开了当前任务,再次返回时这个Activity就会被清除掉。

参考:
[1]. Activity启动模式 https://blog.csdn.net/lixiaodaoaaa/article/details/51700981