Glide: How to disable a GlideModule on some requests?

Created on 23 Jan 2016  路  3Comments  路  Source: bumptech/glide

I have a working config like this:

 public class GlideSetup implements GlideModule

and in the manifest:

    <meta-data
        android:name="com.mo.android.glide.GlideSetup"
        android:value="GlideModule"/>

This module is working on all the requests in entire application. The question is that how can i disable this module in some cases (and maybe set another module)?

question

Most helpful comment

You already have access to chain.request() just get the host name and switch on it. This is the simplest least-changing solution I can think of, but not as flexible as the following options.

Alternatives

Currently you're piggybacking on GlideUrl->InputStream there are better solutions which will decouple the model/headers from networking and will be able to work with any network integration (default/Volley/OkHttp). This also implies that you can remove the OkHttp dependency if you added it just to be able to customize headers. The following two solutions bring the header handling one step back in the process to the level of String/CustomModel->GlideUrl.

1. GlideUrl

If you take just one step back you can create GlideUrl instances with headers, there is no GlideModule needed in this case:

public class TestFragment extends Fragment {
    private LazyHeaders.Builder authHeaders;
    private LazyHeaders.Builder otherHeaders;

    @Override public void onAttach(Activity activity) {
        super.onAttach(activity);
        authHeaders = AuthLoader.createHeaders(activity);
        otherHeaders = OtherLoader.createHeaders(activity);
    }

    void loadImage(Context context, String url) {
        Glide.with(this).load(url).into(imageView);
        Glide.with(this).load(new GlideUrl(url, authHeaders.build())).into(imageView);
        Glide.with(this).load(new GlideUrl(url, otherHeaders.build())).into(imageView);
    }
}

While this is simple and nice, you must manage the header builders and create GlideUrls at all locations where those types of requests are needed (each Adapter, Fragment, Activity that loads with these headers). This may be hard to maintain. The AuthLoader and OtherLoader classes referenced from above are not needed for this solution, just saving space by not duplicating the header builder code.

2. BaseGlideUrlLoader

A more radical change, but much more modular solution is to implement distinct model classes (maybe you already have from REST APIs or similar):

// notice that the classes can have common base,
// Glide won't be bothered unless you register a separate loader for the base class too
abstract class UrlModel { String url; UrlModel(String url) { this.url = url; } }
public class AuthModel extends UrlModel { public AuthModel(String url) { super(url); } }
public class OtherModel extends UrlModel { public OtherModel(String url) { super(url); } }

with these the usages changes to (notice how the UI code loading the url is unaware of headers!):

Glide.with(this).load(url).into(imageView);
Glide.with(this).load(new AuthModel(url)).into(imageView);
Glide.with(this).load(new OtherModel(url)).into(imageView);

Obviously these Model objects can be pre-created no need to create them on each bind of a list adapter.

Register each model separately with their own loader. Here is your current GlideModule reconfigured (notice there's no mention of OkHttp Client or anything related to low level networking):

@Override public void registerComponents(Context context, Glide glide) {
    // no need to register for the "one with no headers at all", just use default behavior
    glide.register(AuthModel.class, InputStream.class, new AuthLoader.Factory());
    glide.register(OtherModel.class, InputStream.class, new OtherLoader.Factory());
}

The rest is the Glide boilerplate to glue stuff together. The key class is BaseGlideUrlLoader which was designed for this use case.

// OtherLoader is the same code, but independently modifiable!
class AuthLoader extends BaseGlideUrlLoader<AuthModel> {
    private LazyHeaders.Builder authHeaders;
    public AuthLoader(final Context context) {
        super(context);
        this.authHeaders = HeaderFactories.createAuthHeaders(context);
    }
    @Override protected String getUrl(AuthModel model, int width, int height) {
        return model.url;
    }
    @Override protected Headers getHeaders(AuthModel model, int width, int height) {
        return authHeaders.build();
    }

    public static LazyHeaders.Builder createHeaders(final Context context) {
        return new LazyHeaders.Builder()
            .addHeader(HeadersContract.HEADER_CLIENT_ID, HeadersContract.CLIENT_ID)
            .addHeader(HeadersContract.HEADER_AUTHORIZATION, new LazyHeaderFactory() {
                private final TokenManager manager = TokenManager.getInstance(context);
                @Override public String buildHeader() {
                    try {
                        String accessToken = manager.getToken();
                        return HeadersContract.O_AUTH_AUTHENTICATION + accessToken;
                    } catch (InterruptedException ex) {
                        Thread.currentThread().interrupt(); // see http://stackoverflow.com/a/3976377/253468
                        ex.printStackTrace();
                        return null; // you may want to throw an unchecked exception here to stop the loading process
                        // no need for a server roundtrip to get a HTTP 403, we know here the token is bad!
                    }
                }
            });
    }

    public static class Factory implements ModelLoaderFactory<AuthModel, InputStream> {
        @Override public ModelLoader<AuthModel, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new AuthLoader(context);
        }
        @Override public void teardown() { }
    }
}

All 3 comments

You can't, Glide is a singleton. Please be more specific, the GlideModule has about 10 customisable things. Which one(s) do you want to change from what value to what value and why?

This is my complete GlideModule code, which i am adding headers to all the requests Glide makes, which i love this piece of code :smile: .

