Android Workmanager - android

I'm having trouble figuring out how to use Android's Workmanager. Essentially, I want to make a OneTime request with a time delay, determine success, then do some stuff. The Worker class does NOTHING.
What I'm expecting is a SUCCESS code to be returned after the time delay. That is, the sole purpose of my Workmanager code is to provide a delay.
I know there are many other ways to effect a delay but my understanding is that Android "protects" Workmanager Workers better than, say, AlarmManager.
Unfortunately, I can't make it happen.
Here's what I've done
Extended my activity to include LifecycleOwner
Added the required getLifeCycle method, as below
#NonNull
#Override
public Lifecycle getLifecycle() {
Log.d (TAG, "getLifecycle(): " + getLifecycle());
return getLifecycle();
}
Here's the Worker class
public class makeAlarm extends Worker {
public makeAlarm(
#NonNull Context context,
#NonNull WorkerParameters params) {
super(context, params);
}
#Override
public Result doWork() {
// Do the work here--in this case, do nothing
// Indicate whether the task finished successfully with the Result
return Result.success();
}
}
And here's my action code
// Create a OneTimeWorkRequest that delays "success" by the required time
OneTimeWorkRequest makeAlarmWorkRequest = new OneTimeWorkRequest.Builder(makeAlarm.class)
.setInitialDelay(120, TimeUnit.SECONDS)
.build();
WorkManager.getInstance(this).enqueue(makeAlarmWorkRequest);
// The following stmt. shows what appears to be a valid id
Log.d (TAG, "makeAlarmWorkRequest.getId(): " + makeAlarmWorkRequest.getId());
WorkManager.getInstance(this).getWorkInfoByIdLiveData(makeAlarmWorkRequest.getId())
.observe(myLifecycleOwner, new Observer<WorkInfo>() {
#Override
public void onChanged(#Nullable WorkInfo workInfo) {
if (workInfo != null && workInfo.getState() == WorkInfo.State.SUCCEEDED) {
// Some amazing work here...
}
}
});
I've tried a number of variations but haven't had success.
The above code results in a stack overflow crash.
Help!

Related

How to call at object with callback/listener in a Worker doWork() method of the

I'm am trying to use a WorkManager to schedule an API call because of my apps offline capability, but I am not sure how to handle listeners in my worker. Inside the ScenarioRunSyncHelper it builds the JsonObject and calls a WebApi class which refreshes an authentication token if required. So there are two layers of listeners. It looks like some of the other examples I have seen like here and here there are no callbacks/listeners in the doWork. Their objects/methods within the doWork method throw errors so the Result.success or Result.failure can be called accordingly. Mine looks like this:
#NonNull
#Override
public Result doWork() {
ScenarioRunSyncHelper helper = new ScenarioRunSyncHelper();
helper.sync(application, scenarioRun, new ScenarioRunSyncHelper.SyncListener() {
#Override
public void onSuccess() {
//return Result.success();
}
#Override
public void onError(Exception e) {
//return Result.failure();
}
});
return Result.success();
}
Is this accurate? Is anybody else facing this issue?

Android WorkManager not starting the worker

I want to download certain files from the server at the start of the app .So I tried using Work Manager which gets enqueued from my custom Application class.But the Worker class is not getting triggered and the state is only ENQUEUED and not going to RUNNING.Below is my code:
Application class:
#Override
public void onCreate() {
Constraints constraints = new Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
//.setRequiresStorageNotLow(true)
//.setRequiresBatteryNotLow(true)
.build();
OneTimeWorkRequest request = new OneTimeWorkRequest
.Builder(MyWorker.class)
//.setConstraints(constraints)
//.setInitialDelay(1,TimeUnit.SECONDS)
.addTag("download")
.build();
WorkManager.getInstance(getApplicationContext()).getWorkInfoByIdLiveData(request.getId()).observeForever(new Observer<WorkInfo>() {
#Override
public void onChanged(WorkInfo workInfo) {
if (workInfo == null) {
Log.d("download", "workInfo == null");
} else {
Log.d("download", "workInfo != null: " + workInfo.getState().toString());//This is giving ENQUEUED once..thats it
}
}
});
WorkManager.getInstance(getApplicationContext()).enqueue(request);
}
MyWorker class
public class MyWorker extends Worker {
public MyWorker(#NonNull Context context, #NonNull WorkerParameters workerParams) {
super(context, workerParams);
}
#NonNull
#Override
public Result doWork() {
//Some file to download
}
}
build.gradle:
implementation "androidx.work:work-runtime:2.3.2"
Things i have tried:
I tried adding an interval and also remove constraints, but then also, MyWorker is not triggered.
I have seen many SOF posts as given below but it didn't help me:
Worker manager: not start work in enqueue
Why workers in work manager still in ENQUEUED state?
Android WorkManager doesn't trigger one of the two scheduled workers

