Android开源代码解读の地图照片应用Panoramio的实现详解(二)

本文分析两个类:程序中用到的数据类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);
        }
    }

}
==============================碎裂吧 镜花水月===============================

你可能感兴趣的:(android,exception,String,null,resources,照片)