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.線程優化
盡量采用線程池,既可以重用内部線程,也避免了建立和銷毀線程帶來性能影響,還可以控制最大并發數。