Hi,众猿们!昨天谈了下json的三级缓存和图片的内存缓存的代码实现,今天就聊聊如何从网络获取图片以及图片的本地缓存吧!主要有以下几个问题:
           1.Android中网络访问的基本知识。
           2.利用URLConnection对象访问网络图片。
           3.图片的本地缓存。
    废话不多说,先看第一个问题,安卓应用中访问网络资源,可以使用URLConnection及其子类HttpURLConnection对象,也可以用于Android系统提供的HttpClient、HttpGet、HttpPost对象。我们需要对Http协议有一定的了解,要知道Get请求方式和Post请求方式的基本知识。同时,像请求网络资源这种费时的操作,我们应该让其远离UI主线程(如果众猿对线程不熟悉的话,那你的Java基础需要加强呀),以避免出现用户操作卡顿的现象,因此我们会用异步任务对象AsyncTask来完成网络请求操作,最后,我们还要熟悉Java中的回调机制,因为我们还要考虑请求资源超时、失败等情况。下面是一个完成网络通信功能的基类,在实际开发中我们可以继承该类以完成特定的网络请求:
public class BaseNetConnection {
    public static final int METHOD_GET = 1;
    public static final int METHOD_POST = 2;
    public static final String CHARSET = "utf-8";
    public static final int ERROR_NET_UNAVAILABLE = 1;
    public static final int ERROR_NORESULT = 2;

    /**
     * 用于执行请求网络资源的方法,需在清单中添加网络权限
     * @param url 网络资源的接口url
     * @param method 请求方式
     * @param successCallback 请求成功的回调接口
     * @param failCallback 请求失败的回调接口
     * @param kvs 请求参数
     */
    public void execute(
            final String url,
            final int method,
            final SuccessCallback successCallback,
            final FailCallback failCallback,
            final String ... kvs) { 
        //判断网络是否已经连接,否则则调用失败接口,并返回函数
        if(!isNetAvailable()){
            if (failCallback != null) {
                failCallback.onFail(ERROR_NET_UNAVAILABLE);
            }
            return;
        }
        //使用异步任务对象请求网络资源
        new AsyncTask<Void, Void, String>() {
            /**
             * 执行异步任务的方式,运行在子线程中
             * @param params 
             * @return 
             */
            @Override
            protected String doInBackground(Void... params) {

                StringBuffer paramsStr = new StringBuffer();
                for (int i = 0; i < kvs.length; i+=2) {
                    paramsStr.append(kvs[i]).append("=").append(kvs[i+1]).append("&");
                }               
                try {
                    URLConnection uc;

                    switch (method) {
                    case METHOD_POST:
                        uc = new URL(url).openConnection();
                        uc.setDoOutput(true);
                        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(uc.getOutputStream(), CHARSET));
                        bw.write(paramsStr.toString());
                        bw.flush();
                        break;
                    default:
                        uc = new URL(url+"?"+paramsStr.toString()).openConnection();
                        break;
                    }
                    BufferedReader br = new BufferedReader(new InputStreamReader(uc.getInputStream(), CHARSET));
                    String line = null;
                    StringBuffer result = new StringBuffer();
                    while((line=br.readLine())!=null){
                        result.append(line);
                    }
                    return result.toString();
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
            /**
             * 任务执行完毕后处理执行结果的方法,运行在主线程中
             * @param result
             */
            @Override
            protected void onPostExecute(String result) {
                if (result!=null) {
                    if (successCallback!=null) {
                        successCallback.onSuccess(result);
                    }
                }else{
                    if (failCallback!=null) {
                        failCallback.onFail(ERROR_NORESULT);
                    }
                }
                super.onPostExecute(result);
            }
        }.execute();
    }

    /**
     * 判断网络是否连接的方法,需在清单文件中添加相关权限
     * @return 若网络已连接则返回true
     */
    private boolean isNetAvailable() {
        ConnectivityManager manager = (ConnectivityManager) MyApplication.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = manager.getActiveNetworkInfo();
        if (info != null) {
            return info.isAvailable();
        }
        return false;
    }


    /**
     * 请求资源成功的回调接口
     * @author 李明忠
     *
     */
    public static interface SuccessCallback{
        void onSuccess(String result);
    }
    /**
     * 请求资源失败的回调接口
     * @author 李明忠
     *
     */
    public static interface FailCallback{
        void onFail(int errorCode);
    }
}


public class BitmapCacheUtil {
    public static final int ERROR_NET_UNAVAILABLE = 1;
    public static final int ERROR_NORESULT = 2;
    public static final String LOCAL_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "BitmapCache";

