携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第7天,点击查看活动详情
读完下面的内容,我们将做一个倒计时程序:
一、我讲
今天,我主要简讲两个知识点,安卓的UI线程模型、Handler的使用。
1.1 UI线程模型
当应用启动,系统会创建一个主线程。这个主线程负责向UI组件分发事件,也是在这个主线程里,你的应用和Android的UI组件发生交互。所以也叫UI线程。
通俗点讲就是:Android专门为用户配备了一个UI线程,主要负责画漂亮的界面,并响应你按下各种按钮等操作,保证面上过得去,你的操作都能得到及时的视觉反馈。
UI线程不做耗时操作
但是,一个操作系统中,除了面子上的事要做,更多的是里子的事情,比如下载个文件。肯定是用户点击了下载按钮,然后系统花大量时间去下载,最后下载完成把文件内容展示到界面上。
你试试,结果系统崩了,出现一个ANR
(Application No Responding 程序无响应)错误。
不要在UI线程做耗时操作,避免阻塞UI主线程,因为UI主线程也是Android主线程。如需费劲的活,请开启新线程来做。
新线程别直接访问主线程
不能去UI线程排队,那我就新启动线程来做,比如IO线程
。
那么问题就来了,读文件的IO线程
费了5分钟下载完了文件,要显示到UI线程
的界面上,怎么整?直接调用视图上的set
方法?
不要在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 提示:时间到!
布局文件没啥好说的,以后文章我不打算放了,今天这是最后一次,就一个文本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系统会有多个Handler
和Message
,要注意分辨。
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天可入门安卓。