How to use IntentService thread using RxJava2 - android

I faced with the next situation. I start my code in onHadleIntent method and some part of the code is worked in IntentService thread but the Observable.zip method in getInfoAboutUser() is worked in RxJava thread.
#Override
protected void onHandleIntent(#Nullable Intent intent) {
LOG.debug(Thread.currentThread().getName());
Call<String> call= mRepository.getInfo();
try {
retrofit2.Response<String> response = call.execute();
if (response.isSuccessful()) {
LOG.debug("Response body "+Thread.currentThread().getName());
getInfoAboutUser();
}
}catch(){}
}
public void getInfoAboutUser(){
LOG.debug("getInfoAboutUser "+Thread.currentThread().getName());
Executor e = new Executor() {
#Override
public void execute(#NonNull Runnable runnable) {
LOG.debug(" executor thread");
runnable.run();
}
};
Observable.zip(
Observable.fromIterable(array),
Observable
.interval((mRandom.nextInt(7)+5) * 1000,
TimeUnit.MILLISECONDS,Schedulers.from(e))
.take(array.size()),
new BiFunction<String, Long, String>() {
#Override
public String apply(String s, Long aLong) throws Exception {
LOG.debug("Result "+Thread.currentThread().getName());
return s;
}
}
).flatMapMaybe(new Function<String, MaybeSource<String>>() {
#Override
public MaybeSource<String> apply(String s) throws Exception {
return mRepository.getInfoAboutUser(s);
}
}).subscribeWith(new DisposableObserver<String>() {})
}
mRepository.getInfo() and mRepository.getInfoAboutUser(s) methods I am using without subscribeOn and observeOn!
My log is:
IntentService thread
IntentService thread Response body
IntentService thread getInfoAboutUser
RxSingleScheduler-1 executor thread
RxSingleScheduler-1 Result
etc
What shall i do to use IntentService thread for Observable.zip and Interval methods? I need only IntentService thread

Schedulers.from(e) wraps the Executor in the internal class ExecutorScheduler. If a task is scheduled with a delay and the given Executor is not a ScheduledExecutorService then it will use an internal scheduler to delay the call to e.execute() until the delay is finished.
Because your executor simply immediately executes, it ends up executing on the helper scheduler which is RxSingleScheduler-1.
To solve this you need to choose from the following solutions:
Create a ScheduledExecutorService which correctly dispatches the runnables to the IntentService.
Create a custom Scheduler which dispatches the runnables to the IntentService's Looper.
Use the RxAndroid library to do 2. for you. AndroidSchedulers.from(Looper.myLooper()) will create a scheduler for the IntentService.
Edit: Please do note that IntentService and asynchronous operations don't mix. The service will terminate when handleIntent returns so it is not the a good
way to execute delayed operations like Observable.interval.

Related

Is it safe to use Looper.getMainLooper() in WorkManager?

This is how I used WorkManager. As you can see, I use Handler in Worker class. Handler is now deprecated since Android 11. Now we should use Handler(Looper) and I know very little about Loopers. I wonder if it's safe to use new Handler(Looper.getMainLooper()) in Worker class, what exactly is Looper, and what types of Looper exist other then main Looper.
public class MyWorker extends Worker {
#Override
public Worker.WorkerResult doWork() {
new Handler().post(new Runnable() {
#Override
public void run () {
//Do Something
}});
return WorkerResult.SUCCESS;
}
}

Threading rules AsyncTask

