Android-Activity 启动模式解析

来源:互联网 发布:ssh登录权限管理源码 编辑:程序博客网 时间:2024/06/11 13:17

1.Standard 标准模式

  • Activity的默认启动模式,在不显示指定启动模式的情况下,Activity都以这种方式进行启动。
  • 在Standard模式下,每启动一个新的Activity,就会在返回栈中进行入栈,并处于栈顶的位置。
  • 对于使用Standard模式启动的Activity,系统不检查该Activity之前是否已经在返回栈中存在,每次启动都会创建一个该Activity的新的实例对象。

代码测试:

    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.d(TAG, this.toString());        setContentView(R.layout.activity_main);        mStandardModeButton = (Button) this.findViewById(R.id.btn_standard_mode);        mStandardModeButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Intent intent = new Intent(MainActivity.this, MainActivity.class);                startActivity(intent);            }        });    }

我们在这里所做的事情是,添加一个按钮,点击该按钮在MainActivity的基础上,再次跳转启动MainActivity。

然后运行程序,假设我们连续点击点击该按钮。查看LogCat,发现得到如下输入信息:
tsr

这里可以发现,每次跳转时,就会有一个全新的MainActivity的实例对象被压入返回栈的栈顶,无论之前是否已经有MainActivity活动的实例存在于栈中。
所以,自然而然的,当我们想要退出整个程序时,就需要连续点击三次back键,以达到目的。

2.SingleTop 栈顶唯一模式

  • SingleTop,顾名思义,最直接的或许就可以理解为,单栈顶模式。
  • 所以,其与Standard启动模式最大的不同之处正是在于:当一个Activity已经位于栈顶时,想要再启动一个该Activity,那么系统会直接复用位于栈顶的该Activity,而不会再去创建新的实例对象。
  • 与此同时,需要注意的就是,SingleTop模式的上述特性,仅仅只针对于正处于栈顶的活动对象。如果一个活动并非位于栈顶位置,那么当再次启动该Activity时,仍然会创建新的对象。

所以,如果我们在AndroidManifest.xml将MainActivity的启动模式改为SingleTop之后,再次运行我们在Standard模式里用到的代码,则会发现:
除了当MainActivity第一次被启动时,会创建一次实例对象,之后无论再点击多少次跳转按钮,都不会再创建新的实例对象。所以,只点击一次back键,就可以退出应用。

那么,下面我们修改代码:

MainActivity.java

@Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.d(TAG, this.toString());        setContentView(R.layout.activity_main);        mSingleTopModeButton =  (Button) this.findViewById(R.id.btn_singletop_mode);        mSingleTopModeButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Intent intent = new Intent(MainActivity.this, SecondActivity.class);                startActivity(intent);            }        });    }

SecondActivity.java

@Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.d(TAG, this.toString());        setContentView(R.layout.activity_second);        mTurnButton = (Button) this.findViewById(R.id.btn_turn);        mTurnButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Intent intent = new Intent(SecondActivity.this,MainActivity.class);                startActivity(intent);            }        });    }

这次,我们创建了两个Activity。我们测试的思路如下:
1.当程序初次运行时,MainAcitivty会被初次创建对象。
2.当从MainActivity跳转到SecondActivity,SecondActivity会被创建对象。
3.当在SecondActivity点击按钮,再次跳转到MainActivity。这时,虽然返回栈里已经有MainActivity的对象存在了,但是因为此时位于栈顶的是SecondActivity,所以仍然会再次创建一个全新的MainActivity对象压入栈内。

而当我们运行程序,得到的日志打印情况也印证了这一点:
这里写图片描述

3.SingleTask 栈唯一模式

上面我们说到,通过SingleTop模式,我们可以解决重复创建栈顶活动的问题。但是,当我们再次启动的活动并不位于栈顶的时候,则就还是不能避免重复创建多个活动实例的问题。这个时候,就可以通过SingleTask来解决。