public class GlideSetup implements GlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {  }

    @Override
    public void registerComponents(final Context context, Glide glide) {
        OkHttpClient client = new OkHttpClient();

        client.interceptors().add(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                String accessToken = null;
                try {
                    accessToken = TokenManager.getInstance(context).getToken();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Request newRequest = chain.request().newBuilder()
                        .addHeader(HeadersContract.HEADER_CLIENT_ID, HeadersContract.CLIENT_ID)
                        .addHeader(HeadersContract.HEADER_AUTHORIZATION, HeadersContract.O_AUTH_AUTHENTICATION + accessToken)
                        .build();
                return chain.proceed(newRequest);
            }
        });

        glide.register(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(client));
    }
}

Now I want an image be loaded from another server which has it's own config and headers, and another one with no headers at all. What are my options?

You already have access to chain.request() just get the host name and switch on it. This is the simplest least-changing solution I can think of, but not as flexible as the following options.

Alternatives

Currently you're piggybacking on GlideUrl->InputStream there are better solutions which will decouple the model/headers from networking and will be able to work with any network integration (default/Volley/OkHttp). This also implies that you can remove the OkHttp dependency if you added it just to be able to customize headers. The following two solutions bring the header handling one step back in the process to the level of String/CustomModel->GlideUrl.

1. GlideUrl

If you take just one step back you can create GlideUrl instances with headers, there is no GlideModule needed in this case:

public class TestFragment extends Fragment {
    private LazyHeaders.Builder authHeaders;
    private LazyHeaders.Builder otherHeaders;

    @Override public void onAttach(Activity activity) {
        super.onAttach(activity);
        authHeaders = AuthLoader.createHeaders(activity);
        otherHeaders = OtherLoader.createHeaders(activity);
    }

    void loadImage(Context context, String url) {
        Glide.with(this).load(url).into(imageView);
        Glide.with(this).load(new GlideUrl(url, authHeaders.build())).into(imageView);
        Glide.with(this).load(new GlideUrl(url, otherHeaders.build())).into(imageView);
    }
}

While this is simple and nice, you must manage the header builders and create GlideUrls at all locations where those types of requests are needed (each Adapter, Fragment, Activity that loads with these headers). This may be hard to maintain. The AuthLoader and OtherLoader classes referenced from above are not needed for this solution, just saving space by not duplicating the header builder code.

2. BaseGlideUrlLoader

A more radical change, but much more modular solution is to implement distinct model classes (maybe you already have from REST APIs or similar):

// notice that the classes can have common base,
// Glide won't be bothered unless you register a separate loader for the base class too
abstract class UrlModel { String url; UrlModel(String url) { this.url = url; } }
public class AuthModel extends UrlModel { public AuthModel(String url) { super(url); } }
public class OtherModel extends UrlModel { public OtherModel(String url) { super(url); } }

with these the usages changes to (notice how the UI code loading the url is unaware of headers!):

Glide.with(this).load(url).into(imageView);
Glide.with(this).load(new AuthModel(url)).into(imageView);
Glide.with(this).load(new OtherModel(url)).into(imageView);

Obviously these Model objects can be pre-created no need to create them on each bind of a list adapter.

Register each model separately with their own loader. Here is your current GlideModule reconfigured (notice there's no mention of OkHttp Client or anything related to low level networking):

@Override public void registerComponents(Context context, Glide glide) {
    // no need to register for the "one with no headers at all", just use default behavior
    glide.register(AuthModel.class, InputStream.class, new AuthLoader.Factory());
    glide.register(OtherModel.class, InputStream.class, new OtherLoader.Factory());
}

The rest is the Glide boilerplate to glue stuff together. The key class is BaseGlideUrlLoader which was designed for this use case.

// OtherLoader is the same code, but independently modifiable!
class AuthLoader extends BaseGlideUrlLoader<AuthModel> {
    private LazyHeaders.Builder authHeaders;
    public AuthLoader(final Context context) {
        super(context);
        this.authHeaders = HeaderFactories.createAuthHeaders(context);
    }
    @Override protected String getUrl(AuthModel model, int width, int height) {
        return model.url;
    }
    @Override protected Headers getHeaders(AuthModel model, int width, int height) {
        return authHeaders.build();
    }

    public static LazyHeaders.Builder createHeaders(final Context context) {
        return new LazyHeaders.Builder()
            .addHeader(HeadersContract.HEADER_CLIENT_ID, HeadersContract.CLIENT_ID)
            .addHeader(HeadersContract.HEADER_AUTHORIZATION, new LazyHeaderFactory() {
                private final TokenManager manager = TokenManager.getInstance(context);
                @Override public String buildHeader() {
                    try {
                        String accessToken = manager.getToken();
                        return HeadersContract.O_AUTH_AUTHENTICATION + accessToken;
                    } catch (InterruptedException ex) {
                        Thread.currentThread().interrupt(); // see http://stackoverflow.com/a/3976377/253468
                        ex.printStackTrace();
                        return null; // you may want to throw an unchecked exception here to stop the loading process
                        // no need for a server roundtrip to get a HTTP 403, we know here the token is bad!
                    }
                }
            });
    }

    public static class Factory implements ModelLoaderFactory<AuthModel, InputStream> {
        @Override public ModelLoader<AuthModel, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new AuthLoader(context);
        }
        @Override public void teardown() { }
    }
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

PatrickMA picture PatrickMA  路  3Comments

billy2271 picture billy2271  路  3Comments

r4m1n picture r4m1n  路  3Comments

MrFuFuFu picture MrFuFuFu  路  3Comments

Tryking picture Tryking  路  3Comments