LucasM
LucasM

Reputation: 431

Android send data from main UI thread to another thread

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!

Upvotes: 6

Views: 12250

Answers (3)

Melbourne Lopes
Melbourne Lopes

Reputation: 4855

You can get values through broadcast receiver......as follows, First create your own IntentFilter as,

Intent intentFilter=new IntentFilter();
intentFilter.addAction("YOUR_INTENT_FILTER");

Then create inner class BroadcastReceiver as,

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
    /** Receives the broadcast that has been fired */
    @Override
    public void onReceive(Context context, Intent intent) {
        if(intent.getAction()=="YOUR_INTENT_FILTER"){
           //HERE YOU WILL GET VALUES FROM BROADCAST THROUGH INTENT EDIT YOUR TEXTVIEW///////////
           String receivedValue=intent.getStringExtra("KEY");
        }
    }
};

Now Register your Broadcast receiver in onResume() as,

registerReceiver(broadcastReceiver, intentFilter);

And finally Unregister BroadcastReceiver in onDestroy() as,

unregisterReceiver(broadcastReceiver);

Now the most important part...You need to fire the broadcast from background thread to send values..... so do as,

Intent i=new Intent();
i.setAction("YOUR_INTENT_FILTER");
i.putExtra("KEY", "YOUR_VALUE");
sendBroadcast(i);

....cheers :)

Upvotes: 0

David Wasser
David Wasser

Reputation: 95618

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 Runnables) 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 Runnables 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);

Upvotes: 4

zapl
zapl

Reputation: 63955

You could solve it using standard Java ways for consumer / producer problems, i.e. a BlockingQueue consumed by a thread and any amount of threads that produce data.

public class SendingWorker {
    private final BlockingQueue<Draw> sendQueue = new LinkedBlockingQueue<Draw>();
    private volatile Socket socket;

    public void start() {
        thread.start();
    }

    public void stop() {
        // interrupt so waiting in queue is interrupted
        thread.interrupt();
        // also close socket if created since that can block indefinitely
        Socket socket = this.socket;
        if (socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    // adding to queues is thread safe
    public void send(Draw draw) {
        sendQueue.add(draw);
    }

    private final Runnable task = new Runnable() {
        @Override
        public void run() {
            try {
                socket = new Socket(InetAddress.getLocalHost(), 8000);
                OutputStream out = socket.getOutputStream();
                while (true) {
                    Draw draw = sendQueue.take();
                    out.write(draw);
                    out.flush();
                }
            } catch (Exception e) {
                // handle 
            } finally {
                // cleanup
            }
        }
    };
    private final Thread thread = new Thread(task);
}

Upvotes: 1

Related Questions