SingleTask的不同之处在于:

  • 当再次启动一个Activity时,首先会检查,在当前返回栈中,是否已经存在该Activity的实例对象。
  • 如果已经存在,则会直接复用该对象,并且将栈中位于该Activity之上的活动全部清除出栈。
  • 如果当前还不存在,则会创建一个全新的Activity对象,并压入栈顶。

弄明白了SingleTask启动模式的特点之后,我们在测试SingleTop模式的代码的基础上,加以修改。

首先,在MainActivity.java中加上:

    @Override    protected void onRestart() {        super.onRestart();        Log.d(TAG, "onRestart");    }

接着,在SecondActivity.java中加上:

    @Override    protected void onDestroy() {        super.onDestroy();        Log.d(TAG, "onDestroy");    }

最后,在AndroidManifest.xml将MainActivity的启动模式改为SingleTask。

再次运行代码,发现在LogCat得到的日志信息为:
这里写图片描述

通过日志,我们也验证了SingleTask启动模式的特征:
1.当我们初次启动程序时,返回栈还是空的,所以系统会创建一个全新的MainActivity对象,压入栈顶。
2.当我们点击按钮,跳转到SecondActivity。此时,返回栈中只有一个MainActivity对象,所以这次会创建一个SecondActivity的全新对象,并压入栈顶。
3.当我们在SecondActivity点击跳转按钮,想要返回到MainActivity。此时,系统检查到返回栈中已经存在MainActivity的对象了,但它并不位于栈顶。于是则会直接使用该对象,放到栈顶的位置,于是我们看到了MainActivity的生命周期方法onRestart得到了执行;
4.于此同时,除了将MainAcitivity压入栈顶之外,我们前面说到,还会降位于其之上的活动对象清除出栈,所以SecondActivity对象被清除,于是我们还看到了SecondActivity的onDestroy方法得到了执行。
4.最后,当我们点击back键,会发现直接就退出应用了。

实际应用场景模拟:
借助一些情景模拟,可以帮助我们进行更好的理解。
假设现在有这样一种情况,例如现在我们的应用中有一个聊天界面。在聊天界面中,我们可以点击当前某个按钮,查看聊天对象的个人资料。
当进入到资料查看界面后,例如,又有用户相册,我们点击进入到相册界面。以此类推……经过一系列操作后,我们已经跳转了多个Activity了。
此时,我们想要返回到聊天界面,回复聊天消息。这个时候,应当提供给用户应当怎么的操作方式?

首先,当然可以通过点击系统回退键,一步一步回退到聊天界面,但这样的操作未免过于枯燥和麻烦和操蛋。
所以,我们可以提供一个按钮“返回聊天”,直接从当前界面跳转回聊天界面。
也就是比如点击该按钮通过”startActivity(AlbumActivity.this,ChatActivity.class);”的方式来返回聊天界面。
那么这里,SingleTask就派上了用场。

试想一下,假如我们没有对Activity的启动模式做任何显示声明,其启动模式为默认的Standard模式。
那么,虽然当用户点击按钮跳转到聊天界面后,带来的不妥显而易见,我们来加以分析一下:

  1. 首先,这样做系统会为我们创建一个全新的”ChatActivity”的实例。那么也就是说,我们将从新进行一次Activity的生命初始化。并且之前的一些临时缓存数据,也将丢失。
  2. 虽然我们看似达到了目的,直接“回到了”聊天界面。但是,这个时候,从用户的思考角度出发,会理所应当的认为:在回到聊天界面之后,他再次点击回退键,应用则应该退回到一个他最初进入到聊天界面之前的界面。例如用户最初是在联系人列表界面,点击某个好友的头像进入到了聊天界面。那么,正确的逻辑则会为,此次点击back键盘之后,应该回到的,则是这个联系人列表界面。
  3. 但实际上,Standard启动模式下,应用却会回到之前的“相册界面”。而想要回到最初的联系人列表界面,则又回到了我们上面说的老问题,需要经历一些列的back按键操作。那么可能最后导致的就是,用户晕了!

