我有一个应用程序在生产中使用ACRA几个星期,我没有错误,直到今天报告了一个奇怪的错误。
我有:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
来自堆栈跟踪中的此方法(已回溯):
at my.app.CountdownFragment$1.void onPostExecute(java.lang.Object)(SourceFile:1)
这是相关的源代码段:
private void addInstructionsIfNeeded() {
if (S.sDisplayAssist) {
new AsyncTask<String, Void, String>() {
@Override
protected String doInBackground(String... params) {
return null;
}
/*
* runs on the ui thread
*/
protected void onPostExecute(String result) {
Activity a = getActivity();
if (S.sHelpEnabled && a != null) {
in = new InstructionsView(a.getApplicationContext());
RelativeLayout mv = (RelativeLayout) a
.findViewById(R.id.main_place);
mv.addView(in.prepareView());
}
};
}.execute("");
}
}
从处理程序调度消息(UI thead)调用addInstructionsIfNeeded()
的位置。
onPostExecute()
在UI线程上运行,为什么我有“错误的线程”?我的问题是:它怎么可能?
修改: 这一切都发生在片段
中答案 0 :(得分:44)
我遇到了同样的问题,这是另一个android框架bug ......
正在发生的事情:
在某些情况下,应用程序可以有多个“looper”,因此不止一个“UI线程”
- 旁注 - 我在这个答案的最宽松的句子中使用术语“UI线程”,因为当人们说“UI线程”时,他们通常表示主要或入口线程, Android就像之前的许多其他操作系统一样,允许多个消息泵(在Android中称为Looper
,请参阅:http://en.wikipedia.org/wiki/Event_loop)用于不同的UI树,因为所有意图和目的的android都能够在某些情况下运行多个“UI线程”并使用该术语会导致模糊不清...... - 端边注释 -
这意味着:
因为一个应用程序可以有多个“UI线程”而AsyncTask
总是“在UI线程上运行”[ref],所以有人决定[不好]而不是AsyncTask总是在其上运行创建线程(在99.999999%的情况下将是正确的“UI线程”)他们决定使用hocus pocus(或者你决定制作一个制作精良的快捷方式)在“主要活套”上执行..
示例:强>
Log.i("AsyncTask / Handler created ON: " + Thread.currentThread().getId());
Log.i("Main Looper: " + Looper.getMainLooper().getThread().getId() + " myLooper: "+ Looper.myLooper().getThread().getId());
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
Log.i("doInBackground ran ON: " + Thread.currentThread().getId());
// I'm in the background, all is normal
handler.post(new Runnable() {
@Override
public void run() {
Log.i("Handler posted runnable ON: " + Thread.currentThread().getId());
// this is the correct thread, that onPostExecute should be on
}
});
return null;
}
@Override
protected void onPostExecute(Void result) {
Log.i("onPostExecute ran ON: " + Thread.currentThread().getId());
// this CAN be the wrong thread in certain situations
}
}.execute();
如果从上述不良情况调用,输出将如下所示:
AsyncTask / Handler created ON: 16
Main Looper: 1 myLooper: 16
doInBackground ran ON: 12
onPostExecute ran ON: 1
Handler posted runnable ON: 16
这是AsyncTask
如图所示这可以使用Handler.post(Runnable)
在我的特定情况下缓解我的“UI线程”情况的双重性是由于我正在创建一个对话框以响应从a调用的JavaScript接口方法WebView
,基本上:WebView
有自己的“UI线程”,那是我当前正在运行的那个..
从我所知道的(没有真正关心或读得太多)似乎AsyncTask
类'回调方法通常运行一个静态实例化的处理程序(参见:http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.0.3_r1/android/os/AsyncTask.java#AsyncTask.0sHandler ),这意味着它总是要在“主线程”或“入口线程”上执行,它们错误地将其称为“UI线程”(假定为发生UI交互的任何线程,例如多线程)在这种情况下)这是来自机器人团队的伪劣工艺和伪劣文件......弱酱,酱汁很弱
希望这可以帮助你 -
答案 1 :(得分:10)
有同样的问题。解决了我的情况
简要解释:
故事:
有两个制作应用: A - 主要Android应用和 B - 一些实用应用。
在集成应用 B ito app A 之后,我们收到了很多崩溃信息:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
从 AsynckTask.onPostExecute()
运行的方法经过一些调查后,似乎实用程序应用程序 B 在 HandlerThread
中使用 AsyncTask在AsyncTask的源代码中找到了跟踪:
private static final InternalHandler sHandler = new InternalHandler();
这是用于将 onPostExecute()发送到UI线程的处理程序。
此处理程序静态,它将在类加载期间初始化,即首先新的AsyncTask()外观
这意味着 onPostExecute 将始终发布到第一次调用 new AsyncTask()的线程中(除非AsyncTask.class将被卸载并再次加载) )
在我的情况下,流程是这样的:
1 - starting app A
2 - initializing B form A
3 - B creates its own HandlerThread and launches AsyncTask <- now onPostExecute wil be posted to this HandlerThread no matter where from an instance of AsyncTask will be launched in future
4 - create AsyncTask in the app A for a long operation and update UI in its onPostExecute
5 - when executing onPostExecute() the CalledFromWrongThreadException is thrown
然后我的一位朋友向我展示了android.developers(线程规则部分)中的相关文档:
必须在UI线程上加载AsyncTask类。这个完成了 自JELLY_BEAN起。必须创建任务实例 UI线程。必须在UI线程上调用execute(Params ...)。
希望能帮助明确这种情况)
答案 2 :(得分:9)
也许原因是乱舞? 当我使用Flurry 3.2.1 时,我有这个例外。但当我回到Flurry 3.2.0 时,我没有这个例外
使用乱舞 3.2.2 及以上。
答案 3 :(得分:3)
在Application onCreate中放置以下代码行可以解决问题:
/**
* Fixing AsyncTask Issue not called on main thread
*/
try {
Class.forName("android.os.AsyncTask");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
似乎问题是在AsyncTask类首次在另一个主线程上启动时创建的,这个主线程不是我们的主线程,我通过在底部添加代码来检查它,我的应用程序onCreate
new Thread(new Runnable() {
@Override
public void run() {
Log.i("tag","1.3onPostExecute ran ON: " + Thread.currentThread().getId());
Looper.prepare();
new AsyncTask<Void,Void,Void>(){
@Override
protected Void doInBackground(Void... params) {
Log.i("tag","2onPostExecute ran ON: " + Thread.currentThread().getId());
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
Log.i("tag","1.2onPostExecute ran ON: " + Thread.currentThread().getId());
super.onPostExecute(aVoid);
}
}.execute();
Looper.loop();
Looper.myLooper().quit();
}
}).start();
此代码将在不是应用程序主体的主线程中初始化AsynTask,并且会导致应用程序在任何其他AsyncTask中崩溃,后者将在后执行时执行任何UI。与CalledFromWrongThreadException崩溃
希望它能更清楚一点。
感谢所有人给予的大力帮助。
答案 4 :(得分:1)
在哪里
runOnUiThread(new Runnable() {
public void run() { /*code*/ } );
代码
/*
* runs on the ui thread
*/
protected void onPostExecute(String result) {
Activity a = getActivity();
if (S.sHelpEnabled && a != null) {
in = new InstructionsView(a.getApplicationContext());
runOnUiThread(new Runnable() {
public void run() {
RelativeLayout mv = (RelativeLayout) a
.findViewById(R.id.main_place);
mv.addView(in.prepareView());
}
}
};
试试这段代码。我认为这可以解决问题
答案 5 :(得分:0)
我认为问题出在第Activity a = getActivity();
行我认为你应该在进入AsyncTask之前做到这一点