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

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

Related

How to create a base thread class to run method async with thread

I want to create Base class to send any function to it to call with a thread.
Base on AsyncTask functionality, I can't use that because I will call a thread in another thread not only from Main Activity.
here is my sample code
in
Android:
public class BaseThread {
public static Thread call(Function threadMethod)
{
Thread thread = new Thread()
{
#Override
public void run()
{
threadMethod();
}
};
thread.start();
return thread;
}
public static Thread Call(Function threadMethod, Object parameter)
{
Thread thread = new Thread(threadMethod);
thread.start();
return thread;
}
public static void sleep(int millisecondsTimeout)
{
try {
Thread.sleep(millisecondsTimeout);
}
catch (InterruptedException ex)
{
///TODO Handle Exception and Logging
}
}
}
example in c#:
internal class BaseThread
{
public static Thread Call(ThreadStart threadMethod)
{
Thread thread = new Thread(threadMethod);
thread.Start();
return thread;
}
public static Thread Call(ParameterizedThreadStart threadMethod, object parameter)
{
Thread thread = new Thread(threadMethod);
thread.Start(parameter);
return thread;
}
public static void Sleep(int millisecondsTimeout)
{
Thread.Sleep(millisecondsTimeout);
}
}
which type of variable should set in method instead of this line :
call(Function threadMethod)
And whats best practice instead of this base class?
Actually Thread can be considered as a "base thread class" because you can achieve such behavior with putting Runnable/Callable implementation into constructor. Put logic into run method of runnable. But I highly recommend you not to use pure threads and take a look at ExecutorService. Good luck.
Android has a very handy class HandlerThread. You can make your custom thread and publish Runnables to it. It has a embedded queue of Messages, every one of which is handler sequentially.

How can I run a specific type of task off the UI thread?

I want to run a function whenever a recyclerview binds a view. It's a really long operation, so I have to keep it off the UI thread. I know how to create a thread, but how can I use the same thread to always run the content of the onBindViewHolder method on the second thread without creating a new one every time the specific function gets executed ?
#Override
public void onBindViewHolder (final Adapter adapter, RecyclerView.ViewHolder holder, final int position) {
new Thread(new Runnable() {
#Override
public void run () {
theMethod(adapter, holder, position);
}
}).start();
}
Consider using a HandlerThread, then scheduling work against that thread with a Handler created with the HandlerThread's Looper. You will have to remember the shut down the thread at the end of the activity, of course, or it will stick around indefinitely and maybe leak objects.
For example:
HandlerThread thread = new HandlerThread("Give Me a Name");
thread.start();
Looper looper = thread.getLooper();
Handler handler = new Handler(mServiceLooper);
// use handler to post work to the thread
These lines of code are cribbed directly from IntentService, which does exactly the same thing you want, except in a service. Maybe that's even what you actually want to use!
Create new thread which can do jobs with a queue.
class YourThread extends Thread {
public List<Job> queue = new ArrayList<>();
#Override
public void run(){
while(queue.size() == 0)
wait(); // nothing to do, wait...
Job job = queue.get(queue.size() - 1); // pop queue
queue.remove(queue.size() - 1);
dosomething(job);
}
public void addJob(....){
...
queue.add(job, 0);
}
}
and onBindViewHolder
YourThread thread = new ...
thread.start()
#Override
public void onBindViewHolder (final Adapter adapter, RecyclerView.ViewHolder holder, final int position) {
thread.addJob(...);
// remember that after the job finished, the item may be recycled
}
You can achieve it by using Looper. The main purpose of Looper is this only, to keep the thread alive and waiting for new messages(Runnables).
Here's a sample code from the official documentation :
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
Now, you can post new runnables (messages) to the thread using the handler. Something like this :
handler.post(new Runnable()
{
public void run() {
//This will be executed on thread using Looper.
}
});
Edit :
The method provided above is more of 'do-it-yourself'. There's a special class in android that can do this automatically for you, i.e. HandlerThread. Sample code :
HandlerThread thread = new HandlerThread("name");
thread.start();
Looper looper = thread.getLooper();
Handler handler = new Handler(looper);
Now you can post runnables to this handler.

android Looper and ui thread

I'm investigating cross platform library published by dropbox.
following code is from it.
I have questions
(1)it makes handler which is connected with main looper.
I have heard that this kind of way we can create ui thread handler.
but does it has any relation with original ui thread(Activity ui thread)
or it really creates another seperated ui threads?
if there are 2 ui threads, then it it possible that one ui thread access another ui components and modify its ui?
public class AndroidEventLoop extends EventLoop
{
Handler mHandler;
public AndroidEventLoop()
{
mHandler = new Handler(Looper.getMainLooper());
}
public void post(final AsyncTask task)
{
mHandler.post(new Runnable()
{
#Override
public void run()
{
task.execute();
}
});
}
}
public abstract class EventLoop {
public abstract void post(AsyncTask task);
}
and it called in Activity
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
EventLoop mainThread = new AndroidEventLoop();
No it's not creating a second main thread. All tasks that are posted to AndroidEventLoop are processed on the Main Thread.
This for example, enables you to do changes in the UI after data was processed on a background thread.
http://developer.android.com/reference/android/os/Looper.html#getMainLooper()
https://blog.nikitaog.me/2014/10/11/android-looper-handler-handlerthread-i/

