当前位置:编程学习 > wap >>

线程更新UI

最近发现一个问题,当在activity中启动一个线程,这个线程类有一个构造参数是textview时,在run中更新这个textview中的setText不出错,,

反而new 无参线程类后,再setTextView(TextView)后在run中setText()就出错,这是怎么回事呢??求教  --------------------编程问答-------------------- 非UI线程是没有更新UI的权限的。 --------------------编程问答-------------------- 为什么上面其中一个办法就能更新呢? --------------------编程问答-------------------- - -第一个能更新么~ 想看看你怎么写的。~ 非UI线程更新UI会报错的 --------------------编程问答--------------------
引用楼主 ghyghost 的回复:
最近发现一个问题,当在activity中启动一个线程,这个线程类有一个构造参数是textview时,在run中更新这个textview中的setText不出错,,

反而new 无参线程类后,再setTextView(TextView)后在run中setText()就出错,这是怎么回事呢??求教


你确定自己启动了一个线程? --------------------编程问答-------------------- 顶一下!
发一下代码:
package extthread;

import android.widget.TextView;

public class MyUpdateUIThread extends Thread {

private TextView textView1;

public MyUpdateUIThread(TextView textView1) {
super();
this.textView1 = textView1;
}

@Override
public void run() {
super.run();
textView1.setText("zzzzzzzzzzzzzz");
}

}

------------------------------
package testtest.test.run;

import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import extthread.MyUpdateUIThread;

public class Main extends Activity {
TextView textView;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

textView = (TextView) this.findViewById(R.id.textView1);
Thread thread = new Thread(new MyUpdateUIThread(textView));
thread.start();

}
};

上面用线程代码不出错! --------------------编程问答-------------------- 只能在主线程中操作UI。
所以如果你想在非主线程中操作UI,就要在使用消息发送的方式。

例:
在主线程中定义handler:

