Android從文件讀取圖像顯示的效率問題


因為從文件讀取圖像到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線程直接做圖像讀取等操作即可,但比較大時還是最好開個子線程。

 

最后是整個工程的文件


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM