一、Activity中的几种启动模式
standard:这个是android默认的Activity启动模式,每启动一个Activity都会被实例化一个Activity,并且新创建的Activity在堆栈中会在栈顶。
singleTop:如果当前要启动的Activity就是在栈顶的位置,那么此时就会复用该Activity,并且不会重走onCreate方法,会直接它的onNewIntent方法,如果不在栈顶,就跟standard一样的。
singleTask:该种情况下就比singleTop厉害了,不管在不在栈顶,在Activity的堆栈中永远保持一个。
singleInstance:该种情况就用得比较少了,主要是指在该activity永远只在一个单独的栈中。
二、android消息机制
消息机制指Handler、Looper、MessageQueue、Message之间如何工作的,首先是Looper会创建好带有Looper的线程等待其他线程发送消息过来,一旦有消息之后,Looper取出MessageQueue对头的Message,之后调用了Handler的dispatchMessage方法,把消息传给了Handler,之后消息到了工作线程(主线程)。 这里会问到,一个线程会有几个Looper,几个Handler,以及Looper会存在线程哪里? 一个线程一个Looper,可以有多个Handler,Looper会存在线程的ThreadLocal对象里,该对象是线程的缓存区
ThreadLocal:它是和线程一一对应的,从Thread类可以看出来,ThreadLocal是作为Thread变量来使用。ThreadLocal只是ThreadLocalMap的一个包装类,实现了get和set方法,而ThreadLocalMap实际是一个由Entry内部类组成的数组,Entry是继承自弱应用,弱引用里面放的就是ThreadLocal当前对象,Entry的value存的是当前线程要存储的对象,value作为Entry的成员变量。 ThreadLocal经常会问到内存泄漏的问题,从上面分析可以发现ThreadLocalMap里面的Entry对象存储的ThreadLocal弱引用,而value直接作为Entry的强引用,因此在用到了ThreadLocal的地方,防止内存泄漏,手动调用remove方法。
三、IntentService
IntentService是google在原生的Service基础上通过创建子线程的Service。也就是说IntentService是专门为android开发者提供的能在service内部实现耗时操作的service。我们可以通过重写onHandleIntent方法实现耗时操作的回调处理,而且IntentService在耗时操作完成后,会主动销毁自己,IntentService可以通过多次启动来完成多个任务,而IntentService只会被启动一次。内部是实现了Handler异步处理耗时操作的过程,一般多用在Service中需要处理耗时操作的功能。
四、HandlerThread
HandlerThread本身也是Thread,只是在Thread基础上封装上了Handler的载体,而我们知道一个线程是可以有多个handler,所以用HandlerThread更加方便我们不用关心Handler的创建,一般用在多线程中直接处理任务。
五、事件分发
事件分发主要分三块:分发、拦截、消费; 当我们触摸到屏幕的时候,默认会先走Activity的分发,接着走ViewGroup的分发,然后到ViewGroup的拦截,后面再到View的分发事件,最后会传到View的消费事件,如果View不消费,紧接着回传到ViewGroup的消费事件,如果ViewGroup也不消费,最后回到View的消费事件
这里会问到事件冲突的问题?
事件遵循一个原则,就是看他有没有事件消费。比如一个LinearLayout里面有一个Button,点击LinearLayout会触发到Button吗,这里就看LinearLayout有没有设置点击事件,如果有就不会传递到Button,如果没有就会传递给Button。
六、android性能优化、内存优化
性能优化:可以从界面、apk瘦身、混淆说起,dex分包处理,插件化动态加载模块,开屏冷启动说起
界面优化:多可以使用include、merge、ViewStub、约束布局来做起,include可以提取公共的布局,merge可以减少布局层次、ViewStub是使用的时候才去创建View,减少空间的占用、约束布局一来可以减少布局的层次、二来可以提高开发的效率
apk瘦身:可以用android studio的lint检测工具检测资源文件等
混淆:可以起到文件大小减少的作用,这个在实践中可以尝试,混淆后可以反编译看看apk包的内容
dex分包:主要是apk包的结构发生了变化,如果dex包的方法数超过了最大数,需要进行分包处理
插件化:主要用到了java中动态代理模式和反射的思想,利用android的activity启动流程,通过动态代理模式动态加载我们需要插件化的activity
开屏冷启动:开屏冷启动主要针对MultiDex启动做优化,在5.0之前对dex分包是不做处理的,所以要兼容到低版本的时候需要使用MultiDex.install做兼容。而MutiDex.install将apk中的dex包获取到,然后又压缩成对应的zip文件,将dex文件通过反射转换成DexFile对象、反射替换数组。所以我们能做的优化可以通过判断如果jvm不支持dex分包处理,通过MutiDex.install做处理,通过监听MutiDex.install开启一个监听MutiDex.install的进程activity。等到MutiDex.install处理完成后,再来处理正常的逻辑。
七、内存优化
内存优化通常指的内存溢出,主要涉及到的问题还是该释放的资源,没有及时让GC处理器回收,通常主要表现是动画、上下文对象、EventBus、AsycTask、Handler、单例Bitmap都会影响,通常要做的是释放他们未终止的动作,释放锁定的上下文对象。
在实际项目有mvp架构的时候,需要注意内存泄漏的问题,p层如果长期持有v层的实例,导致v层的对象难以回收,而v层一般是activity或fragment作为抽象,因此需要在p层使用v层的弱应用或是在p层中实现v层的销毁方法,处理销毁的逻辑。
八、View的绘制
activity界面显示流程:activity启动后,不会立马去显示界面上的view,而是等到onResume的时候才会真正显示view的时机,首先会触发windowManager.addView方法,在该方法中触发代理对象WindowManagerGlobal的addView方法,代理对象的addView方法中创建了viewRootImpl,将setContentView中创建的decorView通过viewRootImpl的setView方法放到了viewRootImpl中,最终经过viewRootImpl一系列的方法最终调用performTraversals方法。
view的绘制:主要指view的onMeasure、onLayout、onDraw几个方法,其实要了解几个方法,需要追溯到android中本身界面的结构,首先整体是一个PhoneWindow的对象,然后是一个DecorView,DecorView里面包括一个ViewStub的ToolBar,然后下面是一个FramLayout,也就是我们经常在Activity中setContentView中的content内容。说完了android界面的结构,下面就是说下如何绘制的,绘制首先是触发到DecorView的onMeasure方法,它的测量规则包含了手机屏的宽高,并且测量模式是MeasureSpec.EXACTLY。所以这里明白了DecorView(FrameLayout)的测量参数是什么意思了,紧接着就是测量它下面的ViewGroup了,其中ViewGroup里面有个measureChild方法去测量孩子,这里会问到几种父布局的测量模式和子View的测量模式组合:
九、Eventbus原理
EventBus是一款在android开发中使用的发布/订阅事件的总线框架,基于观察者模式,将事件的接收者和发送者分开,基本包括了如下几个步骤:
注册事件的订阅方法:该步骤主要是找到订阅者下面有哪些方法需要被订阅 订阅操作:将需要被订阅的方法放到类似HashMap的数据结构中存储起来,方便后面发送事件和取消注册等资源的释放的时候使用 发送事件:该步骤首先遍历事件队列,然后从队列中取出事件,并且将事件从队列中移除,拿到事件后,判断事件处于的什么线程,如果是非UI线程,则需要Handler去处理,如果是的话,则直接通过反射调用被观察的方法。 反注册:该步骤就没什么好说的,主要是上面存储到HashMap中的被订阅的方法的移除,释放在内存中的资源。
十、Rxjava的操作符有哪些,说说他们的作用
- just:将同种数据源组合放到被观察者上面
- from:将类似数组、集合的数据源放到被观察者上面
- map:将一种数据源,转化成另外一种
- flatmap:将一种数据源,转化成另外一种数据,并且被转化的数据是乱序排列的
- concatmap:将一种数据源,转化成另外一种数据,并且被转化的数据是按照先前的数据源顺序排序的
- toList:将数组的形式转化成List集合
- subscribeOn:设置Observable的call方法所在的线程,也就是数据来源的线程
- observeOn:设置subscribe的call方法所在的线程,也就是数据处理的线程
- filter:在被观察者的数据层过滤数据
- onErrorResumeNext:出错的时候,可以指定出错的时候的被观察者
- retryWhen:出错的时候,重新走一遍被订阅的过程
- concat:合并相同类型的被观察者到一个被观察者身上,有点类似集合、数组拼接数据。
- zip:处理多种不同结果集的数据发射,一般用得多的地方是多个网络请求组合然后统一处理业务逻辑。 还有很多操作符就自己去看,这些操作符已经够面试用的了。
十一、线程锁 锁方法和类对象啥的有啥区别
线程锁锁方法:是需要等到该线程用完了该方法才能释放同步锁 线程锁锁类对象:是需要等到该线程用完了该类对象才能释放同步锁 区别:是锁方法的区域要小 锁类对象包括了该类的所有属性。
十二、AsyncTask原理
AsyncTask主要是对android中java的线程池的封装,该类中默认开启了两个线程池,一个线程池负责任务的排队处理,保证任务被单个处理,另外一个线程池用来专门处理任务,最后任务处理完了,交给Handler发送消息到主线程,然后Handler处理线程,交给了onPostExecute方法。
最后
如果你看到了这里,觉得文章写得不错就给个赞呗!欢迎大家评论讨论!如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足,定期免费分享技术干货。喜欢的小伙伴可以关注一下哦。谢谢!