2010-02-02 33 views
7

我的程序在前一天嘗試使用另一個線程上創建的Handler向該線程發送消息時拋出了NullPointerException異常。由另一個線程創建的處理程序尚未創建,或者對於調用線程尚不可見,儘管調用線程已經在另一個線程上調用了啓動。這種情況很少發生。幾乎每個測試運行都沒有得到例外。如何在調用它之前確保另一個線程的處理程序不爲空?

我想知道什麼最好的方法是避免這個問題,肯定會造成最小的併發症和性能損失。該程序是一款遊戲,對性能非常敏感,特別是在運行時。因此,我嘗試避免在安裝後使用同步,例如,並且寧願隨時避免在變量上旋轉。

背景:
在Android中,Handler類可能被用來「排隊要在不同於你自己的線程上執行的動作」。這裏的文檔:
http://developer.android.com/intl/de/reference/android/os/Handler.html

處理程序必須在它將被使用的線程上創建。因此,在創建該線程的線程運行的線程的構造函數中創建它不是一個選項。

當處理程序是用於除UI線程以外的線程,套口類還必須使用:
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. 
      }   
     } 
    } 

} 

隨着鱈魚e在創建線程看起來像這樣:

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

有沒有更好的解決方案?謝謝。

回答

4

準備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) { 
     // ... 
    } 
} 
+1

啊,很好。 HandlerThread#getLooper可以完成阻塞,而不必處理額外的複雜性。對於我的特定應用程序,我甚至不需要子類HandlerThread。 – 2010-02-03 18:48:21

11

我與經典的等待去/通知

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。

+0

乾淨多了。謝謝。這是一個很好的答案,我把它和選中的一樣投了票。 – 2010-02-03 18:47:59

1

我只想補充一點,檢查答案是最好的,但如果你測試它好像是行不通的becouse您需要調用運行梅索德超級因爲它是負責編制活套左右代碼應該是這樣的:

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子類上的運行,或者根本不子類化它。無論如何,Handler通常在其他線程上工作。 HandlerThread的默認實現僅用於運行Looper。 – 2014-04-03 14:31:43

相關問題