run runOnUiThread()重复导致异常

时间:2016-03-14 08:37:07

标签: android android-runonuithread

(2016.3.15更新)

上周我遇到了一个奇怪的问题,我想跟你讨论这个问题。

问题场景:

AppCompatActivity中有一个searchView。每当文本发生变化时,活动中的片段将被替换为“getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout, fragment).commit();”。

在片段中,有一个名为SearchThread的线程,它将在onActivityCreated()中执行。说到这里,它有一个getActivity().runOnUiThread(new Runnable{...})方法。无论“new Runnable()”中的内容是什么,当searchView文本快速更改时都会发生NullPointerException,这会导致频繁重新建立此片段。

logcat的

03-15 20:12:25.912/cn.example.app E/AndroidRuntime: FATAL EXCEPTION: Thread-10820  
   Process: cn.example.app, PID: 31532  
   java.lang.NullPointerException  
       at cn.example.app.homepage.GymFragment$searchThread.run(GymFragment.java:257)  
       at java.lang.Thread.run(Thread.java:841)  

但是,如果我使用sendMessage(searchHandler.obtain ...)而不是runOnUiThread,一切都会顺利进行!!

GymFragment.Java

@Override  
public void onActivityCreated(Bundle savedInstanceState){  
    super.onActivityCreated(savedInstanceState);
    new Thread(new SearchThread()).start();  
}  
class SearchThread implements Runnable{  
        @Override  
        public void run() {  
        String s = "";  
            try {  
                Thread.sleep(4000);  
            } catch (Exception e) {;}  
            //searchHandler.sendMessage(searchHandler.obtainMessage(0, s));//Correct   
            getActivity().runOnUiThread(new Runnable() { //Throw NullPointer Exception or pool-1-thread-1 (with ExecutorService) 
                @Override  
                public void run() {  
                    ;  
                }  
            });  
        }  
}  

SearchActivity.Java

class queryChangeListener implements SearchView.OnQueryTextListener{
    ...
    @Override
    public boolean onQueryTextChange(String newText) {
        currentSearchTip = newText;
        if (newText != null && newText.length() > 0) {
            searchDelayed(newText);
        }
        return true;
    }
}
private Handler searchHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        if (msg == null)
            return;
        String searchText = (String) msg.obj;
        if (currentSearchTip != null && currentSearchTip.isEmpty()==false) {
            GymFragment fragment = GymFragment.newInstance(searchText);
            getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout, fragment).commit();
        }
    }
};
private ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(10);
private String currentSearchTip;
public void searchDelayed(String newText) {
    scheduledExecutor.schedule(new SearchThread(newText),500, TimeUnit.MILLISECONDS);
}
class SearchThread implements Runnable {
    String newText;
    public SearchThread(String newText){
        this.newText = newText;
    }
    public void run() {
        if (newText != null && newText.equals(currentSearchTip)) {
            searchHandler.sendMessage(searchHandler.obtainMessage(0, newText));
        }
    }
}

解决方案

1)注释掉“getActivity()...”,改为使用带注释的“sendMessage(searchhandler.obtain ..)”。

2)在运行getActivity()。runOnUiThread()之前,先判断getActivity()== null。

2016年3月14日:

[有些事情有趣:)]

  1. “runOnUiThread”的源代码
  2. runOnUIThread(Runnable r)的源代码:

    public final void runOnUiThread(Runnable action) {  
            if (Thread.currentThread() != mUiThread) {  
                mHandler.post(action);  
            } else {  
                action.run();  
            }  
    }
    

    实际上,runOnUiThread调用mHandler.post(Runnable r)。

    让我们看看post(Runnable r)中的内容:

    public final boolean post(Runnable r)  {    
           return  sendMessageDelayed(getPostMessage(r), 0);    
    }   
    

    在getPostMessage(Runnable r)中:

    private final Message getPostMessage(Runnable r) {    
            Message m = Message.obtain();    
            m.callback = r;    
            return m;    
    }   
    

    然后在Message.obtain()中:

    public final Message obtainMessage()  {    
          return Message.obtain(this);    
    }
    
    public static Message obtain(Message orig) {    
           Message m = obtain();    
           m.what = orig.what;    
           m.arg1 = orig.arg1;    
           m.arg2 = orig.arg2;    
           m.obj = orig.obj;    
           m.replyTo = orig.replyTo;    
           if (orig.data != null) {    
               m.data = new Bundle(orig.data);    
           }    
           m.target = orig.target;    
           m.callback = orig.callback;    
    
           return m;    
    }  
    
    /**   
         * Return a new Message instance from the global pool. Allows us to   
         * avoid allocating new objects in many cases.   
         */    
    public static Message obtain() {    
          synchronized (sPoolSync) {    
                if (sPool != null) {    
                    Message m = sPool;    
                    sPool = m.next;    
                    m.next = null;    
                    sPoolSize--;    
                    return m;    
                }    
          }    
          return new Message();  
     }   
    

    2.“sendMessage(Message msg)”的源代码:

    public final boolean sendMessage(Message msg)  {    
           return sendMessageDelayed(msg, 0);    
    }
    

    在这里我们可以看到,sendMessage(Message msg)的实现几乎等于post(Runnable r)的实现。差异可能与getPostMessage(Runnable r)相关联。

    因此,简而言之,实际上是什么导致正确执行 searchHandler.SendMessage(searchHandler.obtainMessage(0,STR)), 虽然结果是getActivity()。runOnUiThread(new Runnable {...})除外,当我同时重复执行它们时? 非常感谢!

1 个答案:

答案 0 :(得分:1)

我无法确定您的片段与活动分离的原因。但是,如果您想要运行该代码而不管您的应用程序状态如何,您可以简单地使用Handler而不是runOnUiThread。只需确保在ui / main线程中创建Handler,所有对post()的调用都将导致在ui / main线程上运行Runnable。