Home > Back-end >  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?

Time:12-07

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 one rror(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?

CodePudding user response:

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();
    }
}
  • Related