运行 运行OnUiThread() 重复导致异常
run runOnUiThread() repeatedly causes exception
(2016.3.15更新)
上周遇到了一个奇怪的问题,想和大家一起讨论一下。
问题场景:
AppCompatActivity 中有一个 searchView。每当文本更改时,Activity 中的片段将通过“getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout, fragment).commit();
”替换。
片段中有一个名为SearchThread的线程,将在onActivityCreated()
中执行。言归正传,里面有个getActivity().runOnUiThread(new Runnable{...})
方法。不管"new Runnable()"里面是什么,当searchView的文本快速变化时,都会出现NullPointerException,导致频繁重新建立这个fragment。
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...) 而不是 运行OnUiThread,一切都会顺利!!
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().运行OnUiThread()之前,先判断getActivity()==null.
2016.3.14:
[一些有趣的事情:)]
- "runOnUiThread"
的源代码
运行OnUIThread(Runnable r)的源码:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
事实上,运行OnUiThread调用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.the "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.obtain消息(0,str)),
while 导致 getActivity().运行OnUiThread(new Runnable{...}) 异常,当我同时重复执行它们时?
非常感谢!
我无法确定为什么您的片段与 activity 分离。但是,如果您想 运行 该代码而不考虑您的应用程序状态,您可以简单地使用 Handler 而不是 运行OnUiThread。只需确保在 ui/main 线程中创建处理程序,所有对 post() 的调用都会导致 ui/main 线程上的 Runnable 运行ning。
(2016.3.15更新)
上周遇到了一个奇怪的问题,想和大家一起讨论一下。
问题场景:
AppCompatActivity 中有一个 searchView。每当文本更改时,Activity 中的片段将通过“getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout, fragment).commit();
”替换。
片段中有一个名为SearchThread的线程,将在onActivityCreated()
中执行。言归正传,里面有个getActivity().runOnUiThread(new Runnable{...})
方法。不管"new Runnable()"里面是什么,当searchView的文本快速变化时,都会出现NullPointerException,导致频繁重新建立这个fragment。
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...) 而不是 运行OnUiThread,一切都会顺利!!
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().运行OnUiThread()之前,先判断getActivity()==null.
2016.3.14:
[一些有趣的事情:)]
- "runOnUiThread" 的源代码
运行OnUIThread(Runnable r)的源码:
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
事实上,运行OnUiThread调用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.the "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.obtain消息(0,str)), while 导致 getActivity().运行OnUiThread(new Runnable{...}) 异常,当我同时重复执行它们时? 非常感谢!
我无法确定为什么您的片段与 activity 分离。但是,如果您想 运行 该代码而不考虑您的应用程序状态,您可以简单地使用 Handler 而不是 运行OnUiThread。只需确保在 ui/main 线程中创建处理程序,所有对 post() 的调用都会导致 ui/main 线程上的 Runnable 运行ning。