Android Studio:用handler实现计数

一、哪里需要Handler?

1. UI 更新只能在主线程(UI 线程)中进行

在 Android 中,UI 操作必须在主线程 中进行,不能直接在子线程中更新 UI。比如,修改 TextView 的内容、更新按钮的状态等操作都只能在主线程中执行。如果你尝试在子线程中直接更新 UI,程序会抛出 android.view.ViewRootImpl$CalledFromWrongThreadException 异常。

例如,假设你在子线程做了一个耗时操作,想要更新界面上的信息:

java 复制代码
new Thread(new Runnable() {
    @Override
    public void run() {
        // 做一些耗时操作(例如网络请求)
        tvMessage.setText("任务完成!");  // 错误:子线程不能直接更新 UI
    }
}).start();

上面代码会崩溃,因为 setText() 方法在 主线程 上调用,而这段代码在 子线程 中执行。

2. 如何解决这个问题?

我们需要将子线程中的任务发送到主线程来执行。这时,Handler 就可以帮上忙。你可以使用 Handler 将需要更新 UI 的操作传递到主线程,避免直接在子线程操作 UI。

Handler 解决了哪些问题?

1. 跨线程通信:

Handler 使得不同线程之间的通信变得简单。比如你在子线程执行一个耗时操作,完成后想更新主线程的 UI,Handler 可以帮助你将任务传递到主线程。

2. 延迟执行任务:

有时候你需要让某个任务在一段时间后再执行,或者周期性地执行。Handler 提供了 postDelayed()post() 方法,允许你延迟执行任务或定时执行任务。

二、线程方法回顾

Java中简单的线程创建方法:

方式 1:继承 Thread

步骤:

  1. 创建一个类并继承 Thread 类。
  2. 重写 run() 方法,在其中编写要执行的任务。
  3. 创建 Thread 对象并调用 start() 方法启动线程。
java 复制代码
class MyThread extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ":正在执行 " + i);
        }
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyThread thread1 = new MyThread();
        MyThread thread2 = new MyThread();
        
        thread1.start();  // 启动线程
        thread2.start();  // 启动线程
    }
}

输出示例(结果顺序不确定,因为线程是并发执行的):

java 复制代码
Thread-0:正在执行 0
Thread-1:正在执行 0
Thread-0:正在执行 1
Thread-1:正在执行 1
...

方式 2:实现 Runnable 接口(推荐)

步骤:

  1. 创建一个类并实现 Runnable 接口。
  2. 实现 run() 方法,编写任务逻辑。
  3. 创建 Thread 对象时,将 Runnable 实现类传入构造方法。
  4. 调用 start() 启动线程。
java 复制代码
class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + ":执行任务 " + i);
        }
    }
}

public class RunnableExample {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread1 = new Thread(myRunnable);
        Thread thread2 = new Thread(myRunnable);

        thread1.start();
        thread2.start();
    }
}

Runnable 方式避免 Java 单继承的限制,适用于多个线程共享同一个任务逻辑。

三、用handler实现计数器

准备一个简单的页面

XML 复制代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <Button
        android:id="@+id/btn_count"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="开始计数"
        android:textColor="@color/black"
        android:textSize="17sp" />

    <TextView
        android:id="@+id/tv_result"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingLeft="5dp"
        android:textColor="@color/black"
        android:textSize="17sp" />

</LinearLayout>

页面效果:

要实现的功能很简单,点击开始计数,下面的文本框内开始从0开始计数。这就意味着每过一秒就需要更新UI。问题在于,如何控制每次计数的中间间隔正好是1秒。

handler就有这样的机制,每隔固定时间启动一次任务类:

主页面完整代码

