7

I am developing a music application. I want to load artist's image from LastFM so i do this this way 1. I created a class ArtistImageLoader extends BaseGlideUrlLoader. 2. In the getUrl method i used retrofit2 to get the artist's image url from LastFM via getArtistInfo method.

My problem is i didn't know how to inject the service of retrofit to make the request in ArtistImageLoader. I did this way but i got a NOP exception. lastFmService wasn't be not injected.

// GlideModule
glide.register(MLocalArtist.class, InputStream.class, new    ArtistImageLoader.Factory());

// Use it in onCreate method of ArtistsFragment
DaggerLastFmComponent.builder().activityModule(new ActivityModule(getActivity()))
                .netComponent(getNetComponent())
                .build().inject(this);

// use this code in onBindViewHolder method of artists recycler adapter
Glide.with(getContext())
                .from(MLocalArtist.class)
                .load(localArtist)
                .into(localArtistViewHolder.ivArtwork);

ArtistImageLoader

public class ArtistImageLoader extends BaseGlideUrlLoader<MLocalArtist> {

    @Inject
    LastfmService lastfmService;

    public ArtistImageLoader(Context context) {
        super(context);
    }

    @Override
    protected String getUrl(MLocalArtist model, int width, int height) {
        Call<List<MArtist>> call = lastfmService.getArtistInfo(model.artistName);
        try {
            List<MArtist> artists = call.execute().body();
            if (artists != null && artists.size() > 0) {
                Timber.e(artists.get(0).toString());
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

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

Can you help me to do it? Thank you so much!

Glide Version: 3.7.0

Integration libraries: OkHttp3 + Dagger2

Device/Android Version: Android Emulator + Asus zenfone 5

EDIT 1

ActivityComponent.java

@PerActivity
@Component(dependencies = AppComponent.class, modules = ActivityModule.class)
public interface ActivityComponent {
    Context context();
}

AppComponent.java

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
    App app();
}

NetComponent.java

@Singleton
@Component(modules = {NetModule.class, AppModule.class})
public interface NetComponent {
    @Named("chartSoundCloud")
    Retrofit getSoundcloudChartRetrofit();

    @Named("searchSoundCloud")
    Retrofit getSoundcloudSearchRetrofit();

    @Named("lastFM")
    Retrofit getLastFmRetrofit();
}

LastFmComponent.java

@PerActivity
@Component(dependencies = NetComponent.class, modules = {LastFmModule.class, ActivityModule.class})
public interface LastFmComponent extends ActivityComponent {
    void inject(ArtistsFragment artistsFragment);
}

ActivityModule.java

@Module
public class ActivityModule {
    private final Context mContext;

    public ActivityModule(Context mContext) {
        this.mContext = mContext;
    }

    @Provides
    @PerActivity
    Context provideActivityContext() {
        return mContext;
    }
}

AppModule.java

@Module
public class AppModule {
    private App app;

    public AppModule(App app){
        this.app = app;
    }

    @Singleton
    @Provides
    App provideApplication() {
        return app;
    }

    @Singleton
    @Provides @Named("applicationContext")
    Context provideApplicationContext(){
        return app;
    }
}

LastFmModule.java

@Module
public class LastFmModule {

    @Provides
    @PerActivity
    LastfmService provideLastFmService(@Named("lastFM") Retrofit retrofit) {
        return retrofit.create(LastfmService.class);
    }

}

NetModule.java

@Module
public class NetModule {
    static final int DISK_CACHE_SIZE = (int) MEGABYTES.toBytes(50);

    @Provides
    @Singleton
    Cache provideOkHttpCache(@Named("applicationContext") Context application) {
        Cache cache = new Cache(application.getCacheDir(), DISK_CACHE_SIZE);
        return cache;
    }

    @Provides
    @Singleton
    ScdClientIdInterceptor provideScdClientIdInterceptor() {
        return new ScdClientIdInterceptor();
    }

    @Provides
    @Singleton
    LastFMInterceptor provideLastFmInterceptor() {
        return new LastFMInterceptor();
    }

    @Provides
    @Singleton
    HttpLoggingInterceptor provideHttpLoggingInterceptor() {
        return new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY);
    }

    @Provides
    @Singleton
    @Named("soundcloud-Http")
    OkHttpClient provideOkHttpSoundCloudClient(Cache cache,  ScdClientIdInterceptor clientIdInterceptor, HttpLoggingInterceptor httpLoggingInterceptor) {
        return createOkHttpClient(cache, clientIdInterceptor, httpLoggingInterceptor);
    }

    @Provides
    @Singleton
    @Named("lastFM-Http")
    OkHttpClient provideOkHttpLastFmClient(Cache cache, LastFMInterceptor clientIdInterceptor, HttpLoggingInterceptor httpLoggingInterceptor) {
        return createOkHttpClient(cache, clientIdInterceptor, httpLoggingInterceptor);
    }

    private OkHttpClient createOkHttpClient(Cache cache,  Interceptor clientIdInterceptor, HttpLoggingInterceptor httpLoggingInterceptor) {
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .cache(cache)
                .addInterceptor(clientIdInterceptor)
                .addInterceptor(httpLoggingInterceptor)
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .build();

        return okHttpClient;
    }

    @Provides
    @Singleton
    Gson provideGson() {
        return GsonFactory.create();
    }

    @Provides
    @Singleton
    @Named("searchSoundCloud")
    Retrofit provideSearchSoundCloudRetrofit(Gson gson, @Named("soundcloud-Http") OkHttpClient okHttpClient) {
        Retrofit searchRetrofit = new Retrofit.Builder()
                .baseUrl(Constants.BASE_SOUNDCLOUD_API_URL)
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();
        return searchRetrofit;
    }

    @Provides
    @Singleton
    @Named("chartSoundCloud")
    Retrofit provideChartSoundCloudRetrofit(Gson gson, @Named("soundcloud-Http") OkHttpClient okHttpClient) {
        Retrofit chartRetrofit = new Retrofit.Builder()
                .baseUrl(Constants.BASE_SOUNDCLOUD_API_V2_URL)
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();
        return chartRetrofit;
    }

    @Provides
    @Singleton
    @Named("lastFM")
    Retrofit provideLastFmRetrofit(Gson gson, @Named("lastFM-Http") OkHttpClient okHttpClient) {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(Constants.LASTFM_API_URL)
                .client(okHttpClient)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .build();
        return retrofit;
    }
}
Kien Vi Thanh
  • 71
  • 1
  • 5

1 Answers1

0

My assumption is that your ArtistImageLoader is defined in an separate class. The reason for the your problem is the way dagger works. It only injects fields annotated with @Inject on the class you specified as parameter of the inject method. Therefore nothing inside your ArtistImageLoader annotated with @Inject will be injected, but only the annotated fields, which are defined inside your ArtistsFragment.

I would recommend to define an LastfmService field with the @Inject annotation in your fragment and pass the instance to your Glide LoaderFactory. The factory can provide it to the instances of the loader. It's not the nicest solution, but since you can't directly pass it to the instances this seems to viable workaround.

Another approach would be to build your dependency tree inside your custom Application. This allows you access the dependencies from anywhere without being dependent on the activity lifecycle.

Jacob
  • 493
  • 6
  • 17
  • Thanks for your answer. But i want to register the `ArtistImageLoader` into GlideModule class instead of create and set an instance in `ArtistFragment`. Can i define an `LastFmService` field with the `@Inject` annotation in the GlideModule? How can i do it? – Kien Vi Thanh Oct 19 '16 at 16:42
  • Did I understood you correctly, that you want the ArtistImageLoader instance being provided by an separate module and injected to the ArtistFragment ? – Jacob Oct 19 '16 at 17:05
  • I'm just not sure how to pass the instance to the LoaderFactory. Because the fragment didn't have any instance of LoaderFactory. It just created in register method of `GlideModule` – Kien Vi Thanh Oct 20 '16 at 03:58