而SingleTask则正好可以完美解决上述的问题,假设我们将聊天界面的启动模式设置为了SingleTask。那么:
1.当我们从相册界面跳转回聊天界面的时候,系统首先会检测到返回栈中已经有一个聊天界面的对象。则会直接找到这个对象,将其放到栈顶。
2.而之前位于聊天界面之上的所有活动,从用户操作的角度来说,既是用户在通过聊天界面点击按钮进入到个人资料查看界面之后所跳转的一系列Activity,都会被清除出栈。
3.这个时候,当用户在跳转回的聊天界面再次点击back键的时候,整个返回栈中的情况就是,聊天界面的activity位于栈顶,其之下紧跟着的就是其最初进入聊天界面之前的activity,所以自然的,也就会清楚的回到用户希望回到的界面。

4.SingleInstance 实例唯一模式

  • 当Activity被设置为该启动模式后,会有一条新建的返回栈专门管理里该Activity。所以,不管是哪个应用想要调用或访问该Activity,都会在该条栈里进行操作。
  • 那么,顾名思义,我们也能够发现:简单的来说,如果说SingleTask能够保证,在同一条返回栈里,一个Activity永远只会有唯一一个对应的实例的话。SingleInstance则能够保证,在当前系统下,该Activity永远只会有唯一的一个实例

SingleInstance模式,被认为是最为复杂的一种启动模式。
怎么说呢,之前我们说到的三种启动模式,其Activity的管理都是在同一条返回栈当中进行的。
简单的理解来说,当一个应用启动,系统就会分配一条专门的返回栈来管理该应用的所有activity。
那么这时,就出现了一种之前的3种启动模式解决不了的情况:
假设,我们希望我们的应用能和另一个应用共享某个Activity的实例。

这样说,可能很难以理解,那么举例来说:
1.现在我们的手机上有“某某地图”以及我们自己的应用。
2.我们希望在我们的应用里共享“某某地图”当中的地图功能界面。

那么,假定的操作就应该为,例如:
1.首先,假设我们在“某某地图”里已经做了一定操作,地图界面被我们定位到了成都市。
2.我们返回了HOME界面,打开了自己的应用,在自己的应用里打开了”某某地图”的地图界面。
3.那么,所谓共享该Activity实例,我们要达到的效果就是,当我们打开地图界面,此时地图上显示的位置就应该是我们之前定位到的成都市。而不是地图的初始化显示方位。

那么,显然,通过此前的3种启动模式,我们是实现不了的。因为:
我们最初在“某某地图”中进行定位时,activity是位于该应用的返回栈里的。
当我们在自己的应用再次调用地图界面,系统的操作是,在我们自己的应用的返回栈里新建一个地图界面Activity的实例对象。
所以实际上两个“地图界面”是位于两个不同应用的各自的返回栈里的,两个毫无关联的Activity实例。

我们可以通过代码,来验证一下我们的猜想。

我们现在编写两个Activity,一个MainActivity用以代表我们自己的应用,一个SecondActivity存在于另一个应用。

SecondActivity.java:

    @Override    protected void onCreate(Bundle savedInstanceState) {        // TODO Auto-generated method stub        super.onCreate(savedInstanceState);        Log.d("SecondActivity", "Task ID ==>> "+ getTaskId() + ",class object ==>> "+this.toString());        this.setContentView(R.layout.activity_second);    }

MainActivity.java:

@Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Log.d(TAG, "Task ID ==>> "+getTaskId() + ",class object ==>> "+this.toString());        setContentView(R.layout.activity_main);        mButton = (Button) this.findViewById(R.id.btn_standard_mode);        mButton.setOnClickListener(new View.OnClickListener() {            @Override            public void onClick(View v) {                Intent mIntent = new Intent();                ComponentName comp = new ComponentName("com.tsr.test","com.tsr.test.SecondActivity");                mIntent.setComponent(comp);                mIntent.setAction("android.intent.action.MAIN");                startActivity(mIntent);            }        });    }

运行程序进行测试,得到如下输出结果,分别是:
这里写图片描述
这里写图片描述

