与HandlerThread通信的示例

36

我想从GUI线程设置一个HandlerThread。然后,在GUI上单击按钮后的一段时间内,它会运行callHello(),该方法会向驻留在非GUI线程上的HelloLogger对象发送一条消息,异步记录"Hello World"。我尝试了许多方法,有些方法会无限期地阻塞,有些方法永远不会接收到消息,等等。下面的代码是我实现得最接近的,能否请有人修改它以使其正常工作?

public class HandlerThreadExample {

    private MyHandlerThread mMyHandlerThread;
    private Looper mLooper;
    private Handler mHandler;

    public HandlerThreadExample(){
        mMyHandlerThread = new MyHandlerThread();
        mMyHandlerThread.start();
        mLooper = mMyHandlerThread.getLooper();
    }
    public void callHello() {
        mHandler.sendEmptyMessage(1);
    }
    private class MyHandlerThread extends HandlerThread {
        private HelloLogger mHelloLogger;
        private Handler mHandler;
        public MyHandlerThread() {
            super("The MyHandlerThread thread", HandlerThread.NORM_PRIORITY);
        }
        public void run (){
            mHelloLogger = new HelloLogger();
            mHandler = new Handler(getLooper()){
                public void handleMessage(Message msg){
                    mHelloLogger.logHello();
                }
            };
            super.run();
        }
    }
    private class HelloLogger {
        public HelloLogger (){
        }
        public void logHello(){
            Log.d("HandlerThreadExample", "Hello World");
        }
    }
}

最佳示例:

至少现在我可以关闭这些该死的选项卡了。

感谢pskink提供的帮助。

public class HandlerThreadExample2 {
    private static int MSG_START_HELLO = 0;
    private static int MSG_HELLO_COMPLETE = 1;
    private HandlerThread ht;
    private Handler mHtHandler;
    private Handler mUiHandler;
    private boolean helloReady = false;
    public HandlerThreadExample2(){
        ht = new HandlerThread("The new thread");
        ht.start();
        Log.d(App.TAG, "UI: handler thread started");
        mUiHandler = new Handler(){
            public void handleMessage(Message msg){
                if (msg.what == MSG_HELLO_COMPLETE){
                    Log.d(App.TAG, "UI Thread: received notification of sleep completed ");
                    helloReady = true;              }
            }
        };
        mHtHandler = new Handler(ht.getLooper()){
            public void handleMessage (Message msg){
                if (msg.what == MSG_START_HELLO){
                    Log.d(App.TAG, "handleMessage " + msg.what + " in " + Thread.currentThread() + " now sleeping");
                    try {
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.d(App.TAG, "Woke up, notifying UI thread...");
                    mUiHandler.sendEmptyMessage(MSG_HELLO_COMPLETE);
                }
            }
        };
    }
    public void sendLongHello(){
        if (helloReady){
            Log.d(App.TAG, "sending hello " + Thread.currentThread());      
            mHtHandler.sendEmptyMessage(MSG_START_HELLO);
            helloReady = false;
        } else {
            Log.e(App.TAG, "Cannot do hello yet - not ready");
        }
    }
}

4
你认为可以使用 https://github.com/greenrobot/EventBus 在4行代码内完成吗? - cYrixmorten
非常有趣,谢谢,我会另外的时候再研究。+1。但我认为在跑步之前我应该先学走路。 - Jodes
1
没问题,但说实话,尽管我一直在编写很多Android代码,但我放弃使用Handlers和Messages了。在可以非常简单、易读且易于维护的情况下,没有必要把事情搞复杂,这是我的看法 :) - cYrixmorten
老天知道,我知道你在说什么!!!有些东西告诉我这将是一个赏金问题哈哈 - Jodes
  1. 创建一个新的HandlerThread,
  2. 启动它,
  3. 使用ht.getLooper()作为Looper参数创建一个新的Handler,
  4. 发送一条消息。
- pskink
显示剩余4条评论
2个回答

61

这是一个可行的示例:

HandlerThread ht = new HandlerThread("MySuperAwesomeHandlerThread");
ht.start();
Handler h = new Handler(ht.getLooper()) {
    public void handleMessage(Message msg) {
        Log.d(TAG, "handleMessage " + msg.what + " in " + Thread.currentThread());
    };
};
for (int i = 0; i < 5; i++) {
    Log.d(TAG, "sending " + i + " in " + Thread.currentThread());
    h.sendEmptyMessageDelayed(i, 3000 + i * 1000);
}

更新:

新增两个类字段:

Handler mHtHandler;
Handler mUiHandler;

尝试执行以下操作:

HandlerThread ht = new HandlerThread("MySuperAwsomeHandlerThread");
ht.start();
Callback callback = new Callback() {
    @Override
    public boolean handleMessage(Message msg) {
        if (msg.what == 0) {
            Log.d(TAG, "got a meaasage in " + Thread.currentThread() + ", now sleeping... ");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Log.d(TAG, "woke up, notifying ui thread...");
            mUiHandler.sendEmptyMessage(1);
        } else
        if (msg.what == 1) {
            Log.d(TAG, "got a notification in " + Thread.currentThread());
        }
        return false;
    }
};
mHtHandler = new Handler(ht.getLooper(), callback);
mUiHandler = new Handler(callback);
mHtHandler.sendEmptyMessageDelayed(0, 3000);

当然,您可以摆脱Callback接口并创建两个具有覆盖handleMessage方法的处理程序...

+1,谢谢你。我已经编辑了我的问题,添加了一个示例,试图将你的答案整合进去。我仍然不知道GUI线程怎样才能知道工作线程何时完成一个操作(显然不会阻塞)。我需要添加什么才能调用helloComplete()方法?我需要在其他地方创建或获取另一个Handler吗? - Jodes
@pskink,您能否解释一下回调和循环器如何并行运行,这是否与某种形式的调度程序有关? - Chris Stryczynski
1
@Chris 最好的学习方法是运行我的第二段代码片段。 - pskink
你认为这会怎么样?start() 是非阻塞的,所以下一行的 getLooper() 可能会返回 null。 - Yaroslav Mytkalyk
1
@YaroslavMytkalyk 不行,阅读一下 HandlerThread#getLooper 的文档说明。 - pskink

1
您看到的问题是因为您的外部类正在使用一个私有的mHandler字段,而您的HandlerThread也是如此。外部类的字段未初始化。您不需要内部的mHandler。外部类可以从在调用start()后获取的looper中创建一个handler。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接