Android - Running a delayed task from a Worker Thread (NotificationListenerService Thread)

I need to call a delayed method(runnable) from the NLService thread. However the method never gets called. I would appreciate any help.
public class NLService extends NotificationListenerService {
#Override
public void onNotificationPosted(StatusBarNotification sbn) {
if(sbn.getPackageName().contains("mv.purple.aa")){
AudioManager amanager=(AudioManager)getSystemService(Context.AUDIO_SERVICE);
amanager.setStreamMute(AudioManager.STREAM_NOTIFICATION, true);
//This is the code I am having issues with.
//I used this code to call the method. However it is not working.
private Handler handler = new Handler();
handler.postDelayed(runnable, 100);
}
}
//I want to call the following method
private Runnable runnable = new Runnable() {
#Override
public void run() {
foobar();
}
};
}
The NotificationListenerService is a service which gets activated when notifications are posted within the framework. It does this via a Binder notification internal to the framework, so your onNotificationPosted() callback is being called from one of the binder pool threads, not the usual main thread of your app. In essence, the Handler you are creating is associating itself with a Looper which never gets called because the thread is managed by the internal binder framework rather than the usual main thread or other thread you may create.
Try this: create a HandlerThread the first time your callback is hit (and save it off) and start it. Toss your Runnable over to a Handler you create which is bound to the Looper in the HandlerThread.
There is also a "simpler" solution.
You can create a new Handler inside your onCreate(). Save it as class variable and call it when ever you want again.
Example:
public class NotificationListener extends NotificationListenerService
private mHandler handler;
public void onCreate() {
super.onCreate();
handler = new Handler();
}
#Override
public void onNotificationPosted(StatusBarNotification statusBarNotification) {
handler.postDelayed(new Runnable() {
#Override
public void run() {
// Do something special here :)
}
}, 5*1000);
}
....
// Override other importand methods
....
}

Android unit tests with multiple threads

I have a problem with unit tests in Android.
My object MyObject has a method start() like this :
public void start() {
final Handler onStartHandler = new Handler();
new Thread() {
#Override
public void run() {
super.run();
onStartHandler.post(new Runnable() {
#Override
public void run() {
mIsRunning = true;
onStart();
}
});
}
}.start();
}
And I want to test that onStart() is called.
So I tried something like that :
public void testOnStartIsCalled() {
assertFalse("onStart() should not be called", mMyObject.isRunning());
mMyObject.start();
assertTrue("onStart() should be called", mMyObject.isRunning());
mMyObject.stop();
assertFalse("onStop() should be called", mMyObject.isRunning());
}
But it doesn't work, I guess it's because it's in a Handler and a new Thread.
My test class extends AndroidTestCase.
What should I do ? What is the best practice for this case ?
Regards.
When I deal with testing some multi-threaded code I try to let the program take as much of its natural flow as possible. Additionally, I avoid the use of sleep statements since you don't get any guarantees that the sleep interval you've chosen is enough to allow the subject of your test to finish what it's doing; you often end up having to choose sleep intervals that are too large and it forces a much slower execution of your test cases.
I would recommend that you try to add some code into the class you're testing, in this case MyObject, which call a listener whenever something happens. It seems that you already have callback methods for onStart() and onStop()(if those are events/callbacks), so those should be getting invoked and you should use them to control the flow of your test. When you get an onStart() event, you should then call stop() and wait for an onStop() event.
Update
First and foremost, you have redundant code:
public void start() {
final Handler onStartHandler = new Handler();
new Thread() {
#Override
public void run() {
super.run();
onStartHandler.post(new Runnable() {
#Override
public void run() {
mIsRunning = true;
onStart();
}
});
}
}.start();
}
Either start a new thread to call onStart() or schedule the runnable on the Handler's thread queue.
Version 1- remove the handler and just let the code be executed in a new thread:
public void start() {
new Thread() {
#Override
public void run() {
super.run();
mIsRunning = true;
onStart();
}
}.start();
}
Version 2- only use the handler to asynchronously execute the callback:
public void start() {
final Handler onStartHandler = new Handler();
onStartHandler.post(new Runnable() {
#Override
public void run() {
mIsRunning = true;
onStart();
}
});
}
And second: I noticed is that if you don't have a Looper, then whatever you post with the Handler will be ignored (thus it will never be called). For more information on the Looper-Handler pattern see the article: Android Guts: Intro to Loopers and Handlers. The Looper and the Handler are supposed to be attached to the same thread (usually the main thread). Additionally, if you're creating the Handler on a separate thread as your Looper, then you'll run into the same problem: anything you post with the Handler will be ignored.
Here are a few more good questions and articles on loopers and handlers:
Just do IT: looper and handler in android
Handler-Looper implementation in Android
The relationships between Looper, Handler and MessageQueue is shown below:
The problem here is that you are calling onStart() which invokes a new thread, and then immediately ask if it is started. There is startup time for the new thread and while that is happening, your test is asking if it is started -- it's not YET.
I bet if you waited by using Thread.sleep(), or a loop, you'd find it is started "eventually".
What is it you're actually trying to test?
If you need the new thread, you might want to read up on threads, synchronize, etc.
http://developer.android.com/guide/topics/fundamentals/processes-and-threads.html

Categories

Resources