I'm trying to integrate Retrofit 2 and Android Priority Job Queue by using Dagger 2.
It's likely I'm using the wrong pattern here (I'm new to Java and Android), but I'm trying to access a Dagger-created Retrofit instance from an object that will be serialized then deserialized before execution (Android Job Queue serializes jobs that are persisted to disk). The Retrofit instance is created by an Application Dagger component, because I'm using SharedPreferences in one of its dependencies.
I can't pass Retrofit to the job when it's created because Retrofit itself cannot be serialized.
Application also can't be serialized, so I can't reference the Application Dagger component from the job when it runs (since I cannot inject with ((MyApplication) myApplication).component().inject(this);
like I can do from Activities, because the Application object doesn't exist in the deserialized job.)
I want to use the Retrofit instance used by the rest of the app for efficiency instead of creating another instance just for the job. Is it even possible?
I'm not sure if using Provider<T>
or a Factory might help because that's beyond my understanding right now, but I'd love a hint about how to structure things if that's the case!
EDIT: This how you create a job with Android Priority Job Queue. I've modified a sample to indicate how I'd like the injection to be working. The job is serialized on onAdded()
and deserialized before it's run with onRun()
:
// A job to send a tweet
public class PostTweetJob extends Job {
@Inject MyService webservice;
public static final int PRIORITY = 1;
private String text;
public PostTweetJob(String text) {
// This job requires network connectivity,
// and should be persisted in case the application exits before job is completed.
super(new Params(PRIORITY).requireNetwork().persist());
}
@Override
public void onAdded() {
// Job has been saved to disk.
}
@Override
public void onRun() throws Throwable {
// Job logic goes here
webservice.postTweet(text);
}
@Override
protected void onCancel() {
// Clean up
}
}
Here's the simplest solution that I could manage.
First, create a BaseJob
class. This will be the injection target:
public abstract class BaseJob extends Job {
// annotate fields that should be injected and made available to subclasses
@Inject MyService service;
protected BaseJob(Params params) {
super(params);
}
}
It's declared abstract
so there's no need to override any of the abstract methods declared in the Job
class. Instead, methods will be overridden in the jobs that inherit from BaseJob
.
Create a job that inherits from BaseJob
. Any fields injected into BaseJob
are available for use:
public class MyActualJob extends BaseJob {
public static final int PRIORITY = 1;
public MyActualJob() {
super(new Params(PRIORITY).requireNetwork().persist());
}
@Override
public void onAdded() {
// job added to queue
}
@Override
public void onRun() throws Throwable {
// do the work
// service will be injected into BaseJob, so you can use it here
final Call<User> call = service.getUser();
call.execute();
}
@Override
protected void onCancel() {
// clean up
}
}
Finally, to ensure things are linked up, add a DependencyInjector
to JobManager
when it's created. This injects into the job's BaseJob
:
DependencyInjector dependencyInjector = new DependencyInjector() {
@Override
public void inject(Job job) {
// this line depends on how your Dagger components are setup;
// the important part is to cast job to BaseJob
((MyApplication) app).component().inject((BaseJob) job);
}
};
Configuration configuration = new Configuration.Builder(getApplicationContext())
.injector(dependencyInjector)
.build();
JobManager jobManager = new JobManager(getApplicationContext(), configuration);
Why not skip using BaseJob
and inject directly into MyActualJob
? This will work, however if there are several jobs that are injection targets, I believe you would have to use instanceof
to check what kind of job was being created and cast job
to the correct class when creating DependencyInjector
:
DependencyInjector dependencyInjector = new DependencyInjector() {
@Override
public void inject(Job job) {
if (job instanceof MyActualJob) {
((MyApplication) app).component().inject((MyActualJob) job);
} else if (job instanceof MyRealJob) {
((MyApplication) app).component().inject((MyRealJob) job);
} else if (job instanceof MyBetterJob) {
((MyApplication) app).component().inject((MyBetterJob) job);
}
}
};
In my case most if not all jobs need access to the same global objects, so it's cleaner to subclass BaseJob
and use that as the sole injection target.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With