programing tip

루퍼 스레드를 만든 다음 즉시 메시지를 보내는 방법은 무엇입니까?

itbloger 2020. 10. 25. 11:42
반응형

루퍼 스레드를 만든 다음 즉시 메시지를 보내는 방법은 무엇입니까?


백그라운드에 앉아 메시지를 처리하는 작업자 스레드가 있습니다. 이 같은:

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에서를 간단히 만들 수 없으므로은 잘못된 스레드와 연결됩니다.WorkerHandler

이것은 드문 시나리오처럼 보이지 않습니다. 몇 가지 해결 방법을 찾을 수 있습니다. 모두보기 흉합니다.

  1. 이 같은:

    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()결코 깨어나지 않을 것입니다!

  2. 이니셜 MessageWorker의 생성자에 전달 하고 run()메서드가 게시하도록합니다. 임시 솔루션은 여러 메시지에 대해 작동하지 않거나 즉시 전송하지 않고 곧바로 보내고 싶지 않은 경우 작동합니다.

  3. handler필드가 더 이상되지 않을 때까지 바쁜 대기 중 null입니다. 네, 최후의 수단 ...

스레드 를 대신하여 HandlerMessageQueue을 ( 를) 만들고 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();
                    }
            }
    }

참고 URL : https://stackoverflow.com/questions/4838207/how-to-create-a-looper-thread-then-send-it-a-message-immediately

반응형