android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original
2016-05-19 11:13
483 查看
当应用程序启动,创建了一个叫“main”的线程,用于管理UI相关,又叫UI线程。其他线程叫工作线程(Work
Thread)。
Single Thread Model
一个组件的创建并不会新建一个线程,他们的创建都在UI线程中进行,包括他们的回调方法,如onKeyDown()。
当在UI线程中进行某些耗时的操作时,将会阻塞UI线程,一般阻塞超过5秒就会显示一个ANR对话框。
UI线程是非线程安全的,所以,不能在工作线程中操作UI元素。
两个原则
Do not block the UI thread (不要阻塞UI线程)
Do not access the Android UI toolkit from outside the UI thread (不要在工作线程中操作UI元素)
在工作线程更新UI方法
Activity.runOnUiThread(Runnable)
Handler
sendMessage(Message)
post(Runnable)
AsyncTask
execute()
doInBackground()
onPostExecute()
例子程序
HandlerActivity01
在工作线程中进行UI操作。
HandlerActivity02
Handler的两个重要方法:sendMessage和post。
HandlerActivity03
官方推荐最佳方法。
HandlerActivity01主要代码:
Java代码
btnEnd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run()
{
//在新建的线程(工作线程)中改变Button的文字
btnEnd.setText("Text Changed in Sub Thread");
}
}).start();
}
});
这是一种错误的做法,运行程序,会报错误:
Java代码
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
HandlerActivity02主要代码:
Java代码
public class HandlerActivity02 extends Activity
{
private int title = 0;
Button btnStart,btnEnd;
private Handler mHandler = new Handler()
{
public void handleMessage(Message msg)
{
//更新UI
switch (msg.what)
{
case 1:
updateTitle();
break;
}
};
};
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnStart = (Button)findViewById(R.id.start);
btnEnd = (Button)findViewById(R.id.end);
//新启动一个线程,进行耗时操作
Timer timer = new Timer();
//每六秒执行一次MyTask的run方法
timer.scheduleAtFixedRate(new MyTask(this), 1, 6000);
}
private class MyTask extends TimerTask
{
private Activity context;
MyTask(Activity context)
{
this.context = context;
}
@Override
public void run()
{
//耗时操作略....
//更新UI方法 1
Message message = new Message();
message.what = 1;
mHandler.sendMessage(message);
//更新UI方法 2
mHandler.post(updateThread);
//更新UI方法 3
context.runOnUiThread(updateThread);
}
}
public void updateTitle()
{
setTitle("Welcome to Mr Wei's blog " + title);
title++;
}
Runnable updateThread = new Runnable()
{
@Override
public void run()
{
//更新UI
btnStart.setText(String.valueOf(title));
btnEnd.setText(String.valueOf(title));
}
};
}
这里有个容易出错的地方,在更新UI方法2和3中,我们传入的参数是一个Runnable对象,一般认为这就会启动一个新的线程,而且常有人在这个Runnable对象的run方法中进行耗时操作。看过这块的源码就会知道,其实,android只是调用了这个Runnable对象的run方法而已,并没有启动新的线程,而且我们不应该在run方法中进行耗时操作,因为这个run方法最终是在UI线程里面执行的。也就是说,run方法里面只应该放更新UI的代码,handleMessage方法也一样。
如果你要看这部分源代码的话,相信这个图对你会有帮助:
HandlerActivity03主要代码:
Java代码
public class HandlerActivity03 extends Activity
{
Button btnStart;
@Override
protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnStart = (Button)findViewById(R.id.start);
btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//开始执行AsyncTask,并传入某些数据
new LongTimeTask().execute("New Text");
}
});
}
private class LongTimeTask extends AsyncTask
{
@Override
protected String doInBackground(String... params)
{
try
{
//线程睡眠5秒,模拟耗时操作,这里面的内容Android系统会自动为你启动一个新的线程执行
Thread.sleep(5000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
return params[0];
}
@Override
protected void onPostExecute(String result)
{
//更新UI的操作,这里面的内容是在UI线程里面执行的
btnStart.setText(result);
}
}
}
这个方法确实挺好,因为它为你封装了许多操作,你只需要记住在doInBackground方法中写耗时操作的代码,在onPostExecute方法中写更新UI的方法就行了。
Thread)。
Single Thread Model
一个组件的创建并不会新建一个线程,他们的创建都在UI线程中进行,包括他们的回调方法,如onKeyDown()。
当在UI线程中进行某些耗时的操作时,将会阻塞UI线程,一般阻塞超过5秒就会显示一个ANR对话框。
UI线程是非线程安全的,所以,不能在工作线程中操作UI元素。
两个原则
Do not block the UI thread (不要阻塞UI线程)
Do not access the Android UI toolkit from outside the UI thread (不要在工作线程中操作UI元素)
在工作线程更新UI方法
Activity.runOnUiThread(Runnable)
Handler
sendMessage(Message)
post(Runnable)
AsyncTask
execute()
doInBackground()
onPostExecute()
例子程序
HandlerActivity01
在工作线程中进行UI操作。
HandlerActivity02
Handler的两个重要方法:sendMessage和post。
HandlerActivity03
官方推荐最佳方法。
HandlerActivity01主要代码:
Java代码
btnEnd.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run()
{
//在新建的线程(工作线程)中改变Button的文字
btnEnd.setText("Text Changed in Sub Thread");
}
}).start();
}
});
这是一种错误的做法,运行程序,会报错误:
Java代码
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
HandlerActivity02主要代码:
Java代码
public class HandlerActivity02 extends Activity
{
private int title = 0;
Button btnStart,btnEnd;
private Handler mHandler = new Handler()
{
public void handleMessage(Message msg)
{
//更新UI
switch (msg.what)
{
case 1:
updateTitle();
break;
}
};
};
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnStart = (Button)findViewById(R.id.start);
btnEnd = (Button)findViewById(R.id.end);
//新启动一个线程,进行耗时操作
Timer timer = new Timer();
//每六秒执行一次MyTask的run方法
timer.scheduleAtFixedRate(new MyTask(this), 1, 6000);
}
private class MyTask extends TimerTask
{
private Activity context;
MyTask(Activity context)
{
this.context = context;
}
@Override
public void run()
{
//耗时操作略....
//更新UI方法 1
Message message = new Message();
message.what = 1;
mHandler.sendMessage(message);
//更新UI方法 2
mHandler.post(updateThread);
//更新UI方法 3
context.runOnUiThread(updateThread);
}
}
public void updateTitle()
{
setTitle("Welcome to Mr Wei's blog " + title);
title++;
}
Runnable updateThread = new Runnable()
{
@Override
public void run()
{
//更新UI
btnStart.setText(String.valueOf(title));
btnEnd.setText(String.valueOf(title));
}
};
}
这里有个容易出错的地方,在更新UI方法2和3中,我们传入的参数是一个Runnable对象,一般认为这就会启动一个新的线程,而且常有人在这个Runnable对象的run方法中进行耗时操作。看过这块的源码就会知道,其实,android只是调用了这个Runnable对象的run方法而已,并没有启动新的线程,而且我们不应该在run方法中进行耗时操作,因为这个run方法最终是在UI线程里面执行的。也就是说,run方法里面只应该放更新UI的代码,handleMessage方法也一样。
如果你要看这部分源代码的话,相信这个图对你会有帮助:
HandlerActivity03主要代码:
Java代码
public class HandlerActivity03 extends Activity
{
Button btnStart;
@Override
protected void onCreate(Bundle savedInstanceState)
{
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnStart = (Button)findViewById(R.id.start);
btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//开始执行AsyncTask,并传入某些数据
new LongTimeTask().execute("New Text");
}
});
}
private class LongTimeTask extends AsyncTask
{
@Override
protected String doInBackground(String... params)
{
try
{
//线程睡眠5秒,模拟耗时操作,这里面的内容Android系统会自动为你启动一个新的线程执行
Thread.sleep(5000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
return params[0];
}
@Override
protected void onPostExecute(String result)
{
//更新UI的操作,这里面的内容是在UI线程里面执行的
btnStart.setText(result);
}
}
}
这个方法确实挺好,因为它为你封装了许多操作,你只需要记住在doInBackground方法中写耗时操作的代码,在onPostExecute方法中写更新UI的方法就行了。
相关文章推荐
- 两分钟彻底让你明白Android Activity生命周期(图文)!
- android设计模式之工厂模式
- Android的Drawable缓存机制源码分析
- 第一次使用Android Studio时你应该知道的一切配置
- Android的读写文件权限
- Android 完美实现图片圆角和圆形(对实现进行分析)
- Android开发笔记----Activity再学习(生命周期、启动模式、intent-filter)
- android 4.4 支持透明状态栏和透明导航栏
- ubuntu 12.04.5 LTS搭建海思android SDK编译环境遇到的问题总结
- Android探索之ContentProvider熟悉而又陌生的组件
- Android笔记----动画、属性动画
- Android传感器及简单应用
- android xml 布局进阶
- Android系统中Parcelable和Serializable的区别
- Android Studio设置代理
- SoftReference 为什么被 Android “放弃”
- Android ListView分类/分组效果 - 第二种实现方式
- android开发中常用的快捷键
- .gitignore for Android Studio 怎么写?
- 【FastDev4Android框架开发】重写WebView网页加载以及JavaScript注入详解(二十三)