編輯:關於android開發
把圖片緩存、手勢及OOM三個主題放在一起,是因為在Android應用開發過程中,這三個問題經常是聯系在一起的。首先,預覽大圖需要支持手勢縮放,旋轉,平移等操作;其次,圖片在本地需要進行緩存,避免頻繁訪問網絡;最後,圖片(Bitmap)是Android中占用內存的大戶,涉及高清大圖等處理時,內存占用非常大,稍不謹慎,系統就會報OOM錯誤。
慶幸的是,這三個主題在Android開發中屬於比較普遍的問題,有很多針對於此的通用的開源解決方案。因此,本文主要說明筆者在開發過程中用到的一些第三方開源庫。主要內容如下:
Universal Image Loader(UIL)、Picasso、Glide與Fresco是Android中進行圖片加載的常用第三方庫,主要封裝了內存緩存、磁盤緩存、網絡請求緩存、線程池等方法,抽象了圖片加載的流程,很大程度避免了加載圖片引起的內存溢出,提高了圖片加載的效率。下圖是筆者近期從各個庫的github頁面查詢到的信息:
附上各個庫的github地址:
Universal Image Loader:https://github.com/nostra13/Android-Universal-Image-Loader.git
Picasso:https://github.com/square/picasso.git
Glide:https://github.com/bumptech/glide.git
Fresco:https://github.com/facebook/fresco.git
這四個圖片緩存庫的基本使用(HelloWorld)都可以通過一句代碼實現,分別如下:
UIL:
ImageLoader.getInstance().displayImage(url, imageView);
Picasso:
Picasso.with(context).load(url).into(imageView);
Glide:
Glide.with(context).load(url).into(imageView);
Fresco:
simpleDraweeView.setImageURI(uri);
細心的朋友可以看出,Picasso和Glide的API非常類似。事實上,這四個庫在實現的核心思想上都比較相似,可以抽象為以下五個模塊:
說一句題外話,掌握了各種開源庫的實現的核心思想後,會發現軟件工程的一個共同點,就是通過將流程形式化、抽象化,從而提高效率。不論是業務的效率,還是開發的效率,這或許也是軟件作為一門科學的核心思想。
ImageLoader的設計及優點
ImageLoader加載的流程如下圖。(需要申明:下面三張流程圖來自Trinea,尊重原作者版權)
dependencies { compile 'com.bm.photoview:library:1.3.6' }
(或者也可以將項目下載下來,將Info.java和PhotoView.java兩個文件拷貝到你的項目中,不推薦)——這種方式適用於Eclipse。
2.xml添加
<com.bm.library.PhotoView android:id="@+id/img" android:layout_width="match_parent" android:layout_height="match_parent" android:scaleType="centerInside" android:src="@drawable/bitmap1" />
3.java代碼
PhotoView photoView = (PhotoView) findViewById(R.id.img); // 啟用圖片縮放功能 photoView.enable(); // 禁用圖片縮放功能 (默認為禁用,會跟普通的ImageView一樣,縮放功能需手動調用enable()啟用) photoView.disenable(); // 獲取圖片信息 Info info = photoView.getInfo(); // 從一張圖片信息變化到現在的圖片,用於圖片點擊後放大浏覽,具體使用可以參照demo的使用 photoView.animaFrom(info); // 從現在的圖片變化到所給定的圖片信息,用於圖片放大後點擊縮小到原來的位置,具體使用可以參照demo的使用 photoView.animaTo(info,new Runnable() { @Override public void run() { //動畫完成監聽 } }); // 獲取動畫持續時間 int d = PhotoView.getDefaultAnimaDuring();
PhotoView實現的基本原理是在繼承於ImageView的PhotoView中采用了縮放Matrix及手勢監聽。
public class PhotoView extends ImageView { …… private Matrix mBaseMatrix = new Matrix(); private Matrix mAnimaMatrix = new Matrix(); private Matrix mSynthesisMatrix = new Matrix(); private Matrix mTmpMatrix = new Matrix(); private RotateGestureDetector mRotateDetector; private GestureDetector mDetector; private ScaleGestureDetector mScaleDetector; private OnClickListener mClickListener; private ScaleType mScaleType; …… }
PhotoView的實現與上述原理基本一致,這裡不再贅述。對於自定義控件的實現,後續文章會進行詳細的分析。
GestureImageView的簡介如下:
1.Configured as View in layout.xml
code:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:gesture-image="http://schemas.polites.com/android" android:layout_width="fill_parent" android:layout_height="fill_parent"> <com.polites.android.GestureImageView android:id="@+id/image" android:layout_width="fill_parent" android:layout_height="wrap_content" android:src="@drawable/image" gesture-image:min-scale="0.1" gesture-image:max-scale="10.0" gesture-image:strict="false"/> </LinearLayout>
2.Configured Programmatically
code:
import com.polites.android.GestureImageView; import android.app.Activity; import android.os.Bundle; import android.view.ViewGroup; import android.widget.LinearLayout.LayoutParams; public class SampleActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); GestureImageView view = new GestureImageView(this); view.setImageResource(R.drawable.image); view.setLayoutParams(params); ViewGroup layout = (ViewGroup) findViewById(R.id.layout); layout.addView(view); } }
原理基本同PhotoView一致,不再贅述。
LeakCanary的介紹:
A memory leak detection library for Android and Java.
可見,LeakCanary主要用於檢測各種內存不能被GC,從而導致洩露的情況。
LeakCanary的地址 https://github.com/square/leakcanary.git
Demo地址:
https://github.com/liaohuqiu/leakcanary-demo.git(AS)
https://github.com/teffy/LeakcanarySample-Eclipse.git(Eclipse)
下面是demo中TestActivity中的TextView被靜態變量引用導致無法回收引起的內存洩露的截圖。
LeakCanary的使用較為簡單,首先添加依賴工程:
dependencies { debugCompile 'com.squareup.leakcanary:leakcanary-android:1.3' releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.3' }
其次,在application的onCreate()方法中進行初始化。
public class ExampleApplication extends Application { @Override public void onCreate() { super.onCreate(); LeakCanary.install(this); } }
經過這兩步之後就可以使用了。LeakCanary.install(this)會返回一個預定義的 RefWatcher,同時也會啟用一個ActivityRefWatcher,用於自動監控調用 Activity.onDestroy() 之後洩露的 activity。如果需要監聽fragment,則在fragment的onDestroy()方法進行注冊:
public abstract class BaseFragment extends Fragment { @Override public void onDestroy() { super.onDestroy(); RefWatcher refWatcher = ExampleApplication.getRefWatcher(getActivity()); refWatcher.watch(this); } }
當然,需要對某個變量進行監聽,直接對其進行watch即可。
RefWatcher refWatcher = {...}; // We expect schrodingerCat to be gone soon (or not), let's watch it. refWatcher.watch(schrodingerCat);
需要注意的是,在eclipse中使用LeakCanary需要在AndroidManifest文件中對堆占用分析以及展示的Service進行申明:
<service android:name="com.squareup.leakcanary.internal.HeapAnalyzerService" android:enabled="false" android:process=":leakcanary" /> <service android:name="com.squareup.leakcanary.DisplayLeakService" android:enabled="false" /> <activity android:name="com.squareup.leakcanary.internal.DisplayLeakActivity" android:enabled="false" android:icon="@drawable/leak_canary_icon" android:label="@string/leak_canary_display_activity_label" android:taskAffinity="com.squareup.leakcanary" android:theme="@style/leak_canary_LeakCanary.Base" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
注意:HeapAnalyzerService采用了多進程android:process=":leakcanary"。
上述開源工具的使用都較為簡單,關於詳細使用,請參考其github地址。
內存洩露的常見原因:
兜底回收內存:
Activity洩露會導致該Activity引用的Bitmap/DrawingCache等無法釋放,兜底回收是指對已洩露的Activity,嘗試回收其持有的資源。在onDestroy中從rootview開始,遞歸釋放所有子VIew涉及的圖片,背景,DrawingCache,監聽器等資源。
降低Runtime內存的方法:
android6.0源碼分析之Camera API2.0下的Capture流程分析,android6.0api2.0前面對Camera2的初始化以及預覽的相關流程進行了詳
Android Menu菜單使用,androidmenu菜單 如上圖右上角,菜單選項的編輯,第一種代碼實現方式如下: package com.example.menu;
setting菜單界面的形成--未優化,setting菜單界面-- 代碼: first_preference.xml: 1 <?xml versi
Android逆向之旅---解析編譯之後的Resource.arsc文件格式 一、前言 快過年了,先提前祝賀大家新年快樂,這篇文章也是今年最後一篇了。今天我們繼續來看