Android学习中遇到的问题——新手常见问题之组件(View)刷新失败(计时器刷新失败)

我想组件刷新失败最常见的情况应该是做个计时器类似的东西,发现它不动。当然也不一定是计时器,总之就是类似于每过一段时间将组件刷新一下的功能,这时候经常会发现组件没有动。
我当时遇到这个问题的时候查阅了各种资料,把我学习的书基本全部翻了一遍都没找到原因。最后去一个程序员聚集的群里问了之后才知道是为什么。这个问题很容易犯,所以在这里记录一下,免得自己忘记。

情景描述

首先我们来做一个文本框刷新吧。假设现在要用文本框做一个简单的计时器,显示时间,做个一分钟倒计时,文本框显示当前剩余的秒数。

一般思路

最容易想到的应该是用线程,线程启动后,每睡眠一秒钟刷新一下文本框,让它显示时间的数字-1。

编码

先写一个文本框来显示时间(大致写一下,能看就行了)

<TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="这是一个倒计时计时器:"
        android:textSize="20dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/time"
        android:text="60"
        android:textSize="50dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="30dp"
        android:layout_marginStart="30dp"
        android:layout_marginLeft="30dp"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/title"/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="15dp"
        android:layout_marginLeft="15dp"
        android:text="秒"
        android:textSize="50dp"
        app:layout_constraintStart_toEndOf="@+id/time"
        app:layout_constraintTop_toTopOf="@+id/time" />

页面大概会是这个样子(有点丑,不过问题不大):
在这里插入图片描述
然后为它写一个子线程:

public class MainActivity extends AppCompatActivity {
    private Runnable runnable;
    private TextView time;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        createRunnable();
        Thread mainThread = new Thread(runnable);
        mainThread.start();
    }

    //创建一个runnable
    private void createRunnable() {
        time = findViewById(R.id.time);
        runnable = new Runnable() {
            @Override
            public void run() {
                //循环60次
                for (int i = 0;i < 60;i ++)
                {
                    //每过1秒刷新一次文本框显示的时间
                    try {
                        Thread.sleep(1000);
                        time.setText(Integer.valueOf(time.getText().toString()) - 1 + "");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }


}

一个一般思路下定时刷新文本框的页面就写好了

运行结果

程序看起来好像没有问题,但是拿去模拟器上运行之后我们会发现出现了这样的问题(如果是在手机上运行就会出现闪退):
在这里插入图片描述
当我们把time.setText(Integer.valueOf(time.getText().toString()) - 1 + “”);这条刷新文本框的语句注释掉后就不会出现问题了,但是这样明显不能实现功能。(注意setText()方法中,不是字符串变量的要加上+ “”,否则会因为变量类型问题出现“app has stopped”,即闪退)。
在网上查阅各种资料后很可能会发现找不到原因,这时候可能就会非常头疼,代码看起来明明没有错,可就是运行不了(我就是这样)。后来找了个Android的群,去里面问了大佬之后才知道,原来Android不能够在子线程中更新UI。

原因

Android中的UI控件是非线程安全的,因此在多线程中并发访问可能会导致UI控件进入不可预测的状态,而其他的一些解决这个问题的方法会使得UI控件变得复杂和低效,甚至可能会阻塞某些进程的运行,因此在Android中禁止直接在子线程中修改UI属性。

解决方法

使用Handler机制,在子线程中需要修改UI的地方发送一条消息(Message)给指定Handler,然后在Handler的消息处理方法中对UI进行修改,这样可以将修改UI的任务切换到Handler所在线程中来执行。

成功的代码

public class MainActivity extends AppCompatActivity {
    private TextView time;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        time = findViewById(R.id.time);
        Thread mainThread = new Thread(runnable);
        mainThread.start();
    }

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            //循环60次
            for (int i = 0;i < 60;i ++)
            {
                //每过1秒刷新一次文本框显示的时间
                try {
                    Thread.sleep(1000);
                    /*Message msg = refreshHandler.obtainMessage();
                    msg.what = 1;*/
                    refreshHandler.sendMessage(new Message());      //将消息发送给refreshHandler
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };


    Handler refreshHandler = new Handler()
    {
        @Override
        public void handleMessage(Message msg)
        {
            //刷新显示时间的文本框
            time.setText(Integer.valueOf(time.getText().toString()) - 1 + "");
        }
    };


}

这样就可以顺利运行了,这也是对Handler的一个简单的应用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值