Android——Loading images with HTTP POST

Posted Jason Zhang~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android——Loading images with HTTP POST相关的知识,希望对你有一定的参考价值。

大家在进行安卓开发时,一般都会使用图片加载框架来进行图片的展示,如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。

以上是关于Android——Loading images with HTTP POST的主要内容,如果未能解决你的问题,请参考以下文章

在我的 php 文件解析和下载数据时调用 preloader(gif , loading image)

VMWare------启动虚拟机时出现“start booting fron CD... Error loading image:DFEAULT.EZB”提示

jquery插件,用于轻松创建加载css3/images动画

android loading加载是怎么做的

Android开发:网络请求延迟下使用loading,缓解尴尬~~~

Android 请求网络时实现自动 Loading 的一种方式