Android面试题-深刻剖析activity启动模式(三)

Android程序员面试宝典

自定义控件

  • 一分钟实现贴纸功能
  • 一分钟实现TextView高亮
  • 一分钟实现新手引导页
  • 一分钟实现ViewPager卡片
  • 一分钟实现轮播图
  • 一分钟实现GridView拖拽
  • 一分钟实现底部导航栏
  • 一分钟实现底部FragmentTabhost
  • 一分钟实现多张图片选择
  • 一分钟实现仿美拍直播的点赞动画
  • 一分钟实现高仿今日头条视频列表
  • 一分钟实现购物车加减控件
  • 一分钟实现省市县三级联动
  • 一分钟实现二维码生成和扫描
  • 一分钟实现沉浸式状态栏
  • 一分钟实现图片裁剪
  • 一分钟实现视频弹幕
  • 一分钟实现图片缩放
  • 一分钟实现旋转选择器
  • 一分钟实现ofo小黄车的引导界面
  • 一分钟实现自定义ImageView外貌
  • 一分钟实现向左拖拽跳转详情页
  • 一分钟实现QQ首页动画特效
  • 一分钟实现ViewPager上下滑动

联网

  • 一分钟学会Retrofit并且进行实战
  • 一分钟实现OkHttp3

工具

  • 一分钟实现RecyclerView
  • 一分钟实现H5和Android通信
  • 一分钟实现pinyin4j
  • 一分钟实现分析内存泄漏

数据库

  • 一分钟实现LitePal数据库
  • 一分钟实现ormlite数据库
  • 一分钟实现GreenDao数据库

源码分析相关面试题

  • Volley源码分析
  • 注解框架实现原理
  • okhttp3.0源码分析
  • onSaveInstanceState源码分析
  • 静默安装和源码编译

Activity相关面试题

  • 保存Activity的状态
  • 深刻剖析activity启动模式(一)
  • 深刻剖析activity启动模式(二)
  • 深刻剖析activity启动模式(三)
  • Activity Task和Process之间的关系
  • 源码分析service开启Activity抛异常?activity不会抛异常
  • Activity优雅退出
  • onCreate源码分析

Service相关面试题

  • IntentService源码分析
  • Service是否在main thread中执行, service里面是否能执行耗时的操作?
  • Service不死之身

与XMPP相关面试题

  • XMPP协议优缺点
  • 极光消息推送原理

与性能优化相关面试题

  • 内存泄漏和内存溢出区别
  • UI优化和线程池实现原理
  • 代码优化
  • 内存性能分析
  • 内存泄漏检测
  • App启动优化
  • 与IPC机制相关面试题

与登录相关面试题

  • oauth认证协议原理
  • token产生的意义
  • 微信扫一扫实现原理

与开发相关面试题

  • 迭代开发的时候如何向前兼容新旧接口
  • 手把手教你如何解决as jar包冲突
  • context的原理分析
  • 解决ViewPager.setCurrentItem中间很多页面切换方案
  • 字体适配
  • 软键盘适配
  • 机型适配,例如三星、小米、华为、魅族等
  • CardView 设置水波纹效果

与人事相关面试题

  • 人事面试宝典

前面介绍了通过launchMode设置Activity的启动模式。本章接着介绍Activity的启动模式相关内容,讲解的内容是Intent与启动模式相关的Flag,以及android:taskAffinity的属性。

Intent与启动模式相关的Flag简介

这里仅仅对几个常用的与启动模式相关的Flag进行介绍。

FLAG_ACTIVITY_NEW_TASK

很少单独使用FLAG_ACTIVITY_NEW_TASK,通常与FLAG_ACTIVITY_CLEAR_TASK或FLAG_ACTIVITY_CLEAR_TOP联合使用。因为单独使用该属性会导致奇怪的现象,通常达不到我们想要的效果!尽管如何,后面还是会通过"FLAG_ACTIVITY_NEW_TASK示例一"和"FLAG_ACTIVITY_NEW_TASK示例二"会向你展示单独使用它的效果。