java 复制代码
@SuppressLint("SetTextI18n")
public class HandlerPostActivity extends AppCompatActivity implements View.OnClickListener {
    private Button btn_count; // 声明一个按钮对象
    private TextView tv_result; // 声明一个文本视图对象
    private boolean isStarted = false; // 是否开始计数
    private Handler mHandler = new Handler(Looper.myLooper()); // 声明一个处理器对象
    private int mCount = 0; // 计数值
    // 定义一个计数任务
    private Runnable mCounter = new Runnable() {
        @Override
        public void run() {
            mCount++;
            tv_result.setText("当前计数值为:" + mCount);
            mHandler.postDelayed(this, 1000); // 延迟一秒后重复计数任务
        }
    };
    
    @SuppressLint("MissingInflatedId")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_post);
        btn_count = findViewById(R.id.btn_count);
        tv_result = findViewById(R.id.tv_result);
        btn_count.setOnClickListener(this); // 设置按钮的点击监听器
    }
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.btn_count) {
            if (!isStarted) { // 不在计数,则开始计数
                btn_count.setText("停止计数");
                mHandler.post(mCounter); // 立即启动计数任务
            } else { // 已在计数,则停止计数
                btn_count.setText("开始计数");
                mHandler.removeCallbacks(mCounter); // 立即取消计数任务
            }
            isStarted = !isStarted;
        }
    }
    
}
XML 复制代码
private Handler mHandler = new Handler(Looper.myLooper());

什么是 Handler

Handler 是 Android 中用来处理线程任务的工具,它的作用是把任务(通常是一个 Runnable)发送到线程中执行,或者延迟执行某个任务。我们通常用它来在子线程中做一些耗时操作,然后更新 UI(UI 更新必须在主线程进行)。

什么是 Looper

Looper 是 Android 中用来管理消息循环的机制。每个线程都可以有一个 Looper,负责处理线程中的消息。主线程(UI 线程)默认有一个 Looper,而子线程则需要手动创建一个 Looper

  • 在主线程中,Looper.myLooper() 返回的是主线程的 Looper
  • 在子线程中,你需要先调用 Looper.prepare() 创建一个 Looper,然后再创建 Handler

Looper.myLooper() 获取当前线程的 Looper 对象。

  • 如果你在 主线程 中执行这行代码,myLooper() 会返回主线程的 Looper
  • 如果你在 子线程 中执行这行代码,myLooper() 会返回当前子线程的 Looper

Handler 的一个重要用途是在子线程中执行一些操作后,将任务切换到主线程执行。比如,假设你在子线程中做一些耗时操作(比如网络请求),完成后需要更新 UI(这只能在主线程进行),这时你就需要使用 Handler 来将任务切换到主线程执行。

2.2 定义计时任务

XML 复制代码
private Runnable mCounter = new Runnable() {
        @Override
        public void run() {
            mCount++;
            tv_result.setText("当前计数值为:" + mCount);
            mHandler.postDelayed(this, 1000); // 延迟一秒后重复计数任务
        }
    };

效果:

相关推荐
yoona10203 分钟前
Rust编程语言入门教程 (七)函数与控制流
开发语言·rust·区块链·学习方法
菠菠萝宝6 分钟前
【Java八股文】08-计算机网络面试篇
java·计算机网络·http·面试·https·udp·tcp
alien爱吃蛋挞10 分钟前
【数据结构】队列(Queue)
java·数据结构
taoyong00111 分钟前
Python装饰器本质250220
开发语言·python
zbailing12 分钟前
three.js之特殊材质效果
开发语言·javascript·材质
奔跑吧邓邓子21 分钟前
【Python爬虫(23)】探秘Python爬虫数据存储:MongoDB实战指南
开发语言·爬虫·python·mongodb·实战
独孤求败Ace34 分钟前
第45天:Web开发-JavaEE应用&动态接口代理&原生反序列化&危险Invoke&重写方法&利用链
java·开发语言
杰九42 分钟前
【全栈】SprintBoot+vue3迷你商城-细节解析(1):Token、Jwt令牌、Redis、ThreadLocal变量
java·spring boot·redis
我真不会起名字啊1 小时前
“深入浅出”系列之C++:(8)libevent 库
开发语言·c++·windows
Allen Bright1 小时前
【JMeter使用-2】JMeter中Java Request采样器的使用指南
java·开发语言·jmeter