編輯:關於Android編程
在android編碼中,會有一些簡便的寫法和編碼習慣,會導致我們的代碼有很多內存洩露的問題,在這裡做一個已知錯誤的總結:
1、編寫單例的時候常出現的錯誤。
錯誤方式:
public class Foo{ private static Foo foo; private Context mContext; private Foo(Context mContext){ this.mContext = mContext; } // 普通單例,非線程安全 public static Foo getInstance(Context mContext){ if(foo == null) foo = new Foo(mContext); return foo; } public void otherAction(){ mContext.xxxx(); …. } }
錯誤原因:
如果我們在Activity A中或者其他地方使用Foo.getInstance()時,我們總是會順手寫一個『this』或者『mContext』(這個變量也是指向this)。試想一下,當前我們所用的Foo是單例,意味著被初始化後會一直存在與內存中,以方便我們以後調用的時候不會在此次創建Foo對象。但Foo中的『mContext』變量一直都會持有Activity A中的『Context』,導致Activity A即使執行了onDestroy方法,也不能夠將自己銷毀。但『applicationContext』就不同了,它一直伴隨著我們應用存在(中途也可能會被銷毀,但也會自動reCreate),所以就不用擔心Foo中的『mContext』會持有某Activity的引用,讓其無法銷毀。
正確方式:
public class Foo{ private static Foo foo; private Context mContext; private Foo(Context mContext){ this.mContext = mContext; } // 普通單例,非線程安全 public static Foo getInstance(Context mContext){ if(foo == null) foo = new Foo(mContext.getApplicationContext()); return foo; } public void otherAction(){ mContext.xxxx(); …. } }
2、使用匿名內部類的時候經常出現的錯誤
錯誤方式:
public class FooActivity extends Activity{ private TextView textView; private Handler handler = new Handler(){ @override public void handlerMessage(Message msg){ } }; @override public void onCreate(Bundle bundle){ super.onCreate(bundle); setContextView(R.layout.activity_foo_layout); textView = (TextView)findViewById(R.id.textView); handler.postDelayed(new Runnable(){ @override public void run(){ textView.setText(“ok”); }; },1000 * 60 * 10); } }
錯誤原因:
當我們執行了FooActivity的finish方法,被延遲的消息會在被處理之前存在於主線程消息隊列中10分鐘,而這個消息中又包含了Handler的引用,而Handler是一個匿名內部類的實例,其持有外面的FooActivity的引用,所以這導致了FooActivity無法回收,進而導致FooActivity持有的很多資源都無法回收,所以產生了內存洩露。
注意上面的new Runnable這裡也是匿名內部類實現的,同樣也會持有FooActivity的引用,也會阻止FooActivity被回收。
一個靜態的匿名內部類實例不會持有外部類的引用。
正確方式:
public class FooActivity extends Activity{ private TextView textView; private static class MyHandler extends Handler { private final WeakReference<FooActivity> mActivity; public MyHandler(FooActivity activity) { mActivity = new WeakReference<FooActivity>(activity); } @Override public void handleMessage(Message msg) { FooActivity activity = mActivity.get(); if (activity != null) { // ... } } } private final MyHandler handler = new MyHandler(this); @override public void onCreate(Bundle bundle){ super.onCreate(bundle); setContextView(R.layout.activity_foo_layout); textView = (TextView)findViewById(R.id.textView); handler.postDelayed(new MyRunnable(textView),1000 * 60 * 10); } private static class MyRunnable implements Runnable{ private WeakReference<TextView> textViewWeakReference; public MyRunnable(TextView textView){ textViewWeakReference = new WeakReference<TextView>(textView); } @override public void run(){ final TextView textView = textViewWeakReference.get(); if(textView != null){ textView.setText("OK"); } }; } }
3、在使用handler後,記得在onDestroy裡面handler.removeCallbacksAndMessages(object token);
handler.removeCallbacksAndMessages(null); // removeCallbacksAndMessages,當參數為null的時候,可以清除掉所有跟次handler相關的Runnable和Message,我們在onDestroy中調用次方法也就不會發生內存洩漏了。
開發中需要注意的點以免內存洩漏:
獲取context的方法,以及使用上context和applicationContext的區別:
大家注意看到有一些NO上添加了一些數字,其實這些從能力上來說是YES,但是為什麼說是NO呢?下面一個一個解釋:
數字1:啟動Activity在這些類中是可以的,但是需要創建一個新的task。一般情況不推薦。
數字2:在這些類中去layout inflate是合法的,但是會使用系統默認的主題樣式,如果你自定義了某些樣式可能不會被使用。
數字3:在receiver為null時允許,在4.2或以上的版本中,用於獲取黏性廣播的當前值。(可以無視)
注:ContentProvider、BroadcastReceiver之所以在上述表格中,是因為在其內部方法中都有一個context用於使用。
好了,這裡我們看下表格,重點看Activity和Application,可以看到,和UI相關的方法基本都不建議或者不可使用Application,並且,前三個操作基本不可能在Application中出現。實際上,只要把握住一點,凡是跟UI相關的,都應該使用Activity做為Context來處理;其他的一些操作,Service,Activity,Application等實例都可以,當然了,注意Context引用的持有,防止內存洩漏。
以上就是本文的全部內容,希望對大家的學習有所幫助。
Android中Button控件應該算作是比較簡單的控件,然而,它的使用頻率卻是非常的高,今天,我在這裡總結了三種常用的點擊Button實現其功能的方法。1.很多時候,我
Bluestacks是一個可以讓Android應用程序運行在電腦(現在包括windows系統,mac版)的一種模擬器,就是我們在電腦上也可以運行Androi
關於如何制作濾鏡,知乎的這篇問答中的最高票給了比較專業的回答另外,githunb曾經有個開源項目InstagramFilters給出了Instagram中最初版本的一些濾
我們學的Android 數據持久化的技術包括文件存儲、SharedPreferences 存儲、以及數據庫存儲。不知道你有沒有發現,使用這些持久化技術所保存的數據都只能在