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

来源:互联网 发布:众金在线网络借贷 编辑:程序博客网 时间:2024/05/16 09:51

在前面几篇文章中,我们或多或少了解到了ImageManager类的存在,它负责从Panoramio服务器下载搜索区域内的图片数据,同时进行解析。当然,这一切是在独立的后台线程中进行的,下载的情况通过观察者模式通告给ImageList进行显示(ImageManager是被观察对象Subject)。注意,ImageManager是一个单例类。

本文涉及到的知识点有两个:JSON和WeakReference。

1)JSON(www.json.org)是目前流行的网络数据交换格式,它是JavaScript Object Notation的缩写。JSON数据是一系列键值对的集合,相信曾做过web开发的对这个不会陌生。Android自带的JSON API位于libcore\json\src\main\java\org\json目录中,包括JSON.java、JSONArray.java等6个Java源文件,在代码开头处有个声明可以看下:

Note: this class was written without inspecting the non-free org.json sourcecode.

可以看出,这是是Google自己实现的一个JSON解析类。

2)WeakReference是Java四种引用类型之一的弱引用,其他三种分别是强引用StrongReference,软引用SoftReference和虚引用PhantomReference(又叫幽灵引用)。这四种引用和垃圾收集器GC的交互各不相同,下面就来简单分析下:

StrongReference是Java的默认引用,它会尽可能长地存活在Java虚拟机中,当没有任何对象指向它时,GC执行后才会被回收;

SoftReference和WeakReference类似,最大的区别在于软引用会尽可能长地保留它自己,直到出现Java虚拟机内存不足时才会被回收;因此,软引用常用于对内存敏感的程序中;

WeakReference当它所引用的对象在Java虚拟机中不再存在强引用时,GC执行后弱引用才会被回收;

PhantomReference完全类似于没有引用,虚引用对对象本身没有太大影响,对象甚至感觉不到虚引用的存在。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,它必须和引用队列ReferenceQueue结合使用。

除了强引用外,其他几个引用对应的类都定义在Java.lang.ref包中,而且都继承自Reference类,下面看下它们的源代码吧:

package java.lang.ref;/** * Provides an abstract class which describes behavior common to all reference * objects. It is not possible to create immediate subclasses of * {@code Reference} in addition to the ones provided by this package. It is * also not desirable to do so, since references require very close cooperation * with the system's garbage collector. The existing, specialized reference * classes should be used instead. */public abstract class Reference<T> {    /**     * The object to which this reference refers.     * VM requirement: this field <em>must</em> be called "referent"     * and be an object.     */    volatile T referent;    /**     * If non-null, the queue on which this reference will be enqueued     * when the referent is appropriately reachable.     * VM requirement: this field <em>must</em> be called "queue"     * and be a java.lang.ref.ReferenceQueue.     */    @SuppressWarnings("unchecked")    volatile ReferenceQueue queue;    /**     * Used internally by java.lang.ref.ReferenceQueue.     * VM requirement: this field <em>must</em> be called "queueNext"     * and be a java.lang.ref.Reference.     */    @SuppressWarnings("unchecked")    volatile Reference queueNext;    /**     * Used internally by the VM.  This field forms a singly-linked     * list of reference objects awaiting processing by the garbage     * collector.     */    @SuppressWarnings("unchecked")    volatile Reference pendingNext;    /**     * Constructs a new instance of this class.     */    Reference() {        super();    }    /**     * Makes the referent {@code null}. This does not force the reference     * object to be enqueued.     */    public void clear() {        referent = null;    }    /**     * An implementation of .enqueue() that is safe for the VM to call.     * If a Reference object is a subclass of any of the     * java.lang.ref.*Reference classes and that subclass overrides enqueue(),     * the VM may not call the overridden method.     * VM requirement: this method <em>must</em> be called "enqueueInternal",     * have the signature "()Z", and be private.     *     * @return {@code true} if this call has caused the {@code Reference} to     * become enqueued, or {@code false} otherwise     */    @SuppressWarnings("unchecked")    private synchronized boolean enqueueInternal() {        /* VM requirement:         * The VM assumes that this function only does work         * if "(queue != null && queueNext == null)".         * If that changes, Dalvik needs to change, too.         * (see MarkSweep.c:enqueueReference())         */        if (queue != null && queueNext == null) {            queue.enqueue(this);            queue = null;            return true;        }        return false;    }    /**     * Forces the reference object to be enqueued if it has been associated with     * a queue.     *     * @return {@code true} if this call has caused the {@code Reference} to     * become enqueued, or {@code false} otherwise     */    public boolean enqueue() {        return enqueueInternal();    }    /**     * Returns the referent of the reference object.     *     * @return the referent to which reference refers, or {@code null} if the     *         object has been cleared.     */    public T get() {        return referent;    }    /**     * Checks whether the reference object has been enqueued.     *     * @return {@code true} if the {@code Reference} has been enqueued, {@code     *         false} otherwise     */    public boolean isEnqueued() {        return queueNext != null;    }}

