今天来讲一讲在面试中碰到的Android异常与性能优化相关问题:
1、anr异常面试问题讲解
a) 什么是anr?
应用程序无响应对话框
b) 造成anr的原因?
**主线程中做了耗时操作
c) android中那些操作是在主线程呢?
- activity的所有生命周期回调都是执行在主线程的
- Service默认是执行在主线程的
- BroadcastReceiver的onReceiver回调是执行在主线程的
- 没有使用子线程的Looper的Handler的handlerMessage,post(Runnable)是执行在主线程的
- AsyncTask的回调中除了doInBackground,其它都是执行在主线程
d) 如何解决anr
- 使用AsyncTask处理耗时IO操作
- 使用Thread或者HandlerThread提高优先级
- 使用handler来处理工作线程的耗时任务
- activity的onCreate和onResume回调中尽量避免耗时的代码
2、oom异常面试问题讲解
a) 什么是oom ?
当前占用的内存加上我们申请的内存资源超过了Dalvik虚拟机的最大内存限制就会抛出的Out of memory异常
b) 一些容易混淆的概念
内存溢出 / 内存抖动 / 内存泄漏
- 内存溢出:就是oom
- 内存抖动:短时间内大量对象被创建然后马上被释放
- 内存泄漏:当程序不再使用到的内存时,释放内存失败而产生了无用的内存消耗
d) 如何解决oom
1.有关bitmap优化
- 图片显示(比如监听listview滑动,停止的时候加载大图)
- 及时释放内存
bitmap的够着方法都是私有的,通过BitmapFactory生成Bitmap到内存中都是通过jni实现的,简单点说会有俩部分区域,一部分是java区,一部分是C区,但是Bitmap是通过java分配的,不用的时候也是由java的gc机制回收的。对应C区域不能及时回收的,所以这里说的释放内存就是释放的c的那块区域(通过recycle方法,该方法内部调用了jni的方法)。要是不释放只能等进程死了以后就会被释放
- 图片压缩
- inBitmap属性(图片复用)
- 捕获异常
2.其它方法
- listview:convertview / lru(三级缓存)
- 避免在onDraw方法里面执行对象的创建
- 谨慎使用多进程
3、 bitmap面试问题讲解
a) recycle :释放bitmap内存的时候会释放所对应的native的内存,但是不会立即释放,但是调用完就不能使用该bitmap了,是不可逆的。官方不建议主动调用。垃圾回收器主动会清理。
b) LRU:三级缓存,内部是通过map实现的,里面提供了put、get方法来完成缓存的添加和获取操作。当缓存满的时候,lru算法会提供trimToSize删除最久或者使用最少的缓存对象,添加新的缓存对象
c) 计算inSampleSize:
d) 缩略图
/**
* 获取缩略图
* @param imagePath:文件路径
* @param width:缩略图宽度
* @param height:缩略图高度
* @return
*/
public static Bitmap getImageThumbnail(String imagePath, int width, int height) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true; //关于inJustDecodeBounds的作用将在下文叙述
Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);
int h = options.outHeight;//获取图片高度
int w = options.outWidth;//获取图片宽度
int scaleWidth = w / width; //计算宽度缩放比
int scaleHeight = h / height; //计算高度缩放比
int scale = 1;//初始缩放比
if (scaleWidth < scaleHeight) {//选择合适的缩放比
scale = scaleWidth;
} else {
scale = scaleHeight;
}
if (scale <= 0) {//判断缩放比是否符合条件
be = 1;
}
options.inSampleSize = scale;
// 重新读入图片,读取缩放后的bitmap,注意这次要把inJustDecodeBounds 设为 false
options.inJustDecodeBounds = false;
bitmap = BitmapFactory.decodeFile(imagePath, options);
// 利用ThumbnailUtils来创建缩略图,这里要指定要缩放哪个Bitmap对象
bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height,ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
return bitmap;
}
e) 三级缓存
网络、本地、内存三级缓存,减少流量的使用
4、 ui卡顿面试问题讲解
a) UI卡顿的原理
- 60fps -> 16ms
- overdraw过度绘制
b) UI卡顿的原因分析
1.人为在UI线程中做轻微耗时操作,导致UI线程卡顿
2.布局Layout过于复杂,无法在16ms内完成渲染
3.同一时间动画执行的次数过多,导致CPU、GPU的负载过重
4.View的过度绘制,导致某些像素在同一帧内被绘制多次,从而使CPU、GPU的负载过重
5.View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染
6.内存频繁触发gc过多,导致暂时阻塞渲染操作
7.冗余资源及逻辑等导致加载和执行的缓慢
8.ANR
c) UI卡顿总结
1.布局优化
2.列表及adapter优化
3.背景和图片等内存分配优化
4.避免ANR
4、 内存泄漏
a) java内存泄漏基础知识
1. java内存的分配策略
静态存储区(方法区):主要存放静态数据、全局 static 数据和常量。这块内存在程序编译时就已经分配好,并且在程序整个运行期间都存在。
栈区:当方法被执行时,方法体内的局部变量(其中包括基础数据类型、对象的引用)都在栈上创建,并在方法执行结束时这些局部变量所持有的内存将会自动被释放。因为栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
堆区 : 又称动态内存分配,通常就是指在程序运行时直接 new 出来的内存,也就是对象的实例。这部分内存在不使用时将会由 Java 垃圾回收器来负责回收。
2. java是如何管理内存的
3. java中的内存泄漏
内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间的浪费称为内存泄漏
b) android内存泄漏
- 单例
public class AppManager {
private Context mContext;
private static AppManager instance;
private AppManager(Context context){
//有内存泄漏的问题:传入的是actiivty的context,导致activity没法释放
//this.mContext = context;
//所以这里应该传入Application全局的context
this.mContext = context.getApplicationContext();
}
public static AppManager getInstance(Context context){
if(instance ==null){
instance = new AppManager(context);
}
return instance;
}
}
2.匿名内部类
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
//这样的写法就会造成内存泄漏
//原因是这样写法虽然避免了重复创建,但是非静态内部类持有外部类的引用,
//这时候我们又创建了一个静态实例TAG的话就会和应用的生命周期一样长,所以就会使外部的activity没法释放
class TestResource{
private static final String TAG = "";
}
//正常的写法,这样的话就不会持有外部类的引用。
static class TestResource1{
private static final String TAG = "";
}
}
3.handler
public class MainActivity extends AppCompatActivity {
private TextView mTv;
private MyHandler myHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
myHandler = new MyHandler(this);
}
//这样的写法会造成内存泄漏
//mHandler是MainActivity的非静态内部类的实例,它持有外部类的引用,我们知道handler的消息是在一个loop
//中不断的轮询处理消息,那么当MainActivity退出时,消息队列中还有没处理的消息或正在处理的消息,所以会造成内存泄漏
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
//这样写是正确的写法
static class MyHandler extends Handler{
//创建一个软引用
private WeakReference reference;
public MyHandler(Context context){
reference = new WeakReference(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity mainActivity = (MainActivity) reference.get();
if(mainActivity != null){
//TODO------
mainActivity.mTv.setText("11");
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
myHandler.removeCallbacksAndMessages(null);
}
}
4.避免使用static静态变量
如果声明成静态变量,那么它的生命周期就会和app的生命周期一样长,假如你的app是常驻后台的,即使app退到后台,这部分也不会释放
5.资源未关闭造成的内存泄漏
6.AsyncTask造成的内存泄漏
- 在onDestory调用cancel()方法
- 添加弱引用实现
5、内存管理面试问题
a)内存管理机制概述
1.分配机制
2.回收机制
b)Android内存管理机制
1.分配机制
弹性的分配机制,当发现内存不够用的时候回分配额外的内存。但是额外的内存也不是无限量的。(让更多的进程存活在系统中)
2.回收机制
前台进程 可见进程 服务进程 后台进程 空进程
c)内存管理机制的特点(目标)
1.更少占用的内存
2.在合适的时候,合理的释放系统资源
3.在系统内存紧张的情况下,能释放掉不部分不重要的资源,来为android系统提供可用的内存
4.能够很合理的在特殊的生命周期中,保存或者还原重要数据,以至于系统能够正确的重新恢复该应用
d)内存优化方法
1.当service完成任务后,尽量停止它
2.在UI不可见的时候,释放掉一些只有UI使用的资源
3.在系统资源紧张的时候,尽可能多的释放掉一些非重要资源
4.避免滥用Bitmap导致的内存浪费
5.使用针对内存优化过的数据容器
6.避免使用依赖注入的框架
7.使用ZIP对齐的APK
8.使用多进程
d)内存溢出VS内存泄漏
6、冷启动优化面试问题讲解
a)什么是冷启动?
1.冷启动的定义
冷启动就是在启动应用前,系统没有该应用的任何进程信息
2.冷启动 / 热启动的区别
热启动:用户使用返回键退出应用,然后马上又重新启动应用
区别:
1). 定义
2). 启动特点
- 冷启动:Application -> MainActivity ->UI的绘制
- 热启动:MainActivity ->UI的绘制
3.冷启动时间的计算
这个时间值从应用启动(创建进程)开始计算,到完成视图的第一次绘制(即Activity内容对用户可见)为止
b)冷启动流程
- Zygote进程中fork创建一个新的进程
- 创建和初始化Application类,创建MainActivity类
- inflate布局,当onCreate / onStart / onResume方法都走完
- contentView的measure / layout /draw 显示在界面上
c)如何对冷启动的时间进行优化
1.减少onCreate的工作量
2.不要让Application参与业务的操作
3.不要在Application进行耗时操作
4.不要以静态变量的方式在Application中保存数据
5.布局
6.mainThread
d)冷启动流程-----总结
Application的构造方法-------->attachBaseContext () ---------> onCreate () ----------->Activity的构造方法------------> onCreate () ------------> 配置主题中背景等属性------------->onStart () ---------> onResume() --------> 测量布局绘制显示在界面上
7、其他优化面试问题讲解
a)android 不用静态变量存储数据
1.静态变量等数据由于进程已经被杀死而被初始化
2.使用其它的数据传输方式:文件 / sp / contentProvider
b)有关Sharepreference的安全问题
1.不能跨进程同步数据读写
2.存储Sharepreference的文件过大问题
- 过大的话会读取缓慢,造成UI卡顿
- 读取频繁的key和不易变动的key不要放在一起
c)内存对象序列化
序列化:将对象的状态信息转化为可以存储或传输的形式的过程
1.实现Serializable接口(会产生大量的临时变量,频繁的垃圾回收,这样会造成UI卡顿,内存抖动,)
2.实现Parcelable接口
总结:
1>.Serializable是Java的序列化方式,Parcelable是android特有的序列化方式
2>.在使用内存的时候,Parcelable比Serializable性能高
3>.Serializable序列化的时候会产生大量的临时变量,从而引起频繁的GC
4>.Parcelable不能使用在要数据存储在磁盘上的情况
d)避免在UI线程中做繁重的操作
结尾
文章到这里就结束了,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。
不管怎么样,不论是什么样的大小面试,要想不被面试官虐的不要不要的,只有刷爆面试题题做好全面的准备,当然除了这个还需要在平时把自己的基础打扎实,这样不论面试官怎么样一个知识点里往死里凿,你也能应付如流啊~
最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司19年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。
还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
Android学习PDF+架构视频+面试文档+源码笔记
【Android开发核心知识点笔记】
【Android思维脑图(技能树)】
【Android核心高级技术PDF文档,BAT大厂面试真题解析】
【Android高级架构视频学习资源】
Android精讲视频领取学习后更加是如虎添翼!进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
【Android进阶学习视频】、【全套Android面试秘籍】关注我【主页简介】或者【简信我】查看免费领取方式!