Upgrade to glide 4 with progress listeners working. (#201)

This commit is contained in:
Dylan Vann 2018-05-10 02:40:04 -04:00 committed by GitHub
parent c458712c4a
commit f31a44fc07
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 185 additions and 133 deletions

View File

@ -80,6 +80,7 @@ const YourImage = () =>
- [Manual](docs/installation-manual.md) (might be needed if something went wrong with `react-native link`)
- [CocoaPods (iOS)](docs/installation-cocoapods.md) (you may wish to use this if you are already using CocoaPods)
- [Are you using Glide already using an AppGlideModule?](docs/app-glide-module.md) (you might have problems if you don't read this)
## Proguard

View File

@ -16,9 +16,6 @@ allprojects {
repositories {
mavenLocal()
jcenter()
maven {
url "https://maven.google.com"
}
maven {
// All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
url "$rootDir/../node_modules/react-native/android"

View File

@ -16,11 +16,24 @@ def _compileSdkVersion = _ext.has('compileSdkVersion') ? _ext.compileSdkVersion
def _buildToolsVersion = _ext.has('buildToolsVersion') ? _ext.buildToolsVersion : '23.0.1'
def _minSdkVersion = _ext.has('minSdkVersion') ? _ext.minSdkVersion : 16
def _targetSdkVersion = _ext.has('targetSdkVersion') ? _ext.targetSdkVersion : 22
def _glideVersion = _ext.has('glideVersion') ? _ext.glideVersion : '4.7.1'
def _excludeAppGlideModule = _ext.has('excludeAppGlideModule') ? _ext.excludeAppGlideModule : false
android {
compileSdkVersion _compileSdkVersion
buildToolsVersion _buildToolsVersion
sourceSets {
main {
java {
if (_excludeAppGlideModule) {
srcDir 'src'
exclude '**/FastImageGlideModule.java'
}
}
}
}
defaultConfig {
minSdkVersion _minSdkVersion
targetSdkVersion _targetSdkVersion
@ -42,7 +55,16 @@ repositories {
dependencies {
//noinspection GradleDynamicVersion
compile "com.facebook.react:react-native:${_reactNativeVersion}"
compile "com.android.support:support-v4:${_compileSdkVersion}.+"
compile group: 'com.github.bumptech.glide', name: 'glide', version: '3.8.0'
compile group: 'com.github.bumptech.glide', name: 'okhttp3-integration', version: '1.5.0'
compile "com.android.support:support-v4:${_compileSdkVersion}"
compile("com.github.bumptech.glide:glide:${_glideVersion}") {
exclude group: "com.android.support"
}
compile("com.github.bumptech.glide:annotations:${_glideVersion}") {
exclude group: "com.android.support"
}
annotationProcessor "com.github.bumptech.glide:compiler:${_glideVersion}"
compile("com.github.bumptech.glide:okhttp3-integration:${_glideVersion}") {
exclude group: "com.android.support"
exclude group: 'glide-parent'
}
}

View File

@ -1,17 +1,2 @@
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.dylanvann.fastimage"
>
<application>
<meta-data
android:name="com.dylanvann.fastimage.OkHttpProgressGlideModule"
android:value="GlideModule"
/>
<meta-data
android:name="com.bumptech.glide.integration.okhttp.OkHttpGlideModule"
tools:node="remove"
android:value="GlideModule"
/>
</application>
<manifest package="com.dylanvann.fastimage">
</manifest>

View File

@ -0,0 +1,9 @@
package com.dylanvann.fastimage;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
// We need an AppGlideModule to be present for progress events to work.
@GlideModule
public final class FastImageGlideModule extends AppGlideModule {
}

View File

@ -3,13 +3,14 @@ package com.dylanvann.fastimage;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.NonNull;
import com.bumptech.glide.load.DecodeFormat;
import com.bumptech.glide.Glide;
import com.bumptech.glide.GlideBuilder;
import com.bumptech.glide.Registry;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.integration.okhttp3.OkHttpUrlLoader;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.module.GlideModule;
import com.bumptech.glide.module.LibraryGlideModule;
import com.facebook.react.modules.network.OkHttpClientProvider;
@ -30,19 +31,22 @@ import okio.ForwardingSource;
import okio.Okio;
import okio.Source;
public class OkHttpProgressGlideModule implements GlideModule {
@GlideModule
public class FastImageOkHttpProgressGlideModule extends LibraryGlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888); }
@Override
public void registerComponents(Context context, Glide glide) {
public void registerComponents(
@NonNull Context context,
@NonNull Glide glide,
@NonNull Registry registry
) {
OkHttpClient client = OkHttpClientProvider
.getOkHttpClient()
.newBuilder()
.addInterceptor(createInterceptor(new DispatchingProgressListener()))
.build();
glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client));
OkHttpUrlLoader.Factory factory = new OkHttpUrlLoader.Factory(client);
registry.replace(GlideUrl.class, InputStream.class, factory);
}
private static Interceptor createInterceptor(final ResponseProgressListener listener) {
@ -60,11 +64,11 @@ public class OkHttpProgressGlideModule implements GlideModule {
};
}
public static void forget(String key) {
static void forget(String key) {
DispatchingProgressListener.forget(key);
}
public static void expect(String key, ProgressListener listener) {
static void expect(String key, FastImageProgressListener listener) {
DispatchingProgressListener.expect(key, listener);
}
@ -73,7 +77,7 @@ public class OkHttpProgressGlideModule implements GlideModule {
}
private static class DispatchingProgressListener implements ResponseProgressListener {
private static final Map<String, ProgressListener> LISTENERS = new HashMap<>();
private static final Map<String, FastImageProgressListener> LISTENERS = new HashMap<>();
private static final Map<String, Long> PROGRESSES = new HashMap<>();
private final Handler handler;
@ -87,13 +91,13 @@ public class OkHttpProgressGlideModule implements GlideModule {
PROGRESSES.remove(key);
}
static void expect(String key, ProgressListener listener) {
static void expect(String key, FastImageProgressListener listener) {
LISTENERS.put(key, listener);
}
@Override
public void update(final String key, final long bytesRead, final long contentLength) {
final ProgressListener listener = LISTENERS.get(key);
final FastImageProgressListener listener = LISTENERS.get(key);
if (listener == null) {
return;
}

View File

@ -1,6 +1,6 @@
package com.dylanvann.fastimage;
public interface ProgressListener {
public interface FastImageProgressListener {
void onProgress(String key, long bytesRead, long expectedLength);

View File

@ -0,0 +1,61 @@
package com.dylanvann.fastimage;
import android.graphics.drawable.Drawable;
import com.bumptech.glide.load.DataSource;
import com.bumptech.glide.load.engine.GlideException;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.ImageViewTarget;
import com.bumptech.glide.request.target.Target;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.events.RCTEventEmitter;
public class FastImageRequestListener implements RequestListener<Drawable> {
static final String REACT_ON_ERROR_EVENT = "onFastImageError";
static final String REACT_ON_LOAD_EVENT = "onFastImageLoad";
static final String REACT_ON_LOAD_END_EVENT = "onFastImageLoadEnd";
private String key = null;
FastImageRequestListener(String key) {
this.key = key;
}
private static WritableMap mapFromResource(Drawable resource) {
WritableMap resourceData = new WritableNativeMap();
resourceData.putInt("width", resource.getIntrinsicWidth());
resourceData.putInt("height", resource.getIntrinsicHeight());
return resourceData;
}
@Override
public boolean onLoadFailed(@android.support.annotation.Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) {
FastImageOkHttpProgressGlideModule.forget(key);
if (!(target instanceof ImageViewTarget)) {
return false;
}
FastImageViewWithUrl view = (FastImageViewWithUrl) ((ImageViewTarget) target).getView();
ThemedReactContext context = (ThemedReactContext) view.getContext();
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
int viewId = view.getId();
eventEmitter.receiveEvent(viewId, REACT_ON_ERROR_EVENT, new WritableNativeMap());
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap());
return false;
}
@Override
public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) {
if (!(target instanceof ImageViewTarget)) {
return false;
}
FastImageViewWithUrl view = (FastImageViewWithUrl) ((ImageViewTarget) target).getView();
ThemedReactContext context = (ThemedReactContext) view.getContext();
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
int viewId = view.getId();
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_EVENT, mapFromResource(resource));
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap());
return false;
}
}

View File

@ -1,19 +1,15 @@
package com.dylanvann.fastimage;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.Priority;
import com.bumptech.glide.RequestManager;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.load.resource.drawable.GlideDrawable;
import com.bumptech.glide.request.RequestListener;
import com.bumptech.glide.request.target.ImageViewTarget;
import com.bumptech.glide.request.target.Target;
import com.bumptech.glide.request.RequestOptions;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeMap;
@ -24,31 +20,25 @@ import com.facebook.react.uimanager.annotations.ReactProp;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
class ImageViewWithUrl extends ImageView {
public GlideUrl glideUrl;
import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_ERROR_EVENT;
import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_LOAD_END_EVENT;
import static com.dylanvann.fastimage.FastImageRequestListener.REACT_ON_LOAD_EVENT;
public ImageViewWithUrl(Context context) {
super(context);
}
}
class FastImageViewManager extends SimpleViewManager<ImageViewWithUrl> implements ProgressListener {
class FastImageViewManager extends SimpleViewManager<FastImageViewWithUrl> implements FastImageProgressListener {
private static final String REACT_CLASS = "FastImageView";
private static final String REACT_ON_LOAD_START_EVENT = "onFastImageLoadStart";
private static final String REACT_ON_PROGRESS_EVENT = "onFastImageProgress";
private static final String REACT_ON_ERROR_EVENT = "onFastImageError";
private static final String REACT_ON_LOAD_EVENT = "onFastImageLoad";
private static final String REACT_ON_LOAD_END_EVENT = "onFastImageLoadEnd";
private static final Drawable TRANSPARENT_DRAWABLE = new ColorDrawable(Color.TRANSPARENT);
private static final Map<String, List<ImageViewWithUrl>> VIEWS_FOR_URLS = new HashMap<>();
private static final Map<String, List<FastImageViewWithUrl>> VIEWS_FOR_URLS = new HashMap<>();
private RequestManager requestManager = null;
@Override
public String getName() {
@ -56,68 +46,18 @@ class FastImageViewManager extends SimpleViewManager<ImageViewWithUrl> implement
}
@Override
protected ImageViewWithUrl createViewInstance(ThemedReactContext reactContext) {
return new ImageViewWithUrl(reactContext);
}
private static RequestListener<GlideUrl, GlideDrawable> LISTENER = new RequestListener<GlideUrl, GlideDrawable>() {
@Override
public boolean onException(
Exception e,
GlideUrl uri,
Target<GlideDrawable> target,
boolean isFirstResource
) {
OkHttpProgressGlideModule.forget(uri.toStringUrl());
if (!(target instanceof ImageViewTarget)) {
return false;
}
ImageViewWithUrl view = (ImageViewWithUrl) ((ImageViewTarget) target).getView();
ThemedReactContext context = (ThemedReactContext) view.getContext();
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
int viewId = view.getId();
eventEmitter.receiveEvent(viewId, REACT_ON_ERROR_EVENT, new WritableNativeMap());
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, new WritableNativeMap());
return false;
}
@Override
public boolean onResourceReady(
GlideDrawable resource,
GlideUrl uri,
Target<GlideDrawable> target,
boolean isFromMemoryCache,
boolean isFirstResource
) {
if (!(target instanceof ImageViewTarget)) {
return false;
}
ImageViewWithUrl view = (ImageViewWithUrl) ((ImageViewTarget) target).getView();
ThemedReactContext context = (ThemedReactContext) view.getContext();
RCTEventEmitter eventEmitter = context.getJSModule(RCTEventEmitter.class);
int viewId = view.getId();
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_EVENT, mapFromResource(resource));
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_END_EVENT, mapFromResource(resource));
return false;
}
};
private static WritableMap mapFromResource(GlideDrawable resource) {
WritableMap resourceData = new WritableNativeMap();
resourceData.putInt("width", resource.getIntrinsicWidth());
resourceData.putInt("height", resource.getIntrinsicHeight());
return resourceData;
protected FastImageViewWithUrl createViewInstance(ThemedReactContext reactContext) {
requestManager = Glide.with(reactContext);
return new FastImageViewWithUrl(reactContext);
}
@ReactProp(name = "source")
public void setSrc(ImageViewWithUrl view, @Nullable ReadableMap source) {
public void setSrc(FastImageViewWithUrl view, @Nullable ReadableMap source) {
if (source == null) {
// Cancel existing requests.
Glide.clear(view);
requestManager.clear(view);
if (view.glideUrl != null) {
OkHttpProgressGlideModule.forget(view.glideUrl.toStringUrl());
FastImageOkHttpProgressGlideModule.forget(view.glideUrl.toStringUrl());
}
// Clear the image.
view.setImageDrawable(null);
@ -132,15 +72,15 @@ class FastImageViewManager extends SimpleViewManager<ImageViewWithUrl> implement
final Priority priority = FastImageViewConverter.priority(source);
// Cancel existing request.
Glide.clear(view);
requestManager.clear(view);
String key = glideUrl.toStringUrl();
OkHttpProgressGlideModule.expect(key, this);
List<ImageViewWithUrl> viewsForKey = VIEWS_FOR_URLS.get(key);
FastImageOkHttpProgressGlideModule.expect(key, this);
List<FastImageViewWithUrl> viewsForKey = VIEWS_FOR_URLS.get(key);
if (viewsForKey != null && !viewsForKey.contains(view)) {
viewsForKey.add(view);
} else if (viewsForKey == null) {
List<ImageViewWithUrl> newViewsForKeys = new ArrayList<ImageViewWithUrl>(Arrays.asList(view));
List<FastImageViewWithUrl> newViewsForKeys = new ArrayList<>(Collections.singletonList(view));
VIEWS_FOR_URLS.put(key, newViewsForKeys);
}
@ -149,30 +89,32 @@ class FastImageViewManager extends SimpleViewManager<ImageViewWithUrl> implement
int viewId = view.getId();
eventEmitter.receiveEvent(viewId, REACT_ON_LOAD_START_EVENT, new WritableNativeMap());
Glide
.with(view.getContext().getApplicationContext())
.load(glideUrl)
RequestOptions options = new RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.dontTransform()
.priority(priority)
.placeholder(TRANSPARENT_DRAWABLE)
.listener(LISTENER)
.dontTransform()
.placeholder(TRANSPARENT_DRAWABLE);
requestManager
.load(glideUrl)
.apply(options)
.listener(new FastImageRequestListener(key))
.into(view);
}
@ReactProp(name = "resizeMode")
public void setResizeMode(ImageViewWithUrl view, String resizeMode) {
final ImageViewWithUrl.ScaleType scaleType = FastImageViewConverter.scaleType(resizeMode);
public void setResizeMode(FastImageViewWithUrl view, String resizeMode) {
final FastImageViewWithUrl.ScaleType scaleType = FastImageViewConverter.scaleType(resizeMode);
view.setScaleType(scaleType);
}
@Override
public void onDropViewInstance(ImageViewWithUrl view) {
public void onDropViewInstance(FastImageViewWithUrl view) {
// This will cancel existing requests.
Glide.clear(view);
requestManager.clear(view);
final String key = view.glideUrl.toString();
OkHttpProgressGlideModule.forget(key);
List<ImageViewWithUrl> viewsForKey = VIEWS_FOR_URLS.get(key);
FastImageOkHttpProgressGlideModule.forget(key);
List<FastImageViewWithUrl> viewsForKey = VIEWS_FOR_URLS.get(key);
if (viewsForKey != null) {
viewsForKey.remove(view);
if (viewsForKey.size() == 0) VIEWS_FOR_URLS.remove(key);
@ -199,9 +141,9 @@ class FastImageViewManager extends SimpleViewManager<ImageViewWithUrl> implement
@Override
public void onProgress(String key, long bytesRead, long expectedLength) {
List<ImageViewWithUrl> viewsForKey = VIEWS_FOR_URLS.get(key);
List<FastImageViewWithUrl> viewsForKey = VIEWS_FOR_URLS.get(key);
if (viewsForKey != null) {
for (ImageViewWithUrl view: viewsForKey) {
for (FastImageViewWithUrl view : viewsForKey) {
WritableMap event = new WritableNativeMap();
event.putInt("loaded", (int) bytesRead);
event.putInt("total", (int) expectedLength);

View File

@ -9,6 +9,7 @@ import com.bumptech.glide.Glide;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.model.GlideUrl;
import com.bumptech.glide.request.RequestOptions;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
@ -41,12 +42,16 @@ class FastImageViewModule extends ReactContextBaseJavaModule {
final ReadableMap source = sources.getMap(i);
final GlideUrl glideUrl = FastImageViewConverter.glideUrl(source);
final Priority priority = FastImageViewConverter.priority(source);
RequestOptions options = new RequestOptions()
.placeholder(TRANSPARENT_DRAWABLE)
.diskCacheStrategy(DiskCacheStrategy.AUTOMATIC)
.priority(priority);
Glide
.with(activity.getApplicationContext())
.applyDefaultRequestOptions(options)
.load(glideUrl)
.priority(priority)
.placeholder(TRANSPARENT_DRAWABLE)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.preload();
}
}

View File

@ -0,0 +1,14 @@
package com.dylanvann.fastimage;
import android.content.Context;
import android.widget.ImageView;
import com.bumptech.glide.load.model.GlideUrl;
class FastImageViewWithUrl extends ImageView {
public GlideUrl glideUrl;
public FastImageViewWithUrl(Context context) {
super(context);
}
}

12
docs/app-glide-module.md Normal file
View File

@ -0,0 +1,12 @@
# Removing MyAppGlideModule from react-native-fast-image
If you are using Glide within your application using an `AppGlideModule` then you will
need to prevent the inclusion of the `AppGlideModule` in this package.
To accomplish this you can add to `android/build.gradle`:
```gradle
project.ext {
excludeAppGlideModule = true
}
```