我需要将一些数据从主线程发送到另一个线程。我已经阅读过很多关于线程,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();
}
在我的活动的onCreate()方法中调用initThread()方法。
以下是我的自定义处理程序类的代码:
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。我很确定我误解了一些概念......你能给我一些提示吗?
非常感谢!
答案 0 :(得分:3)
你不能这样做。您已使用HandlerThread
创建了第二个主题。 HandlerThread
是Thread
,其Looper
。这就是run()
HandlerThread
方法中正在发生的事情。它正在运行looper循环。这意味着run()
中的HandlerThread
方法只会在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()
方法处理。
编辑:提供处理程序回调的代码示例
如果您像这样修改回复,可以使用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);
答案 1 :(得分:1)
您可以使用标准Java方法解决消费者/生产者问题,即线程消耗的BlockingQueue
和产生数据的任意数量的线程。
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);
}
答案 2 :(得分:1)
您可以通过广播接收器获取值......如下所示,首先创建您自己的IntentFilter,
Intent intentFilter=new IntentFilter();
intentFilter.addAction("YOUR_INTENT_FILTER");
然后创建内部类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");
}
}
};
现在在onResume()中注册您的广播接收器,
registerReceiver(broadcastReceiver, intentFilter);
最后在onDestroy()中取消注册BroadcastReceiver,
unregisterReceiver(broadcastReceiver);
现在最重要的部分......你需要从后台线程发射广播来发送值.....所以这样做,
Intent i=new Intent();
i.setAction("YOUR_INTENT_FILTER");
i.putExtra("KEY", "YOUR_VALUE");
sendBroadcast(i);
....欢呼:)