RxJava - use it for global jobs in a save way - android

If I want something to run and finish regardless of a lifecycle and subscribers, is following the correct way to do it?
I create a singleton and run a "Job" from it like following:
public void clearImageCache() {
Single.fromCallable(() -> {
ImageManager.clearCacheFromBackground();
return true;
}
)
.observeOn(Schedulers.io())
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe(ignored -> {
// finished
}, error -> {
// error
});
}
Is the above way of using RxJava for background work that is independent from activities and lifecycles fine? I want it to finish in any case. I think this should be save and should finish, if the application is not destroyed. Is it ok to do it like this or am I missing something here that I should take care of?
I'm not talking care of the returned Disposable but I think that's fine as I never want to unsubscribe.

This will work fine until your app is not killed from the memory i.e. swiped from recent tasks, forced stop or killed by os. Also, you should subscribeOn on Schedulers.io() as it will take load of main thread and keep the UI smooth.

Related

RxJava, What happened if I don't call dispose?

My Android app needs to support to upload a big file, but I do not want a user wait until the upload is completed.
Usually, when I use Observable, I call dispose() for it when the view is destroyed.
But in uploading case, I can not dispose it in any case until it finished.
So I was thinking to try to like this,
private val compositeDisposable: CompositeDisposable = CompositeDisposable()
fun upload() {
val disposable = Observable.just(true).delay(20, TimeUnit.SECONDS).subscribe({
Log.d("=>", "Upload finished")
disposeUploader()
})
compositeDisposable.add(disposable)
}
fun disposeUploader() {
compositeDisposable.clear()
compositeDisposable.dispose()
}
But the problem is the upload() could be called multiple times, so the first uploader will dispose of all other processing calls.
What happened if I don't call dispose? or is there any way dispose of itself when it is completed?
The idea of disposing an Observable serves two purposes:
1) Avoid memory leaks - detach view references that might be held by ongoing request module.
2) Release resources - stop ongoing background task to free unneeded resources, when the user exit your activity for instance, a request/processing might not be relevant anymore, so no point to keep it running.
In your case you want your background task (uploading file) to resume, avoiding (2), while having your view detaching from it (1).
Your solution of disposing after some period of time miss the point as Observable will dispose itself at completion. moreover, you can't assume a specific duration for the upload (at most it is timeout value).
The solution with RxJava is multicasting your Observable using operator like publish and share, in your case:
val multicastedObservable = uploadObservable.publish()
.autoConnect()
.subscriber(//your view related subscriber);
This way, uploadObservable will start execute at first subscribe but will not stop when dispose is called, but rather will detach the reference to view.
Having said all that, it's worth noting that your upload scenario cannot be done reliably without foreground service in Android.

Espresso + Picasso + Spoon

Picasso is using threads for loading images in the background. Even when loading from assets, there'a a slight delay until it shows up, which causes the pictures not to appear on a capture with spoon. I could add a 1s sleep in the test, but I was wondering if there was a better way.
I tried to set a Downloader or a RequestHandler to return the image synchronously, but I think I need to set a ExecutorService that uses the main thread or an AsyncTask (such that espresso will wait). With retrofit, we can use AsyncTask.THREAD_POOL_EXECUTOR with MainThreadExecutor but I'm not sure how to do it for picasso.
As a workaround, I wrapped picasso in an ImageUtil which won't be used during instrumentation:
DebugModule {
#Provide
ImageUtil imageUtil() {
if (isTest) {
return TestImageUtil();
} else {
return PicassoImageUtil();
}
}
}
Any suggestions?
update: in picasso's code, attempting to use an Executor instead of ExecutorService, I got stuck on service.shutdown().
I'm going to give you the general method for waiting on asynchronous tasks using espresso, because you can be sure that this will come up again.
You shouldn't use thread sleeping to wait for things to happen. This can cause flakiness, and causes your tests to be less efficient. Espresso was designed with avoiding sleep calls in mind.
You also shouldn't force something to be on the main thread that isn't normally on the main thread. If this causes an ANR your test could fail because of the unexpected dialog pop-up. There could also be a real bug that only happens while multi-threading, but now that you're forcing something to execute on the main thread, your tests could miss it.
You're on the right track swapping out a different wrapper for Picasso for testing. What you need is a way to hook on to when the request gets started (right before it goes off the main thread), and when the request gets finished. Swapping out a wrapper is one way to do accomplish that.
To get notified of when the request is finished, you can use the callback version of the into method.
Now that you have the entry and exit points of your asynchronous task, you can use the CountingIdlingResource to keep your test from moving on until the task is finished. It's as simple as incrementing the counter before the task starts, and decrementing it when it finishes.
Here's a great example of how to use that class: https://android.googlesource.com/platform/frameworks/testing/+/android-support-test/espresso/sample/src/androidTest/java/android/support/test/testapp/AdvancedSynchronizationTest.java?autodive=0%2F%2F

How to assign different threads to different AsyncTasks, so they do not wait for each other in a line

In my application, when the user clicks "Save product"a long chain of network operations is started in an AsyncTask.
In the meanwhile if the user opens up the Navigation Drawer and clicks a menu item, another AsyncTask is used so that loading the data doesnt block the UI and this way I prevent the navigation drawer from being closed in a snappy manner, instead of smoothly.
The problem is that the second AsyncTask apparently waits for the first one to finish, and THEN it opens the newly selected menu item. For a few seconds after the user has clicked on it, the navigation drawer remains open.
When I switch loading data for the menu item to be opened from an AsyncTask to main thread, the navigation drawer closes instantly (but laggy/snappy, dunno whats the better word)
So how do I assign this AsyncTask (the one that closes the navigation drawer) to another thread so it doesnt wait for the first one to finish off before it starts? Or is there a way to instruct both to run parallely?
From the Android documentation:
Order of execution
When first introduced, AsyncTasks were executed serially on a single background thread. Starting with DONUT, this was changed to a pool of threads allowing multiple tasks to operate in parallel. Starting with HONEYCOMB, tasks are executed on a single thread to avoid common application errors caused by parallel execution.
If you truly want parallel execution, you can invoke executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.
http://developer.android.com/reference/android/os/AsyncTask.html
In general if you want to do a lot of threading or long running operations AyncTasks are not recommended. You are better off using a ThreadPoolExecutor or ScheduledThreadPoolExecutor
As Tim B suggested, THREAD_POOL_EXECUTOR will do the trick for you:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
} else {
asyncTask.execute(params);
}
My recommendation is to use an AsyncTaskLoader instead of an AsyncTask.
AsyncTasks have a couple of issues:
An AsyncTask runs regardless of the Activity/Fragment life cycle. Pausing an Activity doesn’t pause the AsyncTask so navigating between Activities might leave a "trail" of running AsyncTasks (bad for performance, battery and responsiveness).
Configuration changes (especially orientation changes) are problematic since the AsyncTask has no way to update the ui after such a change (the Activity has likely been destroyed) and the newly created Activity has no way to "find" an already started and maybe still running AsyncTask.
AsyncTasks have IMO a major design flaw because they do background processing and also update the ui (in onPostExecute). The ui is part of an Activity/Fragment and its life cycle and only those should have access to ui elements and be able to modify/update them. By delegating ui updates to a component (the AsyncTask) that is independent of the ui/Activity life cycle, one will inevitably run into problems.
All these issues are obsolete with Loaders (http://developer.android.com/reference/android/content/AsyncTaskLoader.html). The main difference IMO is that they do background processing only and report back once they finished to let the Activity/Fragment do the ui part. They also handle configuration changes gracefully, meaning the ui part (Activity/Fragment) and the Loader can "reconnect" after such a change, no need to start a new Loader after a screen rotation. Some implementations (namely CursorLoader) are also able to monitor changes to the underlying data and re-query automatically (not so relevant for this question though).
Last but not least (to answer the original question), starting a new AsyncTaskLoader never blocks the running thread:
LoaderManager loaderMgr = getLoaderManager();
loaderMgr.initLoader(LOADER_ID, null, this);
initLoader is a non-blocking call and will always return immediately.
Here's a most basic example of an AsyncTaskLoader:
LoaderManager loaderMgr = getLoaderManager();
loaderMgr.initLoader(LOADER_ID, null, new LoaderManager.LoaderCallbacks<Result>() {
#Override
public Loader<Result> onCreateLoader(int id, Bundle args) {
return new BackgroundTask(maybe some parameters...);
}
#Override
public void onLoaderReset(Loader<Result> loader) {}
#Override
public void onLoadFinished(Loader<Result> loader, Result data) {
// here we update the ui
}
}
class BackgroundTask extends AsyncTaskLoader<Result> {
public BackgroundTask(Context context) {
super(context);
}
#Override
public Result loadInBackground() {
// do the background processing
}
}
While this looks like a lot of boilerplate code, it's really quite simple and shows nicely the separation of background processing (in the BackgroundTask) and the ui part.
I'm aware that the OP might just look for a quick fix and the other answers provide exactly that. The AsyncTaskLoader on the other hand might be the way to go in the long term.
http://developer.android.com/reference/android/content/AsyncTaskLoader.html

Mono Android: terminate thread in onPause() state

I am running into a strange problem...
My application is meant to do some webservice calls on a separate thread. Once the webservice call is finished, it would navigate user to a different activity.
In the case when user press the home button or exit current activity it should terminate the webservice if the webservice call thread is still running. Hence I put a thread termination method in the OnPause state.
Here is the method block that is running inside the thread:
private Thread _webserviceThread;
void WebserviceCallThread(){
WebRestult result= WebserviceCall();
if(!result.containsError()){
RunOnUIThread(delegate{
transitionToActivityXYZ();
});
}
}
void RunThreadAction(){
_webserviceThread = new Thread(new ThreadStart(WebserviceCallThread));
_webserviceThread.Start();
}
protected override void OnPause(){
if(_webserviceThread != null && _webserviceThread.IsAlive){
_webserviceThread.Abort();
}
}
After the webservice call is done and begin the transition to another page, It gets to the OnPause state. However, in some strange cases, it would think that the thread is not finished in the OnPause state, even though the activity transition is the last line of the method.
Has anyone ran into this problem before? If so, how did you solve this problem?
Thanks!
I always use AsyncTask for this kind of thing. Not only does it abstract away the explicit thread handling and provide hooks to do everything you want here; it's also a nice way to represent a unit of work that can be used from other activities.
There's a simple example in this post part way down, but it doesn't use the generic parameters which are quite handy.
Why not use Task Parallel Library,
It is standard .NET, and with AsyncTask, it is only recommended for tasks that take less than few seconds. see the Documentation
AsyncTasks should ideally be used for short operations (a few seconds
at the most.) If you need to keep threads running for long periods of
time, it is highly recommended you use the various APIs provided by
the java.util.concurrent
Below is an example for how to use Task Parallel Library, taken from here
private void loginWithTaskLibrary()
{
_progressDialog.Show();
Task.Factory
.StartNew(() =>
_loginService.Login("greg")
)
.ContinueWith(task =>
RunOnUiThread(() =>
onSuccessfulLogin()
)
);
}

Restarting/Pausing Thread in onResume/onPause

I'm have a game that's uses SurfaceView implementation to display the objects.
I have a thread which draws the SurfaceView time-to-time to the screen.
The game is running completely.
Unfortunately, it needed to have a pause function whenever the game is interrupted.
Well, I know that I need to manipulate onResume and onPause.
But I can't get it right. The error points me back to surfaceCreated where I start the thread telling me that the thread has started already. I tried using the resume and suspend on the onResume and onPause respectively but nothing changed.
How can I achieve this?
I have already done how the objects location would be save using File-I/O handling.
Thanks in advance.
This is what I did:
#Override
public void surfaceCreated(SurfaceHolder arg0) {
if (thread.getState() == Thread.State.TERMINATED){
CreateThread(getHolder(),getContext());
}
thread.setRunning(true);
thread.start();
}
In CreateThread you should have the thread = new MyThread(...);
the setRunning (boolean mRun) use a boolean to start/stop the run function (I think I was inspired by the LunarLander);
If you want to use properly the onPause/onResume don't put the variables used by your thread inside the thread (as done in LunarLander). I suggest you to do like that:
// Variables declarations
public MyGameThread CreateThread(...){
thread = new MyGameThread(holder, context, new Handler() {
// and so on....
});
}
When you pass through the onPause/onResume, your thread will be destroyed and reneweled but if you put your variables outside it, you can continue to use them after.
If you have something important to preserve, use one of this options:
SharedPreferences: an xml will be created and saved locally with variables that persist even after the end of the app;
a SQL db if you would manage more than 5-10 variables because in this case the use of the former option would be difficult.
Actually it's not recommended to stop a thread by yourself, the stop() method is deprecated. The simplest solution is to use a flag in your while loop inside the thread's run() method. When you need to "stop" the thread, you just drop the flag to false and the thread won't do anything anymore, despite it will keep running. Android will stop your thread when it's needed. Hope this helps.
Without knowing the ins and outs of your code.
To "Pause" a thread you can implement functionality like so:
while(! this.isInterrupted())
if(!paused)
{
... Do something ...
} else { try { Thread.sleep(100) } catch (InteruptedException ie) {} }
This is depending if Do something is invalidating your surface view or otherwise controlling progression in your app. An accessor to paused should allow you to pause and resume your thread without getting caught up in any other bit of architecture.
I'm unsure if you've got one or two threads in this question, I'm assuming 2. You need to do three things when you call onPause:
1 - Save the state of the application (all game variables, states, etc)
2 - Kill the surfaceView by calling suspend.
3 - Kill the other thread (we'll call it Thread B).
Killing of Thread B is your problem I think. You want to interrupt the thread and tell it to quit, or else when you call onPause your thread will still be doing its thing. Then, when you go back into the game, the thread will try to be created again which causes the problem. There are 2 ways to kill a thread properly:
In the while() loop of your thread, have a boolean 'run' which while(run) will execute the code. When you change run to false, the thread exits.
If your thread sleeps (I assume it might do since its a game and will be running w.r.t time), catch the InterruptedException and then quit there. When you want to kill the thread, you throw the exception to the thread.
The first one is by far the easiest.

Categories

Resources