How to make the working Thread communicate with the UI Thread when the working thread is launched from a service

StackOverflow https://stackoverflow.com/questions/21658609

Question

I am using Wifi Direct that detects and connects to a device in a background service.

When a WifiDirect connection is established, i launch a serverThread and a Client Thread respectively for the Server (GO) and for the Client.

My problem now, is that i don't know how to make the working thread communicate with the UI Thread.

What i did is that created a looper and a handler in the ClientThread and Server thread and also a handler for it, to get the jobs. The socket connection between the client/server is correctly handled.

But the problem is that i don't know how can i send data from the UI Thread to the Working Thread.

What should i do?

here is the code:

In the service when a connection is established i launch the threads:

   public void onConnectionInfoAvailable(final WifiP2pInfo info) {
    Log.d(MainActivity.TAG, "groupowneradress"+ info.groupOwnerAddress.getHostAddress());

    if (info.groupFormed && info.isGroupOwner) {
        if (WifiDirectStatus!=1){
            isgroupowner=true;
            ServerDataThread serverThread= new ServerDataThread(PORT);
            serverThread.start();

            WifiDirectStatus=1;
        }


    } else if (info.groupFormed) {
        if (WifiDirectStatus!=2){
        ClientDataThread clientThread= new ClientDataThread(info.groupOwnerAddress.getHostAddress(),PORT);
        clientThread.start();
        WifiDirectStatus=2;


  }
  }

 }

The client thread for example:

   public class ClientDataThread extends Thread {
public Handler clientHandler;
private static Socket socket=null;
private static final int SOCKET_TIMEOUT = 5000;
private int port;
private String host;
private BufferedReader in=null;
public  PrintWriter out=null;

static final int MSG_CLIENT = 2;

ClientDataThread(String host, int port){
    this.port=port;
    this.host=host;

}

@Override
    public void run(){
    try{
        Log.d(MainActivity.TAG, "ask for connection to the server");
        socket=new Socket();
        socket.bind(null);
        socket.connect((new InetSocketAddress(host, port)), SOCKET_TIMEOUT);
        Log.d(MainActivity.TAG, "connection socket has been established");


        try{
            in=new BufferedReader(new InputStreamReader(socket.getInputStream()));
             out= new PrintWriter(socket.getOutputStream());

            Log.d(MainActivity.TAG, "The Printwriter is equal to"+out.toString());
            Thread t1=new Thread(new Receive_Client(in));
            t1.start();

        }catch(IOException e){
            Log.d(MainActivity.TAG, "the client disconnect");

    } 

}catch (UnknownHostException e) {
        Log.d(MainActivity.TAG, "impossible to connect to the host"+socket.getLocalSocketAddress());
}catch (IOException e) {
        Log.d(MainActivity.TAG, "No Server Listening the port"+socket.getLocalPort());
}


 Looper.prepare();

 clientHandler = new Handler() {
 public void handleMessage(Message msg) {

    switch (msg.what) {
    case MSG_CLIENT:
        Log.d(MainActivity.TAG,"CDS has received the following message to send to the server"+(String)msg.obj);
        try{
            out.println((String)msg.obj);
            out.flush();
            Log.d(MainActivity.TAG, "The message has been sent to the server"); 
        }catch (Exception e){
            e.printStackTrace();
        }
        break;
    default:
        super.handleMessage(msg);
}
}
  };

  final Messenger mMessenger = new Messenger(clientHandler); 


  Looper.loop();
   }

}

And in the main activity i want to send a message to the working thread whenever i have a new location update:

public void onLocationChanged(Location location) {

         Log.d(MainActivity.TAG, "location changed"+Double.toString(location.getLatitude())+Double.toString(location.getLongitude()));

         if (mBound){
             Log.d(MainActivity.TAG,"Wifi direct Status"+mDeviceListService.getWifiDirectStatus());
             if (mDeviceListService.getWifiDirectStatus()==1){ 
                 // This Device acts as a Server


             }
             if (mDeviceListService.getWifiDirectStatus()==2){
                 Message msg= Message.obtain(null, ClientDataThread.MSG_CLIENT);
                    msg.obj= "bonjour";

                        clientHandler.sendMessage(msg);
             }
Was it helpful?

Solution

The main benefit of declaring a BroadcastReveiver is that you can issue the action wherever you want and declare the receiver also whenever you want, so it makes it suitable for complex workflows.

You simply declare the receiver where you want to receive the information:

class ActivityBroadcast extends BroadcastReceiver {
  @Override
  public void onReceive(final Context context, final Intent intent) {
    if (intent.getAction().equals(CustomActions.RECEIVEMYDATA)) {
      final String mydata = intent.getStringExtra("mydata");
      if ((mydata != null) && (!mydata.isEmpty())) {
        // Do your staff
        ...
      }
    }
  }
}

As you may see, you'll be using Intents, so the data you want to send has to be serializable or parcelable.

Another thing is that you have to register your receiver for some actions, it means that your receiver will only listen for this actions and this way you know that's the data you want to process. Despite Android already has some built-in actions, I strongly recommend defining your own. You may do that simply declaring a public class with public own actions. In my case you'll see I'm using CustomActions.RECEIVEMYDATA, which is just a personalized and unique String.

You now just have to declare your service, register it for your actions and register it. An unregistered receiver won't listen for anything.

final ActivityBroadcast broadcast_signal = new ActivityBroadcast();
final IntentFilter iFilter= new IntentFilter();
iFilter.addAction(CustomActions.RECEIVEMYDATA);
LocalBroadcastManager.getInstance(this).registerReceiver(broadcast_signal, iFilter);

You may see that this is registered as a local broadcast receiver. That means that you'll register actions just within your application. There's another kind of broadcast receiver and it might be used to communicate app-wide, in this case you'd use a global broadcast receiver.

There's just two things left:

  • The first is to send the signal to that receiver, so whenever and wherever you want, use something like this:

    final Intent intentResult = new Intent(CustomActions.RECEIVEMYDATA);
    intentResult.putExtra("mydata", "data to send");
    LocalBroadcastManager.getInstance(this).sendBroadcast(intentResult);  
    
  • Don't forget to unregister your broadcast receiver when it's no longer needed. For both this and registering, you should have in mind the lifecycle of the Activity where you're handling it and unregister/register when needed.

    LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcast_signal);
    
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top