FLAG_ACTIVITY_SINGLE_TOP

在google的官方文档中介绍,它与launchMode="singleTop"具有相同的行为。实际上,的确如此!单独的使用FLAG_ACTIVITY_SINGLE_TOP,就能达到和launchMode="singleTop"一样的效果。

FLAG_ACTIVITY_CLEAR_TOP

顾名思义,FLAG_ACTIVITY_CLEAR_TOP的作用清除"包含Activity的task"中位于该Activity实例之上的其他Activity实例。FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_NEW_TASK两者同时使用,就能达到和launchMode="singleTask"一样的效果!

FLAG_ACTIVITY_CLEAR_TASK

FLAG_ACTIVITY_CLEAR_TASK的作用包含Activity的task。使用FLAG_ACTIVITY_CLEAR_TASK时,通常会包含FLAG_ACTIVITY_NEW_TASK。这样做的目的是启动Activity时,清除之前已经存在的Activity实例所在的task;这自然也就清除了之前存在的Activity实例!
注意:当同时使用launchMode和上面的FLAG_ACTIVITY_NEW_TASK等标签时,以FLAG_ACTIVITY_NEW_TASK为标准。也就是说,代码的优先级比manifest中配置文件的优先级更高!
下面,通过几个实例加深对这几个标记的理解。

FLAG_ACTIVITY_NEW_TASK标签

配置形式:




    
        
            
                

                
            
        
        
    


说明:

在该实例中,有两个MainActivity和SecondActivity。

使用案例:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.button).
                setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
            }
        });
        Log.i("maweiqi", "onCreate:" + getClass().getSimpleName() +
                " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
    }
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        Log.i("maweiqi", "onNewIntent:" + getClass().getSimpleName() +
                " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());

    }
}

注意,跳转的Intent添加了FLAG_ACTIVITY_NEW_TASK标志。

public class SecondActivity extends AppCompatActivity {

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        Button button = (Button) findViewById(R.id.button2);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(SecondActivity.this, MainActivity.class);
                startActivity(intent);
            }
        });
        Log.i("maweiqi", "onCreate:" + getClass().getSimpleName() +
                " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
    }



    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        Log.i("maweiqi", "onNewIntent:" + getClass().getSimpleName() +
                " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());

    }
}

测试方式:

MainActivity--> SecondActivity --> MainActivity--> SecondActivity

输出的日志如下:

onCreate:MainActivity TaskId: 73 hasCode:195694529
onCreate:SecondActivity TaskId: 73 hasCode:54326677
onCreate:MainActivity TaskId: 73 hasCode:121740648
onCreate:SecondActivity TaskId: 73 hasCode:5761837

在命令行中执行以下命令 adb shell dumpsys activity ,有以下输出:

Running activities (most recent first):
    TaskRecord{e3d17e2 #73 A=com.open.android.task5 U=0 sz=4}
       Run #3: ActivityRecord{a34fa00 u0 com.open.android.task5/.SecondActivity t73}
       Run #2: ActivityRecord{21172da u0 com.open.android.task5/.MainActivity t73}
       Run #1: ActivityRecord{b1e1e64 u0 com.open.android.task5/.SecondActivity t73}
       Run #0: ActivityRecord{f015a6e u0 com.open.android.task5/.MainActivity t73}

    mResumedActivity: ActivityRecord{a34fa00 u0 com.open.android.task5/.SecondActivity t73}

测试结果:

1)全部在同一个task中

2)全部创建不同的实例

那如果MainActivity跳转去掉FLAG_ACTIVITY_NEW_TASK?在SecondActivity清单文件添加singleTask,代码和流程跟之前一样,只贴出清单文件配置

配置如下:


测试方式:

MainActivity--> SecondActivity --> MainActivity--> SecondActivity

输出的日志如下:

onCreate:MainActivity TaskId: 74 hasCode:195694529
onCreate:SecondActivity TaskId: 74 hasCode:54326677
onCreate:MainActivity TaskId: 74 hasCode:38155814
onNewIntent:SecondActivity TaskId: 74 hasCode:54326677