从输出结果,我们可以看到:
MainActivity代表的应用,其返回栈ID为4;SecondActivity代表的另一个应用,返回栈ID为3。
当初次打开SecondActivity应用的时候,在Task“3”里新建了一个实例对象@5271b580。
而当我们在MainActivity里再次调用SecondActivity时,在我们自己的应用Task“4”里再次新建了对象@526fbc4c。

从结果,我们可以看到,在标准的Standard模式下,无法实现在不同应用间共享某个Activity的需求。

那么,我们将SecondActivity的启动模式设为SingleInstance,再对代码进行测试,得到输出结果:
这里写图片描述
这里写图片描述

这次,我们看到:
MainActivity位于Task“8”当中,当我们在自己的应用MainActivity当中调用SecondActivity,系统新建了返回栈Task“9”,并在该返回栈中放入一个全新的SecondActivity的实例对象。
此时,当我们再打开SecondActivity本身所在的应用,调用SecondActivity,系统则会复用Task“9”当中的对象,而不会去做其他操作了。
从而,也就是实现了我们的目的,在两个应用间共享某个Activity。

另外,对于SingleInstance启动模式,还有另一个特点值得注意。
以《第一行Android代码》中的用例来说,假设现在有如下情况:

现在,我们的应用里有三个Activity,分别为:FirstActivity,SecondActivity,ThirdActivity。
它们之间的跳转关系是:FirstActivity跳转到SecondActivity,SecondActivity跳转到ThirdActivity。
其中,SecondActivity的启动模式被设定为了SingleInstance。

那么,当完成所有跳转后,点击回退键,发生的事情需要值得注意一下:
当在ThirdActivity点击back后,回到的不是SecondActivity,而是FirstActivity。
而在FirstActivity再次点击back键后,才会回到SecondActivity,再次点击才会退出应用。

相信在上面的了解过后,你已经能够分析出原因,这是因为:
因为SecondActivity被设置为SingleInstance启动模式,所以当跳转时,系统会新开一条返回栈来管理它。
假设之前的FirstActivity存在于ID为1的栈里,那么SecondActivity存在于ID为2的栈里。
当由SecondActivity跳转至ThirdActivity时,ThirdActivity则仍然加入到ID位1的栈里。

所以最后的情况是:
ID为1的栈里,由上到下,分别是ThirdActivity,FirstActivity。而:
ID为2的栈里,则单独存放着SecondActivity。
所以当ThirdActivity出栈,紧接着进入栈顶的也就是FirstActivity。
而FirstActivity出栈后,ID为1的栈被清空,才轮到ID为2的栈里的活动与用户交互。

5.归纳总结

我们已经分别验证和理解了Android当中,Activity4种不同启动模式的特点。
最后我们加以总结,其实4种启动模式的命名很吊,我们在加以理解之后,实际上根据其名字,就可以很直观的记住它们的特点。

  • Standard 模式,默认的启动模式。特点是:不管任何情况,只要调用启动一次Activity,系统就会创建一个全新的实例对象,放入栈顶。
  • SingleTop模式,我们可以理解为:栈顶唯一模式。所以特点就是:对于位于栈顶的活动,系统保证其对象唯一性。也就是说,只要调用启动的目标Activity正位于栈顶,那么系统就绝对不会再重复创建其实例对象。
  • SingleTask模式,我们可以理解为:返回栈唯一模式。所以特点就是:在同一条返回栈里,一个Activity无论重复调用、启动多少次,它都只会有唯一的一个实例存在与该返回栈中。如果栈里当前不存在,则新建。若存在,则将其放到栈顶,并将其之上的活动实例都清除出栈。
  • SingleInstance模式,我们可以理解为:实例唯一模式。自然其特点则为:在当前运行系统中,该Activity只会有唯一的一个实例。系统会在该Activity初次启动时,新分配一条返回栈专门管理它。之后无论多少个不同的应用想要调用、启动该Activity,都是共用该返回栈里这个活动的唯一实例。
3 0