Handler h=new Handler(){
public void handleMessage(Message msg){
//你的操作



在非主线程这样使用:
h.sendMessage(Message.obtain());  //也可以新建一个Message对象 --------------------编程问答--------------------
楼上的是可以的。

以至于你的代码为什么没错,估计是因为把对象传过来了,而对象是在主线程中new的。所以就可以更新了吧。 --------------------编程问答-------------------- --------------------编程问答-------------------- Handler h=new Handler(){
public void handleMessage(Message msg){
//你的操作



在非主线程这样使用:
h.sendMessage(Message.obtain()); //也可以新建一个Message对象
这种方法可行! --------------------编程问答-------------------- Thread thread = new Thread(new MyUpdateUIThread(textView));
thread.start();
首先看到你这样创建线程时,我震惊了~你的MyUpdateUIThread纯当实现Runnable接口的类来使用了~
直接
Thread thread = new MyUpdateUIThread(textView);
thread.start();
就好了。

然后我直接测试下。。发现问题了。。
居然真的能textView1.setText("zzzzzzzzzzzzzz");不报错。
这太操蛋了~于是我分别在onCreate和run方法里加了打印语句Thread.currentThread().getName()
10-31 09:55:15.348: VERBOSE/King(1228): in run : Thread-10
10-31 09:56:33.088: VERBOSE/King(1328): in Activity : main
明明是不同线程是确定了的~
然后。。。我在run方法里加了个sleep()想看看可不可以隔几秒再把“zzzzzzz”更新到UI上去,如下:
public void run() {
super.run();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
textView1.setText("zzzzzzzzzzzzzz");
Log.v("King", "in run : "+Thread.currentThread().getName());
}
这样就出错了。
我也纳闷了。为了不sleep能正常运行。应该是会出错的啊。
建议没试过的童鞋都试一下。。惊天发现啊~


--------------------编程问答-------------------- 正在跟踪这个问题,确实蛮有趣的,不过这和UI单线程不冲突,只是我们理解不全面造成的,网上的单线程安全很少考虑这个问题。这个单线程的内部实现机制有关吧,继续关注。 --------------------编程问答-------------------- 测试过程发现,如果这个线程是通过监听器启动的,也就是说不是在主线程中直接启动,就会报错。现在想来和java的线程触发可能有关联。 --------------------编程问答--------------------
引用 10 楼 t5721654 的回复:
Thread thread = new Thread(new MyUpdateUIThread(textView));
thread.start();
首先看到你这样创建线程时,我震惊了~你的MyUpdateUIThread纯当实现Runnable接口的类来使用了~
直接
Thread thread = new MyUpdateUIThread(textView);
thread.sta……



run方法的里面的sleep()抛的异常是什么? --------------------编程问答-------------------- sleep方法不会抛异常。
textView1.setText("zzzzzzzzzzzzzz");这句才会。
异常信息如下,大家应该曾经都见过~:
10-31 11:47:04.118: ERROR/AndroidRuntime(3878): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

我估计是在主线程初始化界面完毕之前其他线程能改UI的内容吧...或许是在onResume执行完毕之前...像什么通过监听器触发或者sleep了几秒后,UI初始化已经完毕了,于是不能在别的线程里改了... --------------------编程问答-------------------- 呵呵,感谢大家的顶贴,继续顶一顶, --------------------编程问答-------------------- ding顶一下吧
--------------------编程问答-------------------- --------------------编程问答--------------------
引用 10 楼 t5721654 的回复:
Thread thread = new Thread(new MyUpdateUIThread(textView));
thread.start();
首先看到你这样创建线程时,我震惊了~你的MyUpdateUIThread纯当实现Runnable接口的类来使用了~
直接
Thread thread = new MyUpdateUIThread(textView);
thread.st……


都是在onCreate里进行操作。都是不耗时的简单线程。这样有什么意义呢?如果你的线程在onResume之后执行,100%出错。 --------------------编程问答-------------------- 我也测试了一下Thread.sleep()
这句话放到activity里面就没有问题,放到run里面就不行,求解 --------------------编程问答-------------------- 刚才测试了,onResume中启动线程依然没报错,挺神奇的! --------------------编程问答--------------------
引用 20 楼 b87936260 的回复:
刚才测试了,onResume中启动线程依然没报错,挺神奇的!


不神奇。onResume之后就会报错了。比如点击button启动 --------------------编程问答-------------------- 老问题了,用HANDLE实现 --------------------编程问答-------------------- 顶!顶!顶!顶! --------------------编程问答-------------------- 要通过handler --------------------编程问答-------------------- 用handler安全无痛苦。。。 --------------------编程问答-------------------- 在线程的run方法中使用runOnUiThread来更新TextView
Your_Current_Activity.this.runOnUiThread(new Runnable() {
    public void run() {

             //update textview here
    }
});

改变你的 MyUpdateUIThread 构造函数:

private TextView textView1;
Activity activity;
public MyUpdateUIThread(TextView textView1,Activity activity) {
super();
this.textView1 = textView1;
this.activity=activity;
}

@Override
public void run() {
super.run();
activity.runOnUiThread(new Runnable() {
    public void run() {

       //update textview here
       textView1.setText("zzzzzzzzzzzzzz");
    }
});

}

然后在 Main Activity 中创建 MyUpdateUIThread 对象:
Thread thread = new Thread(new MyUpdateUIThread(textView,Main.this));





 
--------------------编程问答-------------------- 在oncreate和onresume之前view是没有调用invalidate()方法,所以调用到ViewRoot里面的    void checkThread() {
        if (mThread != Thread.currentThread()) {
            throw new CalledFromWrongThreadException(
                    "Only the original thread that created a view hierarchy can touch its views.");
        }
    }这个方法,所以不会有异常抛出,而settext的字符已经设置进去了,在onresume后得到焦点,再invalidate()就出现了在线程设置的字符串 --------------------编程问答-------------------- 还没完?给分吧 --------------------编程问答-------------------- 还没完?给分吧+1 --------------------编程问答-------------------- 确实挺奇怪的,用handler吧。。。 --------------------编程问答-------------------- 一般只有在Activity的onResume()回调后,才会通过UI Thread建立View 树,当然最重要的是ViewRoot对象,如
你直接通过一个线程run时,可能onResume()方法还没有回调,此时当然能调用View.setXXX(),在onResume()方法回调后,一定会报异常的。这样就可以解释10L同学的答案了。 --------------------编程问答-------------------- .net可以通过delegate进行线程更新UI线程,java不知道怎么搞 --------------------编程问答--------------------
引用 31 楼 qinjuning 的回复:
一般只有在Activity的onResume()回调后,才会通过UI Thread建立View 树,当然最重要的是ViewRoot对象,如
你直接通过一个线程run时,可能onResume()方法还没有回调,此时当然能调用View.setXXX(),在onResume()方法回调后,一定会报异常的。这样就可以解释10L同学的答案了。


onResume里面更新也没报错 --------------------编程问答--------------------
引用 33 楼 zz307926240 的回复:
引用 31 楼 qinjuning 的回复:一般只有在Activity的onResume()回调后,才会通过UI Thread建立View 树,当然最重要的是ViewRoot对象,如
你直接通过一个线程run时,可能onResume()方法还没有回调,此时当然能调用View.setXXX(),在onResume()方法回调后,一定会报异常的。这样就可以解释10L同学的答……


  基本原理是这样的,但由于这些行为都涉及了很多IPC操作,导致onResume()后,UI Thread建立View 树还没有成功,此时,依旧可以通过线程操作View对象了。不过View添加至窗口后,就只能在UI Thread更新了。 --------------------编程问答--------------------
引用 31 楼 qinjuning 的回复:
一般只有在Activity的onResume()回调后,才会通过UI Thread建立View 树,当然最重要的是ViewRoot对象,如
你直接通过一个线程run时,可能onResume()方法还没有回调,此时当然能调用View.setXXX(),在onResume()方法回调后,一定会报异常的。这样就可以解释10L同学的答案了。
--------------------编程问答-------------------- 用 Handler --------------------编程问答-------------------- 在子线程中除了可以改变textview,button的内容外,还有什么进度条,button,textview等等的显示(setvisibile属性),弄得我一时纳闷了,什么才是UI?不是子线程不能改变UI么? --------------------编程问答--------------------
引用 37 楼 junkimrx 的回复:
在子线程中除了可以改变textview,button的内容外,还有什么进度条,button,textview等等的显示(setvisibile属性),弄得我一时纳闷了,什么才是UI?不是子线程不能改变UI么?

先百度或google一下吧……
补充:移动开发 ,  Android
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,