在命令行中执行以下命令 adb shell dumpsys activity ,有以下输出:

Running activities (most recent first):
   TaskRecord{46d0de2 #74 A=com.open.android.task5 U=0 sz=2}
      Run #1: ActivityRecord{b31602f u0 com.open.android.task5/.SecondActivity t74}
      Run #0: ActivityRecord{f88d9dc u0 com.open.android.task5/.MainActivity t74}

    mResumedActivity: ActivityRecord{b31602f u0 com.open.android.task5/.SecondActivity t74}

测试结果:

1)FLAG_ACTIVITY_NEW_TASK的作用和singleTask具有不同的效果

那如果两个Activity的android:taskAffinity不相同呢?此时会导致什么效果呢?下面,我们通过示例来看看效果。

配置如下:


代码回到最初MainActivity还是添加Flag标记,其他省略,如下:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.button).
                setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent(MainActivity.this, SecondActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
            }
        });
        Log.i("maweiqi", "onCreate:" + getClass().getSimpleName() +
                " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());
    }
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);

        Log.i("maweiqi", "onNewIntent:" + getClass().getSimpleName() +
                " TaskId: " + getTaskId() + " hasCode:" + this.hashCode());

    }
}

测试方式:

MainActivity--> SecondActivity --> MainActivity--> SecondActivity

输出的日志如下:

onCreate:MainActivity TaskId: 77 hasCode:195694529
onCreate:SecondActivity TaskId: 78 hasCode:54326677
onCreate:MainActivity TaskId: 78 hasCode:38155814

在命令行中执行以下命令 adb shell dumpsys activity ,有以下输出:

Running activities (most recent first):
    TaskRecord{65dda1d #78 A=com.maweiqi.second U=0 sz=2}
       Run #2: ActivityRecord{845c9ed u0 com.open.android.task5/.MainActivity t78}
       Run #1: ActivityRecord{f813328 u0 com.open.android.task5/.SecondActivity t78}
     TaskRecord{dd8e492 #77 A=com.open.android.task5 U=0 sz=1}
       Run #0: ActivityRecord{7a85f99 u0 com.open.android.task5/.MainActivity t77}

    mResumedActivity: ActivityRecord{845c9ed u0 com.open.android.task5/.MainActivity t78}

测试结果:

1)当第二次进入到MainActivity中,再企图从MainActivity中进入到SecondActivity时,没有产生任何效果,仍然停留在MainActivity中!即第二次MainActivity--> SecondActivity压根就没发生!

结果分析:

1)当相互跳转的两个Activity的android:taskAffinity不同时,添加FLAG_ACTIVITY_NEW_TASK:第一次启动SecondActivity时,会新建一个task,并将SecondActivity添加到该task中。这与singleTask产生的效果是一样的!但是,当企图再次从MainActivity进入到SecondActivity时,却什么也没有发生!

2)为什么呢?是因为此时SecondActivity实例已经存在,但是它所在的task的栈顶是MainActivity;而单独的添加FLAG_ACTIVITY_NEW_TASK又不会"删除task中位于SecondActivity之上的Activity实例",所以就没有发生跳转!

FLAG_ACTIVITY_CLEAR_TOP。

修改MainActivity里面的点击事件如下:

Intent intent = new Intent(MainActivity.this, SecondActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 
                        | Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);

            
                

                
            
        

测试方式:

MainActivity--> SecondActivity --> MainActivity--> SecondActivity

日志输出

onCreate:MainActivity TaskId: 80 hasCode:58656936
onCreate:SecondActivity TaskId: 80 hasCode:212434303
onCreate:MainActivity TaskId: 80 hasCode:121740648
onCreate:SecondActivity TaskId: 80 hasCode:15009890

测试结果:

1) MainActivity和SecondActivity在同一个task中!

2) 两个SecondActivity是不同的实例。

结果分析:

这与没有添加FLAG_ACTIVITY_CLEAR_TOP时效果一样!这说明,当相互跳转的两个Activity的android:taskAffinity一样时,不会产生任何效果!