    private static LruCache<String, Bitmap> mBitmapMemoryCache = new LruCache<String, Bitmap>((int) (Runtime.getRuntime().maxMemory() / 8)){
        @Override
        protected int sizeOf(String key, Bitmap value) {
            return value.getHeight() * value.getRowBytes();
        }
    };

    /**
     * 加载url对应的图片并将其显示到ImageView控件上,若图片加载成功则将其显示到ImageView上
     * 接口处理失败情况
     * @param imageView 用来显示图片的控件
     * @param url 图片的网络url
     * @param failCallback 加载图片失败的回调接口
     */
    public static void loadBitmap(
            ImageView imageView, 
            String url,
            FailCallback failCallback){
        //先从内存缓存中加载图片
        Bitmap bitmap = loadBitmapFromMemory(url);

        if (bitmap == null) {
            //若内存中不存在该图片则从本地缓存中加载
            bitmap = loadBitmapFromLocal(url);
            if(bitmap == null){
                //若本地缓存中无图片则先判断网络是否连接
                if(isNetAvailable()){
                    //有网络则从网络缓存中加载图片
                    loadBitmapFromNet(imageView, url, failCallback);
                }else {
                    //无网络则执行请求失败回调接口
                    if(failCallback != null){
                        failCallback.onFail(ERROR_NET_UNAVAILABLE);
                    }
                }
            }else {
                //若本地缓存中有该图片则使用该图片
                imageView.setImageBitmap(bitmap);
            }
        }else {
            imageView.setImageBitmap(bitmap);
        }
    }

    /**
     * 判断网络是否建立的方法
     * @return 若网络已连接则返回true
     */
    private static boolean isNetAvailable() {
        ConnectivityManager manager = (ConnectivityManager) MyApplication.getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo info = manager.getActiveNetworkInfo();
        if (info != null) {
            return info.isAvailable();
        }
        return false;
    }

    /**
     * 将图片放置进内存缓存
     * @param url
     * @param bitmap
     */
    private static void putBitmapToMemory(String url, Bitmap bitmap) {
        mBitmapMemoryCache.put(url, bitmap);
    }
    /**
     * 将图片放置进本地缓存
     * @param url
     * @param bitmap
     */
    private static void putBitmapToLocal(String url, Bitmap bitmap) {
        try{
            File file = new File(LOCAL_PATH, url);
            File parent = file.getParentFile();
            if (!parent.exists()) {
                parent.mkdirs();
            }
            FileOutputStream stream = new FileOutputStream(file);
            bitmap.compress(CompressFormat.JPEG, 100, stream);
        } catch (Exception e) {}
    }

    /**
     * 从网络上加载url对应的图片并显示在ImageView上
     * @param imageView 用于显示图片的控件
     * @param url 
     * @param failCallback 请求失败时的回调接口
     */
    private static void loadBitmapFromNet(
            final ImageView imageView, 
            final String url, 
            final FailCallback failCallback) {
        new AsyncTask<Void, Void, Bitmap>(){
            @Override
            protected Bitmap doInBackground(Void... params) {
                HttpURLConnection con = null;
                try {
                    con = (HttpURLConnection) new URL(url).openConnection();
                    con.setConnectTimeout(5000);// 连接超时
                    con.setReadTimeout(5000);
                    con.setRequestMethod("GET");
                    con.connect();
                    int responseCode = con.getResponseCode();

                    if (responseCode == 200) {
                        InputStream inputStream = con.getInputStream();

                        Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
                        // 将图片保存在本地缓存
                        putBitmapToLocal(url, bitmap);
                        // 将图片保存在内存
                        putBitmapToMemory(url, bitmap);
                        return bitmap;
                    }
                } catch (Exception e) {} finally {
                    if (con != null) {
                        con.disconnect();
                    }
                }
                return null;
            }
            @Override
            protected void onPostExecute(Bitmap result) {
                if (result == null) {
                    failCallback.onFail(ERROR_NORESULT);
                }else {
                    imageView.setImageBitmap(result);
                }
            };
        }.execute();
    }
    /**
     * 从本地缓存中加载图片
     * @param url
     * @return
     */
    private static Bitmap loadBitmapFromLocal(String url) {
        File file = new File(LOCAL_PATH, url);
        if (file.exists()) {
            Bitmap bitmap = null;
            try {
                bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
                putBitmapToMemory(url, bitmap);
            } catch (FileNotFoundException e) {}
            return bitmap;
        }
        return null;
    }
    /**
     * 从内存中加载url对应的图片
     * @param url 该图片对应的url
     * @return 若内存中不存在该图片则直接返回null
     */
    private static Bitmap loadBitmapFromMemory(String url) {
        return mBitmapMemoryCache.get(url);
    }

    public interface FailCallback{public void onFail(int errorCode);}
}