在调用另一个线程的处理程序之前,如何确保它不为空?

发布于 2024-08-20 04:58:58 字数 2464 浏览 6 评论 0原文

有一天,当我的程序尝试使用在另一个线程上创建的处理程序向该线程发送消息时,它抛出了 NullPointerException。尽管调用线程已经在另一个线程上调用了 start,但另一个线程创建的 Handler 尚未创建,或者对于调用线程尚不可见。这种情况很少发生。几乎每次测试运行都没有出现异常。

我想知道最好的方法是什么,以最小的复杂性和性能损失来避免这个问题。该程序是一个游戏,对性能非常敏感,尤其是在运行时。因此,例如,我尝试避免在设置后使用同步,并且更愿意避免随时旋转变量。

背景:
在 Android 中,Handler 类可用于“将要在与您自己的线程不同的线程上执行的操作排队”。文档在这里:
http://developer.android.com/intl/de/reference /android/os/Handler.html

Handler 必须在使用它的线程上创建。因此,在线程的构造函数中创建它(该线程由创建该线程的线程运行)不是一种选择。

当Handler用于UI线程以外的线程时,还必须使用Looper类:
http://developer.android.com/intl/de/reference /android/os/Looper.html

文档给出了为此目的使用两个类的示例:

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

我非常丑陋的解决方法目前看起来像这样:

public class LooperThread extends Thread {

    public volatile Handler mHandler;

    public final ArrayBlockingQueue<Object> setupComplete = new ArrayBlockingQueue<Object>(1);

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        setupComplete();

        Looper.loop();
    }

    public void waitForSetupComplete() {
        while ( true ) {
            try {
                setupComplete.take();
                return;
            } catch (InterruptedException e) {
                //Ignore and try again.
            }
        }
    }

    private void setupComplete() {
        while( true ) {
            try {
                setupComplete.put(new Object());
                return;
            } catch (InterruptedException e) {
                //Ignore and try again.
            }        
        }
    }

}

创建线程中的代码看起来像这样:

    LooperThread otherThread = new LooperThread();
    otherThread.start();        
    otherThread.waitForSetupComplete();
    otherThread.mHandler.sendEmptyMessage(0);

有没有更好的方法解决方案?谢谢。

My program threw a NullPointerException the other day when it tried to use a Handler created on another thread to send that thread a message. The Handler created by the other thread was not yet created, or not yet visible to the calling thread, despite the calling thread having already called start on the other thread. This only happens very rarely. Almost every test run does not get the exception.

I was wondering what the best way is to avoid this problem for sure with minimal complication and performance penalty. The program is a game and very performance sensitive, especially once it is running. Therefore I try to avoid using synchronization after setup, for example, and would prefer to avoid spinning on a variable at any time.

Background:
In Android the Handler class may be used to "enqueue an action to be performed on a different thread than your own". Documentation here:
http://developer.android.com/intl/de/reference/android/os/Handler.html

The Handler must be created on the thread where it will be used. So creating it in the constructor of a thread, which is run by the thread creating that thread, is not an option.

When the Handler is for a thread other than the UI thread, the Looper class must also be used:
http://developer.android.com/intl/de/reference/android/os/Looper.html

The documentation gives this example of using the two classes for this purpose:

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }
}

My very ugly workaround currently looks like this:

public class LooperThread extends Thread {

    public volatile Handler mHandler;

    public final ArrayBlockingQueue<Object> setupComplete = new ArrayBlockingQueue<Object>(1);

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        setupComplete();

        Looper.loop();
    }

    public void waitForSetupComplete() {
        while ( true ) {
            try {
                setupComplete.take();
                return;
            } catch (InterruptedException e) {
                //Ignore and try again.
            }
        }
    }

    private void setupComplete() {
        while( true ) {
            try {
                setupComplete.put(new Object());
                return;
            } catch (InterruptedException e) {
                //Ignore and try again.
            }        
        }
    }

}

With the code in the creating thread looking like this:

    LooperThread otherThread = new LooperThread();
    otherThread.start();        
    otherThread.waitForSetupComplete();
    otherThread.mHandler.sendEmptyMessage(0);

Are there any better solutions? Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

你怎么这么可爱啊 2024-08-27 04:58:58

我会选择

public class LooperThread extends Thread {

    private Handler mHandler;