SoftReference.java文件如下所示:

public class SoftReference<T> extends Reference<T> {    /**     * Constructs a new soft reference to the given referent. The newly created     * reference is not registered with any reference queue.     *     * @param r the referent to track     */    public SoftReference(T r) {        super();        referent = r;    }    /**     * Constructs a new soft reference to the given referent. The newly created     * reference is registered with the given reference queue.     *     * @param r the referent to track     * @param q the queue to register to the reference object with. A null value     *          results in a weak reference that is not associated with any     *          queue.     */    public SoftReference(T r, ReferenceQueue<? super T> q) {        super();        referent = r;        queue = q;    }}
接下来是WeakReference.java,它的实现几乎和SoftReference一样,只是类名不同而已:

public class WeakReference<T> extends Reference<T> {    /**     * Constructs a new weak reference to the given referent. The newly created     * reference is not registered with any reference queue.     *     * @param r the referent to track     */    public WeakReference(T r) {        super();        referent = r;    }    /**     * Constructs a new weak reference to the given referent. The newly created     * reference is registered with the given reference queue.     *     * @param r the referent to track     * @param q the queue to register to the reference object with. A null value     *          results in a weak reference that is not associated with any     *          queue.     */    public WeakReference(T r, ReferenceQueue<? super T> q) {        super();        referent = r;        queue = q;    }}
而最后的PhantomReference.java的实现稍微有点区别,它的get函数返回null:
public class PhantomReference<T> extends Reference<T> {    /**     * Constructs a new phantom reference and registers it with the given     * reference queue. The reference queue may be {@code null}, but this case     * does not make any sense, since the reference will never be enqueued, and     * the {@link #get()} method always returns {@code null}.     *     * @param r the referent to track     * @param q the queue to register the phantom reference object with     */    public PhantomReference(T r, ReferenceQueue<? super T> q) {        super();        referent = r;        queue = q;    }    /**     * Returns {@code null}.  The referent of a phantom reference is not     * accessible.     *     * @return {@code null} (always)     */    @Override    public T get() {        return null;    }}
经过上面知识点的分析,ImageManager类也就没什么其他好讲的了,直接看代码以及注释应该就很清楚了:

