Java转Android:第7天 Handler做一个倒计时功能

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情


读完下面的内容,我们将做一个倒计时程序:

image6.GIF

一、我讲

今天,我主要简讲两个知识点,安卓的UI线程模型、Handler的使用。

1.1 UI线程模型

当应用启动,系统会创建一个主线程。这个主线程负责向UI组件分发事件,也是在这个主线程里,你的应用和Android的UI组件发生交互。所以也叫UI线程。

image.png

通俗点讲就是:Android专门为用户配备了一个UI线程,主要负责画漂亮的界面,并响应你按下各种按钮等操作,保证面上过得去,你的操作都能得到及时的视觉反馈。

UI线程不做耗时操作

但是,一个操作系统中,除了面子上的事要做,更多的是里子的事情,比如下载个文件。肯定是用户点击了下载按钮,然后系统花大量时间去下载,最后下载完成把文件内容展示到界面上。

你试试,结果系统崩了,出现一个ANRApplication No Responding 程序无响应)错误。

image.png

不要在UI线程做耗时操作,避免阻塞UI主线程,因为UI主线程也是Android主线程。如需费劲的活,请开启新线程来做。

新线程别直接访问主线程

不能去UI线程排队,那我就新启动线程来做,比如IO线程

那么问题就来了,读文件的IO线程费了5分钟下载完了文件,要显示到UI线程的界面上,怎么整?直接调用视图上的set方法?

image.png

不要在UI线程之外访问UI对象。如果任意新线程就可以配置UI,那到底听谁的。所以,你要让界面变化,请通过某种方式告诉UI主线程。这种方式就是Handler。不用问我是谁,我叫雷锋!

1.2 把持者 Handler

很奇怪,Android中很多英文名称都有中文翻译,比如Layout叫布局,BroadcastReceiver叫广播接收者,还就是这个Handler,大家都叫Handler,真的没有比较合适的翻译。今天,我给它起名叫把持者Handler,表示它是UI线程秩序的把持人。

Handler是线程间访问的桥梁,我们可以通过它,从子线程中合理访问UI线程。

第一,主线程里建立一个类,让它继承Handler,表示让主线程干什么。

class MyHandler extends Handler{
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        // 这里可以操作UI控件
    }
}
复制代码

第二,新线程里,发送Message消息给Handler处理,表示自己是哪条线上的。

final MyHandler handler = new MyHandler();
new Thread(){
    @Override
    public void run() {
       //子线程里可以向Handler发送消息
        Message message = new Message();
        message.what = 1;
        handler.sendMessage(message);
    }
}.start();
复制代码

两者主要靠android.os.Message传递消息。

二、你做

再来回顾一下,我们要做的这个小应用,从30秒开始倒计时,每秒钟减1并显示在手机上,时间剩余0秒时,弹出Toast 提示:时间到!

image6.GIF

布局文件没啥好说的,以后文章我不打算放了,今天这是最后一次,就一个文本TextView控件。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    tools:context=".MainActivity">
    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="倒计时"
        android:textSize="33sp" />
</android.support.constraint.ConstraintLayout>
复制代码

我们重点看下逻辑代码,这次我把import也放进来了,因为Android系统会有多个HandlerMessage,要注意分辨。

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

    Handler handler;
    TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.tv);

        handler = new MyHandler();

        new Thread(){
            int times = 31;
            @Override
            public void run() {
                super.run();
                while (times >= 0){
                    Message message = new Message();
                    message.what = times;
                    handler.sendMessage(message);
                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    times --;
                }
            }
        }.start();
    }

    class MyHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            textView.setText(""+msg.what);
            if (msg.what == 0){
                Toast.makeText(getApplicationContext(), "时间到", Toast.LENGTH_SHORT).show();
            }
        }
    }
    
}
复制代码

首先看MyHandler定义在MainActivity中,如果有人发来Message消息,取出里面的what变量(我们定义它就是剩余时间),把数值让文本显示。如果数值为0,就弹出Toast说时间到了。

我们发现Handler里面只处理UI相关的操作,只有UI的set工作,不要让它做别的逻辑。

onCreate方法里,我们新启动了一个线程,这里可以类比用户按下了下载按钮,或者其他耗时操作。这里面肯定涉及到耗时操作的进度和结果,每一次沟通都是通过handler.sendMessage(message)发送出去的,接收方正是MyHandler

点击运行,你就有了一个属于你的计时器。是不是很哇塞!

三、关于

现在,行业内多是Android转Java,很少有Java转Android的。

但是,这并不妨碍Java同学了解Android开发,从学习的角度去拓宽自己的知识面。

所以,我会以最精简的语言来编写一个系列教程《Java转Android》(第一季30篇,日更)。

其实,不管是Java还是python,只要有编程基础的同学,一天看800字,30天可入门安卓。

猜你喜欢

转载自juejin.im/post/7128964814491090951
今日推荐