루퍼 스레드를 만든 다음 즉시 메시지를 보내는 방법은 무엇입니까?
백그라운드에 앉아 메시지를 처리하는 작업자 스레드가 있습니다. 이 같은:
class Worker extends Thread {
public volatile Handler handler; // actually private, of course
public void run() {
Looper.prepare();
mHandler = new Handler() { // the Handler hooks up to the current Thread
public boolean handleMessage(Message msg) {
// ...
}
};
Looper.loop();
}
}
메인 스레드 (UI 스레드, 중요하지 않음)에서 다음과 같이하고 싶습니다.
Worker worker = new Worker();
worker.start();
worker.handler.sendMessage(...);
문제는 이것이 나를 아름다운 경쟁 조건으로 설정한다는 것입니다. 시간 worker.handler
을 읽을 때 작업자 스레드가 이미이 필드에 할당되었는지 확인할 방법이 없습니다!
생성자가 메인 스레드에서 실행되기 때문에의 생성자 Handler
에서를 간단히 만들 수 없으므로은 잘못된 스레드와 연결됩니다.Worker
Handler
이것은 드문 시나리오처럼 보이지 않습니다. 몇 가지 해결 방법을 찾을 수 있습니다. 모두보기 흉합니다.
이 같은:
class Worker extends Thread { public volatile Handler handler; // actually private, of course public void run() { Looper.prepare(); mHandler = new Handler() { // the Handler hooks up to the current Thread public boolean handleMessage(Message msg) { // ... } }; notifyAll(); // <- ADDED Looper.loop(); } }
그리고 메인 스레드에서 :
Worker worker = new Worker(); worker.start(); worker.wait(); // <- ADDED worker.handler.sendMessage(...);
그러나 이것도 신뢰할 수 없습니다.
notifyAll()
이전에 일 이 발생 하면wait()
결코 깨어나지 않을 것입니다!이니셜
Message
을Worker
의 생성자에 전달 하고run()
메서드가 게시하도록합니다. 임시 솔루션은 여러 메시지에 대해 작동하지 않거나 즉시 전송하지 않고 곧바로 보내고 싶지 않은 경우 작동합니다.handler
필드가 더 이상되지 않을 때까지 바쁜 대기 중null
입니다. 네, 최후의 수단 ...
스레드 를 대신하여 Handler
및 MessageQueue
을 ( 를) 만들고 Worker
싶지만 이것이 가능하지 않은 것 같습니다. 이것에서 가장 우아한 방법은 무엇입니까?
CommonsWare 덕분에 최종 솔루션 (오류 검사 제외) :
class Worker extends HandlerThread {
// ...
public synchronized void waitUntilReady() {
d_handler = new Handler(getLooper(), d_messageHandler);
}
}
그리고 메인 스레드에서 :
Worker worker = new Worker();
worker.start();
worker.waitUntilReady(); // <- ADDED
worker.handler.sendMessage(...);
이것은 HandlerThread.getLooper()
루 퍼가 초기화 될 때까지 블록 의 의미 체계 덕분에 작동합니다 .
덧붙여서, 이것은 HandlerThread
대략 다음과 같이 구현 되기 때문에 위의 솔루션 # 1과 유사 합니다 (오픈 소스를 좋아합니다).
public void run() {
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Looper.loop();
}
public Looper getLooper() {
synchronized (this) {
while (mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
주요 차이점은 작업자 스레드가 실행 중인지 확인하지 않고 실제로 루퍼를 생성했다는 것입니다. 그렇게하는 방법은 개인 필드에 루퍼를 저장하는 것입니다. 좋은!
소스 코드를 살펴보세요 HandlerThread
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
기본적으로 작업자에서 Thread를 확장하고 고유 한 Looper를 구현하는 경우 기본 스레드 클래스는 작업자를 확장하고 거기에 처리기를 설정해야합니다.
이것은 내 솔루션입니다. MainActivity :
//Other Code
mCountDownLatch = new CountDownLatch(1);
mainApp = this;
WorkerThread workerThread = new WorkerThread(mCountDownLatch);
workerThread.start();
try {
mCountDownLatch.await();
Log.i("MsgToWorkerThread", "Worker Thread is up and running. We can send message to it now...");
} catch (InterruptedException e) {
e.printStackTrace();
}
Toast.makeText(this, "Trial run...", Toast.LENGTH_LONG).show();
Message msg = workerThread.workerThreadHandler.obtainMessage();
workerThread.workerThreadHandler.sendMessage(msg);
WorkerThread 클래스 :
public class WorkerThread extends Thread{
public Handler workerThreadHandler;
CountDownLatch mLatch;
public WorkerThread(CountDownLatch latch){
mLatch = latch;
}
public void run() {
Looper.prepare();
workerThreadHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Log.i("MsgToWorkerThread", "Message received from UI thread...");
MainActivity.getMainApp().runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(MainActivity.getMainApp().getApplicationContext(), "Message received in worker thread from UI thread", Toast.LENGTH_LONG).show();
//Log.i("MsgToWorkerThread", "Message received from UI thread...");
}
});
}
};
Log.i("MsgToWorkerThread", "Worker thread ready...");
mLatch.countDown();
Looper.loop();
}
}
class WorkerThread extends Thread {
private Exchanger<Void> mStartExchanger = new Exchanger<Void>();
private Handler mHandler;
public Handler getHandler() {
return mHandler;
}
@Override
public void run() {
Looper.prepare();
mHandler = new Handler();
try {
mStartExchanger.exchange(null);
} catch (InterruptedException e) {
e.printStackTrace();
}
Looper.loop();
}
@Override
public synchronized void start() {
super.start();
try {
mStartExchanger.exchange(null);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
'programing tip' 카테고리의 다른 글
.net 서비스 버스 권장 사항? (0) | 2020.10.25 |
---|---|
jQuery 이벤트 유형의 전체 목록 찾기 (0) | 2020.10.25 |
프런트 엔드 개발자 인터뷰 질문 (0) | 2020.10.25 |
모든 주소 대신 단일 IP 주소 (localhost)에 바인딩하도록 tomcat을 어떻게 구성합니까? (0) | 2020.10.24 |
클래스 선언 중괄호 뒤의 세미콜론 (0) | 2020.10.24 |