美文网首页
Android Glide 添加代理支持

Android Glide 添加代理支持

作者: _年少 | 来源:发表于2020-05-05 10:47 被阅读0次

    1.添加依赖

        //具体看项目使用版本
        implementation 'com.github.bumptech.glide:glide:4.0.0'
        annotationProcessor 'com.github.bumptech.glide:compiler:4.0.0'
    

    2.添加代码

    1.ProxyModule.java

    @GlideModule
    public class ProxyModule extends AppGlideModule {
    
        @Override
        public void registerComponents(Context context, Glide glide, Registry registry) {
            // 替换Uri的默认行为
            registry.replace(Uri.class, InputStream.class, new ProxyHttpLoader.Factory());
        }
    }
    

    2.ProxyHttpLoader.java

    
    public class ProxyHttpLoader implements ModelLoader<Uri, InputStream> {
    
        private static final Set<String> SCHEMES = Collections.unmodifiableSet(new HashSet<>(Arrays.asList("http", "https")));
        public static final Option<Integer> TIMEOUT = Option.memory(
                "com.bumptech.glide.load.model.stream.HttpGlideUrlLoader.Timeout", 2500);
        private final ModelCache<Uri, GlideUrl> mModelCache;
    
        public ProxyHttpLoader(ModelCache<Uri, GlideUrl> modelCache) {
            mModelCache = modelCache;
        }
    
        @Override
        public LoadData<InputStream> buildLoadData(Uri model, int width, int height, Options options) {
            Log.d("TAG", "buildLoadData: ");
    
            GlideUrl url = new GlideUrl(model.toString());
            if (mModelCache != null) {
                url = mModelCache.get(model, 0, 0);
                if (url == null) {
                    url = new GlideUrl(model.toString());
                    mModelCache.put(model, 0, 0, url);
                }
            }
            int timeout = options.get(TIMEOUT);
            return new LoadData<>(url, new ProxyHttpUrlFetcher(url, timeout));
        }
    
        @Override
        public boolean handles(Uri model) {
            return SCHEMES.contains(model.getScheme());
        }
    
        /**
         * Factory for loading {@link InputStream}s from http/https {@link Uri}s.
         */
        public static class Factory implements ModelLoaderFactory<Uri, InputStream> {
    
            private final ModelCache<Uri, GlideUrl> mCache = new ModelCache<>(500);
    
            @Override
            public ModelLoader<Uri, InputStream> build(MultiModelLoaderFactory multiFactory) {
                return new ProxyHttpLoader(mCache);
            }
    
            @Override
            public void teardown() {
                // Do nothing.
            }
        }
    }
    

    3.ProxyHttpUrlFetcher.java

    public class ProxyHttpUrlFetcher implements DataFetcher<InputStream> {
    
        private static final String TAG = "HttpUrlFetcher";
        private static final int MAXIMUM_REDIRECTS = 5;
        static final HttpUrlConnectionFactory DEFAULT_CONNECTION_FACTORY = new DefaultHttpUrlConnectionFactory();
    
        private final GlideUrl glideUrl;
        private final int timeout;
        private final HttpUrlConnectionFactory connectionFactory;
    
        private HttpURLConnection urlConnection;
        private InputStream stream;
        private volatile boolean isCancelled;
    
        public ProxyHttpUrlFetcher(GlideUrl glideUrl, int timeout) {
            this(glideUrl, timeout, DEFAULT_CONNECTION_FACTORY);
        }
    
        // Visible for testing.
        ProxyHttpUrlFetcher(GlideUrl glideUrl, int timeout, HttpUrlConnectionFactory connectionFactory) {
            this.glideUrl = glideUrl;
            this.timeout = timeout;
            this.connectionFactory = connectionFactory;
        }
    
        @Override
        public void loadData(Priority priority, DataCallback<? super InputStream> callback) {
            long startTime = LogTime.getLogTime();
            final InputStream result;
            try {
                result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/,
                        glideUrl.getHeaders());
            } catch (IOException e) {
                Log.d(TAG, "Failed to load data for url", e);
                callback.onLoadFailed(e);
                return;
            }
    
            if (Log.isLoggable(TAG, Log.VERBOSE)) {
                Log.v(TAG, "Finished http url fetcher fetch in " + LogTime.getElapsedMillis(startTime)
                        + " ms and loaded " + result);
            }
            callback.onDataReady(result);
        }
    
        private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
                                                  Map<String, String> headers) throws IOException {
            if (redirects >= MAXIMUM_REDIRECTS) {
                throw new HttpException("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 HttpException("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(timeout);
            urlConnection.setReadTimeout(timeout);
            urlConnection.setUseCaches(false);
            urlConnection.setDoInput(true);
    
            // Stop the urlConnection instance of HttpUrlConnection from following redirects so that
            // redirects will be handled by recursive calls to this method, loadDataWithRedirects.
            urlConnection.setInstanceFollowRedirects(false);
    
            // 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 HttpException("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 HttpException(statusCode);
            } else {
                throw new HttpException(urlConnection.getResponseMessage(), statusCode);
            }
        }
    
        private InputStream getStreamForSuccessfulRequest(HttpURLConnection urlConnection)
                throws IOException {
            if (TextUtils.isEmpty(urlConnection.getContentEncoding())) {
                int contentLength = urlConnection.getContentLength();
                stream = ContentLengthInputStream.obtain(urlConnection.getInputStream(), contentLength);
            } else {
                if (Log.isLoggable(TAG, Log.DEBUG)) {
                    Log.d(TAG, "Got non empty content encoding: " + urlConnection.getContentEncoding());
                }
                stream = urlConnection.getInputStream();
            }
            return stream;
        }
    
        @Override
        public void cleanup() {
            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException e) {
                    // Ignore
                }
            }
            if (urlConnection != null) {
                urlConnection.disconnect();
            }
        }
    
        @Override
        public void cancel() {
            // TODO: we should consider disconnecting the url connection here, but we can't do so
            // directly because cancel is often called on the main thread.
            isCancelled = true;
        }
    
        @Override
        public Class<InputStream> getDataClass() {
            return InputStream.class;
        }
    
        @Override
        public DataSource getDataSource() {
            return DataSource.REMOTE;
        }
    
        interface HttpUrlConnectionFactory {
            HttpURLConnection build(URL url) throws IOException;
        }
    
        private static class DefaultHttpUrlConnectionFactory implements HttpUrlConnectionFactory {
    
            @Synthetic
            DefaultHttpUrlConnectionFactory() {
            }
    
            @Override
            public HttpURLConnection build(URL url) throws IOException {
                // 代理服务器配置
                Proxy proxy = new Proxy(Proxy.Type.SOCKS, new InetSocketAddress("127.0.0.1", 9001));
                return (HttpURLConnection) url.openConnection(proxy);
            }
        }
    }
    

    参考资料

    1. 官方文档
    2. glide源码(HttpGlideUrlLoader、HttpUriLoader)

    相关文章

      网友评论

          本文标题:Android Glide 添加代理支持

          本文链接:https://www.haomeiwen.com/subject/rlnpghtx.html