一,问题引入
异常: Only the original thread that created a view hierarchy can touch its views的解决方案
为什么会有这种异常产生呢?
当每个应用程序apk第一次启动时,Android会同时启动一个对应的
主线程(Main Thread),
主线程负责处理与UI相关的事件,如:用户的按键事件,用户接触屏幕的事件以及屏幕绘图事件,
并把相关的事件分发到对应的组件进行处理,所以主线程通常又被叫做
UI线程。
但是在开发Android应用时必须遵守单线程模型的原则:
Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行,如果在非UI线程中直接操作UI线程,
会抛出android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that
created a view hierarchy can touch its views,这与普通的java程序不同。
由于UI线程负责事件的监听和绘图,因此,必须保证UI线程能够随时响应用户的需求,
UI线程里的操作应该向中断事件那样短小,费时的操作(如网络连接)需要另开线程,
否则,如果UI线程超过5s没有响应用户请求,会弹出对话框提醒
用户终止应用程序(ANP)。
如果在新开的线程中需要对UI进行设定,就可能违反单线程模型,
因此android采用一种的Message Queue机制保证
线程间通信。
Message Queue是一个消息队列,用来存放通过Handler发送的消息。
Android在第一启动程序时会默认会为UI thread创建一个关联的消息队列,
可以通过Looper.myQueue()得到当前线程的消息队列,用来管理程序的一些上层组件,
activities,broadcast receivers 等,你可以在自己的子线程中创建Handler与UI thread通讯。
Handler会向message queue通过两种方法发送消息:send或post。
这两种消息都会插在message queue队尾并按先进先出执行,
但通过这两种方法发送的消息执行的方式略有不同:
1)通过send发送的是一个message对象, 会被handler的 handleMessage()函数处理;
2)而通过post方法发送的是一个runnable对象,则会自己执行。
每个带图形界面的应用启动后,都会创建一个主线程,可称之为UI线程。
这个线程自动就会创建一个message queue,来自于系统的消息都会投放到这个message queue里面,
并按先进先出的顺序处理。
UI线程图形界面中的view可通过post方法向GUI线程的message queue投递一个runnable。
对于除UI线程以外的其他线程,创建时缺省并没有message queue,
而对于UI线程,则可以直接(比如在onCreate)创建一个handler并重载handleMessage,
省去创建message queue的过程。
二,解决方案一
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
if(msg.what == 0)//成功
{
Log.d(TAG, "**********************start****************************");
playVideo(strVideoPath);//包含更新界面的方法
Log.d(TAG, "***********************end*****************************");
}
}
};
new Thread()
{
@Override
public void run()
{
// handler.post(runnableUi);
try{
Log.d(TAG, "######################start###############################");
handler.sendEmptyMessage(0);//UI线程外想更新UI线程
Log.d(TAG, "######################end###############################");
}
catch(Exception e)
{
Log.d(TAG, "***************************"+e.toString());
}
}
}.start();
三,解决方案二
Runnable runnableUi=new Runnable(){
@Override
public void run() {
//更新界面
textView.setText("the Content is:"+content);
}
};
new Thread(){
public void run(){
content=df.downLoadFiles();
handler.post(runnableUi);
}
}.start();
四,特别注意,引入函数包得时候,不要引入错误的包
import android.os.Handler; //这是正确的包
import java.util.logging.Handler;//不是这个包,而是上一个