前言
新年伊始,本打算大展宏图的本人却一直处于项目经理的忽视之中,终于的终于,本人迎来了新年的第一个重磅需求。
作为一个拥有上亿用户的APP,本APP的用户条款竟然未受到任何投诉和质疑,已经被告上法庭的某APP决定也使用我们的用户条款。设计
Android松耦合的应用组织方式一直是开发者的小确幸,理所当然,我将原来的用户条款dialog封装至Activity里,Activity背景做成透明,再将Acitivty提供给第三方APP调用,这样就大功告成啦。
代码
Activity需要在menifest中声明,恩,经验老道的我当然知道,直接copy一个过来,再把名字改一下。
public class DialogActivity extends Activity{ private AlertDialog noticeDialog; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); showNoticeDialog(); } private void showNoticeDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(DialogActivity.this); builder.setTitle(getString(R.string.title)); builder.setMessage(R.string.xxx_content); builder.setPositiveButton(getString(R.string.ok), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { DialogActivity.this.finish(); } }); builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { DialogActivity.this.finish(); } }); noticeDialog = builder.create(); noticeDialog.show(); }}
再在第三方应用和自己的应用里分别写一个测试调用的Acitivity,分别叫AlphaActivity、BetaActivity,Alpha背景色为橙色,Beta为绿色,BetaActivity的顶部有一段透明区域。run一下看看,(1)进入B应用,点击BetaActivity中的按钮,弹出对话框,取消,按返回键退出应用B;(2)点击A应用桌面图标进入AlphaActivity,点击按钮,弹出对话框。
(1)
(2)
一切正常,恩,下班找MM去咯。...要不再试一次吧,增加成就感,也避免老是被老大说粗心大意,
(1)进入B应用,点击BetaActivity中的按钮,弹出对话框,按Home键返回桌面;(2)点击A应用桌面图标进入AlphaActivity,点击按钮,弹出对话框。
咦,调用对话框怎么把BetaActivity也拉起来了。
分析
首先看看DialogActivity的声明,声明中有这样一个东西,
android:launchMode="singleTask"
看名字好高深,赶紧去developers补补脑,恩,原来是这样,
1.Task 和 BackStack
Task是用户所交互的activity的序列,通常情况下,一个Task是从用户点击app图标首次运行一个app开始的,在不返回Home界面的情况下,后续所跳转的所有Activity都属于该Task。
一个Task中的所有Activity都是放在一个栈中去管理的,这个栈就是BackStack,每一个Task都有一个BackStack。在简单的场景下,用户每打开一个新的Activity,这个Activity就会被Push到BackStack顶部,用户按Back键退出一个Acticity时,这个Activity会被Pop出BackStack并Destroy。
Task的状态也分为前台和后台(Background),当我们按Home键时,整个Task都会被切换到后台,当Task处于后台时,Task中所有Activity都处于stopped状态。
盗一张developers的图:
2.launchMode
launchMode用来定义Activity加载进Task的方式,它有以下四种:
(1)standard
如果不声明的话默认使用的就是这种方式,当你使用Intent启动一个Activity时,系统每次都会创建一个新的实例,并将该实例放入当前的BackStack中。在这种模式下,一个Activity可以被实例化多次,一个Task中可以有该Activity的多个实例,多个实例也可能在不同的Task中。(2)singleTop
在这种模式下,如果某个Activity的实例处于BackStack顶部,那么当有Intent启动该Activity时,系统将会使用顶部的该Activity实例而不会创建新的实例。如果某个Activity的实例并不处于栈顶的话,系统将会创建新的实例,和standard一样。(3)singleTask
在该模式下,一个Activity只存在于一个Task中,当有Intent启动该Activity时,系统会将该Activity所在Task的BackStack Push到调用该Activity的Task的栈顶,看google的一张图可以清晰的理解该方式:
(4)singleInstance
该模式与singleTask的唯一区别就是,一个Task中只存在一个Avtivity,换句话说,只要一个Activity不存在,系统创建它时都会为它创建一个Task。问题回溯
再回头来看看我们的问题,我将DialogActivity的launchmode声明为singleTask,当BetaActivity调用DialogActivity后,栈是这样的,打开Hierarchy Viewwer看看,
如果我们按返回键,DialogActivity将会出栈并destroy,
而如果我们按home键,DialogActivity将会移向栈底,launcher重新压入栈顶,
第二次测试时,我正是按home键而不是back键返回桌面的,再从A应用进入AlphaActivity, AlphaActivity被压入栈顶,
点击按钮,打开对话框,
这时我们看到DialogActivity的整个Task栈(BetaActivity在栈底)被压入了栈顶,这就出现了我们不希望看到的场景。
我们将launchmode改为standard看看,
AlphaActivity拉起了一个新的DialogActivity实例,这次就满足要求啦。
最后再看看singleInstance,
只有DialogActivity被重新压入了栈顶,BetaActivity仍在栈的底部,这样也满足要求。
结语
Android是弱化了Application这个概念的,一个应用可以理解为一串Acitivity的交互序列,而不局限于一个进程本身。当一个Activity可能被不同应用调用时,如何设计launchmode很重要,如果Acitivity在不同应用中的调用层次不同,就应谨慎使用SingleTask。
最后祝大家周末开(jia)心(ban)。