码仔,今天就给大家带来了《每日一道面试题》的第十期:
01
标记—清除 Mark-Sweep 过程:标记可回收对象,进行清除 缺点:标记和清除效率低,清除后会产生内存碎片
复制算法 过程:将内存划分为相等的两块,将存活的对象复制到另一块内存,把已经使用的内存清理掉 缺点:使用的内存变为了原来的一半 进化:将一块内存按8:1的比例分为一块Eden区(80%)和两块Survivor区(10%) 每次使用Eden和一块Survivor,回收时,将存活的对象一次性复制到另一块Survivor上,如果另一块Survivor空间不足,则使用分配担保机制存入老年代
标记—整理 Mark—Compact 过程:所有存活的对象向一端移动,然后清除掉边界以外的内存
分代收集算法 过程:将堆分为新生代和老年代,根据区域特点选用不同的收集算法,如果新生代朝生夕死,则采用复制算法,老年代采用标记清除,或标记整理
02
Final,Finally,Finalize的区别
final 用于声明属性,方法和类,分别表示属性不可变,方法不可覆盖,类不可继承。 内部类要访问局部变量,局部变量必须定义成final类型.
finally是异常处理语句结构的一部分,表示总是执行。
finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。 JVM不保证此方法总被调用
03
简述App启动过程
App启动一般分为两种:
冷启动:当应用启动的时候,后台没有当前应用的进程,这时系统会创建一个新的进程分配给应用。
热启动:当前应用已经打开,但是被按下返回键或者Home键退出到桌面或者去到其他App,当再次回到应用时就是热启动。
这里主要介绍冷启动,大致分为5个步骤:
当点击桌面图标,就会利用Launcher通过Binder进程间通信机制通知ActivityManagerService(AMS),它要启动一个Activity;
AMS得到Launcher的通知,就会新建一个Task去准备启动Activity,并通过Binder机制通知Launcher进入Paused状态;
Launcher得到消息,就会直接挂起,并通过Binder告诉AMS我已经Paused了;AMS知道了Launcher已经挂起之后,就可以放心的为新的Activity准备启动工作了,首先,APP肯定需要一个新的进程去进行运行,所以需要创建一个新进程,这个过程是需要Zygote参与的,AMS通过Socket去和Zygote协商,然后利用Zygote.fork()创建一个新的进程,在这个进程里启动ActivityThread类,这就是每一个应用程序都有一个ActivityThread与之对应的原因;
进程创建好了,通过调用上述的ActivityThread的main方法,这是应用程序的入口,在这里开启Looper消息循环队列,这也是主线程默认绑定Looper的原因;(另外,ActivityThread通过Binder将一个ApplicationThread类型的Binder对象传递给AMS,以便以后AMS能够通过这个Binder对象和它进行通信);
这时候,App还没有启动完,要永远记住,四大组件的启动都需要AMS去启动,将上述的应用进程信息注册到AMS中,所以AMS在通过BinderActivityThread,现在一切准备就绪,它可以真正执行Activity的启动操作了。
补充知识: Zygote zygote名字翻译叫受精卵,zygote进程的创建是由Linux系统中init进程创建的,Android中所有的进程都是直接或者间接的由init进程fork出来的,Zygote进程负责其他进程的创建和启动,比如创建SystemServer进程。当需要启动一个新的android应用程序的时候,ActivityManagerService就会通过Socket通知Zygote进程为这个应用创建一个新的进程。
Launcher 我们要知道手机的桌面也是一个App我们叫它launcher,每一个手机应用都是在Launcher上显示,而Launcher的加载是在手机启动的时候加载Zygote,然后Zygote启动SystenServer,SystenServer会启动各种ManageService, 包括ActivityManagerService,并将这些ManageService注册到ServiceManage 容器中,然后ActivityManagerService就会启动Home应用程序Launcher.
ActivityManagerService ActivityManagerService我们简称AMS,四大组件都归它管,四大组件的跨进程通信都要和它合作。
Binder Binder是Android跨进程通信(IPC)的一种方式,也是Android系统中最重要的特性之一,android 四大组件以及不同的App都运行在不同的进程,它则是各个进程的桥梁将不同的进程粘合在一起。
ActivityThread 首先ActivityThread并不是一个Thread,其作用就是在main方法内做消息循环。那我们常说的主线程是什么?主线程就是承载ActivityThread的Zygote fork而创建的进程。 ActivityThread的调用是在ActivityManageService.startProcessLocked()方法里调用并创建,这个类主要做了这几个事:
创建Looper,开启Looper循环
创建内部类 H,H继承于Handler 用于跨进程通信切换线程
创建ApplicationThread跨进程Binder对象mAppThread。 这里要说一点,ActivityThread通过ApplicationThread与AMS进行通信,ApplicationThread通过H与ActivityThread进行通信(handler机制),处理Activity的事务。
04
四种LaunchMode及其使用场景
standard 模式 这是默认模式,每次激活Activity时都会创建Activity实例,并放入任务栈中。使用场景:大多数Activity。
singleTop 模式 如果在任务的栈顶正好存在该Activity的实例,就重用该实例( 会调用实例的 onNewIntent() ),否则就会创建新的实例并放入栈顶,即使栈中已经存在该Activity的实例,只要不在栈顶,都会创建新的实例。使用场景如新闻类或者阅读类App的内容页面。
singleTask 模式 如果在栈中已经有该Activity的实例,就重用该实例(会调用实例的 onNewIntent() )。重用时,会让该实例回到栈顶,因此在它上面的实例将会被移出栈。如果栈中不存在该实例,将会创建新的实例放入栈中。使用场景如浏览器的主界面。不管从多少个应用启动浏览器,只会启动主界面一次,其余情况都会走onNewIntent,并且会清空主界面上面的其他页面。
singleInstance 模式 在一个新栈中创建该Activity的实例,并让多个应用共享该栈中的该Activity实例。一旦该模式的Activity实例已经存在于某个栈中,任何应用再激活该Activity时都会重用该栈中的实例( 会调用实例的 onNewIntent() )。其效果相当于多个应用共享一个应用,不管谁激活该 Activity 都会进入同一个应用中。使用场景如闹铃提醒,将闹铃提醒与闹铃设置分离。singleInstance不要用于中间页面,如果用于中间页面,跳转会有问题,比如:A -> B (singleInstance) -> C,完全退出后,在此启动,首先打开的是B。
05
如何减小apk安装包体积
1.代码混淆 minifyEnabled true
2.资源压缩 1)shrinkResources true 2)微信的AndResGuard
3.图片压缩 1)tinypng 2)svg 3)webp
4.so库配置 只保留两个abi平台,即armeabi和armeabi-v7a
5.dex优化 Facebook的redex
06
结束语
如果你有好的答案可以提交至:
https://github.com/codeegginterviewgroup/CodeEggDailyInterview
往期文章:
《每日一道面试题》第一期
《每日一道面试题》 第二期
《每日一道面试题》 第三期
《每日一道面试题》第四期
《每日一道面试题》第五期
《每日一道面试题》 第六期
《每日一道面试题》 第七期
《每日一道面试题》 第八期
《每日一道面试题》 第九期
专属社群:
《这件事情,我终于想明白了》
今日问题:
大家最近有看面试题吗?