2010-02-02 30 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

相关问题