当从服务启动工作线程时,如何使工作线程与UI线程通信

时间:2014-02-09 11:41:15

标签: android multithreading service handler looper

我正在使用Wifi Direct检测并连接到后台服务中的设备。

当建立WifiDirect连接时,我分别为服务器(GO)和客户端启动serverThread和Client Thread。

我现在的问题是,我不知道如何使工作线程与UI线程通信。

我所做的是在ClientThread和Server线程中创建了一个looper和一个处理程序,并为它创建了一个处理程序来获取作业。 正确处理客户端/服务器之间的套接字连接。

但问题是我不知道如何将数据从UI线程发送到工作线程。

我该怎么办?

这是代码:

在建立连接时的服务中,我启动线程:

   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;


  }
  }

 }

客户端线程例如:

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

}

在主要活动中,每当我有新的位置更新时,我都想向工作线程发送消息:

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

1 个答案:

答案 0 :(得分:0)

声明BroadcastReveiver的主要好处是,您可以随时发出操作,并随时声明接收器,因此它适用于复杂的工作流程。

您只需声明接收者要接收信息的位置:

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

如您所见,您将使用Intent,因此您要发送的数据必须是可序列化的或可分割的。

另一件事是你必须为某些actions注册你的接收器,这意味着你的接收器只会听取这个动作,这样你知道你想要处理的数据。尽管Android已经有一些内置动作,但我强烈建议您定义自己的动作。您可以这样做,只是声明一个公共类具有公共自己的操作。在我的情况下,您会看到我使用CustomActions.RECEIVEMYDATA,这只是一个个性化且独特的String

您现在只需申报服务,注册您的行动并注册。未注册的接收者不会听取任何事情。

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

您可能会看到这是注册为本地广播接收器。这意味着您将在应用程序中注册操作。还有另一种广播接收器,它可能用于在应用范围内进行通信,在这种情况下,您使用全球广播接收器。

还剩下两件事:

  • 第一种是将信号发送到接收器,因此无论何时何地,都可以使用以下内容:

    final Intent intentResult = new Intent(CustomActions.RECEIVEMYDATA);
    intentResult.putExtra("mydata", "data to send");
    LocalBroadcastManager.getInstance(this).sendBroadcast(intentResult);  
    
  • 如果不再需要广播接收器,请不要忘记取消注册。对于此和注册,您应该记住Activity的生命周期,并在需要时注销/注册。

    LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcast_signal);