How can I use my ApiService and Room Dao into WorkManager with Hilt, implementing MVVM pattern?

How can I use my ApiService and Room Dao into WorkManager with Hilt, implementing MVVM pattern?

Problem Description:

I want to use my ApiService and my Room Dao in the WorkManager class implementing the MVVM pattern and Hilt as dependency injection.

This is my code:

ViewModel

@HiltViewModel
public class SyncViewModel extends ViewModel {

    private final SyncRepository mRepository;

    @Inject
    public SyncViewModel(SyncRepository repository) {
        mRepository = repository;
    }

    public LiveData<SyncStatus> getObservable() {
        return mRepository.getFromDB();
    }

    public void launchSync(){
        mRepository.launchWorker();
    }
}

Repository

public class SyncRepository {
    final ApiService apiService;
    private final LiveData<SyncStatus> mData;
    final Context context;

    @Inject
    public SyncRepository(
            ApiService apiService,
            TodayDao todayDao, @ApplicationContext Context context) {
        this.apiService = apiService;
        this.mData= todayDao.getSyncInfo();
        this.context = context;
    }

    public LiveData<SyncStatus> getFromDB() {
        return mData;
    }

    public void launchWorker() {
        WorkManager mWorkManager = WorkManager.getInstance(this.context);

        // Create Network constraint
        Constraints constraints = new Constraints.Builder()
                .setRequiredNetworkType(NetworkType.CONNECTED)
                .build();

        PeriodicWorkRequest periodicSyncDataWork =
                new PeriodicWorkRequest.Builder(SyncWorker.class, 15, TimeUnit.MINUTES)
                        .addTag("TAG_SYNC_DATA")
                        .setConstraints(constraints)
                        //.setInputData(inputData)
                        // setting a backoff on case the work needs to retry
                        .setBackoffCriteria(BackoffPolicy.LINEAR, PeriodicWorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS)
                        .build();
        mWorkManager.enqueueUniquePeriodicWork(
                "SYNC_TODAY",
                ExistingPeriodicWorkPolicy.REPLACE, //Existing Periodic Work
                // policy
                periodicSyncDataWork //work request
        );

    }
}

Worker

@HiltWorker
public class SyncWorker extends Worker {

    private final ApiService workerDependency;
    private final TodayDao mTodayDao;
    private SyncRequest syncRequest;

    @AssistedInject
    public SyncWorker(
            @Assisted @NonNull Context context,
            @Assisted @NonNull WorkerParameters params,
            ApiService workerDependency,
            TodayDao mTodayDao

    ) {
        super(context, params);
        this.workerDependency = workerDependency;
        this.mTodayDao = mTodayDao;
    }

    @NonNull
    @Override
    public Result doWork() {

        try {
            setSyncRequest();
            loadCrud();
            return Result.success();
        } catch (Throwable e) {
            return Result.failure();
        }
    }



    private void setSyncRequest() {
        // ...
    }

    public void loadCrud() {
        workerDependency.callInsert(syncRequest)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(new DisposableSingleObserver<Crud>() {

                    @Override
                    public void onStart() {
                    }

                    @Override
                    public void onSuccess(Crud r) {
                        try {
                            if (r.haveData) {
                                r.doCrud(mTodayDao);
                                mTodayDao.syncUpdate(r.lastUpdate);
                            }
                        } catch (Exception e) {
                            Log.e("ERR", e.getMessage());

                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        Log.e("ERR", e.getMessage());   
                    }
                });
    }
}

Module (TodayDao)

@Module
@InstallIn(SingletonComponent.class)

public class DatabaseModule {

    @Provides
    @Singleton
    public static AppDatabase provideDatabase(Application application){
        return Room.databaseBuilder(application,AppDatabase.class,
                "MyDB")
                .createFromAsset("database/mydb_v0001.db")
                .fallbackToDestructiveMigration()
                .allowMainThreadQueries()
                .build();
    }

    @Provides
    @Singleton
    public static TodayDao provideTodayDao(AppDatabase appDB){
        return appDB.todayDao();
    }
}

Module (ApiService)

@Module
@InstallIn(SingletonComponent.class)
public abstract class NetworkModule {
    @Provides
    @Singleton
    static Retrofit provideRetrofit(GsonConverterFactory gsonConverterFactory,
                                    RxJava3CallAdapterFactory rxJava3CallAdapterFactory,
                                    OkHttpClient okHttpClient
    ) {    

        return new Retrofit.Builder().baseUrl(URL_API)
                .addConverterFactory(gsonConverterFactory)
                .addCallAdapterFactory(rxJava3CallAdapterFactory)    
                .client(okHttpClient)
                .build();
    }   

