一.Activity面试详解
1.activity生命周期
4种状态
running/paused/stopped/killed
activity生命周期
android进程优先级: 前台/可见/服务/后台/空
2.Android任务栈
3.activity启动模式
onNewIntent何时调用:在singleTop和singleTask模式下,某个acitivity已经启动。当再次调用的时候,才会调用
4.scheme跳转协议
二. Fragment面试详解
1.Fragment为什么被称为第五大组件
为什么会成为第五大组件
使用频率,有自己声明周期,动态灵活加载到activity,更节省内存
加载到Activity的两种方式
FragmentPagerAdpter和FragmentStatePagerAdapter的区别
前者使用于页面较少的,后者适用于页面较多的情况。
2.Fragment的声明周期
3.Fragment之间的通信
fragment调用activity中方法 getActivity
4.FragmentManager
三. Service面试详解
1.service应用场景,与Thread的区别
service基础 是什么?区别?
2.启动的两种方式
startService
使用:
生命周期
onCreate->onStartCommand->onDestroy
多次调用startService,会多次调用onStartCommand函数,但不会调用onCreate
特点:与开始服务的组件没有关系,开启服务的组件销毁,服务仍旧会继续运行。
bindService
使用:
生命周期
onCreate->onBind->onUnBind->onDestroy
特点:与绑定服务的组件强绑定,绑定服务的组件如果销毁,该服务也会销毁。
四. Broadcast Receiver面试详解
1.广播
定义
场景 同一app的多个进程 不同app之间的组件之间的通信
种类 普通广播,有序广播,本地广播
2.实现广播 receiver
3.广播实现机制
4.LocalBroadcastManager详解
五. Binder机制
1.Linux内核基础知识
进程隔离/虚拟地址空间
系统调用:内核空间,用户空间
2.Binder通信机制介绍
为什么使用binder
binder通信模型
通信的两个用户进程:手机
binder驱动:基站
serviceManager:通讯录
binder通信原理
3.AIDL
使用步骤
六.Android多线程
1.Handler面试详解
1.1 什么是Handler
1.2 使用方法
1.3 机制原理
1.4 handler引起的内存泄露和解决方法
原因
静态内部类持有外部类的应用,导致外部类activity无法被释放
解决方法
handler内部持有外部acitvity的弱引用,并把handler改为静态内部类,在activity的ondestroy中加入mHandler.removeCallback()
2.asynctask面试详解
2.1.什么是asynctask 封装了线程池和handler的异步框架
2.2.使用方法
2.3.内部原理
2.4.AsyncTask的注意事项
AsyncTask的对象必须在主线程中创建
execute方法必须在UI线程中加载
一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报一场
不适合做长时间耗时的操作,如果要执行长时间操作,最好使用线程池Excutor
避免内存泄露,解决内存泄露的方法跟handler一样。
3.handlerThread面试详解
3.1.是什么
产生的背景:为了解决 多次创建和销毁线程是很消耗系统资源的。
3.2.源码解析
4.intentService面试详解
4.1.是什么
一个封装了HandlerThread和handler的异步框架。每次任务完成会自动关闭service。普通service是运行在主线程的,无法处理耗时操作,intentService提供了这种机制。
4.2.使用方法
继承intentService,覆写onhandleIntent方法,在里面处理耗时操作。
4.3.源码解析
可以启动多个intentService,会将任务存储在队列中依次执行。
内部是利用handlerThread实现的。
七.view
1.绘制问题面试
1.1.view树的绘制流程
1.2.measure
测量是从viewgroup从上到下依次递归测量的。viewgroup是抽象类,里面没有重写onMeasure,如果要自定义viewgroup类的时候必须重写onMeasure,里面定义如何通过遍历子类最后计算出viewgroup的大小。
onMeasure中有两个参数,分别是widthMeasureSpec, heightMeasureSpec,每个measurespec包含两个值,一个是specmode,一个是specsize。每个view的measurespec是由父控件的measurespec和当前view的layoutparam共同决定的。
如果为EXACTLY,说明view的layoutparam是具体的size,或者match_parent,此时specsize就是最后的size。
如果是AT_MOST,说明view的layoutparam是wrap_content,此时specsize需要子view根据自己的情况获取最后的size。
也就是说当我们自定义view的时候,如果在布局文件中只需要设置具体size或者match_parent两种属性,那么就不需要重写onMeasure。如果需要设置wrap_content,就需要重写onMeasure,计算这种情况下的 控件的大小。
参考:https://www.jianshu.com/p/d07f633c37e7
1.3 layout
onLayout()都是由ViewGroup的子类实现的,他的作用就是确定容器中每个子控件的位置,由于不同的容器有不容的布局策略,所以每个容器对onLayout()方法的实现都不同,onLayout()方法会遍历容器中所有的子控件,然后计算他们左上右下的坐标值,最后调用child.layout()方法为子控件设置坐标
首先我们要明确布局的本质是什么,布局就是为 View 设置四个坐标值,这四个坐标值保存在View的成员变量 mLeft、mTop、mRight、mBottom 中,方便 View 在绘制(onDraw)的时候知道应该在那个区域内绘制控件。
自定义viewgroup必须实现onLayout,对子控件进行摆放。
参考:https://www.jianshu.com/p/0b444c4c5cf6
1.4 draw
1.5 getWidth和getMeasureWidth区别
getMeasureWidth是在onMeasure执行完成后获得的,而getWidth是在onLayout执行完成后获得的
getMeasureWidth是onMeasure最后通过setMeasuredDimension设置大小后获取的宽度。getWidth是onLayout后,mRight-mLeft获得的。也就是说在onMeasure后可以通过onLayout改变控件的显示宽度。
2.view的事件分发机制面试
2.1.为什么会有事件分发机制
由于view是树形结构,点击屏幕可能引起多个响应,为了确定哪个view进行响应,引入了事件分发机制。
2.2.三个重要的方法
dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent
三者之间的伪代码如下
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if(onInterceptTouchEvent(ev)){
consume = onTouchEvent(ev);
}else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
viewgroup接收到TouchEvent后会将事件通过dispatchTouchEvent分发。如果onInterceptTouchEvent返回true,则事件交由viewgroup的onTouchEvent来处理;如果返回false,则会交给子View的dispatchTouchEvent来处理。
默认情况下,viewgroup的onterinceptTouchEvent返回false,及不拦截事件。view没有onterinceptTouchEvent,所以view只要已接收到事件,就会传递给自身的onTouchEvent。
滑动冲突解决
八.优化
1.内存溢出oom
当前占用的内存加上我们申请的内存资源超过了Davik虚拟机的最大内存限制就会抛出out of memory异常。
主要原因就是bitmap
1.1 有关bitmap
(1)图片的存储优化
图片占用内存计算:图片宽度 x 图片高度 x 一个像素占用的内存大小
尺寸压缩
(1)设置inJustDecodeBound=true,,使图片能够没加载进内存就能够获取图片的狂傲。
(2)设置imSampleSize,只要大于2,就能够压缩图片占用内存。
(3)单纯改变ImageView不会对图片占用内存有任何的改变,必须针对bitmap本身的优化才起作用。
质量压缩
(1)常见的PNG,JPG,WEBP等格式的图片在设置到UI上之前需要经过解码过程;
使用tinyPNG网站压缩PNG
使用Webp
(2)使用RGB_565替代ARGB_8888可以降低图片占用内存;
RGB_565一个像素占用两个字节,而ARGB_8888一个像素占用四个字节
内存重用
使用inBitmap属性,对内存进行重用。
4.cache
使用lru算法。
内部利用linkedHashMap实现,提供了get,put操作,缓存满了之后提供trimToSize方法,移除较早的缓存对象,添加最新的对象
listview convertview/lru
1.2 避免在onDraw方法里面执行对象的创建
容易造成内存抖动,内存抖动积累到一定程度也会造成oom。因为频繁内存抖动会将内存碎片化,从而当需要分配内存的时候,虽然总体上还有内存分配,但是由于这些内存不是连 续的,导致无法分配,系统就直接返回OOM了
1.3 使用多进程,需非常谨慎
2.内存抖动
短时间内创建大量对象,内存达到阈值后,触发gc。内存抖动积累到一定程度也会造成oom。因为
频繁内存抖动会将内存碎片化,从而当需要分配内存的时候,虽然总体上还有内存分配,但是由于这些内存不是连续的,导致无法分配,系统就直接返回OOM了
3.内存泄露:
3.1 java内存泄露基础知识
指的是无用对象持续占有内存或无用对象的内存得不到及时释放,造成的内存空间的浪费称为内存泄露。
1.java内存分配策略
静态存储区(方法区)
存储静态变量,全局变量。编译的时候已经分配好了,并且该区域存储的变量在整个程序运行的过程中都存在。
栈区
方法中的局部变量,一些基本类型的变量,对象的引用变量。方法结束后自动释放。
堆区
动态内存分配。存储new出来的对象和数组,由java的垃圾回收器来管理
2.java如何管理内存
3.java中的内存泄露
3.2 android内存泄露
1.单例.
因为静态变量存在与app的整个生命周期。如果静态变量持有了某个activity的context,就会造成activity无法释放。
2.非静态内部类:
包括成员内部类,局部内部类和匿名内部类。因为非静态内部类会持有外部类的引用
3.handler
4.避免使用static
5.资源未关闭造成的内存泄漏
6.AsyncTask造成的内存泄漏
android内存管理
内存管理机制概述
分配机制:为每个进程分配机制
回收机制:
android内存管理机制
分配机制:
回收机制:根据进程优先级回收进程
内存管理机制的特点
更少的占用内存
在合适的时候,合理的释放系统资源(不是说对象不需要了就立刻回收掉就是最好的内存管理,频繁的释放内存
会造成内存抖动,这样会造成anr,oom,卡顿)
在系统内存紧张的时候,尽可能的释放掉一些非重要资源
内存优化方法
service完成任务后,尽量停止它
在UI不可见的时候,尽可能的释放掉一些非重要资源
在系统内存紧张的时候,尽可能多的释放掉一些非重要资源 onTrimMerory
避免滥用Bitmap导致的内存浪费
使用针对内存优化过的数据容器,避免使用枚举
避免使用依赖注入的框架
使用zip对齐的apk
使用多进程 例如开启一个进程 定位,webview,推送
内存溢出vs内存泄露
内存溢出oom:
内存泄漏:
4.UI卡顿
4.1 UI卡顿原理
60fps->16ms
Android系统每隔16ms触发对android进行渲染,如果每次都能渲染成功,就能够达到流畅的画面,也就是每秒60帧画面。
4.2 UI卡顿原因分析
1. 人为在UI线程中做轻微耗时操作,导致UI线程卡顿
2. 布局Layout过于复杂,或者过度绘制,无法在16ms内完成渲染。
3. 同一时间动画执行的次数过多,导致CPU或GPU负载过重。
4. View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染。
5. 内存频繁触发GC过多,导致暂时阻塞渲染操作。gc过程会暂停所有线程的工作。
4.3 UI卡顿总结
(1)布局优化
消除冗余嵌套和过复杂的布局
Gong替换invisible
尽量使用weight代替长宽
如果存在非常复杂的嵌套,可以考虑使用自定义的view。减少measure和layout的次数
(2) 列表及adpter优化
在滑动过程中,只加载缺省图片,停止的时候才加载图片和数据
(3) 背景和图片等内存分配优化
去掉不必要的背景
图片要进行压缩
(4) 避免ANR
5. 冷启动优化
5.1什么是冷启动
(1)冷启动的定义
第一次启动应用或者被进程杀死后重新启动。
启动之前,系统中没有该应用的任何进程信息。
(2)冷启动热启动的区别
a.启动之前有没有该应用的进程
b.要不要启动Application的类
(3)冷启动的时间的计算
这个时间值从应用启动(创建进程开始计算),到完成视图的第一次绘制(即Activity内容对用户可见)为止。
5.2 冷启动流程
Zygote进程汇总fock创建出一个新的进程
创建和初始化Application类,创建MainActivity类。
Inflate布局、当onCreate、onStart、onResume方法都走完
contentView的measure、layout、draw显示在界面上
Application的构造器方法attachBaseContextonCreate()Activity的构造方法onCreate()配置主题中背景等属性onStartonResume测量布局绘制显示在界面上
5.3 启动优化
(1) 减少onCreate方法的工作量
(2) 不要让Application参与业务操作
(3) 不要在Application进行耗时操作
如果耗时操作过多,应用冷启动后会出现白屏现象。
解决方法:https://www.jianshu.com/p/436b91175826
(4) 不要以静态变量的方式在application中保存数据
(5) 布局/mainThread
6 其他优化
6.1 android中不要用静态变量存储数据
App转入后台,由于内存过低,系统可能会将app进程杀死。但是系统为了用户体验,会将最后的Activity恢复,而此时app的进程使重新开启的,静态变量会重新初始化。该Activity如果应用了静态变量,会造成nullpointer的错误
6.2 有关Sharepreference的问题
(1)不能跨进程同步
每个进程都有sharepreference的副本,只有在进程结束的时候才会同步。
(2)Sharepreference的文件过大的问题
如果文件过大,在获取Sharepreference值的时候,会阻塞主线程,造成卡顿
如果文件过大,在解析Sharepreference的时候,会产生大量的临时对象,造成频繁GC,造成内存抖动,也会造成界面卡顿,甚至会发生oom
(3)内存对象序列化
序列化
将对象的状态信息转换为可以存储活传输的形式的过程
Serializeble
产生大量临时变量,引起频繁gc,造成内存抖动,页面卡顿,严重的会产生oom
Parcelable
android独有的序列化方式。不能适用将数据存储在磁盘的情况。适用的场合使进程间通信。使用内存的时候Parcelable性能更高。
java的垃圾回收机制
Java的一个重要有点就是通过垃圾收集器GC自动管理内存回收,程序员不需要通过调用函数释放内存。但是并不是说java就不存在内存泄露。虽然简化了程序员的工作,但同时也加重了jvm的工作,这也是Java程序运行速度较慢的原因之一。因为,gc为了能够正确释放对象,gc必须监控每一个对象的运行状态,包括对象的申请、被引用、引用、赋值等,gc都需要进行监控。
从几个方面来说:
1.什么时候回收?
a.在cpu空闲的时候
b.在堆内存满了的时候
c.system.gc()调用的时候尝试回收
2.怎么判断对象可以被回收
java和c#都是使用根搜索算法(GC Roots Tracing)判断的。这个算法的基本思路就是通过一系列名为“GC Roots”的对象作为起点,从这些几点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。
九. Activity的窗口机制
每个activity含有一个phoneWindow对象,phonewindow中含有DecorView。phoneWindow是Activity和DecorView的桥梁。DecorView只有一个LinerLayout的子view。包含通知栏,标题栏,内容栏。setContentView就是将xml的view添加到内容栏位置。
事件分发:Activity接到事件后,会传到Window对象中,window再将事件传递给DeocrView,Decorview传给它唯一的子View,一个ViewGroup,然后递归传递下去。