Android面试一天一题(Day 19:程序员何苦为难程序员(上))

2010年从韩国回来时,我也是信心爆满,感觉三星的Android项目都能做下来了,Android的开发水平那是杠杠的。有一次想跳槽,面试一家公司时就被问了Activity启动模式的问题,微胖的面试官问我怎么看待四种启动模式,我吧嗒吧嗒后,面试官接着问我Launcher这个应用的Home界面(一般是指Launcher.java)用的是哪种模试。

我自信的回答用singleInstance,要不是面试官早有准备,估计他都要被我的自信弄得要开始怀疑人生。我的相法很简单,认为它全局只有一个实例而且应该只有一个实例,用singleInstance最好。

当我回来查询Launcher的源代码时发现使用的是SingleTask模式。之后虽然拿到了offer,但我仍然为这个问题耿耿于怀。当我后来到MTK公司工作时,才对Android的四种模式有了更深入的理解。

面试题:Activity的启动模式(launchMode)有哪些,有什么区别?

这应该是一道很虐人的面试题,很多人都答不上来,很多人根本就没有用过。当我发现在被我面试的人中有80%的比例对它不了解时,我找过一些同事讨论是否还有在面试中考查这个问题的必要,得到的回答是“程序员何苦为难程序员”!

因为很多程序员都认为这个启动模式没有多大用处。好吧,我用一个实际中很容易遇到的问题来引出它有多么有用。

很多人在使用startActivityForResult启动一个Activity时,会发现还没有开始界面跳转本身的onActivityResult马上就被执行了,这是为什么呢?

遇到过吧,我见过很多人为了这个问题抓耳挠腮的。在Activity.java的startActivityForResult方法上看一下官方的说明吧:

     * <p>Note that this method should only be used with Intent protocols
     * that are defined to return a result.  In other protocols (such as
     * {@link Intent#ACTION_MAIN} or {@link Intent#ACTION_VIEW}), you may
     * not get the result when you expect.  For example, if the activity you
     * are launching uses the singleTask launch mode, it will not run in your
     * task and thus you will immediately receive a cancel result.

很多人出现这个问题,确实是因为startActivityForResult启动的Activity设置了singleTask的启动模式。但是,除了这种情况还有可能会马上执行吗?

有,而且很多。如下面表格,左边第1列代表MainActivity的启动模式,第一行代表SecondActivity(即要startActivityForResult启动的Activity)的启动模式,打叉代表在这种组合下onActivityResult会被马上调用。

|stand|singleTop| singleTask | singleInstance
----|:------:|:----:|:----:|:----:
stand|√|√|x| x
singleTop|√|√| x|x
singleTask|√|√| x |x
singleInstance|x|x|x|x

好在幸运的是,Android在5.0及以后的版本修改了这个限制。也就是说上面x的地方全部变成了√。

那么在Android 5.0后,还会有这个问题吗?

还是会的。如在Intent中设置了FLAG_ACTIVITY_NEW_TASK再startActivityForResult,即使是标准的启动模式仍然会有这个问题。

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

Log如下:

07-12 14:21:14.849 20774-20774/net.goeasyway.test I/MainActivity: onCreate
07-12 14:21:14.875 20774-20774/net.goeasyway.test I/MainActivity: onResume
07-12 14:21:19.995 20774-20774/net.goeasyway.test I/MainActivity: onPause
07-12 14:21:19.995 20774-20774/net.goeasyway.test I/MainActivity: onActivityResult requestCode=1 resultCode=0
07-12 14:21:19.996 20774-20774/net.goeasyway.test I/MainActivity: onResume
07-12 14:21:19.996 20774-20774/net.goeasyway.test I/MainActivity: onPause
07-12 14:21:20.005 20774-20774/net.goeasyway.test I/SecondActivity: onCreate
07-12 14:21:20.018 20774-20774/net.goeasyway.test I/SecondActivity: onResume

注意:MainActivity的onResume也会被触发。因为onActivityResult被执行时,它会重新获得焦点。很多人也会遇到onResume被无故调用,也许就是这种情况。

所以,最终我们发现只要是不和原来的Activity在同一个Task就会产生这种立即执行onActivityResult的情况,从原代码也可以得到验证,详情查看ActivityStackSupervisor.java

        if (r.resultTo != null && (launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) != 0
                && r.resultTo.task.stack != null) {
            // For whatever reason this activity is being launched into a new
            // task...  yet the caller has requested a result back.  Well, that
            // is pretty messed up, so instead immediately send back a cancel
            // and let the new task continue launched as normal without a
            // dependency on its originator.
            Slog.w(TAG, "Activity is launching as a new task, so cancelling activity result.");
            r.resultTo.task.stack.sendActivityResultLocked(-1,
                    r.resultTo, r.resultWho, r.requestCode,
                    Activity.RESULT_CANCELED, null);
            r.resultTo = null;
        }

原因

其实上面代码中的英文注解也说得很清楚了,Android认为不同的Task之间对这种要求返回结果的启动方式会产生一些依赖(对Task),所以干脆简单粗暴在跳转前直接返回RESULT_CANCELED结果。

我们还是用一个例子简单解释一下,如下图,有两个任务栈(stack),处于前可视状态的是“Back Stack”也叫返回栈,处理后台的是“Background Task”。

当“Activity 2”通过startActivityForResult启动“Activity Y”时,“Background Task”中的Activity会被压入返回栈的栈顶。这种情况下,如果没有在跳转前直接返回RESULT_CANCELED给“Activity 2”,那么按Back键,应该要跳转到“Activity X”,而按Back键“Activity Y”就会调用finish会发送Result给启动它的“Activity 2”。这时就很难搞清楚,到底是“Activity 2”还是“Activity X”应该获得焦点了,会产生一些混乱或是违反的原有的一些约定。

小结

关于启动模式的问题,其实我开始写这个系统的文章时就想介绍它的,不过发现它的水实现太深了,需要用比较长的篇幅才能说明清楚。今天也只是通过一个实际中容易碰到的问题引起大家的关注,也同时引出了“任务”和“返回栈”。

所以,就让程序员多为难程序员一次,进一步的说明请听下回分解。

Even 原创
简书账号goeasyway:http://www.jianshu.com/users/f9fbc7a39b36/latest_articles
转载请注明出处。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容