    @Provides
    @Singleton
    static Context provideContext(Application application) {
        return application;
    }    

    @Provides
    @Singleton
    static OkHttpClient provideHttpClient(Application application) {
        Dispatcher dispatcher = new Dispatcher();
        HttpLoggingInterceptor logging = new HttpLoggingInterceptor();

        OkHttpClient.Builder client = new OkHttpClient.Builder()
                .connectTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .dispatcher(dispatcher);

        client.addInterceptor(new ConnectivityIntecepter(application));
        Interceptor interceptor = chain -> {
            Request original = chain.request();
            Request request = original.newBuilder()
                    .method(original.method(), original.body())
                    .build();
            return chain.proceed(request);
        };
        client.addInterceptor(interceptor);
        client.interceptors().add(logging);
        return client.build();
    }    

    @Provides
    @Singleton
    static Gson provideGson() {
        return new GsonBuilder().setLenient().create();
    }
    
    @Provides
    @Singleton
    static GsonConverterFactory providesGsonConverterFactory() {
        return GsonConverterFactory.create();
    }

    @Provides
    @Singleton
    static RxJava3CallAdapterFactory providesRxJavaCallAdapterFactory() {
        return RxJava3CallAdapterFactory.create();
    }

    @Provides
    @Singleton
    static ApiService provideService(Retrofit retrofit) {
        return retrofit.create(ApiService.class);
    }
}

Manifest

<application
    ... >
    
    <provider
        android:name="androidx.startup.InitializationProvider"
        android:authorities="${applicationId}.androidx-startup"
        android:exported="false"
        tools:node="merge">
        <meta-data
            android:name="androidx.work.WorkManagerInitializer"
            android:value="androidx.startup"
            tools:node="remove" />
    </provider>

</application>

When trying to open the Fragment that launches the worker, it doesn’t work, giving this error:

E/WM-WorkerFactory: Could not instantiate
org.my.app.workers.SyncWorker
java.lang.NoSuchMethodException: org.my.app.workers.SyncWorker. [class android.content.Context,
class androidx.work.WorkerParameters]
at java.lang.Class.getConstructor0(Class.java:2363)
at java.lang.Class.getDeclaredConstructor(Class.java:2201)
at androidx.work.WorkerFactory.createWorkerWithDefaultFallback(WorkerFactory.java:95)
at androidx.work.impl.WorkerWrapper.runWorker(WorkerWrapper.java:245)
at androidx.work.impl.WorkerWrapper.run(WorkerWrapper.java:137)
at androidx.work.impl.utils.SerialExecutor$Task.run(SerialExecutor.java:91)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:920) E/WM-WorkerWrapper: Could not create Worker org.my.app.workers.SyncWorker

The error occurs because I try to pass ApiService workerDependency and TodayDao mTodayDao into the constructor:

@AssistedInject
public SyncWorker(
        @Assisted @NonNull Context context,
        @Assisted @NonNull WorkerParameters params,
        ApiService workerDependency,
        TodayDao mTodayDao
) {
    super(context, params);
    this.workerDependency = workerDependency;
    this.mTodayDao=mTodayDao;
}

If I remove those two parameters, the error goes away:

@AssistedInject
public SyncWorker(
        @Assisted @NonNull Context context,
        @Assisted @NonNull WorkerParameters params
) {
    super(context, params);
}

How can you have a reference to ApiService and TodayDao in the class SyncWorker? What is the error in my code?

Solution – 1

You can try with Injecting instance of ApiService and TodayDao as variable in your worker.

@HiltWorker
public class SyncWorker extends Worker {

    @Inject
    ApiService workerDependency;

    @Inject
    TodayDao mTodayDao;

    @AssistedInject
    public TestWorker(
            @Assisted @NonNull Context context,
            @Assisted @NonNull WorkerParameters workerParams
    ) {
        super(context, workerParams);
    }

    @NonNull
    @Override
    public Result doWork() {

        ...

        return Result.success();
    }
}
Rate this post
We use cookies in order to give you the best possible experience on our website. By continuing to use this site, you agree to our use of cookies.
Accept
Reject