Glide Glide is an image loading & caching library for Android. https://github.com/bumptech/glide
If it is Gradle, you can install it as follows.
build.gradle
repositories {
mavenCentral()
google()
}
dependencies {
implementation 'com.github.bumptech.glide:glide:4.8.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
}
Define a class called GcsImage
that specifies the files that exist on GCS with Bucket and PATH.
GcsImage.java
public class GcsImage {
private final String mBucket;
private final String mFilename;
public GcsImage(@NonNull String bucket, @NonNull String filename) {
mBucket = bucket;
mFilename = filename;
}
public String getBucket() { return mBucket; }
public String getFilename() { return mFilename; }
}
Glide Module
Follow the link below to define a ModelLoader
to load the GcsImage
.
Writing a custom ModelLoader
The first step is to implement the ModelLoader interface. Before we do so, we need to make two decisions:
- What type of Model should we handle?
- What type of Data should we produce for that Model?
As you can see, ModelLoader
specifies the Model and Data type to handle.
Model may be String
etc., but this time we will use our own GcsImage
.
As the Data type, ʻInputStream and
ByteBuffer` decoders are provided as standard.
GcsImageLoader.java
public class GcsImageLoader implements ModelLoader<GcsImage, InputStream> {
private Context mContext;
public GcsImageLoader(Context context) {
mContext = context;
}
@Nullable
@Override
public LoadData<InputStream> buildLoadData(@NonNull GcsImage gcsImage, int width, int height, @NonNull Options options) {
//If the bucket name and file name are the same, cache data will be used.
return new LoadData<>(new ObjectKey(gcsImage.getBucket() + '/' + gcsImage.getFilename()), new GcsImageFetcher(gcsImage));
}
@Override
public boolean handles(@NonNull GcsImage gcsImage) {
return true;
}
private class GcsImageFetcher implements DataFetcher<InputStream> {
private GcsImage gcsImage;
GcsImageFetcher(GcsImage gcsImage) {
this.gcsImage = gcsImage;
}
@Override
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
try {
//Describes the actual reading process. Here, we implemented our own class.
InputStream stream = CloudStorageUtils.downloadToStream(mContext, gcsImage.getBucket(), gcsImage.getFilename());
callback.onDataReady(stream);
} catch (IOException e) {
callback.onLoadFailed(e);
}
}
@Override
public void cleanup() {}
@Override
public void cancel() {}
@NonNull
@Override
public Class<InputStream> getDataClass() {
return InputStream.class;
}
@NonNull
@Override
public DataSource getDataSource() {
return DataSource.REMOTE;
}
}
public static class GcsImageLoaderFactory implements ModelLoaderFactory<GcsImage, InputStream> {
private Context context;
public GcsImageLoaderFactory(Context context) {
this.context = context;
}
@NonNull
@Override
public ModelLoader<GcsImage, InputStream> build(@NonNull MultiModelLoaderFactory multiFactory) {
return new GcsImageLoader(context);
}
@Override
public void teardown() {}
}
}
I also created a method called CloudStorageUtils.downloadToStream ()
and defined it in the following class.
CloudStorageUtils.java
public class CloudStorageUtils {
@WorkerThread
public static InputStream downloadToStream(@NonNull Context context, @NonNull String bucket,
@NonNull String name) throws IOException {
Storage storage = getStorageService(context);
Storage.Objects.Get get = storage.objects().get(bucket, name);
return get.executeMediaAsInputStream();
}
@WorkerThread
@NonNull
public static Storage getStorageService(@NonNull Context context) throws IOException {
HttpTransport transport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
//The part for acquiring authentication information is also defined in another class.
Credential credential = GoogleApiCredentialFactory.getCredential(context);
return new Storage(transport, jsonFactory, credential);
}
}
GoogleApiCredentialFactory.java
public class GoogleApiCredentialFactory {
private static Credential sCredential;
public static synchronized Credential getCredential(@NonNull Context context) {
if (sCredential == null) {
//Read the JSON file with Stream and get the Credential.
try (InputStream is = context.getAssets().open(BuildConfig.GCP_CREDENTIAL)) {
List<String> scopes = new ArrayList<>();
scopes.add(StorageScopes.DEVSTORAGE_FULL_CONTROL);
GoogleCredential credential = GoogleCredential.fromStream(is);
sCredential = credential.createScoped(scopes);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
return sCredential;
}
}
For BuildConfig.GCP_CREDENTIAL
, specify the path to the JSON file that contains the GCP service account key.
Now, let's add the created GcsImageLoader
.
GcsImageGlideModule.java
@GlideModule
public class GcsImageGlideModule extends AppGlideModule {
@Override
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
registry.prepend(GcsImage.class, InputStream.class, new GcsImageLoader.GcsImageLoaderFactory(context));
}
}
By annotating @GlideModule
, Glide will recognize GcsImageGlideModule
, which inherits from ʻAppGlideModule`.
As mentioned above, build.gradle
needs to describe Annotation Processor.
See also: Module classes and annotations.
The program that reads the image on GCS using the GlideModule that I made is as follows.
Here, the image is displayed in ʻImageView called
mImageView`.
Glide.with(this).load(new GcsImage(bucket, filename)).into(mImageView);
With the above, the image can be displayed safely.
Recommended Posts