package com.google.android.panoramio;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.json.JSONArray;import org.json.JSONException;import org.json.JSONObject;import android.content.Context;import android.database.DataSetObserver;import android.graphics.Bitmap;import android.os.Handler;import android.util.Log;import java.io.BufferedReader;import java.io.IOException;import java.io.InputStream;import java.io.InputStreamReader;import java.lang.ref.WeakReference;import java.net.URI;import java.util.ArrayList;public class ImageManager {   private static final String TAG = "Panoramio";        /**     * Panoramio服务API的基本URL,更多内容可见http://www.programmableweb.com/api/panoramio     */    private static final String THUMBNAIL_URL = "//www.panoramio.com/map/get_panoramas.php?order=popularity&set=public&from=0&to=20&miny=%f&minx=%f&maxy=%f&maxx=%f&size=thumbnail";    /**     * 用于后台线程将结果反馈给UI线程     */    private Handler mHandler = new Handler();    /**     * 存储ImageManager的唯一实例     */    private static ImageManager sInstance;        /**     * 存储从网络上下载的图片和相关信息     */    private ArrayList<PanoramioItem> mImages = new ArrayList<PanoramioItem>();        /**     * 存储对当前搜索结果感兴趣的观察者实例     */    private ArrayList<WeakReference<DataSetObserver>> mObservers =             new ArrayList<WeakReference<DataSetObserver>>();        /**     * 当处于下载阶段时为true,下载结束后置为false     */    private boolean mLoading;        private Context mContext;        /**     * Key for an Intent extra. The value is the zoom level selected by the user.     */    public static final String ZOOM_EXTRA = "zoom";    /**     * Key for an Intent extra. The value is the latitude of the center of the search     * area chosen by the user.     */    public static final String LATITUDE_E6_EXTRA = "latitudeE6";    /**     * Key for an Intent extra. The value is the latitude of the center of the search     * area chosen by the user.     */    public static final String LONGITUDE_E6_EXTRA = "longitudeE6";        /**     * Key for an Intent extra. The value is an item to display     */    public static final String PANORAMIO_ITEM_EXTRA = "item";        /**     * 延迟初始化,单例模式      */    public static ImageManager getInstance(Context c) {        if (sInstance == null) {            sInstance = new ImageManager(c.getApplicationContext());        }        return sInstance;    }        /**     * 注意,构造函数必须是private,保证只能通过getInstance获取该类的唯一实例      */    private ImageManager(Context c) {        mContext = c;    }        /**     * 如果目前还在下载图片信息则返回true     */    public boolean isLoading() {        return mLoading;    }        /**     * 清除所有下载内容,并通知观察者     */    public void clear() {        mImages.clear();           notifyObservers();    }        /**     * 内存中添加一个PanoramioItem实例,并通知观察者这个变化     */    private void add(PanoramioItem item) {        mImages.add(item);           notifyObservers();    }        /**     * 目前显示的PanoramioItem数目     */    public int size() {        return mImages.size();    }    /**     * 获取内存中指定索引处的PanoramioItem实例     */    public PanoramioItem get(int position) {        return mImages.get(position);    }        /**     * 绑定一个观察者(当ImageManager保存的PanoramioItem集合发生变化时通告它们)     */    public void addObserver(DataSetObserver observer) {        WeakReference<DataSetObserver> obs = new WeakReference<DataSetObserver>(observer);        mObservers.add(obs);    }        /**     * 根据新的搜索区域信息,开启独立的线程从服务器下载该区域内的图片信息     *      * @param minLong 搜索区域的最小经度     * @param maxLong 搜索区域的最大经度     * @param minLat  搜索区域的最小纬度     * @param maxLat  搜索区域的最大纬度     */    public void load(float minLong, float maxLong, float minLat, float maxLat) {        mLoading = true;        new NetworkThread(minLong, maxLong, minLat, maxLat).start();    }        /**     * 当PanoramioItem数据集发生变化时,调用这个函数通告观察者     * 同时,清除无效的观察者对象弱引用     */    private void notifyObservers() {        final ArrayList<WeakReference<DataSetObserver>> observers = mObservers;        final int count = observers.size();        for (int i = count - 1; i >= 0; i--) {            WeakReference<DataSetObserver> weak = observers.get(i);            DataSetObserver obs = weak.get();            if (obs != null) {                obs.onChanged();            } else {                observers.remove(i);            }        }            }        /**     * 这个线程实现图片数据的下载和解析     *     */    private class NetworkThread extends Thread {    //搜索区域的最大最小经纬度        private float mMinLong;        private float mMaxLong;        private float mMinLat;        private float mMaxLat;        public NetworkThread(float minLong, float maxLong, float minLat, float maxLat) {            mMinLong = minLong;            mMaxLong = maxLong;            mMinLat = minLat;            mMaxLat = maxLat;        }        @Override        public void run() {                        String url = THUMBNAIL_URL;            url = String.format(url, mMinLat, mMinLong, mMaxLat, mMaxLong);            try {                URI uri = new URI("http", url, null);                HttpGet get = new HttpGet(uri);                                HttpClient client = new DefaultHttpClient();                HttpResponse response = client.execute(get);                HttpEntity entity = response.getEntity();                String str = convertStreamToString(entity.getContent());                JSONObject json = new JSONObject(str);                parse(json);            } catch (Exception e) {                Log.e(TAG, e.toString());            }        }                /**         * 将JSON对象解析出来          */        private void parse(JSONObject json) {            try {                JSONArray array = json.getJSONArray("photos"); //JSON数组                int count = array.length();                for (int i = 0; i < count; i++) {                    JSONObject obj = array.getJSONObject(i); //JSON数组中的对象                    long id = obj.getLong("photo_id");                    String title = obj.getString("photo_title");                    String owner = obj.getString("owner_name");                    String thumb = obj.getString("photo_file_url");                    String ownerUrl = obj.getString("owner_url");                    String photoUrl = obj.getString("photo_url");                    double latitude = obj.getDouble("latitude");                    double longitude = obj.getDouble("longitude");                    Bitmap b = BitmapUtils.loadBitmap(thumb); //加载图片缩略图                    if (title == null) {                        title = mContext.getString(R.string.untitled);                    }                    //根据解析出来的数据封装PanoramioItem对象                    final PanoramioItem item = new PanoramioItem(id, thumb, b,                            (int) (latitude * Panoramio.MILLION),                            (int) (longitude * Panoramio.MILLION), title, owner,                            ownerUrl, photoUrl);                                        final boolean done = i == count - 1; //是否全部完成                                        //每解析完一项就通知UI线程                    mHandler.post(new Runnable() {                        public void run() {                            sInstance.mLoading = !done;                            sInstance.add(item);                        }                    });                }            } catch (JSONException e) {                Log.e(TAG, e.toString());            }        }        /**         * 将输入流转换成字符串形式,因此使用了字符流API(Reader)         */        private String convertStreamToString(InputStream is) {            BufferedReader reader = new BufferedReader(new InputStreamReader(is), 8*1024);            StringBuilder sb = new StringBuilder();                 String line = null;            try {                while ((line = reader.readLine()) != null) {                    sb.append(line + "\n");                }            } catch (IOException e) {                e.printStackTrace();            } finally {                try {                    is.close();                } catch (IOException e) {                    e.printStackTrace();                }            }                 return sb.toString();        }    }    }





原创粉丝点击