詳談Android從檔案讀取影象顯示的效率問題

NO IMAGE

因為從檔案讀取影象到Bitmap是一件比較費時的事情,所以研究了一下幾種可行的辦法,並做了對比。

首先解釋一下為什麼耗時,這是因為,在從jpg或者png檔案中讀取Bitmap時,一來需要對外存進行操作並且影象檔案一般都比較大,二來在建立Bitmap時,基本都需要對原始影象做操作,例如:降取樣、剪下、旋轉等等。所以如何高效的讀取圖片並呈現出來,是一個很值得研究的問題。

根據我的想法,大致想出了3種方案:

1、在當前的UI執行緒直接讀取並操作影象,然後呈現。

2、新開一個子執行緒讀取並操作影象,然後利用Bundle中Serializable的相關方法將其傳回UI執行緒並呈現。

3、其他做法與2一樣,但是利用的是Bundle中Parcelable的相關方法。

方法一


start_time = System.currentTimeMillis();
BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bitmap=BitmapFactory.decodeFile(path,options);
options.inSampleSize=calculateSize(options,width,height);
options.inJustDecodeBounds=false;
//整個影象,下采樣
bitmap=BitmapFactory.decodeFile(path,options);
//部分影象
Bitmap patch=Bitmap.createBitmap(bitmap, 10, 10, 100, 100);
end_time = System.currentTimeMillis();
Log.v("BitmapTest", "UI time consume:" (end_time - start_time));
imageView.setImageBitmap(bitmap);
patchView.setImageBitmap(patch);

操作很簡單,先將圖片檔案的尺寸等資訊讀取出來, 然後根據其尺寸計算其縮放比例,並將圖片中的一部分剪下出來。最後將圖片顯示在ImageView空間上。大致測了幾十次,得到的平均消耗時間為:72.75ms

方法二

啟動子執行緒


start_time = System.currentTimeMillis();
String path=Environment.getExternalStorageDirectory().getPath() File.separator "image1.jpg";
ImgThread imgThread=new ImgThread(msgHandler,path,width,height);
imgThread.start();

子執行緒中的操作,與1基本相同


BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds = true;
Bitmap bitmap=BitmapFactory.decodeFile(path,options);
options.inSampleSize=calculateSize(options,width,height);
options.inJustDecodeBounds=false;
//整個影象,下采樣
bitmap=BitmapFactory.decodeFile(path,options);
//部分影象
Bitmap patch=Bitmap.createBitmap(bitmap, 10, 10, 100, 100);
array=new ArrayList<Bitmap>(2);
array.add(bitmap);
array.add(patch);
//Serializable傳遞
Bundle bundle=new Bundle();    
bundle.putSerializable("img", array);
//Parcelable傳遞
/*
MyList l=new MyList(Parcel.obtain());
l.array=array;
bundle.putParcelable("img", l);
*/
Message msg= new Message();
msg.what=1;
msg.setData(bundle);
handler.sendMessage(msg);

將Bitmap傳回到UI執行緒並呈現


Bundle bundle=msg.getData();
//Serializable傳遞
ArrayList<Bitmap> array=(ArrayList<Bitmap>) bundle.getSerializable("img");
//Parcelable傳遞
//MyList l=(MyList)bundle.getParcelable("img");
//ArrayList<Bitmap> array=l.array;//=(ArrayList<Bitmap>) bundle.getParcelable("img");
Bitmap bitmap=array.get(0);
Bitmap patch=array.get(1);
end_time = System.currentTimeMillis();
Log.v("BitmapTest", "Th time consume:" (end_time - start_time));
imageView.setImageBitmap(bitmap);
patchView.setImageBitmap(patch);

方法二的平均消耗時間為:83.93ms

方法三

該方法需要新建一個類用來實現Parcelable介面


package com.example.bitmaptest;
import java.util.ArrayList;
import android.os.Parcel;
import android.os.Parcelable;
public class MyList implements Parcelable{
public ArrayList array;
public MyList(Parcel in)
{
in.readValue(null);
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeValue(array);
}
public static final Parcelable.Creator<MyList> CREATOR = new Parcelable.Creator<MyList>() {
@Override
public MyList createFromParcel(Parcel source) {
return new MyList(source);
}
@Override
public MyList[] newArray(int size) {
return new MyList[size];
}
};
}

在子執行緒中的操作


//Parcelable傳遞
MyList l=new MyList(Parcel.obtain());
l.array=array;
bundle.putParcelable("img", l);

方法三的平均消耗時間為:87.35ms

結果分析

三種方法都是在魅族MX1型號的手機上測試的,理論上方法三應該比方法二快,但至少根據我的實驗結果來看,在傳送小資料量時(影象大概是幾mB或幾百kB),資料的傳遞耗時並不是關鍵,兩種方法的耗時差不多。方法一由於沒有使用執行緒間的資料傳遞,因此耗時是最少的。

因此,我總結得到如下結論:

1、如果必須等到影象載入完成才允許使用者操作的這種場景,可以直接在UI執行緒做影象的操作,這時可以新增一個ProgressDialog用來提示正在載入。

2、如果需要一邊允許使用者操作一邊載入影象的話,應該新開一個子執行緒,但是在資料量不大的情況下,Serializable和Parcelable差距不大。

3、總而言之,影象的尺寸和數量不大時,在UI執行緒直接做影象讀取等操作即可,但比較大時還是最好開個子執行緒。

以上這篇詳談Android從檔案讀取影象顯示的效率問題就是小編分享給大家的全部內容了,希望能給大家一個參考,也希望大家多多支援指令碼之家。

您可能感興趣的文章:

android程式設計之xml檔案讀取和寫入方法Android中讀取中文字元的檔案與檔案讀取相關介紹Android local.properties 檔案讀取例項詳解