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