欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

详谈Android从文件读取图像显示的效率问题

程序员文章站 2023-11-29 13:02:52
因为从文件读取图像到bitmap是一件比较费时的事情,所以研究了一下几种可行的办法,并做了对比。 首先解释一下为什么耗时,这是因为,在从jpg或者png文件中读取bitm...

因为从文件读取图像到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从文件读取图像显示的效率问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。