接下来,看看不同android:taskAffinity的情况,代码一致,清单文件修改

文件配置


            
                

                
            
        

测试方式:

MainActivity--> SecondActivity --> MainActivity--> SecondActivity

日志输出

onCreate:MainActivity TaskId: 83 hasCode:195694529
onCreate:SecondActivity TaskId: 84 hasCode:54326677
onCreate:MainActivity TaskId: 84 hasCode:190178689
onCreate:SecondActivity TaskId: 84 hasCode:5761837

在命令行中执行以下命令 adb shell dumpsys activity ,有以下输出:

 Running activities (most recent first):
    TaskRecord{12e942 #84 A=com.maweiqi.second U=0 sz=1}
       Run #1: ActivityRecord{e0674b5 u0 com.open.android.task5/.SecondActivity t84}
     TaskRecord{62b9d53 #83 A=com.open.android.task5 U=0 sz=1}
       Run #0: ActivityRecord{71ec08a u0 com.open.android.task5/.MainActivity t83}

    mResumedActivity: ActivityRecord{e0674b5 u0 com.open.android.task5/.SecondActivity t84}

测试结果:

1)MainActivity和SecondActivity在不同task中!

2) 两个SecondActivity是不同实例。

结果分析:

FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP的使用和android:taskAffinity相关。
在同时使用FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_CLEAR_TOP的情况下,以A启动B来说

1)当A和B的taskAffinity相同时:添加FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_CLEAR_TOP没有任何作用。和没有添加时的效果一样!

2)当A和B的taskAffinity不同时:添加FLAG_ACTIVITY_NEW_TASK|FLAG_ACTIVITY_CLEAR_TOP后,如果该activity没有设置启动模式(即默认是standard),或者intent没有设置一个FLAG_ACTIVITY_SINGLE_TOP 的flag,那么这个activity就会销毁,然后重新创建这个实例

总结:

Activity的启动模式

在AndroidManifest.xml中,可以配置每个activity的启动模式:例如:
android:launchMode="standard"

(1) standard 标准模式

此模式,不管有没有已存在的实例,都生成新的实例。每次调用startActivity()启动Activity时都会创建一个新的Activity放在栈顶,每次返回都会销毁实例并出栈,可以重复创建。

(2) singletop 单一顶部模式

如果任务栈的栈顶存在这个要开启的activity,不会重新创建新的activity,而是复用已存在的activity。保证栈顶如果存在,则不会重复创建,但如果不在栈顶,那么还是会创建新的实例。
应用场景:浏览器的书签

(3) singletask 单一任务模式

是一个比较严格的模式,在当前任务栈里面只能有一个实例存在,当开启activity的时候,就去检查在任务栈里面是否有实例已经存在,如果有实例存在就复用这个已经存在的activity,并且把这个activity上面的所有的别的activity都清空,复用这个已经存在的activity。
应用场景:BrowserActivity 浏览器界面
如果一个activity的创建需要占用大量的系统资源(cpu,内存)一般配置这个activity为singletask的启动模式。webkit内核(c) 初始化需要大量内存 js解析引擎 html渲染引擎 http解析,下载…减少内存开销,cpu占用。播放器的播放Activity

(4) singleInstance

这种启动模式比较特殊,它会启用一个新的任务栈,activity会运行在自己的任务栈里,这个任务栈里面只有一个实例存在并且保证不再有其他Activity实例进入。在整个手机操作系统里面只有一个实例存在。
应用场景:来电页面。InCallScreenActivity

最后的总结:

实话说,关于任务栈的bug有点多,如果真的添加了启动模式,一定要多多测试,有的地方和官方文档描述完全不相符。
stackoverflow上也有人吐糟。
http://stackoverflow.com/questions/9772927/flag-activity-new-task-clarification-needed

  • 欢迎关注微信公众号,长期推荐技术文章和技术视频

  • 微信公众号名称:Android干货程序员

Android面试题-深刻剖析activity启动模式(三)_第1张图片

你可能感兴趣的:(Android面试题-深刻剖析activity启动模式(三))