本文分析两个类:程序中用到的数据类PanoramioItem,以及工具类BitmapUtils。
1)Parcelable接口和PanoramioItem类
任何类如果希望自己的实例能够写入到Parcel中或者从Parcel中恢复出来,都必须实现Parcelable接口,实现这个接口的类除了要重写接口中定义的函数,还需要定义一个名为CREATOR的静态域,而CREATOR是实现Parcelable.Creator接口的对象,说了这么多,下面看下Parcelable的代码就一目了然了:
public interface Parcelable { /** * Flag for use with {@link #writeToParcel}: the object being written * is a return value, that is the result of a function such as * "<code>Parcelable someFunction()</code>", * "<code>void someFunction(out Parcelable)</code>", or * "<code>void someFunction(inout Parcelable)</code>". Some implementations * may want to release resources at this point. */ public static final int PARCELABLE_WRITE_RETURN_VALUE = 0x0001; /** * Bit masks for use with {@link #describeContents}: each bit represents a * kind of object considered to have potential special significance when * marshalled. */ public static final int CONTENTS_FILE_DESCRIPTOR = 0x0001; /** * Describe the kinds of special objects contained in this Parcelable's * marshalled representation. * * @return a bitmask indicating the set of special object types marshalled * by the Parcelable. */ public int describeContents(); /** * Flatten this object in to a Parcel. * * @param dest The Parcel in which the object should be written. * @param flags Additional flags about how the object should be written. * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}. */ public void writeToParcel(Parcel dest, int flags); /** * Interface that must be implemented and provided as a public CREATOR * field that generates instances of your Parcelable class from a Parcel. */ public interface Creator<T> { /** * Create a new instance of the Parcelable class, instantiating it * from the given Parcel whose data had previously been written by * {@link Parcelable#writeToParcel Parcelable.writeToParcel()}. * * @param source The Parcel to read the object's data from. * @return Returns a new instance of the Parcelable class. */ public T createFromParcel(Parcel source); /** * Create a new array of the Parcelable class. * * @param size Size of the array. * @return Returns an array of the Parcelable class, with every entry * initialized to null. */ public T[] newArray(int size); } }
接口中的注释已经明确说明各项的作用,下面就是实现了该接口的PanoramioItem类(位于PanoramioItem.java文件中):
package com.google.android.panoramio; import com.google.android.maps.GeoPoint; import android.graphics.Bitmap; import android.os.Parcel; import android.os.Parcelable; /** * 这个类用于存储从Panoramio服务器返回的数据,包括一个位图和其他相关元数据 * */ public class PanoramioItem implements Parcelable { private long mId; //id标识 private Bitmap mBitmap; //位图数据 private GeoPoint mLocation; //经纬度 private String mTitle; //照片的标题 private String mOwner; //照片的作者 private String mThumbUrl; //照片缩略图的Url private String mOwnerUrl; //作者信息的Url private String mPhotoUrl; //照片的Url public PanoramioItem(Parcel in) { mId = in.readLong(); mBitmap = Bitmap.CREATOR.createFromParcel(in); //位图读取的特殊性 mLocation = new GeoPoint(in.readInt(), in.readInt()); mTitle = in.readString(); mOwner = in.readString(); mThumbUrl = in.readString(); mOwnerUrl = in.readString(); mPhotoUrl = in.readString(); } public PanoramioItem(long id, String thumbUrl, Bitmap b, int latitudeE6, int longitudeE6, String title, String owner, String ownerUrl, String photoUrl) { mBitmap = b; mLocation = new GeoPoint(latitudeE6, longitudeE6); mTitle = title; mOwner = owner; mThumbUrl = thumbUrl; mOwnerUrl = ownerUrl; mPhotoUrl = photoUrl; } public long getId() { return mId; } public Bitmap getBitmap() { return mBitmap; } public GeoPoint getLocation() { return mLocation; } public String getTitle() { return mTitle; } public String getOwner() { return mOwner; } public String getThumbUrl() { return mThumbUrl; } public String getOwnerUrl() { return mOwnerUrl; } public String getPhotoUrl() { return mPhotoUrl; } //实现Parcelable接口必须实现的静态变量CREATOR public static final Parcelable.Creator<PanoramioItem> CREATOR = new Parcelable.Creator<PanoramioItem>() { //从Parcel读取数据的顺序和writeToParcel函数写入Parcel的顺序保持一致 //此处读取数据在PanoramioItem构造函数中进行 public PanoramioItem createFromParcel(Parcel in) { return new PanoramioItem(in); } public PanoramioItem[] newArray(int size) { return new PanoramioItem[size]; } }; //实现Parcelable接口中的函数 public int describeContents() { return 0; } //实现Parcelable接口中的函数 public void writeToParcel(Parcel parcel, int flags) { parcel.writeLong(mId); mBitmap.writeToParcel(parcel, 0); parcel.writeInt(mLocation.getLatitudeE6()); parcel.writeInt(mLocation.getLongitudeE6()); parcel.writeString(mTitle); parcel.writeString(mOwner); parcel.writeString(mThumbUrl); parcel.writeString(mOwnerUrl); parcel.writeString(mPhotoUrl); } }
2)BitmapFactory和工具类BitmapUtils
BitmapFactory类中提供的生成位图的函数不少,分别从不同数据源获取数据并解码成位图,这些函数的代码如下所示,权当没事多看看:
/** * 从指定字节数组中解码出位图 */ public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) { if ((offset | length) < 0 || data.length < offset + length) { throw new ArrayIndexOutOfBoundsException(); } return nativeDecodeByteArray(data, offset, length, opts); } /** * 从指定字节数组中解码出位图 */ public static Bitmap decodeByteArray(byte[] data, int offset, int length) { return decodeByteArray(data, offset, length, null); } /** * 从指定文件路径中解码出位图,如果文件名不存在,则函数返回null */ public static Bitmap decodeFile(String pathName, Options opts) { Bitmap bm = null; InputStream stream = null; try { stream = new FileInputStream(pathName); bm = decodeStream(stream, null, opts); } catch (Exception e) { /* do nothing. If the exception happened on open, bm will be null. */ } finally { if (stream != null) { try { stream.close(); } catch (IOException e) { // do nothing here } } } return bm; } /** * 从指定文件路径中解码出位图,如果文件名不存在,则函数返回null */ public static Bitmap decodeFile(String pathName) { return decodeFile(pathName, null); } /** * 从指定的文件描述符中解码出位图,失败时函数返回null * 当这个函数返回时,文件描述符中的指针位置不会受到影响,因此可以照常使用 */ public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) { try { if (MemoryFile.isMemoryFile(fd)) { int mappedlength = MemoryFile.getSize(fd); MemoryFile file = new MemoryFile(fd, mappedlength, "r"); InputStream is = file.getInputStream(); Bitmap bm = decodeStream(is, outPadding, opts); return finishDecode(bm, outPadding, opts); } } catch (IOException ex) { // invalid filedescriptor, no need to call nativeDecodeFileDescriptor() return null; } Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts); return finishDecode(bm, outPadding, opts); } /** * 从指定的文件描述符中解码出位图,失败时函数返回null * 当这个函数返回时,文件描述符中的指针位置不会受到影响,因此可以照常使用 */ public static Bitmap decodeFileDescriptor(FileDescriptor fd) { return decodeFileDescriptor(fd, null, null); } /** * 从指定Resources中解码出位图 */ public static Bitmap decodeResource(Resources res, int id, Options opts) { Bitmap bm = null; InputStream is = null; try { final TypedValue value = new TypedValue(); is = res.openRawResource(id, value); bm = decodeResourceStream(res, value, is, null, opts); } catch (Exception e) { /* do nothing. If the exception happened on open, bm will be null. If it happened on close, bm is still valid. */ } finally { try { if (is != null) is.close(); } catch (IOException e) { // Ignore } } return bm; } /** * 从指定Resources中解码出位图 */ public static Bitmap decodeResource(Resources res, int id) { return decodeResource(res, id, null); } /** * 从InputStream中解码出位图,这个InputStream是从Resources中获取的 * 仍然将Resources作为参数传入是为了根据Resources信息缩放位图 */ public static Bitmap decodeResourceStream(Resources res, TypedValue value, InputStream is, Rect pad, Options opts) { if (opts == null) { opts = new Options(); } if (opts.inDensity == 0 && value != null) { final int density = value.density; if (density == TypedValue.DENSITY_DEFAULT) { opts.inDensity = DisplayMetrics.DENSITY_DEFAULT; } else if (density != TypedValue.DENSITY_NONE) { opts.inDensity = density; } } if (opts.inTargetDensity == 0 && res != null) { opts.inTargetDensity = res.getDisplayMetrics().densityDpi; } return decodeStream(is, pad, opts); } /** * 将InputStream解码成位图,如果InputStream是null或者不能用来生成一个位图 * 则函数返回null,InputStream中流的位置不会受到这个函数的任何影响 */ public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) { // we don't throw in this case, thus allowing the caller to only check // the cache, and not force the image to be decoded. if (is == null) { return null; } // we need mark/reset to work properly if (!is.markSupported()) { is = new BufferedInputStream(is, 16 * 1024); } // so we can call reset() if a given codec gives up after reading up to // this many bytes. FIXME: need to find out from the codecs what this // value should be. is.mark(1024); Bitmap bm; if (is instanceof AssetManager.AssetInputStream) { bm = nativeDecodeAsset(((AssetManager.AssetInputStream) is).getAssetInt(), outPadding, opts); } else { // pass some temp storage down to the native code. 1024 is made up, // but should be large enough to avoid too many small calls back // into is.read(...) This number is not related to the value passed // to mark(...) above. byte [] tempStorage = null; if (opts != null) tempStorage = opts.inTempStorage; if (tempStorage == null) tempStorage = new byte[16 * 1024]; bm = nativeDecodeStream(is, tempStorage, outPadding, opts); } return finishDecode(bm, outPadding, opts); } /** * 将InputStream解码成位图,如果InputStream是null或者不能用来生成一个位图 * 则函数返回null,InputStream中流的位置不会受到这个函数的任何影响 */ public static Bitmap decodeStream(InputStream is) { return decodeStream(is, null, null); }我们的位图工具类BitmapUtils其实只用到了BitmapFactory.decodeByteArray(...)函数,如下所示,代码比较简单,主要涉及Java IO操作和BitmapFactory的使用(位于文件BitmapUtils.java中)
package com.google.android.panoramio; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.util.Log; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.Closeable; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; /** * 从指定URL加载位图的工具类 * */ public class BitmapUtils { private static final String TAG = "Panoramio"; private static final int IO_BUFFER_SIZE = 4 * 1024; /** * 从指定的url加载位图,这将会持续一段时间,因此不应该从UI线程中调用 */ public static Bitmap loadBitmap(String url) { Bitmap bitmap = null; InputStream in = null; BufferedOutputStream out = null; try { in = new BufferedInputStream(new URL(url).openStream(), IO_BUFFER_SIZE); final ByteArrayOutputStream dataStream = new ByteArrayOutputStream(); out = new BufferedOutputStream(dataStream, IO_BUFFER_SIZE); copy(in, out); out.flush(); final byte[] data = dataStream.toByteArray(); //调用BitmapFactory类的函数从字节数组中解码出位图 bitmap = BitmapFactory.decodeByteArray(data, 0, data.length); } catch (IOException e) { Log.e(TAG, "Could not load Bitmap from: " + url); } finally { closeStream(in); closeStream(out); } return bitmap; } /** * 关闭指定的数据流 */ private static void closeStream(Closeable stream) { if (stream != null) { try { stream.close(); } catch (IOException e) { android.util.Log.e(TAG, "Could not close stream", e); } } } /** * 使用临时的字节数组缓存将InputStream中的数据拷贝到OutputStream */ private static void copy(InputStream in, OutputStream out) throws IOException { byte[] b = new byte[IO_BUFFER_SIZE]; int read; while ((read = in.read(b)) != -1) { out.write(b, 0, read); } } }==============================碎裂吧 镜花水月===============================