android入门(六) UI线程阻塞及其优化

本文介绍了Android中主线程(UI线程)的作用及其在应用程序中的重要性。文章详细阐述了如何避免阻塞UI线程,并提供了两种解决方案:使用View.post(Runnable)方法和继承AsyncTask类,以确保在后台线程完成耗时操作后能够安全地更新UI。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当一个应用程序启动之后,android系统会为这个应用程序创建一个主线程(Main Thread),它负责渲染视图,分发事件到响应监听器并执行,对界面进行轮询的监听。因此,一般也叫做“UI线程”(UI Thread)。

android系统不会给应用程序的多个元素组件建立多个线程来执行。一个视图(Activity)中的多个view组件运行在同一个UI线程当中。因此,多个view组件的监听器的执行可能会相互影响。

如果在UI线程中做一些比较耗时的操作,比如访问网络或者数据库,都可能阻塞UI线程,导致事件停止分发(包括绘制事件)。对于用户来说,应用看起来像是卡住了,更坏的情况是,如果UI线程阻塞时间太长(超过5秒),android系统会弹出ANR(application not responding)的对话框,询问用户是否关闭应用程序。

针对此问题,Android的单线程模型有两条原则:
1,不要阻塞UI线程;
2,不要在UI线程之外的其他线程中,对视图当中的组件(主要是这两个包中的组件:android.widget和android.view)进行设置。

可能很多同学想到解决第一个问题的办法是new一个新的线程进行耗时操作,但这往往会触发第二个问题,系统会抛出异常:Only the original thread that created a view hierarchy can touch its views(只有创建view的那个线程(UI线程)才能对其进行修改)。

当然,android官方提供了解决方案:
一、通过View.post(Runnable)方法

public void onClick(View v) {
    new Thread(new Runnable() {//new一个新线程来执行耗时操作,解决问题一
        @Override
        public void run() {
            try{
                Thread.sleep(5000);//执行耗时操作
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            v.post(new Runnable() {//使用View.post(Runnable)进行组件设置,解决问题二
                @Override
                public void run() {
                    TextView view = (TextView) v;
                    v.setText("设置组件")
                }
            });
        }
    }).start();
}

这种解决方法中,UI线程与我们新建的线程之间的关系类似于生产者与消费者之间的关系,新线程通过View.post(Runnable)方法在任务队列中加入任务,而UI线程对任务队列进行轮询,有任务的话就拿出来执行,修改界面。

但这种解决方法可读性和维护性较差。

二、继承AsyncTask类(异步任务)
在activity中建一个内部类,继承AsyncTask,重写doInBackground()和onPostExecute()方法

    private class task extends AsyncTask<String, Void, String>{
        @Override
        protected String doInBackground(String... params) {
            try {
                Thread.sleep(5000);// 执行耗时操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "通过AsyncTask设置";
        }

        @Override
        protected void onPostExecute(String result) {
            textView.setText(result);// textView写成Activity的成员变量,方便内部类调用
        }

    }

doInBackground的返回值会做为onPostExecute的参数执行。

然后我们只需要在需要调用的时候new task().execute()就可以执行了。

第二种方案其实是对第一种方法进行了封装,提高了代码可读性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值