Example communicating with HandlerThread
This is a working example:
HandlerThread ht = new HandlerThread("MySuperAwesomeHandlerThread");
ht.start();
Handler h = new Handler(ht.getLooper()) {
public void handleMessage(Message msg) {
Log.d(TAG, "handleMessage " + msg.what + " in " + Thread.currentThread());
};
};
for (int i = 0; i < 5; i++) {
Log.d(TAG, "sending " + i + " in " + Thread.currentThread());
h.sendEmptyMessageDelayed(i, 3000 + i * 1000);
}
UPDATE:
Make two class fields:
Handler mHtHandler;
Handler mUiHandler;
and try this:
HandlerThread ht = new HandlerThread("MySuperAwsomeHandlerThread");
ht.start();
Callback callback = new Callback() {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == 0) {
Log.d(TAG, "got a meaasage in " + Thread.currentThread() + ", now sleeping... ");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "woke up, notifying ui thread...");
mUiHandler.sendEmptyMessage(1);
} else
if (msg.what == 1) {
Log.d(TAG, "got a notification in " + Thread.currentThread());
}
return false;
}
};
mHtHandler = new Handler(ht.getLooper(), callback);
mUiHandler = new Handler(callback);
mHtHandler.sendEmptyMessageDelayed(0, 3000);
You can of course get rid of Callback
interface and create two Handlers with overridden handleMessage
method...
Best use of HandlerThread over other similar classes
Here is a real life example where HandlerThread becomes handy. When you register for Camera preview frames, you receive them in onPreviewFrame()
callback. The documentation explains that This callback is invoked on the event thread open(int) was called from.
Usually, this means that the callback will be invoked on the main (UI) thread. Thus, the task of dealing with the huge pixel arrays may get stuck when menus are opened, animations are animated, or even if statistics in printed on the screen.
The easy solution is to create a new HandlerThread()
and delegate Camera.open()
to this thread (I did it through post(Runnable)
, you don't need to implement Handler.Callback
).
Note that all other work with the Camera can be done as usual, you don't have to delegate Camera.startPreview()
or Camera.setPreviewCallback()
to the HandlerThread. To be on the safe side, I wait for the actual Camera.open(int)
to complete before I continue on the main thread (or whatever thread was used to call Camera.open()
before the change).
So, if you start with code
try {
mCamera = Camera.open(1);
}
catch (RuntimeException e) {
Log.e(LOG_TAG, "failed to open front camera");
}
// some code that uses mCamera immediately
first extract it as is into a private method:
private void oldOpenCamera() {
try {
mCamera = Camera.open(1);
}
catch (RuntimeException e) {
Log.e(LOG_TAG, "failed to open front camera");
}
}
and instead of calling oldOpenCamera()
simply use newOpencamera()
:
private void newOpenCamera() {
if (mThread == null) {
mThread = new CameraHandlerThread();
}
synchronized (mThread) {
mThread.openCamera();
}
}
private CameraHandlerThread mThread = null;
private static class CameraHandlerThread extends HandlerThread {
Handler mHandler = null;
CameraHandlerThread() {
super("CameraHandlerThread");
start();
mHandler = new Handler(getLooper());
}
synchronized void notifyCameraOpened() {
notify();
}
void openCamera() {
mHandler.post(new Runnable() {
@Override
public void run() {
oldOpenCamera();
notifyCameraOpened();
}
});
try {
wait();
}
catch (InterruptedException e) {
Log.w(LOG_TAG, "wait was interrupted");
}
}
}
Note that the whole notify() -- wait() inter-thread communication is not necessary if you don't access mCamera in the original code immediately after opening it.
Update: Here the same approach is applied to accelerometer: Acclerometer Sensor in Separate Thread
Worker Thread to Worker Thread Communication
public class ModelFragment extends Fragment implements Handler.Callback {
Handler backHandler1, backHandler2, mainHandler;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
HandlerThread backThread1 = new HandlerThread("BACK_THREAD_1");
backThread1.start();
backHandler1 = new Handler(backThread1.getLooper(), this);
HandlerThread backThread2 = new HandlerThread("BACK_THREAD_2");
backThread2.start();
backHandler2 = new Handler(backThread2.getLooper(), this);
mainHandler = new Handler(Looper.getMainLooper(), this);
// sending message from main thread to thread 1
backHandler1.obtainMessage(BACK1_WHAT, backObj).sendToTarget();
}
@Override
public boolean handleMessage(Message msg) {
switch(msg.what){
case BACK1_WHAT:
// this code runs on thread 1
// sending message to thread 2 from thread 1
backHandler2.obtainMessage(BACK2_WHAT, backObj1).sendToTarget();
return true;
case BACK2_WHAT:
// this code runs on thread 2
// sending message to thread 1 from thread 2
backHandler1.obtainMessage(BACK1_WHAT, backObj2).sendToTarget();
// sending message to main thread from thread 2
mainHandler.obtainMessage(MAIN_WHAT, backObj2).sendToTarget();
return true;
case MAIN_WHAT:
// runs on ui thread
return true;
}
return false;
}
@Override
public void onDestroy() {
if(null != backHandler1) {
backHandler1.removeCallbacksAndMessages(null);
if(null != backHandler1.getLooper())
backHandler1.getLooper().quit();
}
if(null != backHandler2) {
backHandler2.removeCallbacksAndMessages(null);
if(null != backHandler2.getLooper())
backHandler2.getLooper().quit();
}
super.onDestroy();
}
}
Android: how to communicate from worker thread to a service
It looks like I've finally nailed it!
In order to pass data from thread back to a service you will need to do this:
Subclass a
Handler
class inside of your service (call it e.g. aLocalHandler
). You will have to make it static. Override ahandleMessage
method, it will receive messages from the thread.Add a
Handler
argument to yourThread
constructor. Instantiate yourLocalHandler
class in a service and inject it to your thread via constructor.Save reference to the
Handler
inside of your thread and use it to send messages whenever appropriate.
Here's the complete example:
Service Class
package com.example.app;
import android.app.Service;
import android.content.Intent;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
public class ConnectionService extends Service {
protected ConnectionWorker thread;
static class LocalHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Log.d(this.getClass().getName(), "Message received: " + (String) msg.obj);
}
}
protected Handler handler;
@Override
public void onCreate() {
super.onCreate();
// Instantiating overloaded handler.
this.handler = new LocalHandler();
// Creating a connection worker thread instance.
// Not starting it yet.
// Injecting our handler.
this.thread = new ConnectionWorker(this.handler);
Log.d(this.getClass().getName(), "Service created");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(this.getClass().getName(), "Trying to start the service");
// Checking if worker thread is already running.
if (!this.thread.isAlive()) {
Log.d(this.getClass().getName(), "Starting working thread");
// Starting the worker thread.
this.thread.start();
Log.d(this.getClass().getName(), "Service started");
}
return Service.START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
Log.d(this.getClass().getName(), "Stopping thread");
// Stopping the thread.
this.thread.interrupt();
Log.d(this.getClass().getName(), "Stopping service");
super.onDestroy();
Log.d(this.getClass().getName(), "Service destroyed");
}
}
Worker Class (Thread)
package com.example.app;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.Log;
public class ConnectionWorker extends Thread {
// Reference to service's handler.
protected Handler handler;
public ConnectionWorker(Handler handler) {
super(ConnectionWorker.class.getName());
// Saving injected reference.
this.handler = handler;
}
@Override
public void run() {
super.run();
Log.d(this.getClass().getName(), "Thread started");
// Doing the work indefinitely.
while (true) {
if (this.isInterrupted()) {
// Terminating this method when thread is interrupted.
return;
}
Log.d(this.getClass().getName(), "Doing some work for 3 seconds...");
// Sending a message back to the service via handler.
Message message = this.handler.obtainMessage();
message.obj = "Waiting for 3 seconds";
this.handler.sendMessage(message);
SystemClock.sleep(3 * 1000);
}
}
}
I hope it's a valid implementation. If you now a better approach - please let me know.
How to correctly use the HandlerThread with a socket?
Think of HandlerThread
as a worker thread that has a Looper
waiting for messages to execute (which can be spawning a new Thread
). So to communicate with it, just prepare a Message
object and dispatch it to the handler (e.g. you can start a new thread
for socket reading ) e.g.
Handler handler = new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
// Do action based on this message
}
}
Typically HandlerThread
is handy when you are listening to events
that require a Looper
e.g. requestLocationUpdates
.
You do not need to use a HandlerThread
to avoid NetworkOnMainThreadException
(I do not know how you get this exception if you are using Thread
as per your question ) and to use it with sockets
, just use a normal thread and do your socket
operation in the run
method
Android: posting message to HandlerThread makes UI thread unresponsive or gives IllegalStateException
Got it. Obsiously the situation about recycling messages is this:
If you send it to a handler, the handler/looper will recycle it for you.
So one must not recycle the message within handleMessage
.
HandlerThread.getLooper() blocks indefinitely
on the MediaPlayerThread you need to call the super method for run:
public void run (){
mMediaPlayer = new MediaPlayer();
super.run();
}
Important things are executed in its parent class run method. More specifically the looper is created and if you call getLooper() and looper is not created it will wait for its creation.
Related Topics
No Generated R.Java File in My Project
Android 2.1 Programmatically Unmount Sdcard
How to Create a Table with Borders in Android
Actionbaractivity Is Deprecated
How to Use a Custom Typeface in a Widget
How to Hide an Item from Recycler View on a Particular Condition
Build and Install Unsigned APK on Device Without the Development Server
How to Set Layout_Gravity Programmatically
How to Disable the Clear Data Button in Application Info of Manage Application
Android - How to Set the Wallpaper Image
How to Get the APK of an Installed App Without Root Access
Change the Color of a Checked Menu Item in a Navigation Drawer
Permission from Manifest Doesn't Work in Android 6
How to Handle the Click Event in Listview in Android
Manifest Merger Failed:Attribute Application@Appcomponentfactory - Androidx