Volleyを無理矢理file schemeに対応してみる


NetworkImageView#setImageUrl(String, ImageLoader)はfileスキームに未対応なので扱えるように改造してみました

BasicNetwork#performRequest()メソッドをOverrideしてfile schemeの場合はFileInputStream経由でデータを取得するようにしたらあっさりできました。

```java:CustomNetwork.java
package com.example.volleysample;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;

import org.apache.http.HttpStatus;

import android.net.Uri;
import android.util.Log;

import com.android.volley.NetworkError;
import com.android.volley.NetworkResponse;
import com.android.volley.Request;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.BasicNetwork;
import com.android.volley.toolbox.ByteArrayPool;
import com.android.volley.toolbox.HttpStack;
import com.example.volleysample.BuildConfig;

public class CustomNetwork extends BasicNetwork {
	private static final String TAG = CustomNetwork.class.getSimpleName();

	public CustomNetwork(HttpStack stack) {
		super(stack);
	}

	public CustomNetwork(HttpStack stack, ByteArrayPool pool) {
		super(stack, pool);
	}

	@Override
	public NetworkResponse performRequest(Request<?> request)
			throws VolleyError {
		// file schemeの場合は自前で処理
		Uri uri = Uri.parse(request.getUrl());
		if (uri != null && "file".equals(uri.getScheme())) {
			return performFileRequest(uri);
		}

		// file scheme以外の処理は親クラスに丸投げ
		return super.performRequest(request);
	}

	private NetworkResponse performFileRequest(Uri uri) throws VolleyError {
		File file = new File(uri.getPath());
		NetworkResponse response;
		if (file.exists()) {
			byte[] data = readBytes(file);
			response = new NetworkResponse(data);
		} else {
			// 404エラーを返す
			if (BuildConfig.DEBUG)
				Log.w(TAG, "performFileRequest: file not found: " + file);
			response = new NetworkResponse(HttpStatus.SC_NOT_FOUND, null,
					Collections.<String, String> emptyMap(), false);
		}
		return response;
	}

	private byte[] readBytes(File file) throws VolleyError {
		if (BuildConfig.DEBUG) Log.d(TAG, "readBytes: reading data from " + file);
		byte[] data = null;
		try {
			ByteArrayOutputStream out = new ByteArrayOutputStream();
			InputStream in = null;
			try {
				in = new FileInputStream(file);
				byte[] buf = new byte[1024];
				int len = 0;
				while ((len = in.read(buf, 0, buf.length)) >= 0) {
					out.write(buf, 0, len);
				}
				data = out.toByteArray();
				if (BuildConfig.DEBUG) Log.d(TAG, "readBytes: read " + data.length + " bytes.");
			} finally {
				if (in != null) in.close();
				if (out != null) out.close();
			}
		} catch (IOException e) {
			if (BuildConfig.DEBUG) Log.w(TAG, "readBytes: failed to read data", e);
			throw new NetworkError(e);
		}
		return data;
	}
}

```

実際に使う場合にはRequestQueueのコンストラクターにCustomNetworkのインスタンスを渡します。

```java
    public static RequestQueue createRequestQueue(Context context, HttpStack stack, int maxCacheSizeInBytes) {
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

        String userAgent = "volley/0";
        try {
            String packageName = context.getPackageName();

            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
            userAgent = packageName + "/" + info.versionCode;
        } catch (NameNotFoundException e) {
        }

        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

//        Network network = new BasicNetwork(stack);
        Network network = new CustomNetwork(stack); // ここだけ修正

        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir, maxCacheSizeInBytes), network);
        queue.start();

        return queue;
	}

```