1.布局优化
1.1.尽量要减少布局文件的层级数量
单个
ViewGroup
的时候使用
LinearLayout
,因为
RelativeLayout
功能复杂,要花费更多的CPU时间,而
LinearLayout
和
FrameLayout
是简单高效的ViewGroup。而当出现嵌套的布局时候,尽量使用
RelativeLayout
。
布局优化还可以采用
<include>
,
<merge>
和
ViewStub
来控制
<include>
—– 可以将一个指定的布局文件加载到当前布局中,例如:
代码:
<include layout="@layout/title" />
<merge>
—– 用于层级包含的时候去除多余的层级,比如上述的内层布局
title
中,如果也是
LinearLayout
,则可以直接用
<merge>
代替,默认布局是
FrameLayout
,例如:
代码:
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="我是button3" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="我是button2" />
</merge>
ViewStub
—– 继承
View
,不参与绘制过程,在按需加载的时候才显示出来,比如网络异常出错的时候的页面,不需要在初始化的时候就加载,用
ViewStub
就可以在使用的时候加载出来,提高初始化页面的效率。例如。
代码:
<ViewStub
android:id="@+id/stub"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inflatedId="@+id/root"
android:layout="@layout/net_error" />
在需要加载的时候使用:
ViewStub stub = (ViewStub) findViewById(R.id.stub);
stub.setVisibility(View.VISIBLE);
或者使用
inflate
:
View view = stub.inflate();
2.绘制优化
在自定义View中的
onDraw()
方法里,避免创建新的对象,因为
onDraw()
会频繁被调用,降低执行效率。而且也不要有太多的循环操作。
3.内存泄漏优化
- 避免写出内存泄漏的代码,这需要靠丰富的编程经验和对语言的熟练掌握。
- 使用内存泄漏工具MAT来分析
几个内存泄漏的场景:
3.1. 静态变量导致的内存泄漏
代码:
public class AndroidTestActivity extends Activity {
private static View view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common);
view = new View(this);
}
}
静态变量生命周期是整个
Application
的生命周期,而其内部持有当前
Activity
,所以
Activity
无法正常释放,造成内存泄漏。
3.2. 单利模式造成的内存泄漏
单例的静态特性使得单例的生命周期和应用的生命周期一样长,若一个对象已经不需要使用了,而单例对象还持有该对象的引用,那么这个对象将不能被正常回收,就导致了内存泄漏。例如:
代码:
public class AppManager {
private static AppManager appManager;
private Context context;
private AppManager(Context context) {
this.context = context;
}
public static AppManager getInstance(Context context) {
if (appManager == null) {
appManager = new AppManager(context);
}
return appManager;
}
}
然后在
Avtivity
中使用该单例
代码:
public class AndroidTestActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common);
AppManager appManager = AppManager.getInstance(this);//传入的是activity
}
}
Activity
退出的时候,单例还持有该
Activity
的引用,就造成了内存泄漏。
可将
this.context = context;
修改为
this.context = context.getApplicationContext();
这样外加不论传入什么
context
,生命周期都一样长,就不会存在内存泄漏。
3.3. 非静态内部类导致内存泄漏
非静态内部类(包括匿名内部类)会持有外部类的引用(静态的内部类不会持有外部类的引用),当非静态内部类对象的生命周期比外部类对象的生命周期长时,就会导致内存泄露。
平时经常写的
Handler
的代码
代码:
public class AndroidTestActivity extends Activity {
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//逻辑处理
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep( * );
Message message = Message.obtain();
message.what = ;
handler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
如果
Activity
关闭了,但线程仍在运行,之后发送消息,这时候该线程持有
Handler
的引用,
Handler
又持有
Activity
的引用,就导致了
Activity
无法回收。可以使用静态内部类加弱引用的方式,或者直接在
Activity
结束的时候直接清除
Handler
代码:(静态内部类+弱引用(因为handler不再持有外部activity的引用,所以无法直接操作activity的数据))
private MyHandler myHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_common);
myHandler = new MyHandler(this);
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep( * );
Message message = Message.obtain();
message.what = ;
myHandler.sendMessage(message);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
private static class MyHandler extends Handler {
private WeakReference<AndroidTestActivity> activityWeakReference;
public MyHandler(AndroidTestActivity activity) {
activityWeakReference = new WeakReference<AndroidTestActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
AndroidTestActivity androidTestActivity = activityWeakReference.get();
if (activityWeakReference != null) {
if (msg.what == ) {
//逻辑操作
}
}
}
}
或者直接在结束的时候销毁handler的回调和消息。
代码:
@Override
protected void onDestroy() {
super.onDestroy();
handler.removeCallbacksAndMessages(null);
}
线程造成的内存泄漏还有
Thread
和
AsyncTask
。
上述中的
Runnable
就是匿名内部类,也需要改成静态内部类的方式。
3.4. 资源未关闭导致内存泄漏
对于使用了
BraodcastReceiver
,
ContentObserver
,
File
,
Cursor
,
Stream
,
Bitmap
、
IO
等资源的代码,应该在Activity销毁时及时关闭或者注销,否则这些资源将不会被回收,造成内存泄漏。
3.5. 集合中的资源未及时清除
使用集合的时候,若某个已经加入的对象不再使用,而集合引用还在使用,则会造成内存泄漏,要及时清除掉不使用的集合对象。
remove
或者
clear
3.6. 属性动画造成的内存泄漏
在
ACtivity
中播放属性动画,如果动画没有播放完整就退出
Activity
,虽然无法再看到动画,但是动画的控件
View
引用了
Activity
,即
Activity
并没有销毁。可在
Activity
销毁的时候同时清除动画。
代码:
@Override
protected void onDestroy() {
super.onDestroy();
animator.cancel();
}
3.7. WebView造成的内存泄漏
销毁WebView之前需要先将WebView从父容器中移除,然后再销毁WebView
@Override
protected void onDestroy() {
super.onDestroy();
// 先从父控件中移除WebView
webViewContainer.removeView(mWebView);
//一系列关闭操作
webView.stopLoading();
webView.getSettings().setJavaScriptEnabled(false);
webView.clearHistory();
webView.removeAllViews();
//最后销毁
webView.destroy();
}
4.响应速度优化
Activity
如果5秒没有屏幕触摸事件或者键盘输入事件,
BroadcastReceiver
如果10秒内还未执行完操作,都会出现
ANR
。
5.线程优化
尽量采用线程池,既可以重用内部线程,也避免了创建和销毁线程带来性能影响,还可以控制最大并发数。