Android RxJava Thread Reusal, Is it a bad practice?

I am using retrofit and Rxjava to handle api calls for my mvvm android application. Based on some tutorial, i am currently using RxJava like this.
ViewModel.java
CompositeDisposable disposable = new CompositeDisposable();
private void fetchTodolist(){
loading.setValue(true);
disposable.add(
service.getToDoList("A1833")
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<ApiResponse<ArrayList<TodoItem>>>() {
#Override
public void onSuccess(ApiResponse<ArrayList<TodoItem>> value) {
if(value.getStatus() == 200){
//on call success code
} else {
//on call rejected code
}
}
#Override
public void onError(Throwable e) {
// on call error code
}
})
);
}
And now i want to cache the result of the api call on successful call into room database. So i need to use another async method and tried to reuse the new thread i created before. And here's the code.
private void fetchTodolist(){
loading.setValue(true);
Scheduler a = Schedulers.newThread();
disposable.add(
service.getToDoList("A1833")
.subscribeOn(a)
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<ApiResponse<ArrayList<TodoItem>>>() {
#Override
public void onSuccess(ApiResponse<ArrayList<TodoItem>> value) {
if(value.getStatus() == 200){
a.scheduleDirect(new Runnable() {
#Override
public void run() {
long inserted = dao.insert(value);
}
});
} else {
//on call rejected code
}
}
#Override
public void onError(Throwable e) {
// on call error code
}
})
);
}
I wonder if it is a bad practice and will lead to a serious problem. And if so, what's the alternative.
Schedulers uses cached references thus newThread() returns the same Scheduler instance.
Schedulers.newThread() == Schedulers.newThread()
Generally you should avoid using newThread because it creates a new thread for every application of the operator. So if you run the sequence multiple times, new worker threads are created and dismissed without any kind of reuse. This is especially true for newThread().scheduleDirect which will start a new thread just for that single runnable and stop it afterwards.
It is recommended you use Schedulers.io() for IO operations so that those underlying worker threads are reused as much as possible later.

RxJava chain requests and update UI

