Android将数据从主UI线程发送到另一个线程
我需要将一些数据从主线程发送到另一个线程。 我已经阅读了很多关于线程,asynctasks和处理程序的资料,但也许他们给我带来了一些困惑。 我读到我需要为我的’第二个线程’创建一个Handler,以便我可以从主线程向它发送消息(现在我不担心将任何内容发送回主线程)。
我需要第二个线程连接到服务器(通过套接字)并在某些用户事件上发送一些日期。 我试图以有效的方式(不要打开/关闭不必要的套接字连接)。 所以我想知道我应该在哪里打开socket命令? 此外,在我的处理程序的handleMessage()方法中,我需要对套接字输出流的引用,以便将数据发送到服务器。
我目前有以下代码:
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(); }
方法initThread()在我的activity的onCreate()方法中调用。
以下是我的自定义处理程序类的代码:
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(); } } } }
}
再次,在我的活动中,我触发sendDataToServer()方法
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); } }
但是我对WorkerHandler对象的引用始终为null。 我很确定我误解了一些概念……你能不能给我一些提示?
非常感谢!
你不能这样做。 您已使用HandlerThread
创建了第二个Thread。 HandlerThread
是一个具有Looper
的Thread
。 这就是HandlerThread
的run()
方法中发生的事情。 它正在运行looper循环。 这意味着HandlerThread
中的run()
方法只有在Looper
退出时才会完成。
在您的initThread()
方法中,您写道:
@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(); } }
您可以看到重写的run()
方法首先调用super.run()
。 这将运行looper循环并且不会完成。 所以initThread()
的其余代码永远不会执行。
如果你想使用HandlerThread()
那么你就不能搞乱它的run()
方法。 如果你想让它为你做的东西,你需要发布消息(或Runnable
s),并在那里做你的工作。 这是一个例子:
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);
现在您可以将消息和Runnable
发布到“处理程序”。 在此示例中,消息将由创建类的重写handleMessage()
方法处理。
编辑:提供Handler回调的代码示例
你可以使用你的WorkerHandler
类来处理回调,如果你这样修改它(我已经将名称更改为Worker
因为它实际上不是Handler
,它只是实现了Handler.Callback
接口):
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(); } } } } }
现在,您需要创建此Worker
类的实例,并在创建Handler
时将其作为回调参数传递。 在你的活动中做:
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);
您可以使用标准Java方法解决消费者/生产者问题,即线程消耗的BlockingQueue
和产生数据的任意数量的线程。
public class SendingWorker { private final BlockingQueue sendQueue = new LinkedBlockingQueue (); 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); }
您可以通过广播接收器获取值……如下所示,首先创建您自己的IntentFilter,
Intent intentFilter=new IntentFilter(); intentFilter.addAction("YOUR_INTENT_FILTER");
然后创建内部类BroadcastReceiver,
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"); } } };
现在在onResume()中注册您的广播接收器,
registerReceiver(broadcastReceiver, intentFilter);
最后在onDestroy()中取消注册BroadcastReceiver,
unregisterReceiver(broadcastReceiver);
现在最重要的部分……你需要从后台线程发送广播来发送值…..所以这样做,
Intent i=new Intent(); i.setAction("YOUR_INTENT_FILTER"); i.putExtra("KEY", "YOUR_VALUE"); sendBroadcast(i);
….欢呼:)