• 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线程直接做图像读取等操作即可,但比较大时还是最好开个子线程。

    最后是整个工程的文件

  • 相关阅读:
    Docker-compose编排微服务顺序启动解决方案
    在笔记本上使用virtualbox搭建lvs dr 实验遇到的问题
    MongoDB安装配置(RedHat/CentOS)
    wget 用法
    Ubuntu查看crontab运行日志
    CentOS-6.5安装Zabbix 3.0.4
    centos 6 安装vsftpd与PAM虚拟用户
    python socket常用接口说明
    cmake 构建工程
    std::vector的下标访问和迭代器访问的效率
  • 原文地址:https://www.cnblogs.com/hrlnw/p/3586523.html
Copyright © 2020-2023  润新知