子线程可不可以更新UI?

子线程通常不直接更新UI,但可以通过特定方法实现,如Android的runOnUiThread()或Handler。子线程直接修改UI可能导致错误,应确保在主线程执行UI更新操作。当使用TimerTask或进度条更新时,系统会自动处理线程同步。

面试回答:子线程不可以更新UI。

如果面试官问得比较深,那就这样回答他:一般来说子线程是不可以更新UI的,但是非要用子线程更新UI,那也可以做到。

在讨论这个问题之前先普及几个常识,即:什么叫更新UI?

什么叫线程?

什么是主线程?

什么是子线程?

更新UI - - 就是改变页面效果,视觉上可以看到的变化。

线程 - - - 一个程序运行时,内部可能包含了多个顺序执行流,每个顺序执行 流就是一个线程。(借鉴《疯狂Java讲义》)

  (这种解释太官方了是吧,简言之,线程就是一个小程序,可以实现一定的功能)

主线程 - - 当一个程序启动时,就有一个进程被操作系统创建,与此同时一个线程也立刻运行,该线程通常叫做程序的主线程(Main Thread)。需要单独创建的线程都是这个主线程的子线程。(借鉴百度)

子线程 - - 主线程之外的线程都是子线程。

  直白来讲,所有New 类名 (){......}省略号部分的方法都是子线程。

 

下面讨论一下子线程可不可以更新UI

1、Android规定子线程是不可以更新UI界面的,但是也有例外,

如以下代码:

public class MainActivity extends Activity {

private TextView tv;

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

tv = (TextView) findViewById(R.id.tv);

new Thread(){

public void run() {

tv.setText("你好!!!");

};

}.start();

}  }

这样写,是可以更新UI的,但是仅限于第一次开启程序,线程之间的执行机会是平等的,其实严格意义上讲,UI界面并不是被子线程改掉了,而是程序运行的时候,先走的子线程这段代码,直接就让TextView显示的“你好!!!”,没有给“helloworld”显示的机会。

 

2、如果给上面这个子线程一个休眠时间,也就是说让其在主线程之后执行,就不能更新UI界面了。

代码如下:

public class MainActivity extends Activity {

private TextView tv;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

tv = (TextView) findViewById(R.id.tv);

new Thread(){

public void run() {

try {

sleep(2000);

} catch (InterruptedException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

tv.setText("你好!!!");

};

}.start();

}  }

这是因为,程序进入之后主线程先执行,子线程有休眠时间限制,所以晚于主线程被执行,这样它就是正在意义上的“子线程”,不能更新UI

 

3、有一个方法是可以在子线程更新UI的,runOnUiThread()方法。

代码如下:

public class MainActivity extends Activity {

private TextView tv;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

tv = (TextView) findViewById(R.id.tv);

new Thread(){

public void run() {

runOnUiThread(new Runnable() {

public void run() {

tv.setText("你好!!!");

}

});

};

}.start();

}

 

先看一下runOnUiThread()这个方法的原理:

利用Activity.runOnUiThread(Runnable)把更新UI的代码创建在Runnable中,然后在需要更新UI时,把这个Runnable对象传给Activity.runOnUiThread(Runnable)。  

Runnable对像就能在UI程序中被调用。如果当前线程是UI线程(主线程),那么行动是立即执行。如果当前线程不是UI线程,操作就要发布到事件队列的UI线程(即,程序会判断更新UI操作是不是在主线程,如果是,那就直接更新;如果不是,那就把信息传递给主线程,最终还是由主线程操作,可以看一下这个方法的底层代码)。

runOnUiThread()这个方法的底层代码:

    public final void runOnUiThread(Runnable action) {

        if (Thread.currentThread() != mUiThread) {

            mHandler.post(action);

        } else {

            action.run();

        }

}

 

 

总结:

目前来看,子线程想更新UI就两种方法:

1、runOnUiThread()方法

2、通过Handler把信息传递给主线程,让主线程去做更新操作。

    注意--如果在服务(Service)里,子线程做更新UI操作,就必须通过Handler将信息传递给主线程来完成,因为服务里,没有runOnUiThread()这个方法,它是Activity的方法。

严格意义上讲,子线程不可以直接更新UI,无论哪种子线程更新UI,其实都是想方设法地把信息交给主线程来做更新。

    既然子线程不可以更新UI,那为什么又有“TimerTaskrun方法中的语句相当于在子线程执行进度条可以在子线程更新UI”这样的说法呢?

首先,针对TimerTaskrun方法中的语句相当于在子线程执行”,这种说法对就对在了“相当于”三个字上,因为TimerTask是通过new得到的。

其次,“进度条可以在子线程更新UI”,是因为setProgress()这个方法会自动交给主线程去操作,也就是说对于setProgress()这个方法,runOnUiThread()可有可无,因为两者目的都是将子线程的信息发送到主线程去操作。这就要区别于更新控件显示内容的setText()方法,此方法如果放到子线程就要报错了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值