android - 反复运行 runOnUiThread() 导致异常

标签 android android-runonuithread

(2016.3.15更新)

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

问题场景:

AppCompatActivity 中有一个 searchView。每当文本更改时,Activity 中的 fragment 将通过“getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout, fragment).commit();”替换。

在 fragment 中,有一个名为SearchThread 的线程,它将在onActivityCreated() 中执行。言归正传,里面有一个getActivity().runOnUiThread(new Runnable{...})方法。无论“new Runnable()”中的内容是什么,当searchView的文本快速变化时,都会出现NullPointerException,导致频繁重建这个fragment。

逻辑猫:

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”源码

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)), while 导致 getActivity().runOnUiThread(new Runnable{...}) 异常,当我同时重复执行它们时? 非常感谢!

最佳答案

我不能确定为什么您的 fragment 与 Activity 分离。但是,如果您想在不考虑应用程序状态的情况下运行该代码,则可以简单地使用 Handler 而不是 runOnUiThread。只需确保在 ui/主线程中创建处理程序,所有对 post() 的调用都会导致 Runnable 在 ui/主线程上运行。

关于android - 反复运行 runOnUiThread() 导致异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35982808/

相关文章:

java - 如何使用 kotlin 实现延迟加载 recyclerView 适配器,如 realm recyclerView 适配器

android - 所有司机的虚拟现实

java - 请求历史结果时 Google fit 集成 UI 线程错误

android - 如何清除堆?

android - 拨号应用程序中的暂停等待功能

android - android runOnUiThread和java中简单代码的区别

android - RecyclerView 在更新期间阻塞 ui 线程

c++ - 跨线程调用 a.k.a 从其他线程在主/UI 线程上运行而无需依赖

android BroadcastReceiver 调用了两次

Java String TRIM 函数不起作用