標籤:
android記憶體流失最佳化摘要 部落格分類: android
android記憶體溢出OutOfMemoryError .
android行動裝置 App程式的記憶體配置一般是8凱瑟琳約,不正確地假定處理記憶體處理非常easy建立OutOfMemoryError。我們的產品是最常見的錯誤是OutOfMemoryError的異常,
在解決這個異常時在網上發現非常多關於OutOfMemoryError的原因的介紹。
OutOfMemoryError主要由下面幾種情況造成:
1.資料庫的cursor沒有關閉。
操作Sqlite資料庫時,Cursor是資料庫表中每一行的集合,Cursor提供了非常多方法,能夠非常方便的讀取資料庫中的值,
能夠依據索引。列名等擷取資料庫中的值,通過遊標的方式能夠調用moveToNext()移到下一行
當我們操作完資料庫後。一定要記得調用Cursor對象的close()來關閉遊標,釋放資源。
2.構造adapter沒有使用緩衝contentview。
在繼承BaseAdapter時會讓我們重寫getView(int position, View convertView, ViewGroup parent)方法。
第二個參數convertView就是我們要用到的重用的對象
Java代碼
[email protected]
2.public View getView(int position, View convertView, ViewGroup parent) {
3. ViewHolder vHolder = null;
4. //假設convertView對象為空白則建立新對象,不為空白則複用
5. if (convertView == null) {
6. convertView = inflater.inflate(..., null);
7. // 建立 ViewHodler 對象
8. vHolder = new ViewHolder();
9. vHolder.img= (ImageView) convertView.findViewById(...);
10. vHolder.tv= (TextView) convertView
11. .findViewById(...);
12. // 將ViewHodler儲存到Tag中
13. convertView.setTag(vHolder);
14. } else {
15. //當convertView不為空白時,通過getTag()得到View
16. vHolder = (ViewHolder) convertView.getTag();
17. }
18. // 給對象賦值。改動顯示的值
19. vHolder.img.setImageBitmap(...);
20. vHolder.tv.setText(...);
21. return convertView;
22.}
23. //將顯示的View 封裝成類
24.static class ViewHolder {
25. TextView tv;
26. ImageView img;
27.}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder vHolder = null;
//假設convertView對象為空白則建立新對象,不為空白則複用
if (convertView == null) {
convertView = inflater.inflate(..., null);
// 建立 ViewHodler 對象
vHolder = new ViewHolder();
vHolder.img= (ImageView) convertView.findViewById(...);
vHolder.tv= (TextView) convertView
.findViewById(...);
// 將ViewHodler儲存到Tag中
convertView.setTag(vHolder);
} else {
//當convertView不為空白時,通過getTag()得到View
vHolder = (ViewHolder) convertView.getTag();
}
// 給對象賦值,改動顯示的值
vHolder.img.setImageBitmap(...);
vHolder.tv.setText(...);
return convertView;
}
//將顯示的View 封裝成類
static class ViewHolder {
TextView tv;
ImageView img;
}
這裡僅僅講用法,詳細效能測試文章請見:
ListView中getView的原理+怎樣在ListView中放置多個item
http://www.cnblogs.com/xiaowenji/archive/2010/12/08/1900579.html
Android開發之ListView適配器(Adapter)最佳化
http://shinfocom.iteye.com/blog/1231511
3.調用registerReceiver()後未調用unregisterReceiver().
廣播接收者(BroadcastReceiver)常常在應用中用到。能夠在多線程任務完畢後發送廣播通知UI更新,也能夠接收系統廣播實現一些功能
能夠通過代碼的方式注冊:
IntentFilter postFilter = new IntentFilter();
postFilter.addAction(getPackageName() + ".background.job");
this.registerReceiver(receiver, postFilter);
當我們Activity中使用了registerReceiver()方法注冊了BroadcastReceiver。一定要在Activity的生命週期內調用unregisterReceiver()方法取消注冊
也就是說registerReceiver()和unregisterReceiver()方法一定要成對出現,通常我們能夠重寫Activity的onDestory()方法:
Java代碼
[email protected]
2.protected void onDestroy() {
3. this.unregisterReceiver(receiver);
4. super.onDestroy();
5.}
@Override
protected void onDestroy() {
this.unregisterReceiver(receiver);
super.onDestroy();
}
4.未關閉InputStream/OutputStream。
這個就不多說了,我們操作完輸入輸出資料流都要關閉流
5.Bitmap使用後未調用recycle()。
圖片處理不好是造成記憶體溢出的又一個頭號原因,(在我們的產品中也有體現),
當我們處理完圖片之後能夠通過調用recycle()方法來回收圖片對象
Java代碼
1.if(!bitmap.isRecycled())
2.{
3. bitmap.recycle()
4.}
if(!bitmap.isRecycled())
{
bitmap.recycle()
}
除此之外:
直接使用ImageView顯示bitmap會佔用較多資源。特別是圖片較大的時候,可能導致崩潰。
使用BitmapFactory.Options設定inSampleSize, 這樣做能夠降低對系統資源的要求。
屬性值inSampleSize表示縮圖大小為原始圖片大小的幾分之中的一個,即假設這個值為2,則取出的縮圖的寬和高都是原始圖片的1/2,圖片大小就為原始大小的1/4。
BitmapFactory.Options bitmapFactoryOptions = new BitmapFactory.Options();
bitmapFactoryOptions.inJustDecodeBounds = true;
bitmapFactoryOptions.inSampleSize = 2;
// 這裡一定要將其設定回false,由於之前我們將其設定成了true
// 設定inJustDecodeBounds為true後,decodeFile並不分配空間,即。BitmapFactory解碼出來的Bitmap為Null,但可計算出原始圖片的長度和寬度
options.inJustDecodeBounds = false;
Bitmap bmp = BitmapFactory.decodeFile(sourceBitmap, options);
6.Context泄漏。
這是一個非常隱晦的OutOfMemoryError的情況。先看一個Android官網提供的範例:
Java代碼
1.private static Drawable sBackground;
[email protected]
3.protected void onCreate(Bundle state) {
4. super.onCreate(state);
5.
6. TextView label = new TextView(this);
7. label.setText("Leaks are bad");
8.
9. if (sBackground == null) {
10. sBackground = getDrawable(R.drawable.large_bitmap);
11. }
12. label.setBackgroundDrawable(sBackground);
13.
14. setContentView(label);
15.}
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);
}
這段代碼效率非常快,但同一時候又是極其錯誤的。
在第一次螢幕方向切換時它泄露了一開始建立的Activity。當一個Drawable附加到一個 View上時,
View會將其作為一個callback設定到Drawable上。
上述的程式碼片段。意味著Drawable擁有一個TextView的引用,
而TextView又擁有Activity(Context類型)的引用。換句話說,Drawable擁有了很多其它的對象引用。即使Activity被 銷毀,記憶體仍然不會被釋放。
另外,對Context的引用超過它本身的生命週期,也會導致Context泄漏。所以盡量使用Application這樣的Context類型。
這樣的Context擁有和應用程式一樣長的生命週期,而且不依賴Activity的生命週期。
假設你打算儲存一個長時間的對象,
而且其須要一個 Context,記得使用Application對象。你能夠通過調用Context.getApplicationContext()或 Activity.getApplication()輕鬆得到Application對象。
近期遇到一種情況引起了Context泄漏,就是在Activity銷毀時,裡面有其它線程沒有停。
總結一下避免Context泄漏應該注意的問題:
1.使用Application這樣的Context類型。
2.注意對Context的引用不要超過它本身的生命週期。
3.謹慎的使用“static”keyword。
4.Context裡假設有線程。一定要在onDestroy()裡及時停掉。
7.statickeyword
當類成員變數聲明為static後,它屬於類而不是屬於對象。假設我們將非常大的資來源物件(Bitmap。context等待)聲明static。那麼這些資源不會被回收的回收目標。
它會一直存在。因此,使用statickeyword成員變數定義時要小心。
Android 記憶體流失最佳化匯總