    public void run() {
        Looper.prepare();

        synchronized (this) {
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    // process incoming messages here
                }
            };
            notifyAll();
        }

        Looper.loop();
    }

    public synchronized Handler getHandler() {
        while (mHandler == null) {
            try {
                wait();
            } catch (InterruptedException e) {
                //Ignore and try again.
            }
        }
        return mHandler;
    }
}

从 getHandler 返回的经典等待/通知处理程序,然后可以多次使用而无需调用同步 getHandler。

I'd go with the classic wait/notify

public class LooperThread extends Thread {

    private Handler mHandler;

    public void run() {
        Looper.prepare();

        synchronized (this) {
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    // process incoming messages here
                }
            };
            notifyAll();
        }

        Looper.loop();
    }

    public synchronized Handler getHandler() {
        while (mHandler == null) {
            try {
                wait();
            } catch (InterruptedException e) {
                //Ignore and try again.
            }
        }
        return mHandler;
    }
}

Handler returned from getHandler can then be used many times without invoking synchronized getHandler.

泪之魂 2024-08-27 04:58:58

准备 Looper 可能会阻塞一段时间,所以我想您遇到了 prepare() 需要一段时间才能完成的情况,因此 mHandler > 仍未定义。

您可以让您的 Thread 扩展 HandlerThread,但即便如此,您仍然必须等待以确保 Looper 已初始化。也许这样的事情可能会起作用,您单独定义了 Handler ,但利用了自定义线程的 Looper

或许。

private void setUp() {
    mHandlerThread = new CustomThread("foo", Process.THREAD_PRIORITY_BACKGROUND);
    mHandlerThread.start();

    // Create our handler; this will block until looper is initialised
    mHandler = new CustomHandler(mHandlerThread.getLooper());
    // mHandler is now ready to use
}

private class CustomThread extends HandlerThread {
    public void run() {
        // ...
    }
}   

private class CustomHandler extends Handler {
    CustomHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        // ...
    }
}

Preparing a Looper can block for a while, so I imagine you're hitting a condition where prepare() takes a moment to complete, thus mHandler is still undefined.

You could have your Thread extend HandlerThread, though even then you still have to wait to ensure the Looper has initialised. Perhaps something like this might work, where you have the Handler defined separately, but utilising the Looper of your custom thread.

Maybe.

private void setUp() {
    mHandlerThread = new CustomThread("foo", Process.THREAD_PRIORITY_BACKGROUND);
    mHandlerThread.start();

    // Create our handler; this will block until looper is initialised
    mHandler = new CustomHandler(mHandlerThread.getLooper());
    // mHandler is now ready to use
}

private class CustomThread extends HandlerThread {
    public void run() {
        // ...
    }
}   

private class CustomHandler extends Handler {
    CustomHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        // ...
    }
}
萌化 2024-08-27 04:58:58

我只是想补充一点,检查的答案是最好的答案,但如果你像这样测试它是行不通的,因为你需要在运行方法上调用 super ,因为它负责准备循环器,所以代码应该是这样的:

private void setUp() {
  mHandlerThread = new CustomThread("foo", Process.THREAD_PRIORITY_BACKGROUND);
  mHandlerThread.start();

  // Create our handler; this will block until looper is initialised
  mHandler = new CustomHandler(mHandlerThread.getLooper());
  // mHandler is now ready to use
}

private class CustomThread extends HandlerThread {
   public void run() {
    super.run() // <- VERY IMPORTANT OTHERWISE IT DOES NOT WORK
    // your code goes here
   }
}   

private class CustomHandler extends Handler {
CustomHandler(Looper looper) {
    super(looper);
}

 @Override
 public void handleMessage(Message msg) {
    // ...
 }

}

I just want to add that the checked answer is the best one but if you test it like that is not going to work becouse you need to call super on run methode since it's in charge of preparing the looper so the code should be like this:

private void setUp() {
  mHandlerThread = new CustomThread("foo", Process.THREAD_PRIORITY_BACKGROUND);
  mHandlerThread.start();

  // Create our handler; this will block until looper is initialised
  mHandler = new CustomHandler(mHandlerThread.getLooper());
  // mHandler is now ready to use
}

private class CustomThread extends HandlerThread {
   public void run() {
    super.run() // <- VERY IMPORTANT OTHERWISE IT DOES NOT WORK
    // your code goes here
   }
}   

private class CustomHandler extends Handler {
CustomHandler(Looper looper) {
    super(looper);
}

 @Override
 public void handleMessage(Message msg) {
    // ...
 }

}

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文