Android send data from main UI thread to another thread
android, android-asynctask, java, multithreading, sockets
Solution
You can't do it this way. You've created your second Thread by using `HandlerThread`. A `HandlerThread` is a `Thread` that has a `Looper`. So that is what is going on in the `run()` method of `HandlerThread`. It is running the looper loop. That means that the `run()` method in the `HandlerThread` will only complete when the `Looper` exits.
In your `initThread()` method you wrote:
@Override
public void run() {
super.run(); // <-- This call runs the Looper loop and doesn't complete!!
try{
handler = new WorkerHandler(getLooper());
}catch(Exception e){
e.printStackTrace();
}
}
You can see that your overridden `run()` method first calls `super.run()`. This runs the looper loop and doesn't complete. So the rest of your code in `initThread()` never executes.
If you want to use a `HandlerThread()` then you can't mess with its `run()` method. If you want it to do stuff for you then you'll need to post messages (or `Runnable`s) to it, and do your work there. Here's an example:
HandlerThread handlerThread = new HandlerThread("myHandlerThread");
handlerThread.start();
// Now get the Looper from the HandlerThread so that we can create a Handler that is attached to
// the HandlerThread
// NOTE: This call will block until the HandlerThread gets control and initializes its Looper
Looper looper = handlerThread.getLooper();
// Create a handler attached to the background message processing thread
handler = new Handler(looper, this);
Now you can post messages and `Runnable`s to the "handler". In this example, the messages will be processed by an overridden `handleMessage()` method of the creating class.
EDIT: Provide code example for the Handler callback
You can use your `WorkerHandler` class to handle the callbacks if you modify it like this (I've changed the name to `Worker` because it isn't really a `Handler`, it just implements the `Handler.Callback` interface):
public class Worker implements Handler.Callback {
protected Socket socket;
protected BufferedWriter writer;
public Worker() throws Exception{
this.socket = new Socket("192.168.1.7", 5069);
this.writer = new BufferedWriter(new OutputStreamWriter(this.socket.getOutputStream(), "utf-8"));
}
public BufferedWriter getWriter(){
return this.writer;
}
public Socket getSocket(){
return this.socket;
}
@Override
public void handleMessage(Message msg) {
Draw draw = (Draw) msg.obj;
if (draw != null){
if (getWriter() != null){
try{
getWriter().write(DrawUtil.toJson(draw)+"\n");
getWriter().flush();
}catch(IOException e){
e.printStackTrace();
}
}
}
}
}
Now you need to create an instance of this `Worker` class and pass it as the callback argument when you create the `Handler`. In your activity do:
HandlerThread handlerThread = new HandlerThread("myHandlerThread");
handlerThread.start();
Looper looper = handlerThread.getLooper();
// Create an instance of the class that will handle the messages that are posted
// to the Handler
Worker worker = new Worker();
// Create a Handler and give it the worker instance to handle the messages
handler = new Handler(looper, worker);
Problem
I need to send some data from the main thread to another thread. I've already read a lot of materials on threads, asynctasks and handlers but maybe they created some confusion to me. I read that I need to create a Handler to my 'second thread' so that I can send messages from the main thread to it (for now I'm not worrying about send back anything to the main thread). I need the second thread to connect to a server (via socket) and send some date on some user events. I'm trying to do it in an efficiently manner (do not open/close unnecessary socket connections). So I'm wondering where should I put my open socket command? Also, in my handler's handleMessage() method I need a reference to the socket output stream in order to send data to server. I have currently the following code: ``` protected void initThread(){ this.thread = new HandlerThread(WorkerHandler.class.getCanonicalName()){ @Override public void run() { super.run(); try{ handler = new WorkerHandler(getLooper()); }catch(Exception e){ e.printStackTrace(); } } }; this.thread.start(); } ``` The method initThread() is called in the onCreate() method of my activity. Here follows the code of my custom handler class: ``` public class WorkerHandler extends Handler { protected Socket socket; protected BufferedWriter writer; public WorkerHandler(Looper looper) throws Exception{ super(looper); this.socket = new Socket("192.168.1.7", 5069); this.writer = new BufferedWriter(new OutputStreamWriter(this.socket.getOutputStream(), "utf-8")); } public BufferedWriter getWriter(){ return this.writer; } public Socket getSocket(){ return this.socket; } @Override public void handleMessage(Message msg) { Draw draw = (Draw) msg.obj; if (draw != null){ if (getWriter() != null){ try{ getWriter().write(DrawUtil.toJson(draw)+"\n"); getWriter().flush(); }catch(IOException e){ e.printStackTrace(); } } } } ``` } And again, in my activity, I trigger the sendDataToServer() method ``` protected void sendDataToServer(){ Draw draw = new Draw(getFlowType(), getID(), getSeq(), Calendar.getInstance(), startX, startY, endX, endY); if (getWorkerHandler() != null){ Message msg = getWorkerHandler().obtainMessage(); msg.obj = draw; getWorkerHandler().sendMessage(msg); } } ``` But my reference to the WorkerHandler object is always null. I'm pretty sure I misunderstood some concept... Could you please give me some hints? Thanks a lot!