本文是为了面试而写的性能优化。目的不是为了具体的深入而是对于要面试的同学在面试的时候能和面试官说出的性能优化的方面。在面试的时候基本现在每个面试官都会问一些关于性能优化方法的问题。那么该怎么回答呢?面试不同于我们学习新的知识点,要完全学会,要学精,对于面试官这个问题,可以从下面几个方面来回答,ANR,内存溢出,内存抖动,内存泄漏,UI卡顿,冷启动优化等方面来回答。
ANR(Applicatino not responding)是指程序无响应,主要原因为:
解决方式:
内存溢出主要是由于加载大的图片引起的。解决方式:
内存抖动是指内存在短时间内频繁地分配和回收,而频繁的gc会导致卡顿,严重时和内存泄漏一样会导致OOM。
常见的内存抖动场景:
内存抖动的原因:
瞬间产生大量的对象会严重占用新生代的内存区域,当达到阀值,剩余空间不够的时候,就会触发GC。系统花费在GC上的时间越多,进行界面绘制或流音频处理的时间就越短。即使每次分配的对象占用了很少的内存,但是他们叠加在一起会增加Heap的压力,从而触发更多其他类型的GC。这个操作有可能会影响到帧率,并使得用户感知到性能问题。
内存泄漏是指无用对象(不在使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成的内存空间的浪费称为内存泄漏。
Android内存泄漏:
public class SingleInstanceTest {
private static SingleInstanceTest sInstance;
private Context mContext;
private SingleInstanceTest(Context context){
this.mContext = context;
}
public static SingleInstanceTest newInstance(Context context){
if(sInstance == null){
sInstance = new SingleInstanceTest(context);
} return sInstance;
}
}
上面是一个比较简单的单例模式用法,需要外部传入一个 Context 来获取该类的实例,如果此时传入的 Context 是 Activity 的话,此时单例就有持有该 Activity 的强引用(直到整个应用生命周期结束)。这样的话,即使该 Activity 退出,该 Activity 的内存也不会被回收,这样就造成了内存泄露,特别是一些比较大的 Activity,甚至还会导致 OOM(Out Of Memory)。
解决方式:
public class SingleInstanceTest {
private static SingleInstanceTest sInstance;
private Context mContext;
private SingleInstanceTest(Context context){
his.mContext = context.getApplicationContext();
}
public static SingleInstanceTest newInstance(Context context){
if(sInstance == null){
sInstance = new SingleInstanceTest(context);
} return sInstance;
}
}
可以看到在 SingleInstanceTest 的构造函数中,将 context.getApplicationContext() 赋值给 mContext,此时单例引用的对象是 Application,而 Application 的生命周期本来就跟应用程序是一样的,也就不存在内存泄露。
2.内部类导致内存泄漏
非静态内部类会默认持有外部类的引用。会导致内部类的生命周期过长。
正确的做法就是修改成静态内部类。
3.Handler
看下面的代码
public class HandlerActivity extends AppCompatActivity {
private final static int MESSAGECODE = 1 ;
private final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Log.d("mmmmmmmm" , "handler " + msg.what ) ;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
//点击结束Activity
findViewById( R.id.finish ).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
//新建线程,内部类
new Thread(new Runnable() {
@Override
public void run() {
handler.sendEmptyMessage( MESSAGECODE ) ;
try {
Thread.sleep( 8000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
//持有对象的引用
handler.sendEmptyMessage( MESSAGECODE ) ;
}
}).start() ;
}
}
这段代码运行起来后,立即点击 finish 按钮,通过检测,发现 HandlerActivity 出现了内存泄漏。
当Activity finish后,延时消息会继续存在主线程消息队列中8秒钟,然后处理消息。而该消息引用了Activity的Handler对象,然后这个Handler又引用了这个Activity。这些引用对象会保持到该消息被处理完,这样就导致该Activity对象无法被回收,从而导致了上面说的 Activity泄露。
Handler 是个很常用也很有用的类,异步,线程安全等等。如果有下面这样的代码,会发生什么呢? handler.postDeslayed ,假设 delay 时间是几个小时… 这意味着什么?意味着只要 handler 的消息还没有被处理结束,它就一直存活着,包含它的 Activity 就跟着活着。
我们来想办法修复它,修复的方案是 WeakReference ,也就是所谓的弱引用。垃圾回收器在回收的时候,是会忽视掉弱引用的,所以包含它的 Activity 会被正常清理掉。
解决方式
1.静态内部类
2.弱引用
3.注意在onDestroy中移除消息
public class HandlerActivity extends AppCompatActivity {
private final static int MESSAGECODE = 1 ;
private static Handler handler ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler);
findViewById( R.id.finish ).setOnClickListener(new View.OnClickListener()
@Override
public void onClick(View v) {
finish();
}
});
//创建Handler
handler = new MyHandler( this ) ;
//创建线程并且启动线程
new Thread( new MyRunnable() ).start();
}
private static class MyHandler extends Handler {
WeakReference weakReference ;
public MyHandler(HandlerActivity activity ){
weakReference = new WeakReference( activity) ;
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if ( weakReference.get() != null ){
// update android ui
Log.d("mmmmmmmm" , "handler " + msg.what ) ;
}
}
}
private static class MyRunnable implements Runnable {
@Override
public void run() {
handler.sendEmptyMessage( MESSAGECODE ) ;
try {
Thread.sleep( 8000 );
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage( MESSAGECODE ) ;
}
}
}
解决ui卡顿:
1.布局优化 include,merge,viewsuble
2.背景和图片等内存分配优化
内存管理
优化方法:
冷启动就是在启动应用前,系统中没有该应用的任何进程信息。
热启动就是用户使用返回键退出应用,然后马上又重新启动应用。
Application只初始化一次,冷启动会先创建Application,然后初始化MainActivity,热启动会直接初始化MainActivity。
冷启动流程:
减少冷启动的时间进行优化:
android Studio 中 Android Monitor
更多详细的参考 https://www.jianshu.com/p/797395731747