I want to chain up three network calls with RxJavaand Retrofit. The first call (retrieves the session token) has to be the first, the other two depend on this call and if the first call isn't finished before, the other two calls will result in an error.
For the other two calls, they should retrieve some information and update the UI. What would be the best way to proceed?
I first thought about using the zip Operator, but I'm not sure if it respects the order of the requests and as it returns a value, it felt like abusing it to just use it to bundle up the requests without any further processing.
My second approach would be to flatmap the requests and use doOnNext to update the UI once, but I'm not certain if this is the correct way.
private void setUpInitialUIState() {
restClient.requestSessionToken()
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
.flatMap(new Func1<SessionTokenResponse, Observable<CurrentPlmnResponse>>() {
#Override
public Observable<CurrentPlmnResponse> call(SessionTokenResponse sessionTokenResponse) {
return restClient.requestCurrentPlmn();
}
})
.doOnNext(new Action1<CurrentPlmnResponse>() {
#Override
public void call(CurrentPlmnResponse currentPlmnResponse) {
if (!currentPlmnResponse.isError()) {
tvProvider.setText(currentPlmnResponse.getData().getFullName());
}
}
})
.flatMap(new Func1<CurrentPlmnResponse, Observable<MonitoringStatusResponse>>() {
#Override
public Observable<MonitoringStatusResponse> call(CurrentPlmnResponse currentPlmnResponse) {
return restClient.requestMonitoringStatus();
}
})
.subscribe(new Subscriber<MonitoringStatusResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
Log.d("onError", throwable.toString());
}
#Override
public void onNext(MonitoringStatusResponse monitoringStatusResponse) {
if (monitoringStatusResponse != null && !monitoringStatusResponse.isError() && monitoringStatusResponse.getData().getSignalIcon() >= 0 && monitoringStatusResponse.getData().getSignalIcon() <= 5) {
ivSignalStrength.setImageResource(getResources().getIdentifier("ic_signal_" + monitoringStatusResponse.getData().getSignalIcon(), "drawable", getPackageName()));
tvNetworkType.setText(getNetworkTypeTitle(monitoringStatusResponse.getData().getCurrentNetworkType()));
}
}
});
}
Depends if you want your 2nd and 3rd calls to be executed in parallel or one after another. If in parallel go for the .zip and don't feel bad about it :)
3 tips on your (current) code (maybe you are aware already or slightly different in your app, so apologies):
Catch the subscription returned from the .subscribe and kill (unsubscribe) at onDestroy the latest. If the app closes the network calls will continue to live.
If .requestCurrentPlmn() is in a thread then the .setText will complain from a touching view from not ui thread exception.
You miss a .onError in your .subscribe. If a request fails, the app will crash.

rxJava monitor task until complete

Here is a use case I am trying to resolve with rxJava and Dagger2 in my android app.
Load recording details
Check backend server if HLS transcode exists (REST Call)
If exists, monitor until process is 100% (REST Call every n seconds until 100%)
If does not exist, don't call monitor process
The REST Calls are injected through a dagger component. I am struggling with setting up rxJava to create a monitor that will refresh the REST Call until the process is 100% and stops, or the user just backs out the screen.
I am not sure I am asking this question in the correct way, so if an update is required, please let me know.
Here is a link to my presenter on github repo. This loads the data and needs to trigger the updates back to the fragment that is responsible for displaying data.
UPDATE: 2015-10-26 PM
I know this is probably a hack, but this is how I implemented the repeating delayed calls:
#Override
protected Observable buildUseCaseObservable() {
Action1<List<LiveStreamInfo>> onNextAction = new Action1<List<LiveStreamInfo>>() {
#Override
public void call( List<LiveStreamInfo> liveStreamInfos ) {
try {
Thread.sleep( 5000 );
} catch( InterruptedException e ) { }
}
};
return this.contentRepository.liveStreamInfos( this.filename )
.repeat( Schedulers.io() )
.doOnNext( onNextAction );
}
Then, in the call method that establishes a subsriber:
private void getProgramDetails() {
this.getProgramDetailsUseCase.execute(new ProgramDetailsSubscriber());
}
And the subscriber:
private final class LiveStreamInfosListSubscriber extends DefaultSubscriber<List<LiveStreamInfo>> {
#Override
public void onCompleted() {
...
}
#Override
public void onError( Throwable e ) {
...
}
#Override
public void onNext( List<LiveStreamInfo> liveStreamInfos ) {
if( null != liveStreamInfos && !liveStreamInfos.isEmpty() ) {
ProgramDetailsPresenter.this.showLiveStreamDetailsInView( liveStreamInfos.get( 0 ) );
if( liveStreamInfos.get( 0 ).getPercentComplete() == 100 ) {
ProgramDetailsPresenter.this.getLiveStreamsListUseCase.unsubscribe();
}
}
}
}
The subscriber will unsubscribe from the observable once the percent complete reaches 100%, cancelling all future call. The benefit here is that this subscriber fires when a user initiates the transcode, creating the live stream, from within the app, or it picks it up from the backend is it is initiated from the backend web interface.
How about adding .retry() with how often you want to retry and a large value for the number of retries to your rx observer. Then just unsubscribe from your source observable when exiting your fragment to stop the polling.

Categories

Resources