当前位置: 首页 > article >正文

Android Studio:用handler实现计数

一、哪里需要Handler?

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

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

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

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() 方法启动线程。
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();  // 启动线程
    }
}

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

Thread-0:正在执行 0
Thread-1:正在执行 0
Thread-0:正在执行 1
Thread-1:正在执行 1
...

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

步骤:

  1. 创建一个类并实现 Runnable 接口。
  2. 实现 run() 方法,编写任务逻辑。
  3. 创建 Thread 对象时,将 Runnable 实现类传入构造方法。
  4. 调用 start() 启动线程。
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实现计数器

准备一个简单的页面

<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就有这样的机制,每隔固定时间启动一次任务类:

主页面完整代码

@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;
        }
    }
    
}
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 定义计时任务 

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

效果:


http://www.kler.cn/a/551684.html

相关文章:

  • 基于遗传算法排课系统
  • Flutter 3.29.0 新特性 CupertinoNavigationBar 可配置bottom属性
  • AI(人工智能)会给嵌入式领域带来哪些机遇与挑战?
  • golang常用库之-swaggo/swag根据注释生成接口文档
  • 电磁铁在生产与生活中的广泛应用
  • Ubuntu 24.04.1 LTS 本地部署 DeepSeek 私有化知识库
  • QT QLineEdit如何支持文件拖放
  • 项目2 数据可视化--- 第十五章 生成数据
  • 【开源免费】基于SpringBoot+Vue.JS个人博客系统(JAVA毕业设计)
  • 在Nodejs中使用kafka(四)消息批量发送,事务
  • Ollama 部署本地 Deepseek-R1 大模型及可视化聊天工具指南
  • springboot399-中文社区交流平台(源码+数据库+纯前后端分离+部署讲解等)
  • mapbox基础,使用geojson加载circle圆点图层
  • 汽车通信未来新趋势:Eclipse uProtocol
  • 无第三方依赖 go 语言工具库
  • 【复现DeepSeek-R1之Open R1实战】系列6:GRPO源码逐行深度解析(上)
  • 深入解析「卡顿帧堆栈」 | UWA GPM 2.0 技术细节与常见问题
  • 25工商管理研究生复试面试问题汇总 工商管理专业知识问题很全! 工商管理复试全流程攻略 工商管理考研复试真题汇总
  • 解决DeepSeek服务器繁忙的有效方法
  • vue3项目,商城系统