Android报错android.view.ViewRootImpl$CalledFromWrongThreadException求助
解决android.view.ViewRootImpl$CalledFromWrongThreadException异常
这个异常的核心原因是你在非主线程(比如AsyncTask的doInBackground()方法)里直接操作了UI控件——Android明确规定:只有创建View层级的主线程(UI线程)才能修改UI控件,后台线程直接触碰UI就会抛出这个错误。
错误代码示例(你大概率是这么写的)
// 错误写法:在后台线程直接更新UI class AngryDebugTask extends AsyncTask<Void, Void, String> { private TextView resultTv; public AngryDebugTask(TextView tv) { this.resultTv = tv; } @Override protected String doInBackground(Void... voids) { // 耗时的数据库查询操作 String dbData = queryDatabase(); // 致命错误:这里在后台线程直接修改TextView resultTv.setText(dbData); return dbData; } private String queryDatabase() { // 模拟数据库耗时操作 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return "数据库返回的内容"; } }
修正后的正确代码
AsyncTask专门提供了主线程回调方法来安全更新UI,核心是把耗时操作和UI操作彻底分离:
// 正确写法:用onPostExecute在主线程更新UI class AngryDebugTask extends AsyncTask<Void, Void, String> { private TextView resultTv; public AngryDebugTask(TextView tv) { this.resultTv = tv; } @Override protected String doInBackground(Void... voids) { // 只在这里做耗时操作:数据库查询、网络请求等,绝不碰UI return queryDatabase(); } // 这个方法自动在主线程执行,安全更新UI @Override protected void onPostExecute(String dbData) { super.onPostExecute(dbData); resultTv.setText(dbData); } private String queryDatabase() { // 模拟数据库耗时操作 try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } return "数据库返回的内容"; } }
如果需要中途更新进度(比如加载提示)
可以用publishProgress()配合onProgressUpdate()实现:
class AngryDebugTask extends AsyncTask<Void, Integer, String> { private TextView resultTv; private ProgressBar progressBar; public AngryDebugTask(TextView tv, ProgressBar pb) { this.resultTv = tv; this.progressBar = pb; } // 任务启动前在主线程初始化UI(比如显示进度条) @Override protected void onPreExecute() { super.onPreExecute(); progressBar.setVisibility(View.VISIBLE); } @Override protected String doInBackground(Void... voids) { // 模拟分步执行的耗时操作,发布进度 for (int i = 0; i < 100; i += 20) { publishProgress(i); try { Thread.sleep(400); } catch (InterruptedException e) { e.printStackTrace(); } } return queryDatabase(); } // 在主线程更新进度UI @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); progressBar.setProgress(values[0]); } @Override protected void onPostExecute(String dbData) { super.onPostExecute(dbData); progressBar.setVisibility(View.GONE); resultTv.setText(dbData); } private String queryDatabase() { return "数据库返回的内容"; } }
关键注意点
- 所有耗时操作(数据库、网络、文件IO)必须放在
doInBackground(),这个方法运行在后台线程; - 所有UI更新操作必须放在
onPreExecute()、onProgressUpdate()、onPostExecute(),这三个方法都自动运行在主线程; - 不要在AsyncTask的其他方法或自定义后台线程里直接操作UI,也不要通过非Handler/AsyncTask回调的方式跨线程更新UI。
内容的提问来源于stack exchange,提问作者Tech Geek
相关产品推荐
相关产品推荐

