• Android开发之---Activity启动模式


      在Android开发中,启动一个新的activity我们可以使用startActivity或startActivityForResult,Android系统使用栈的方式来管理一个APP的页面显示与保存顺序,那么,在新启动一个activity时,栈的里面是每次都新增实例还是只有一个实例呢?这个与在清单文件注册一个activity使用的参数:launchMode有关。

      Acvitity启动模式有4种:

      1. 标准模式standard

      2. 栈顶复用模式singleTop

      3. 栈内复用模式singleTask

      4. 单实例模式singleInstance

      为了测试这几种模式,新建了一个测试工程:https://github.com/linjk/TestLaunchMode,可以下载代码进行测试。

      1. 新建测试项目:

      

      2. 新建一个类,用于管理activity栈和打印当前栈的activity名字,代码如下:

    package cn.linjk.testlaunchmode.utils;
    
    import android.app.Activity;
    import android.util.Log;
    
    import java.util.Stack;
    
    /**
     * Created by LinJK on 05/12/2016.
     *
     * <p>Class to manage activity stack, base activity
     * can use it.</p>
     *
     * @version 1.0
     */
    
    public class ActivityStackManager {
    
        /**
         * Single Instance
         * @return
         */
        public static ActivityStackManager getINSTANCE() {
            if (null == INSTANCE) {
                INSTANCE = new ActivityStackManager();
            }
            return INSTANCE;
        }
    
        //region Add and remove activity
        public void addActivity(Activity ac) {
            if (null == stack_activity) {
                stack_activity = new Stack<>();
            }
            stack_activity.add(ac);
    
            printAllActivityName();
        }
    
        public void removeActivity(Activity ac) {
            if (ac != null) {
                stack_activity.remove(ac);
            }
    printAllActivityName();     } //endregion //region Print Activity Stack Info public void printAllActivityName() { for (int i = stack_activity.size()-1; i >= 0; i--) { Log.d(TAG, "[" + i + "]Activity Name: " + stack_activity.get(i).getClass().getName()); } } //endregion /////////////////////////////////////////////////////////////////////////////// private ActivityStackManager() {} private static final String TAG = ActivityStackManager.class.getSimpleName(); private static ActivityStackManager INSTANCE; private static Stack<Activity> stack_activity; } 

      3. MainActivity作为第一个页面,这里再新建第二个activity:

      两个页面的布局xml文件分别如下:

      MainActivity:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="cn.linjk.testlaunchmode.activity.MainActivity">
    
        <Button
            android:id="@+id/btn_start_new_activity"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="启动新的activity_Main"/>
    </RelativeLayout>
    

         ActivitySecond:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
    
        <Button
            android:id="@+id/btn_start_new_activity"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="启动新的activity_Second"/>
    
    </LinearLayout>
    

         MainActivity.java:

        

    package cn.linjk.testlaunchmode.activity;
    
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    
    import cn.linjk.testlaunchmode.base.activity.BaseActivity;
    import cn.linjk.testlaunchmode.R;
    
    public class MainActivity extends BaseActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            btnStartNewActivity = (Button)findViewById(R.id.btn_start_new_activity);
    
            btnStartNewActivity.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View pView) {
                    startActivity(new Intent(MainActivity.this, ActivitySecond.class));
                }
            });
        }
    
        //////////////////////////////////////////////////////
    
        private Button btnStartNewActivity;
    
    }
    

         ActivitySecond.java:

    package cn.linjk.testlaunchmode.activity;
    
    import android.content.Intent;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    
    import cn.linjk.testlaunchmode.R;
    import cn.linjk.testlaunchmode.base.activity.BaseActivity;
    
    /**
     * Created by LinJK on 05/12/2016.
     */
    
    public class ActivitySecond extends BaseActivity{
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_second);
    
            btnStartNewActivity = (Button)findViewById(R.id.btn_start_new_activity);
    
            btnStartNewActivity.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View pView) {
                    startActivity(new Intent(ActivitySecond.this, ActivitySecond.class));
                }
            });
        }
    
        //////////////////////////////////////////////////////
    
        private Button btnStartNewActivity;
    }
    

         在清单文件中的application节点,activity配置如下,都是默认的启动模式配置:

    <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".activity.MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
    
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
            </activity>
    
            <activity android:name=".activity.ActivitySecond"/>
        </application>

      4. 启动模式测试:

        APP在启动后,未配置情况下,默认会分配一个任务栈,所有的actiivty默认都会在该栈启动与退出,这是通过任务共用性(Affinities)完成的,任务共用性    

          为这个运行一个或多个Activity的Task提供了一个独特的静态名称,默认的一个活动的任务共用性(Affinity)是实现了该Activity的.apk包的名字,可以在acitivty的onCreate启动后通过getTaskID获取其所在任务栈id,新启动一个页面后,新的页面会在启动它的任务栈打开,如果加了标志:FLAG_ACTIVITY_NEW_TASK,那个这两个activity就会在两个不同的任务栈里面。

        注意,只有actvitivy的上下文才可以启动activity,因为这才存在任务栈,而application级别的,如getApplicationContext().startActivity(new Intent(MainActivity.this, ActivitySecond.class));则无法启动,因为application的上下文并不会分配上下文,需要加新增任务栈标记位:FLAG_ACTIVITY_NEW_TASK。

      4.1 standard

        从第一个页面进入第二个页面后,在第二个页面再启动第二个页面,打印的日志如下:

        可以看到,默认的standard模式,每次都会新建实例来启动activity.

      4.2 singleTop

        配置第二个页面的启动模式:

    <activity
                android:launchMode="singleTop"
                android:name=".activity.ActivitySecond"/>
    

         第一次进入第二个页面日志如下:

        再点击启动新页面:

        这是发现只打印多红色圈住的这一句日志,没调用onCreate() ?

        查看一下activity源码,发现这部分:

        

        原来,配置了singleTop模式后,会调用onNewIntent()重新加载页面,我们修改ActivitySecond代码,增加如下部分:

    @Override
        protected void onNewIntent(Intent intent) {
            super.onNewIntent(intent);
    
            Log.d("ActivitySecond", "[Task ID]" + getTaskId());
            ActivityStackManager.getINSTANCE().printAllActivityName();
    
        }
    

         重复刚才操作,日志如下:

        可以发现,当栈顶是ActivitySecond,重复打开时只有一个ActivitySecond实例。注意,如果ActivitySecond不是在栈顶,新打开还是会新建的!

      4.3 singleTask

        与singleTop类似,就不重复测试了,大概说明一下差异:

        1. singleTop是在栈顶在复用,不在栈顶则新建,而singleTask是只要在任务栈内就复用,不会新建

        2. singleTask模式中,默认具有clearTop效果,如果当前栈状态ABCD, 此时新建B,会把B页面移到栈顶,并清除原来B以上的栈页面,此时变为AB。

        3. 使用singleTask,可以在清单文件加上taskAffinity属性,其值不能与包名一致,否则会无效,这样,后面以singleTask启动的activity,如果该属性一样,就会分配到同一个新的任务栈中。

        (

        附:如一个activity的taskAffnity与allowTasReparenting属性配合使用,那么该activity可以在两个app的任务栈共用,假设该activity叫C,现有两个应用A、B,名叫activity的C注册在B应用的清单文件中,配置如下:

    <activity
                android:name=".activity.ActivitySecond">
                <intent-filter>
                    <action android:name="android.intent.action.TESTLAUNCHMODE_SECOND_ACTIVITY"/>
                    <category android:name="android.intent.category.DEFAULT"/>
                </intent-filter>
            </activity>
    

         则应用A中启动该activity可如下启动:

    Intent intent = new Intent("android.intent.action.TESTLAUNCHMODE_SECOND_ACTIVITY"); 
    startActivity(intent);

         此时,如果应用B打开C,会把C从应用A的任务栈中转移过来。

        )

      4.4 singleInstance

        singleTask的加强版本,以此模式启动的activtiy,系统会新建一个任务栈并把它独立存放,这个activity后面会被重复使用,直到系统把该任务栈kill掉。

  • 相关阅读:
    GCC-windows的预编译版本——nuwen MinGW
    stm32 flash和sram
    NAND Flash和NOR Flash的比较
    emwin如何在windows10下vs2015或2017进行仿真。
    IIC简介(转载)
    ubuntu版本查看
    下载速度更加快的 SourceForge 镜像
    使用virtualbox安装的Ubuntu,窗口分辨率过小,使用增强工具完成和vmtools一样的功能。
    C#中的结构体与类的区别
    理解C#值类型和引用类型
  • 原文地址:https://www.cnblogs.com/linjk/p/6134264.html
Copyright © 2020-2023  润新知