『壹』 如何定位和解決android的內存溢出問題(大總
一、定位內存泄漏:
可以用LeakCanary:檢測所有的內存泄漏
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0509/2854.html
二、解決:
1.對各種流,文件資源這些比如:InputStream/OutputStream,SQLiteOpenHelper,SQLiteDatabase,Cursor,文件,I/O,Bitmap圖片等操作等都應該記得顯示關閉。
2.盡量避免static成員變數引用資源耗費過多的實例,比如Context。因為Context的引用超過它本身的生命周期,會導致Context泄漏。所以盡量使用Application這種Context類型。
3.使用線程池,不要newthread
4.UI視圖檢查,減少視圖層級(hierarchyviewer)。
5.圖片優化
6. 重用系統資源:系統定義id,系統圖片,系統布局,系統style,系統字元串,系統顏色定義
『貳』 使用ConnectivityManager的內存泄漏隱患
Android裡面內存泄漏問題最突出的就是Activity的泄漏,而泄漏的根源大多在於單例的使用,也就是一個靜態實例持有了Activity的引用。靜態變數的生命周期與應用(Application)是相同的,而鍵啟Activity生命周期通常比它短,也就會造成在Activity生命周期結束後,還被引用導致無法被系統回收釋放。
生成靜態引用內存泄漏可能有兩種情況:
這個主要講下系統級的情況,這樣的情況可能也有很多,舉個最近發現的問題ConnectivityManager。
通常我們獲取系統服務時採用如下方式:
在Android6.0系統上,如果這里的Context如果是Activity的實例,那麼即使你什麼也不幹也會造成內存泄漏。
用LeakCanary可以直接看到內存泄漏:
一步一步來分析下。
先從Context的getSystemService方法開始,我們知道Activity是從ContextWrapper繼承而來的,ContextWrapper中持有一個mBase實例,這個實例指向一個ContextImpl對象,同時ContextImpl對象持有一個OuterContext對象,對於Activity來說,這個OuterContext就是Activity對象。所以調用getSystemService最終會調用到ContextImpl的getSystemService方法。
在6.0上 ContextImpl 的getSystemService方法調用 SystemServiceRegistry 來完成。
SystemServiceRegistry提供ConnectivityManager的實例。
在6.0上, ConnectivityManager 實現為單例:
精彩的部分來了,ConnectivityManager 持慶亮賀有了一個Context的引用:
這個Context在ConnectivityManager 創建時傳入,這個Context在中由ContextImpl對象轉換為OuterContext,與就是Activity對象,所以最終ConnectivityManager的單實例持有了Activity的實例引用。這樣即使Activity退出後仍然無法釋放,導致譽派內存泄漏。
這個問題僅在6.0上出現,在5.1上ConnectivityManager實現為單例但不持有Context的引用,在5.0有以下版本ConnectivityManager既不為單例,也不持有Context的引用。
其他服務沒認真研究,不確定有沒有這個問題。不過為了避免類似的情況發生,最好的解決辦法就是:
『叄』 android 內存泄露 會導致什麼問題
1. 查詢資料庫而沒有關閉Cursor
在Android中,Cursor是很常用的一個對象,但在寫代碼是,經常會有人忘記調用close, 或者因為代碼邏輯問題狀況導致close未被調用。
通常,在Activity中,我們可以調用startManagingCursor或直接使用managedQuery讓Activity自動管理Cursor對象。
但需要注意的是,當Activity介紹後,Cursor將不再可用!
若操作Cursor的代碼和UI不同步(如後台線程),那沒需要先判斷Activity是否已經結束,或者在調用OnDestroy前,先等待後台線程結束。
除此之外,以下也是比較常見的Cursor不會被關閉的情況:
雖然表面看起來,Cursor.close()已經被調用,但若出現異常,將會跳過close(),從而導致內存泄露。
所以,我們的代碼應該以如下的方式編寫:
Cursor c = queryCursor();
try {
int a = c.getInt(1);
......
} catch (Exception e) {
} finally {
c.close(); //在finally中調用close(), 保證其一定會被調用
}
try {
Cursor c = queryCursor();
int a = c.getInt(1);
......
c.close();
} catch (Exception e) {
}
2. 調用registerReceiver後未調用unregisterReceiver().
在調用registerReceiver後,若未調用unregisterReceiver,其所佔的內存是相當大的。
而我們經常可以看到類似於如下的代碼:
這是個很嚴重的錯誤,因為它會導致BroadcastReceiver不會被unregister而導致內存泄露。
registerReceiver(new BroadcastReceiver() {
...
}, filter); ...
3. 未關閉InputStream/OutputStream
在使用文件或者訪問網路資源時,使用了InputStream/OutputStream也會導致內存泄露
4. Bitmap使用後未調用recycle()
根據SDK的描述,調用recycle並不是必須的。但在實際使用時,Bitmap佔用的內存是很大的,所以當我們不再使用時,盡量調用recycle()以釋放資源。
5. Context泄露
這是一個很隱晦的內存泄露的情況。
先讓我們看一下以下代碼:
在這段代碼中,我們使用了一個static的Drawable對象。
這通常發生在我們需要經常調用一個Drawable,而其載入又比較耗時,不希望每次載入Activity都去創建這個Drawable的情況。
此時,使用static無疑是最快的代碼編寫方式,但是其也非常的糟糕。
當一個Drawable被附加到View時,這個View會被設置為這個Drawable的callback (通過調用Drawable.setCallback()實現)。
這就意味著,這個Drawable擁有一個TextView的引用,而TextView又擁有一個Activity的引用。
這就會導致Activity在銷毀後,內存不會被釋放。
private static Drawable sBackground;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
TextView label = new TextView(this);
label.setText("Leaks are bad");
if (sBackground == null) {
sBackground = getDrawable(R.drawable.large_bitmap);
}
label.setBackgroundDrawable(sBackground);
setContentView(label);
}
『肆』 Android線程泄漏場景以及解決辦法
1.非靜態內部類的靜態實例
非靜態內部類會持有外部類的引用,如果非靜態內部類的實例是靜態的,就會長期的維持著外部類的引用,組織被系統回收,解決辦法是使用靜態內部類
2.多線程相關的匿名內部類和非靜態內部類
匿名內部類同樣會持有外部類的引用,如果在線程中執行耗時操作就有可能發生內存泄漏,導致外部類無法被回收,直到耗時任務結束,解決辦法是在頁面退出時結束線程中的任務
3.Handler內存泄漏
Handler導致的內存泄漏也可以被歸納為非靜態內部類導致的,Handler內部message是被存儲在MessageQueue中的,有些message不能馬上被處理,存在的時間會很長,導致handler無法被回收,如果handler是非靜態的,就會導致它的外部類無法被回收,解決辦法是1.使用靜態handler,外部類引用使用弱引用處理2.在退出頁面時移除消息隊列中的消息
4.Context導致內存泄漏
根據場景確定使用Activity的Context還是Application的Context,因為二者生命周期不同,對於不必須使用Activity的Context的場景(Dialog),一律採用Application的Context,單例模式是最常見的發生此泄漏的場景,比如傳入一個Activity的Context被靜態類引用,導致無法回收
5.靜態View導致泄漏
使用靜態View可以避免每次啟動Activity都去讀取並渲染View,但是靜態View會持有Activity的引用,導致無法回收,解決辦法是在Activity銷毀的時候將靜態View設置為null(View一旦被載入到界面中將會持有一個Context對象的引用,在這個例子中,這個context對象是我們的Activity,聲明一個靜態變數引用這個View,也就引用了activity)
6.WebView導致的內存泄漏
WebView只要使用一次,內存就不會被釋放,所以WebView都存在內存泄漏的問題,通常的解決辦法是為WebView單開一個進程,使用AIDL進行通信,根據業務需求在合適的時機釋放掉
7.資源對象未關閉導致
如Cursor,File,Broadcast等,內部往往都使用了緩沖,會造成內存泄漏,一定要確保關閉它並將引用置為null
8.集合中的對象未清理
集合用扮行團於保存對象,如果集合越來越大,不進行合理的清理,尤其是入股集合是靜態的
9.Bitmap導致內存泄漏
bitmap是比較占內存的,所以一定要在不使用的時候及時進行清理,避免靜態變數持有大的bitmap對象
10.監聽器未關閉
很多需要register和unregister的系統服務要在合適的時候進行unregister,手動添加的listener也廳橘需要帶顫及時移除
『伍』 如何使用MAT分析Android應用內存泄露
使用Android Studio,Android Device Monitor 配合Eclipse的MAT(
Memory Analyzer)工具來分析android內存泄露。
1、新建一個Android 測試應用。填寫好應用的名稱,以及保存位置後,直接下一步到最後點擊「Finish」。
『陸』 關於Android EditText導致的內存泄漏的問題
泄露信息如下:
====================================
HEAP ANALYSIS RESULT
====================================
1 APPLICATION LEAKS
References underlined with "~~~" are likely causes.
Learn more at https://squ.re/leaks.
Displaying only 1 leak trace out of 4 with the same signature
Signature:
┬───
│ GC Root: System class
│
├─ android.os.AsyncTask class
│ Leaking: NO (a class is never leaking)
│ ↓ static AsyncTask.SERIAL_EXECUTOR
│ ~~~~~~~~~~~~~~~
├─ android.os.AsyncTask$SerialExecutor instance
│ Leaking: UNKNOWN
│ ↓ AsyncTask$SerialExecutor.mTasks
│ ~~~~~~
├─ java.util.ArrayDeque instance
│ Leaking: UNKNOWN
│ ↓ ArrayDeque.elements
│ ~~~~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ ↓ Object[].[0]
│ ~~~
├─ android.os.AsyncTask$SerialExecutor$1 instance
│ Leaking: UNKNOWN
│ Anonymous class implementing java.lang.Runnable
│ ↓ AsyncTask$SerialExecutor$1.val$r
│ ~~~~~
├─ android.widget.TextView$3 instance
│ Leaking: UNKNOWN
│ Anonymous class implementing java.lang.Runnable
│ ↓ TextView$3.this$0
│ ~~~~~~
├─ androidx.appcompat.widget.AppCompatEditText instance
│ Leaking: YES (View.mContext references a destroyed activity)
│ View not part of a window view hierarchy
│ View.mAttachInfo is null (view detached)
│ View.mID = R.id.input_edit
│ View.mWindowAttachCount = 1
│ mContext instance of RoomMainActivity with mDestroyed = true
│ ↓ View.mContext
╰→ RoomMainActivity instance
Leaking: YES (ObjectWatcher was watching this because RoomMainActivity
received Activity#onDestroy() callback and Activity#mDestroyed is true)
key = cb388a5c-0ff2-452f-a9b0-6ea7d03a5105
watchDurationMillis = 37631
retainedDurationMillis = 32630
mApplication instance of ChatApplication
mBase instance of androidx.appcompat.view.ContextThemeWrapper
====================================
原因分析:
1、引用鏈結構:AsyncTask的SerialExecutor執行器引用了EditText對象,而EditText對象中的mContext引用到了RoomMainActivity中context,導致RoomMainActivity 無法銷毀。
查看源碼得知在TextView中有updateTextServicesLocaleAsync()方法,調用了AsyncTask.execute()向其中傳入了匿名內部類Runnable,而持有了控制項對象。
2、解決方法:因為在源碼層面無法修改源碼,在引用端切斷引用鏈。
給EditText使用Application的上下文,在EditText使用的頁面退出銷毀時移除EditText控制項,包括置空它的監聽器、清除它的焦點。
import android.content.Context;
import android.os.Build;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.ViewGroup;
import androidx.appcompat.widget.AppCompatEditText;
/**
* 關於Android EditText導致的內存泄漏的問題
*/
public class NoMemoryLeakEditTextextends AppCompatEditText {
public NoMemoryLeakEditText(Context context) {
super(context.getApplicationContext());
}
public NoMemoryLeakEditText(Context context, AttributeSet attrs) {
super(context.getApplicationContext(), attrs);
}
public NoMemoryLeakEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context.getApplicationContext(), attrs, defStyleAttr);
}
public void clearMemoryLeak(TextWatcher watcher, ViewGroup container) {
clearFocus();
setOnTouchListener(null);
setOnClickListener(null);
setOnDragListener(null);
setOnKeyListener(null);
setOnLongClickListener(null);
setOnEditorActionListener(null);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
(null);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setOnScrollChangeListener(null);
}
setOnFocusChangeListener(null);
removeTextChangedListener(watcher);
container.removeView(this);
}
}
最後在頁面銷毀的地方調用clearMemoryLeak方法
『柒』 android開發什麼叫內存泄露
下面圖片是解決內存泄露的例子。例子來自android學習手冊,android學習手冊,裡面有源碼。android學習手冊包含9個章節,108個例子,源碼文檔隨便看,例子都是可交互,可運行,源碼採用android studio目錄結構,高亮顯示代碼,文檔都採用文檔結構圖顯示,可以快速定位。360手機助手中下載,圖標上有貝殼
在android程序開發中,當一個對象已經不需要再使用了,本該被回收時,而另外一個正在使用的對象持有它的引用從而導致它不能被回收,這就導致本該被回收的對象不能被回收而停留在堆內存中,內存泄漏就產生了。
內存泄漏有什麼影響呢?它是造成應用程序OOM的主要原因之一。由於android系統為每個應用程序分配的內存有限,當一個應用中產生的內存泄漏比較多時,就難免會導致應用所需要的內存超過這個系統分配的內存限額,這就造成了內存溢出而導致應用Crash。
了解了內存泄漏的原因及影響後,我們需要做的就是掌握常見的內存泄漏,並在以後的android程序開發中,盡量避免它。下面小編搜羅了5個android開發中比較常見的內存泄漏問題及解決辦法,分享給大家,一起來看看吧。
一、單例造成的內存泄漏
Android的單例模式非常受開發者的喜愛,不過使用的不恰當的話也會造成內存泄漏。因為單例的靜態特性使得單例的生命周期和應用的生命周期一樣長,這就說明了如果一個對象已經不需要使用了,而單例對象還持有該對象的引用,那麼這個對象將不能被正常回收,這就導致了內存泄漏。
如下這個典例:
public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context;
}
public static AppManager getInstance(Context context) {
if (instance != null) {
instance = new AppManager(context);
}
return instance;
}
}
這是一個普通的單例模式,當創建這個單例的時候,由於需要傳入一個Context,所以這個Context的生命周期的長短至關重要:
1、傳入的是Application的Context:這將沒有任何問題,因為單例的生命周期和Application的一樣長 ;
2、傳入的是Activity的Context:當這個Context所對應的Activity退出時,由於該Context和Activity的生命周期一樣長(Activity間接繼承於Context),所以當前Activity退出時它的內存並不會被回收,因為單例對象持有該Activity的引用。
所以正確的單例應該修改為下面這種方式:
public class AppManager {
private static AppManager instance;
private Context context;
private AppManager(Context context) {
this.context = context.getApplicationContext();
}
public static AppManager getInstance(Context context) {
if (instance != null) {
instance = new AppManager(context);
}
return instance;
}
}
這樣不管傳入什麼Context最終將使用Application的Context,而單例的生命周期和應用的一樣長,這樣就防止了內存泄漏。
二、非靜態內部類創建靜態實例造成的內存泄漏
有的時候我們可能會在啟動頻繁的Activity中,為了避免重復創建相同的數據資源,會出現這種寫法:
public class MainActivity extends AppCompatActivity {
private static TestResource mResource = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(mManager == null){
mManager = new TestResource();
}
//...
}
class TestResource {
//...
}
}
這樣就在Activity內部創建了一個非靜態內部類的單例,每次啟動Activity時都會使用該單例的數據,這樣雖然避免了資源的重復創建,不過這種寫法卻會造成內存泄漏,因為非靜態內部類默認會持有外部類的引用,而又使用了該非靜態內部類創建了一個靜態的實例,該實例的生命周期和應用的一樣長,這就導致了該靜態實例一直會持有該Activity的引用,導致Activity的內存資源不能正常回收。正確的做法為:
將該內部類設為靜態內部類或將該內部類抽取出來封裝成一個單例,如果需要使用Context,請使用ApplicationContext 。
三、Handler造成的內存泄漏
Handler的使用造成的內存泄漏問題應該說最為常見了,平時在處理網路任務或者封裝一些請求回調等api都應該會藉助Handler來處理,對於Handler的使用代碼編寫一不規范即有可能造成內存泄漏,如下示例:
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//...
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
}
private void loadData(){
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
}
這種創建Handler的方式會造成內存泄漏,由於mHandler是Handler的非靜態匿名內部類的實例,所以它持有外部類Activity的引用,我們知道消息隊列是在一個Looper線程中不斷輪詢處理消息,那麼當這個Activity退出時消息隊列中還有未處理的消息或者正在處理消息,而消息隊列中的Message持有mHandler實例的引用,mHandler又持有Activity的引用,所以導致該Activity的內存資源無法及時回收,引發內存泄漏,所以另外一種做法為:
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference<Context> reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText("");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
loadData();
}
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
}
創建一個靜態Handler內部類,然後對Handler持有的對象使用弱引用,這樣在回收時也可以回收Handler持有的對象,這樣雖然避免了Activity泄漏,不過Looper線程的消息隊列中還是可能會有待處理的消息,所以我們在Activity的Destroy時或者Stop時應該移除消息隊列中的消息,更准確的做法如下:
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference<Context> reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText("");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
loadData();
}
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler.removeCallbacksAndMessages(null);
}
}
使用mHandler.removeCallbacksAndMessages(null);是移除消息隊列中所有消息和所有的Runnable。當然也可以使用mHandler.removeCallbacks();或mHandler.removeMessages();來移除指定的Runnable和Message。
四、線程造成的內存泄漏
對於線程造成的內存泄漏,也是平時比較常見的,如下這兩個示例可能每個人都這樣寫過:
//——————test1
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null;
}
}.execute();
//——————test2
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(10000);
}
}).start();
上面的非同步任務和Runnable都是一個匿名內部類,因此它們對當前Activity都有一個隱式引用。如果Activity在銷毀之前,任務還未完成, 那麼將導致Activity的內存資源無法回收,造成內存泄漏。正確的做法還是使用靜態內部類的方式,如下:
static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
private WeakReference<Context> weakReference;
public MyAsyncTask(Context context) {
weakReference = new WeakReference<>(context);
}
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
MainActivity activity = (MainActivity) weakReference.get();
if (activity != null) {
//...
}
}
}
static class MyRunnable implements Runnable{
@Override
public void run() {
SystemClock.sleep(10000);
}
}
//——————
new Thread(new MyRunnable()).start();
new MyAsyncTask(this).execute();
這樣就避免了Activity的內存資源泄漏,當然在Activity銷毀時候也應該取消相應的任務AsyncTask::cancel(),避免任務在後台執行浪費資源。
五、資源未關閉造成的內存泄漏
對於使用了BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap等資源的使用,應該在Activity銷毀時及時關閉或者注銷,否則這些資源將不會被回收,造成內存泄漏。
『捌』 Android技術分享|Android 中部分內存泄漏示例及解決方案
內存泄漏:
舉例:
請注意以下的例子是虛構的
內存抖動
源自Android文檔中的 Memory churn 一詞,中文翻譯為內存抖動。
指快速頻繁的創建對象從而產生的性能問題。
引用Android文檔原文:
Java內存泄漏的根本原因是 長生命周期 的對象持有 短生命周期 對象的引用就很可能發生內存泄漏。
盡管短生命周期對象已經不再需要,但因為長生命周期依舊持有它的引用,故不能被回收而導致內存泄漏。
靜態集合類引起的內存泄漏
如果僅僅釋放引用本身(tO = null), ArrayList 依然在引用該對象,GC無法回收。
監聽器
在Java應用中,通常會用到很多監聽器,一般通過 addXXXXListener() 實現。但釋放對象時通常會忘記刪除監聽器,從而增加內存泄漏的風險。
各種連接
如資料庫連接、網路連接(Socket)和I/O連接。忘記顯式調用 close() 方法引起的內存泄漏。
內部類和外部模塊的引用
內部類的引用是很容易被遺忘的一種,一旦沒有釋放可能會導致一系列後續對象無法釋放。此外還要小心外部模塊不經意的引用,內部類是否提供相應的操作去除外部引用。
單例模式
由於單例的靜態特性,使其生命周期與應用的生命周期一樣長,一旦使用不恰當極易造成內存泄漏。如果單利持有外部引用,需要注意提供釋放方式,否則當外部對象無法被正常回收時,會進而導致內存泄漏。
集合類泄漏
如集合的使用范圍超過邏輯代碼的范圍,需要格外注意刪除機制是否完善可靠。比如由靜態屬性 static 指向的集合。
單利泄漏
以下為簡單邏輯代碼,只為舉例說明內存泄漏問題,不保證單利模式的可靠性。
AppManager 創建時需要傳入一個 Context ,這個 Context 的生命周期長短至關重要。
1. 如果傳入的是 Application 的 Context ,因為 Application 的生命周期等同於應用的生命周期,所以沒有任何問題。
2. 如果傳入的是 Activity 的 Context ,則需要考慮這個 Activity 是否在整個生命周期都不會被回收了,如果不是,則會造成內存泄漏。
非靜態內部類創建靜態實例造成的內存泄漏
應該將該內部類單獨封裝為一個單例來使用。
匿名內部類/非同步線程
Runnable都使用了匿名內部類,將持有MyActivity的引用。如果任務在Activity銷毀前未完成,將導致Activity的內存無法被回收,從而造成內存泄漏。
解決方法:將Runnable獨立出來或使用靜態內部類,可以避免因持有外部對象導致的內存泄漏。
Handler造成的內存泄漏
Handler屬於TLS(Thread Local Storage)變數,生命周期與Activity是不一致的,容易導致持有的對象無法正確被釋放
當Android應用程序啟動時,該應用程序的主線程會自動創建一個Looper對象和與之關聯的MessageQueue。
當主線程中實例化一個Handler對象後,它就會自動與主線程Looper的MessageQueue關聯起來。所有發送到MessageQueue的Messag都會持有Handler的引用,所以Looper會據此回調Handle的handleMessage()方法來處理消息。只要MessageQueue中有未處理的Message,Looper就會不斷的從中取出並交給Handler處理。
另外,主線程的Looper對象會伴隨該應用程序的整個生命周期。
在Java中,非靜態內部類和匿名類內部類都會潛在持有它們所屬的外部類的引用,但是靜態內部類卻不會。
當該 Activity 被 finish() 掉時,延遲執行任務的 Message 還會繼續存在於主線程中,它持有該 Activity 的 Handler 引用,所以此時 finish() 掉的 Activity 就不會被回收了從而造成內存泄漏(因 Handler 為非靜態內部類,它會持有外部類的引用,在這里就是指 SampleActivity)。
避免不必要的靜態成員變數
對於BroadcastReceiver、ContentObserver、File、Cursor、Stream、Bitmap等資源的使用,應在Activity銷毀前及時關閉或注銷。
不使用WebView對象時,應調用`destroy()`方法銷毀。