Android——Loading images with HTTP POST

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiaoyu_93/article/details/73852302

大家在进行安卓开发时,一般都会使用图片加载框架来进行图片的展示,如universalimageloader、Glide等。开发者只需要传值网络图片的URL以及要显示的图片控件即可。简单粗暴。但是如果你遇到了我这样的奇葩需求(图片是一个post请求的URL),那么这篇文章你算是找对了。
大家都知道,一般请求查询数据或者打开一个图片的url都是GET请求,当然要使用post也是ok的啦。但是测试发现项目中的universalimageloader、Glide两个图片加载框架,在处理POST请求的图片URL时就没用了。猜测这两个框架内默认都是使用了GET请求,所以,查看源码,寻找到对应的代码然后进行自定义或者修改成POST请求就行啦!
这里记录本人的操作,以便后续查看。
1、universalimageloader。源码修改。
因为项目中使用的universalimageloader框架是使用了源码,所以这里是直接找到源码进行修改的,如果不能直接修改源码的话,不知道是否提供了自定义方法来修改(Glide是使用自定义加载,可参考,但本人没研究)。
com.nostra13.universalimageloader.core.download.BaseImageDownloader 类,就是该框架使用HttpURLConnection的方式通过GET请求拿到InputStream来显示图片的。HttpURLConnection没有设置请求方式,就是使用了默认的GET请求。所以,我们在创建HttpURLConnection的时候加上设置请求方式为POST的代码即可。

    // 源码
    protected HttpURLConnection createConnection(String url, Object extra) throws IOException {
        String encodedUrl = Uri.encode(url, ALLOWED_URI_CHARS);
        HttpURLConnection conn = (HttpURLConnection) new URL(encodedUrl).openConnection();
        conn.setConnectTimeout(connectTimeout);
        conn.setReadTimeout(readTimeout);

        return conn;
    }

    // 修改后的代码
    protected HttpURLConnection createConnection(String url, Object extra) throws IOException {
        String encodedUrl = Uri.encode(url, ALLOWED_URI_CHARS);
        HttpURLConnection conn = (HttpURLConnection) new URL(encodedUrl).openConnection();
        conn.setConnectTimeout(connectTimeout);
        conn.setReadTimeout(readTimeout);

        // 只针对我们的项目,或某些只能使用POST拿到图片的URL才使用POST,其他的照旧使用GET
        if (url.startsWith("XXXXXXX")) {
            conn.setRequestMethod("POST");
        }

        return conn;
    }

经测试,OK。

2、Glide。自定义加载类。
Glide框架是通过配置gradle的形式使用的,所以就无法修改源码了。但是我们通过查看源码还是找到了它请求URL拿到InputStream的类。

com.bumptech.glide.load.data.HttpUrlFetcher

以下是请求的具体方法:

private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl, Map<String, String> headers)
            throws IOException {
        if (redirects >= MAXIMUM_REDIRECTS) {
            throw new IOException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");
        } else {
            // Comparing the URLs using .equals performs additional network I/O and is generally broken.
            // See http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.
            try {
                if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {
                    throw new IOException("In re-direct loop");
                }
            } catch (URISyntaxException e) {
                // Do nothing, this is best effort.
            }
        }
        urlConnection = connectionFactory.build(url);
        for (Map.Entry<String, String> headerEntry : headers.entrySet()) {
          urlConnection.addRequestProperty(headerEntry.getKey(), headerEntry.getValue());
        }
        urlConnection.setConnectTimeout(2500);
        urlConnection.setReadTimeout(2500);
        urlConnection.setUseCaches(false);
        urlConnection.setDoInput(true);

        // Connect explicitly to avoid errors in decoders if connection fails.
        urlConnection.connect();
        if (isCancelled) {
            return null;
        }
        final int statusCode = urlConnection.getResponseCode();
        if (statusCode / 100 == 2) {
            return getStreamForSuccessfulRequest(urlConnection);
        } else if (statusCode / 100 == 3) {
            String redirectUrlString = urlConnection.getHeaderField("Location");
            if (TextUtils.isEmpty(redirectUrlString)) {
                throw new IOException("Received empty or null redirect url");
            }
            URL redirectUrl = new URL(url, redirectUrlString);
            return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers);
        } else {
            if (statusCode == -1) {
                throw new IOException("Unable to retrieve response code from HttpUrlConnection.");
            }
            throw new IOException("Request failed " + statusCode + ": " + urlConnection.getResponseMessage());
        }
    }

很显然,它也是使用了HttpURLConnection的默认请求方式GET。如果你使用的是源码的话,可以仿照universalimageloader的方式,在urlConnection.connect();方法前做判断和设置成POST请求。
如果你也和我一样无法直接修改源码的话,可以使用如下方式:
1、复制HttpUrlFetcher类,把名字改成HttpUrlPostFetcher。要做的修改就是在urlConnection.connect();方法前做判断和设置成POST请求。
2、新建一个ModelLoader的子类HttpUrlPostLoader,表明使用刚刚自定义的HttpUrlPostFetcher类,代码如下:

public class HttpUrlPostLoader implements ModelLoader<GlideUrl, InputStream> {

    public static class Factory implements ModelLoaderFactory<GlideUrl, InputStream> {

        public Factory() {}

        @Override
        public ModelLoader<GlideUrl, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new HttpUrlPostLoader();
        }

        @Override
        public void teardown() {}
    }

    public HttpUrlPostLoader() {
    }

    @Override
    public DataFetcher<InputStream> getResourceFetcher(GlideUrl model, int width, int height) {
        return new HttpUrlPostFetcher(model);
    }
}

3、自定义要使用的GlideModule的子类CustomGlideModule,使用第二步创建的ModelLoader,代码如下:

public class CustomGlideModule implements GlideModule {

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        //通过builder.setXXX进行配置.
    }

    @Override
    public void registerComponents(Context context, Glide glide) {
        //通过glide.register进行配置.
        glide.register(GlideUrl.class, InputStream.class, new HttpUrlPostLoader.Factory());
    }
}

4、在AndroidManifest.xml中注册,代码如下:

<meta-data
   android:name="XXXX.CustomGlideModule"
   android:value="GlideModule" />

经测试,OK。

猜你喜欢

转载自blog.csdn.net/xiaoyu_93/article/details/73852302