2010-02-02 2 views
7

내 프로그램은 다른 스레드에서 생성 된 핸들러를 사용하여 해당 스레드에 메시지를 보내려고 할 때 NullPointerException을 던졌습니다. 호출 쓰레드가 이미 다른 쓰레드에서 start를 호출 했음에도 불구하고, 다른 쓰레드에 의해 생성 된 Handler가 아직 생성되지 않았거나 호출 스레드에서 보이지 않았다. 이것은 매우 드물게 발생합니다. 거의 모든 테스트를 실행해도 예외가 발생하지 않습니다.다른 스레드 처리기를 호출하기 전에 null이 아닌지 어떻게 확인합니까?

최소한의 복잡성과 성능 저하로이 문제를 피하는 것이 가장 좋은 방법인지 궁금합니다. 이 프로그램은 게임이며 매우 성능에 민감합니다. 특히 한번 실행하면됩니다. 따라서 예를 들어 설정 후 동기화를 사용하지 않으려 고하고 언제든지 변수를 회전하지 않는 것을 선호합니다.

배경 :
안드로이드에서 처리기 클래스는 "자신의 것과 다른 스레드에서 수행 할 작업을 대기열에 넣기"위해 사용될 수 있습니다. 여기에 문서 :
http://developer.android.com/intl/de/reference/android/os/Handler.html

처리기는 사용할 스레드에서 만들어야합니다. 따라서 해당 스레드를 생성하는 스레드가 실행하는 스레드 생성자에서 생성하는 것은 옵션이 아닙니다. 처리기는 UI 스레드 이외의 스레드이면

는 루퍼 클래스는 사용되어야

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(); 
    } 
} 
:
http://developer.android.com/intl/de/reference/android/os/Looper.html

문서가이 목적을 위해 두 클래스를 사용하는 예를 제공한다

내 매우 추한 해결 방법은 현재 다음과 같습니다

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. 
      }   
     } 
    } 

} 

을 대구로 e를 만드는 스레드에서 다음과 같이 표시됩니다.

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

더 좋은 해결책이 있습니까? 감사.

답변

4

Looper을 준비하는 것은 잠시 차단 될 수 있습니다. 따라서 prepare()이 완료되는 데 걸리는 시간이 걸리므로 mHandler은 아직 정의되지 않았습니다.

ThreadHandlerThread까지 확장 할 수 있습니다. 그렇다고해도 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) { 
     // ... 
    } 
} 
+1

아, 좋은. HandlerThread # getLooper는 여분의 복잡성을 처리해야하는 대신 차단을 할 수 있습니다. 내 응용 프로그램에서는 HandlerThread를 서브 클래스화할 필요조차 없습니다. –

11

나는/고전 대기로 이동 핸들러는 다음 및 getHandler 동기화 호출하지 않고 여러 번 사용할 수 있습니다 및 getHandler에서 반환

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; 
    } 
} 

알릴 것입니다.

+0

많은 청소기. 고맙습니다. 이것은 좋은 대답이고 나는 체크 한 것뿐만 아니라 그것을 투표했다. –

1

난 그냥 체크 대답은 최고의 하나입니다 추가 할 수 있지만 그렇게 테스트하는 경우가 너무 루퍼 준비를 담당하고 이후 실행 메도 슈퍼를 호출 할 필요가 렸기 때문에 작동하지 않을 코드는 다음과 같이해야합니다 :

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) { 
    // ... 
} 

}

+0

좋은 교정, 고마워. 또 다른 옵션은 HandlerThread 서브 클래스에서 run을 오버라이드하지 않거나 전혀 서브 클래 싱하지 않는 것입니다. 핸들러는 어쨌든 메시지를받을 때 다른 스레드에서 정상적으로 작동합니다. HandlerThread의 기본 구현은 Looper를 실행하는 데 필요합니다. –

관련 문제