AsyncTask has 5 threads rules:
There are a few threading rules that must be followed for this class
to work properly:
The AsyncTask class must be loaded on the UI thread. This is done
automatically as of Build.VERSION_CODES.JELLY_BEAN.
The task instance must be created on the UI thread.
execute(Params...) must be invoked on the UI thread.
Do not call onPreExecute(), onPostExecute(Result),
doInBackground(Params...), onProgressUpdate(Progress...) manually.
The task can be executed only once (an exception will be thrown if a
second execution is attempted.)
However, I didn't understand rules 2 and 3 very well. I've tried them on the following code:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
log(Thread.currentThread().getName());
new Thread(new Runnable() {
#Override
public void run() {
log(Thread.currentThread().getName());
Task task = new Task();
task.execute();
}
}).start();
}
public class Task extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... voids) {
return null;
}
}
And this is the result:
09-15 21:27:10.179 3310-3310/com.xxx.test D/com.xxx.test.MainActivity: main
09-15 21:27:10.179 3310-3329/com.xxx.test D/com.xxx.test.MainActivity: Thread-264
I have a question:
Why can I create the task instance and call the execute() method in another thread(Thread-264) besides the UI Thread (main)?
I read this post, but it don't explain why. Thanks so much!
From Android official site
An asynchronous task is defined by a computation that runs on a
background thread and whose result is published on the UI thread.
There are some points that we need to clarify.
Our computation will be ran on a background thread
Result of the computation will be published on the UI thread
They do not prevent developers from creating or invoking AsyncTask from non-UI threads
Step 1: When you call
Task task = new Task();
Take a look into AsyncTask source code.
public AsyncTask(#Nullable Looper callbackLooper) {
mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
? getMainHandler()
: new Handler(callbackLooper);
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
mFuture = new FutureTask<Result>(mWorker) {
#Override
protected void done() {
try {
postResultIfNotInvoked(get());
} catch (InterruptedException e) {
android.util.Log.w(LOG_TAG, e);
} catch (ExecutionException e) {
throw new RuntimeException("An error occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
First they create a handler which refer to handler of the UI thread, then create a Runnable which call doInBackground method (our computation here) and then returns a Future (will return result of the computation some point in the future).
Step 2: Then you call
task.execute();
Take a look into AsyncTask source code.
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute();
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
onPreExecute() will be called on calling thread which invoke the AsyncTask (in this case your anonymous thread). Then it executes the Future in its executor.
After the computation is completed, it will call postResult method.
private Result postResult(Result result) {
#SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}
private static class InternalHandler extends Handler {
public InternalHandler(Looper looper) {
super(looper);
}
#SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
#Override
public void handleMessage(Message msg) {
AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
switch (msg.what) {
case MESSAGE_POST_RESULT:
// There is only one result
result.mTask.finish(result.mData[0]);
break;
case MESSAGE_POST_PROGRESS:
result.mTask.onProgressUpdate(result.mData);
break;
}
}
}
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
getHandler in this case refer to handler of the UI thread, so onPostExecute will always be called on UI thread.
Conclusion:
AsyncTask enables proper and easy use of the UI thread. This class
allows you to perform background operations and publish results on the
UI thread without having to manipulate threads and/or handlers.
There are 3 protect methods that user code can expect them to be run from UI
onPreExecute()
onProgressUpdate(Progress...)
onPostExecute(Result)
Although onPreExecute() will be run on whatever thread calling execute(), the rest 2 methods will be run by Handler.
The Handler class will associate with the thread that create it, it allows user code to post Runable to run on that specific thread. Before AsyncTask came, user code who want update UI (which must be updated on UI thread) would have to create Handler on UI thread first and then post Runable to that Handler to execute their task on UI thread.
AsyncTask was design to simplify those tedious works, their internal static Handler is created by UI/main thread before your AsyncTask instance is created.
Even though you can use AsyncTask (except onPreExecute()) in worker thread, I recommend you to follow documentation and create/run AsyncTask on UI thread.

Serialized incoming aidl calls in one Thread

I have an App (Client) that performs remote calls using AIDL to a second App (Server). Each call to through the Binder is executed in the Server app in a different thread (TID) as designed by AIDL solution.
Is it possible to make all calls executed in the Server app be executed in just one thread? We have control over all callers (Client apps) and they will perform call in a serial mode and we don't need Server app perform the calls in a multithread way.
So, if the Client App 1 performs a remote call to a method that takes 30 seconds and before it, a second Client App 2 performs a call to the same method (or even other method) we want this second call be executed in the same Thread of the first call.
Messenger is not an option for now.
=== Updated ====
Message is not an option (for now). Here more details: We have a service with 2 type of binders: a) TransacionManager (tm) and DAOImpl (dao).
We first do a call to tm.begin() in the client and even its processed synchronously, on the Service side its is executed in a thread from Thread Pool (android aidl code). This thread TID #1 performs the begin transaction command in SQLite database.
Then we do a call to dao.selectNextId() - synchronously - and in the Service it is executed in the TID #2. In the selectNextId() method we check if the database is inTransaction and it returns false.
To confirm that the threads was the problem, we put everything in a single call to another binder (allDAO). So when we call allDAO.do() it runs on the Service side in another thread TID #3 and performs begin transc and insert very well.
Not sure if the problem is SQLite that manage different threads as separated requests (how to deal with)... We just want the Service (using aidl) perform every call from any clients in a same single thread everytime.
I was working with Mario on this issue and using the #pskink's code snippet we solved the multithreading issue.
The issue was solved redirecting all aidl calls to the main thread. To do this, we used a Handler thats receives the MainLooper and a Runnable that extends CountDownLatch.
The code of our solution bellow:
// SyncHandler.class
public class SyncHandler {
private SyncRunnable mRunnable;
public SyncHandler() {
super();
}
public SyncHandler start(#NonNull SyncRunnable runnable) {
mRunnable = runnable;
final Looper looper = Looper.getMainLooper();
Handler handler = new Handler(looper);
handler.post(mRunnable);
try {
mRunnable.await();
} catch (InterruptedException e) {
Log.e(this, "Error when SyncHandler was awaiting.", e);
}
return this;
}
public static class ReturnValue<T> {
public T value;
}
}
// SyncRunnable.class
public final class SyncRunnable extends CountDownLatch implements Runnable {
private Runnable mRunnable;
public static SyncRunnable create(Runnable runnable) {
return new SyncRunnable(runnable);
}
private SyncRunnable(Runnable runnable) {
super(1);
mRunnable = runnable;
}
#Override
public void run() {
Log.d(this, "SyncRunnable.run() executed on thread: " + Thread.currentThread());
mRunnable.run();
countDown();
}
}
//And the database call:
// TransactionManager.class
public synchronized void begin(final int ownerHashCode, String ownerName) throws RemoteException {
SyncHandler handler = new SyncHandler().start(SyncRunnable.create(new Runnable() {
#Override
public void run() {
if (mOwner == null) {
mOwner = ownerHashCode;
for (Database database : mDatabases) {
database.beginTransaction();
}
} else if (mOwner == ownerHashCode) {
throw new DbTransactionException("Error: TransactionOwner == owner");
}
}
}));
}
// DaoHelper.class
public synchronized long insert(Dao dao) {
final SyncHandler.ReturnValue<Long> value = new SyncHandler.ReturnValue<>();
SyncHandler handler = new SyncHandler().start(SyncRunnable.create(new Runnable() {
#Override
public void run() {
Log.d(DaoHelper.this, "db.inTransaction: " + mManagerDb.getDatabase().inTransaction());
value.value = mManagerDb.getDatabase().insert(mTable, null, mContentValues);
}
}));
return value.value;
}

How to cancel a synchronized method of a library?

In an android project of mine I'm using the jamod library for Modbus communication and it works alright. Except when the network connection isn't available then my asynchronous task will be stuck on transaction.execute for seconds. I would like to cancel it after a given timeout. I tried to implement a handler for this to cancel the asynchronous task:
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
if (task.getStatus() == AsyncTask.Status.RUNNING
|| task.getStatus() == AsyncTask.Status.PENDING) {
task.cancel(true);
}
}
}, WRITE_TASK_TIMOUT);
But this approach won't work. I looked into the library and saw that the ModbusTCPTransaction.execute() method uses synchronized().
Does this mean i have no chance to cancel this method after x seconds?
You could try to use Android class Future. It has method to set the timeout period of the thread execution.
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new Task());
future.get(5, TimeUnit.SECONDS);
static class Task implements Callable<String> {
#Override
public String call() throws Exception {
//ModbusTCPTransaction.execute()
return null;
}
}

Android: Possible for background thread to block until UI thread finishes operation?

Is it possible for a background thread to enqueue a message to the main UI thread's handler and block until that message has been serviced?
The context for this is that I would like my remote service to service each published operation off its main UI thread, instead of the threadpool thread from which it received the IPC request.
This should do what you need. It uses notify() and wait() with a known object to make this method synchronous in nature. Anything inside of run() will run on the UI thread and will return control to doSomething() once finished. This will of course put the calling thread to sleep.
public void doSomething(MyObject thing) {
String sync = "";
class DoInBackground implements Runnable {
MyObject thing;
String sync;
public DoInBackground(MyObject thing, String sync) {
this.thing = thing;
this.sync = sync;
}
#Override
public void run() {
synchronized (sync) {
methodToDoSomething(thing); //does in background
sync.notify(); // alerts previous thread to wake
}
}
}
DoInBackground down = new DoInBackground(thing, sync);
synchronized (sync) {
try {
Activity activity = getFromSomewhere();
activity.runOnUiThread(down);
sync.wait(); //Blocks until task is completed
} catch (InterruptedException e) {
Log.e("PlaylistControl", "Error in up vote", e);
}
}
}

Categories

Resources