Android Glide 添加代理支持
2020-05-05 本文已影响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);
}
}
}
参考资料
- 官方文档
- glide源码(HttpGlideUrlLoader、HttpUriLoader)