出于性能优化的考虑,android的UI操作并不是安全的,如果有多个线程并发操作UI组件,则可能导致线程安全问题.为了解决这个问题,Android制定了一个规则,只允许UI线程来修改Activity中的UI组件.
UI线程(主线程 Main Thread):当程序第一次启动时,android会同时启动一条主线程(Main Thread),主线程主要负责处理与UI相关的事件,比如说用户的按键事件,用户接触屏幕的事件及屏幕绘图事件,并把相应的事件分发到对应的组件进行处理.即主线程就是UI线程.
Android的消息传递机制主要是为了解决Android应用的多线程问题------Android只允许UI线程修改Activity里的UI组件,这就导致了一个问题:如果新启动一个线程便无法动态改变界面组件的属性值.但是在实际的Android应用开发中,尤其是涉及到动画的游戏开发中,需要让新启动的线程周期性的改变界面组件的属性值,这便需要Handler的消息传递机制来实现了
Handler类的主要作用
Handler类的主要作用有两个:
1.在新启动的线程中发送消息
2.在主线程中获取,处理消息.
牵扯到两个问题:新启动的线程何时发送消息?主线程何时去获取消息?
为了让主线程"适时"地处理新启动线程发送的消息,只能通过回调的方式实现.需要我们重写Handler类中处理消息的方法,
当启动的新线程发送消息时,消息会发送到与这个新线程关联的MessageQueue,而Handler会不断地从MessageQueue中获取并处理消息-------这将导致Handler类中处理消息的方法被回调.
天哥在视频中说了一个经常用的方法是handler.postDelayed(Runnable ,delayMiuis);
我们可以这么认为,在onCreate方法中的进程为主进程,因为onCreat方法多与UI相关事务有关.而Android只允许在主进程中访问/修改Activity界面组件.而在主线程中可以直接创建Handler对象,因为系统会自动帮我们创建好Looper对象,而Looper对象会自动帮我们配置好相关的MessageQueue对象,然后就是重写Handler的handleMessage方法(注意:因为主线程中没有显示创建Looper对象,系统自动创建的Looper对象所以最后不用显示启动Looper的loop()方法,或者我们可以认为是系统自动启动了Looper的loop()方法)(若是新建线程则最后不要忘了启动Looper的loop()方法来启动Looper.)
Handler相关的三大组件
与Handler一起工作的组件有:
Message:Handler接收和处理的对象
Looper:每个线程只能拥有一个Looper.它的loop方法负责读取MessageQueue中的消息,读到消息之后就把消息交给发送该消息的Handler进行处理
MessageQueue:消息队列,采用先进先出的方式来管理Message.程序在创建Looper对象的时候,会在Looper的构造器中创建MessageQueue对象.Looper的构造器源码如下:
在我们平时要多看一些源码提高我们的理解水平
总结
Handler的作用发送消息和接收消息.
如果程序使用Handler发送消息,由Handler发送消息必须被指定到指定的MessageQueue.如果希望Handler正常工作,必须在当前进程中有一个MessageQueue;否则消息Message就没有MessageQueue进行保存了.不过经过我们上面的分析:MessageQueue是由Looper管理的,在创建Looper对象的时候,会在Looper的构造器中创建MessageQueue对象,也就是说如果希望Handler正常工作,则必须在当前进程中有一个Looper对象,为了保证当前进程中有个Looper对象可以分下列情况处理:
prepare方法保证每个线程最多只能有一个Looper对象,然后调用Looper的静态loop()方法来启动它.loop()方法使用一个死循环不断去除MessageQueue中的消息,并将取出的消息分给该消息对应的Handler进行处理.
ANR异常:只要在主线程(UI线程)中执行需要消耗大量时间的操作,都会引发ANR,因为这样会导致Android应用程序无法响应输入事件和Broadcast.
在新建线程中创建Handler必须要创建Looper对象!!
package org.crazyit.handler;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;
public class MainActivityextends AppCompatActivity {
static final StringUPPER_NUM ="upper";
EditTextetNum;
CalThreadcalThread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etNum = findViewById(R.id.etNum);
calThread =new CalThread();
// 启动新线程
calThread.start();
}
// 为按钮的点击事件提供事件处理方法
public void cal(View source)
{
// 创建消息
Message msg =new Message();
msg.what =0x123;
Bundle bundle =new Bundle();
bundle.putInt(UPPER_NUM ,
Integer.parseInt(etNum.getText().toString()));
msg.setData(bundle);
// 向新线程中的Handler发送消息
calThread.mHandler.sendMessage(msg);
}
//定义一个线程类
class CalThreadextends Thread
{
public HandlermHandler;
public void run()
{
Looper.prepare();
mHandler =new Handler()
{
// 定义处理消息的方法
@Override
public void handleMessage(Message msg)
{
if(msg.what ==0x123)
{
int upper = msg.getData().getInt(UPPER_NUM);
List nums =new ArrayList<>();
// 计算从2开始、到upper的所有质数
outer:
for (int i =2 ; i <= upper; i++)
{
// 用i除以从2开始、到i的平方根的所有数
for (int j =2 ; j <= Math.sqrt(i); j++)
{
// 如果可以整除,则表明这个数不是质数
if(i !=2 && i % j ==0)
{
continue outer;
}
}
nums.add(i);
}
// 使用Toast显示统计出来的所有质数
Toast.makeText(MainActivity.this, nums.toString()
, Toast.LENGTH_LONG).show();
}
}
};
Looper.loop();
}
}
}
布局文件xml代码如下