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); // 延迟一秒后重复计数任务
        }
    };

效果:

相关推荐
Scott9999HH2 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔2 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社3 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海3 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖4 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区6 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大7 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai7 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说7 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json