Android開發筆記——圖片緩衝、手勢及OOM分析,

來源:互聯網
上載者:User

Android開發筆記——圖片緩衝、手勢及OOM分析,

      把圖片緩衝、手勢及OOM三個主題放在一起,是因為在Android應用開發過程中,這三個問題經常是聯絡在一起的。首先,預覽大圖需要支援手勢縮放,旋轉,平移等操作;其次,圖片在本地需要進行緩衝,避免頻繁訪問網路;最後,圖片(Bitmap)是Android中佔用記憶體的大戶,涉及高清大圖等處理時,記憶體佔用非常大,稍不謹慎,系統就會報OOM錯誤。

     慶幸的是,這三個主題在Android開發中屬於比較普遍的問題,有很多針對於此的通用的開源解決方案。因此,本文主要說明筆者在開發過程中用到的一些第三方開源庫。主要內容如下:

一、Universal Image Loader、Picasso、Glide與Fresco的對比及使用

      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一致,不再贅述。

三、OOM分析工具——LeakCanary

     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地址。

 

四、一些雜亂的總結

      記憶體泄露的常見原因:

  • 靜態對象:監聽器,廣播,webview;
  • this$0:線程,定時器,Handler;
  • 系統:TextLine,IME,音頻

      兜底回收記憶體:

      Activity泄露會導致該Activity引用的Bitmap/DrawingCache等無法釋放,兜底回收是指對已泄露的Activity,嘗試回收其持有的資源。在onDestroy中從rootview開始,遞迴釋放所有子VIew涉及的圖片,背景,DrawingCache,監聽器等資源。

      降低Runtime記憶體的方法:

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.