Category 副本解析

目录:

1. 前言

本篇文章介绍 Android Handler 的基本使用方法,且 Demo 会以 Java & Kotlin 两种代码形式进行展示。 在 Android 实际开发中,我们经常会遇到耗时任务,比如:网络请求API接口来获取数据、数据库 CRUD 操作等等,我们需要额外创建开启工作线程来处理这些耗时任务。由于 Android 规定只能由主线程才能处理 UI 工作,所以这时候我们就需要通过 Handler 来通知主线程处理 UI 工作。

1.1 定义

Handler:子线程与主线程之间的沟通中介,用于传递处理消息。

在 Android 中,为保证处理 UI 工作的线程稳定安全,规定只有主线程才能更新处理 UI 工作。所以当子线程想处理 UI 工作时,就需要通过 Handler 来通知主线程作出相对应的 UI 处理工作。 如下图所示:

本篇文章,我将介绍 Handler 的三种使用方法,分别是:

Handler.sendMessage(Message)

Handler.post(Runnable)

Handler.obtainMessage(what).sendToTarget();

2. Handler.sendMessage()方法

Handler.sendMessage(Msg) 方法是最为常见的一种方法。

2.1 使用步骤说明

其使用步骤分四步,如下所示:

步骤一:新建 Handler 对象,覆写 handleMessage(Message) 方法。

步骤二:新建 Message 对象,设置其携带的数据。

步骤三:在子线程中通过 Handler.sendMessage(Message) 方法发送信息。

步骤四:在 Handler 的 handleMessage(Message msg) 方法中处理消息,通知主线程作出相对应的 UI 工作。

步骤一:新建 Handler 对象,覆写 handleMessage(Message) 方法

复制代码

//创建 Handler对象,并关联主线程消息队列

mHandler = new Handler(Looper.getMainLooper()) {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

···略···

}

}

};

步骤二:新建 Message 对象,设置其携带的数据

复制代码

Bundle bundle = new Bundle();

bundle.putInt(CURRENT_PROCESS_KEY, i);

Message msg = new Message();

msg.setData(bundle);

msg.what = 2;

步骤三:在子线程中通过 Handler.sendMessage(Message) 方法发送信息

复制代码

mHandler.sendMessage(msg)

步骤四:在 Handler 的 handleMessage(Message msg) 方法中处理消息,通知主线程作出相对应的 UI 工作

复制代码

mHandler = new Handler(Looper.getMainLooper()) {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

//根据信息编码及数据做出相对应的处理

switch (msg.what) {

case 1:

//更新 TextView UI

mDisplayTv.setText("CustomChildThread starting!");

break;

case 2:

//获取 ProgressBar 的进度,然后显示进度值

Bundle bundle = msg.getData();

int process = bundle.getInt(CURRENT_PROCESS_KEY);

mProgressBar.setProgress(process);

break;

default:

break;

}

}

};

2.2 具体例子

根据上述的使用步骤,我们来完成一个完整的例子: 新建一个线程,模拟处理耗时任务,然后将处理进度通过 ProgressBar 显示出来。 效果如下所示:点击按钮,开启线程处理耗时操作。

具体代码见下:

2.2.1 Java版本Demo

Java版本的具体代码如下所示:

复制代码

public class HandlerAddThreadActivity extends AppCompatActivity {

public static final String CURRENT_PROCESS_KEY = "CURRENT_PROCESS";

private TextView mDisplayTv;

private Handler mHandler;

private ProgressBar mProgressBar;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_handler_add_thread);

TextView titleTv = findViewById(R.id.title_tv);

titleTv.setText("Handler + Thread");

mDisplayTv = findViewById(R.id.display_tv);

mProgressBar = findViewById(R.id.test_handler_progress_bar);

//mHandler用于处理主线程消息队列中的子线程消息

mHandler = new Handler(Looper.getMainLooper()) {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

switch (msg.what) {

case 1:

//更新 TextView UI

mDisplayTv.setText("CustomChildThread starting!");

break;

case 2:

//获取 ProgressBar 的进度,然后显示进度值

Bundle bundle = msg.getData();

int process = bundle.getInt(CURRENT_PROCESS_KEY);

mProgressBar.setProgress(process);

break;

default:

break;

}

}

};

Button mClickBtn = findViewById(R.id.click_btn);

mClickBtn.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

//开启子线程,子线程处理UI工作

CustomChildThread customThread = new CustomChildThread();

customThread.start();

}

});

}

/**

* 子线程,用于处理耗时工作

*/

public class CustomChildThread extends Thread {

@Override

public void run() {

//在子线程中创建一个消息对象

Message childThreadMessage = new Message();

childThreadMessage.what = 1;

//将该消息放入主线程的消息队列中

mHandler.sendMessage(childThreadMessage);

//模拟耗时进度,将进度值传给主线程用于更新 ProgressBar 进度。

for (int i = 1; i <= 5; i++) {

try {

//让当前执行的线程(即 CustomChildThread)睡眠 1s

Thread.sleep(1000);

//Message 传递参数

Bundle bundle = new Bundle();

bundle.putInt(CURRENT_PROCESS_KEY, i);

Message progressBarProcessMsg = new Message();

progressBarProcessMsg.setData(bundle);

progressBarProcessMsg.what = 2;

mHandler.sendMessage(progressBarProcessMsg);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

}

执行结果看上图 Handler.sendMessage() Demo。

2.2.2 Kotlin版本Demo

Kotlin版本的代码如下所示:

复制代码

class TestThreadAddHandlerActivity : AppCompatActivity() {

companion object {

const val PROGRESS_VALUE_KEY = "PROGRESS_VALUE"

}

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_thread_add_handler)

handlerAddThreadStartBtn.setOnClickListener(View.OnClickListener {

//工作线程开始模拟下载任务

val workThread: WorkThread = WorkThread(this)

workThread.start()

})

}

class WorkThread(activity: TestThreadAddHandlerActivity) : Thread() {

private var handler: MyHandler = MyHandler(activity)

override fun run() {

super.run()

for (i in 0..6) {

sleep(1000)

//通过 Handler 将进度参数传递给 主线程,让其更新 progressBar 进度

val message = Message()

message.what = 1

val bundle = Bundle()

bundle.putInt(PROGRESS_VALUE_KEY, i)

message.data = bundle

handler.sendMessage(message)

}

}

}

/**

* 静态内部类,防止内存泄漏

*/

class MyHandler(activity: TestThreadAddHandlerActivity) : Handler() {

private var weakReference = WeakReference(activity)

override fun handleMessage(msg: Message) {

super.handleMessage(msg)

//处理消息

when (msg.what) {

1 -> {

val activity = weakReference.get()

if (activity != null && !activity.isFinishing) {

//获取消息中携带的任务处理进度参数,然后设置成 ProgressBar 的进度。

val progressValue: Int = msg.data.get(PROGRESS_VALUE_KEY) as Int

activity.handlerAddThreadProgressBar.progress = progressValue

}

}

}

}

}

}

执行结果看上图 Handler.sendMessage() Demo

3. Handler.post()方法

除了使用 Handler.sendMessage(Message) 来发送信息,Handler 还支持 post(Runnable) 方法来传递消息,通知主线程做出相对应的 UI 工作。使用方法如下:

复制代码

/**

* 将可运行的 Runnable 添加到消息队列。Runnable 将在该 Handler 相关的线程上运行处理。

* The runnable will be run on the thread to which this handler is attached.

*/

new Handler().post(new Runnable() {

@Override

public void run() {

//更新处理 UI 工作

}

});

3.1 具体例子

实现上述同样的功能 -> 点击按钮,子线程开始工作模拟处理耗时任务,通过 Handler 将进度告诉主线程,再通过 ProgressBar 将进度显示出来。 效果如下:

具体代码见下:

3.1.1 Java版本Demo

Java版本的具体代码如下:

复制代码

public class HandlerPostFunctionActivity extends AppCompatActivity {

private Handler mMainHandler;

private ProgressBar mProgressBar;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_handler_add_thread);

TextView titleTv = findViewById(R.id.title_tv);

titleTv.setText("Handler post() function");

mProgressBar = findViewById(R.id.test_handler_progress_bar);

//新建静态内部类 Handler 对象

mMainHandler = new Handler(getMainLooper());

Button mClickBtn = findViewById(R.id.click_btn);

mClickBtn.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

//开启子线程,子线程处理UI工作

CustomChildThread customThread = new CustomChildThread();

customThread.start();

}

});

}

/**

* 子线程,用于处理耗时工作

*/

public class CustomChildThread extends Thread {

@Override

public void run() {

//模拟耗时进度,将进度值传给主线程用于更新 ProgressBar 进度。

for (int i = 1; i <= 5; i++) {

try {

//让当前执行的线程(即 CustomChildThread)睡眠 1s

Thread.sleep(1000);

//新创建一个 Runnable 用户处理 UI 工作

MyRunnable runnable = new MyRunnable(HandlerPostFunctionActivity.this, i);

//调用Handler post 方法。

mMainHandler.post(runnable);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

/**

* 将 Runnable 写成静态内部类,防止内存泄露

*/

public static class MyRunnable implements Runnable {

private int progressBarValue;

private WeakReference weakReference;

MyRunnable(HandlerPostFunctionActivity activity, int value) {

this.weakReference = new WeakReference<>(activity);

this.progressBarValue = value;

}

@Override

public void run() {

HandlerPostFunctionActivity activity = weakReference.get();

if (activity != null && !activity.isFinishing()) {

activity.mProgressBar.setProgress(progressBarValue);

}

}

}

}

执行结果看上图 Handler.post() Demo

3.1.2 Kotlin版本Demo

Kotlin版本的具体代码如下:

复制代码

class TestHandlerPostRunnableActivity : AppCompatActivity() {

private var mMainHandler: Handler? = null

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_thread_add_handler)

handlerAddThreadStartBtn.setOnClickListener(View.OnClickListener {

//工作线程开始模拟下载任务

val workThread: WorkThread = WorkThread(this)

workThread.start()

})

//创建 Handler,关联App的 主Looper 对象

mMainHandler = Handler(Looper.getMainLooper())

}

class WorkThread(private var activity: TestHandlerPostRunnableActivity) : Thread() {

private var handler: Handler? = activity.mMainHandler

override fun run() {

super.run()

for (i in 0..6) {

sleep(1000)

//新建 Runnable 设置进度参数传,然后通过 post(Runnable) 方法,让其更新 progressBar 进度

val runnable: MyRunnable = MyRunnable(activity, i)

handler?.post(runnable)

}

}

}

/**

* 处理 UI 工作。

* 静态内部类,防止内存泄露

*/

class MyRunnable(activity: TestHandlerPostRunnableActivity, value: Int) : Runnable {

private var weakReference = WeakReference(activity)

private var progressValue = value

override fun run() {

val activity = weakReference.get()

if (activity != null && !activity.isFinishing) {

//获取任务执行进度参数,更新 progressBar 进度

activity.handlerAddThreadProgressBar.progress = progressValue

}

}

}

}

执行结果看上图 Handler.post() Demo

4. obtainMessage()方法

obtainMessage() 方法与 sendMessage() 方法很相似,通过 mHandler.obtainMessage().sendToTarget() 发送信息。该方法与 sendMessage() 的区别就是你不用额外去创建一个 Message 对象。

obtainMessage() 方法有三种,分别是:

复制代码

//指定 what 用于区分,通过 Message.what 获得

public final Message obtainMessage(int what);

//传递obj参数,通过 Message.obj 获得

public final Message obtainMessage(int what, @Nullable Object obj)

//传递arg1 arg2参数,通过 Message.arg1 Message.arg2 获得

public final Message obtainMessage(int what, int arg1, int arg2)

4.1 具体例子

同样实现上述同样的功能 -> 点击按钮,子线程开始工作,通过 Handler 将进度告诉主线程,通过 ProgressBar 将进度显示出来。 效果如下:

具体代码如下:

4.1.1 Java版本Demo

Java版本的具体代码如下:

复制代码

public class HandlerObtainMessageActivity extends AppCompatActivity {

private TextView mDisplayTv;

private Handler mHandler;

private ProgressBar mProgressBar;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_handler_add_thread);

TextView titleTv = findViewById(R.id.title_tv);

titleTv.setText("Handler + Thread");

mDisplayTv = findViewById(R.id.display_tv);

mProgressBar = findViewById(R.id.test_handler_progress_bar);

//mHandler用于处理主线程消息队列中的子线程消息

mHandler = new Handler(Looper.getMainLooper()) {

@Override

public void handleMessage(Message msg) {

super.handleMessage(msg);

switch (msg.what) {

case 1:

//更新 TextView UI

mDisplayTv.setText("Handler obtainMessage() Test!!");

break;

case 2:

//通过 msg.obj 获取 ProgressBar 的进度,然后显示进度值

int process = (int) msg.obj;

mProgressBar.setProgress(process);

break;

default:

break;

}

}

};

Button mClickBtn = findViewById(R.id.click_btn);

mClickBtn.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

//开启子线程,子线程处理UI工作

CustomChildThread customThread = new CustomChildThread();

customThread.start();

}

});

}

/**

* 子线程,用于处理耗时工作

*/

public class CustomChildThread extends Thread {

@Override

public void run() {

//发送第一条消息,代表开始执行异步任务

mHandler.obtainMessage(1).sendToTarget();

//模拟耗时进度,将进度值传给主线程用于更新 ProgressBar 进度。

for (int i = 1; i <= 5; i++) {

try {

//让当前执行的线程(即 CustomChildThread)睡眠 1s

Thread.sleep(1000);

//将执行进度参数 i 传递给主线程 progressBar

mHandler.obtainMessage(2, i).sendToTarget();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

}

执行结果看上图 Handler.obtainMessage() Demo

4.1.2 kotlin版本Demo

Kotlin版本的具体代码如下:

复制代码

class TestHandlerObtainMessageActivity : AppCompatActivity() {

companion object {

const val PROGRESS_VALUE_KEY = "PROGRESS_VALUE"

}

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_thread_add_handler)

handlerAddThreadStartBtn.setOnClickListener(View.OnClickListener {

//工作线程开始模拟下载任务

val workThread: WorkThread = WorkThread(this)

workThread.start()

})

}

class WorkThread(activity: TestHandlerObtainMessageActivity) : Thread() {

private var handler: MyHandler = MyHandler(activity)

override fun run() {

super.run()

for (i in 0..6) {

sleep(1000)

//通过 Handler 将进度参数传递给 主线程,让其更新 progressBar 进度

val bundle = Bundle()

bundle.putInt(PROGRESS_VALUE_KEY, i)

handler.obtainMessage(1, bundle).sendToTarget()

}

}

}

/**

* 静态内部类,防止内存泄漏

*/

class MyHandler(activity: TestHandlerObtainMessageActivity) : Handler() {

private var weakReference = WeakReference(activity)

override fun handleMessage(msg: Message) {

super.handleMessage(msg)

when (msg.what) {

1 -> {

val activity = weakReference.get()

if (activity != null && !activity.isFinishing) {

//获取任务执行进度参数,然后通过 ProgressBar 显示出来

val bundle: Bundle = msg.obj as Bundle

val progressValue: Int = bundle.get(PROGRESS_VALUE_KEY) as Int

activity.handlerAddThreadProgressBar.progress = progressValue

}

}

}

}

}

}

执行结果看上图 Handler.obtainMessage() Demo

5. 总结:

在实际开发中,三种方法的使用都可行,具体用哪种方法,需结合你的实际情况及个人喜好。另外,在实际使用中往往将 Handler 写成静态内部类,这时需要注意防止内存泄露!(The handler class should be static or leaks might occur),具体代码见上方!

5.1 在子线程中创建Handler

思考: 在上面代码中, 我们都是在主线程中创建了 Handler 对象,那如果在子线程中创建一个 Handler 对象呢?会发生什么呢? 如下所示:我们在 CustomChildThread 线程中,新建一个 Handler 对象。

复制代码

public class CustomChildThread extends Thread {

@Override

public void run() {

Handler handler = new Handler(Activity.this);

//会报错:java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()

}

}

结果: 抛出异常: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()。 原因: 因为在创建 Handler对象时要关联所处线程的 Looper对象,而我们的子线程没有 Looper,所以会抛出上述异常。 解决方法: 通过调用,Looper.prepare() 方法为子线程创建一个 Looper 对象,并且调用 Looper.loop() 方法开始消息循环。如下所示:

复制代码

class CustomChildThread extends Thread {

@Override

public void run() {

//为当前线程创建一个 Looper 对象

Looper.prepare();

//在子线程中创建一个 Handler 对象

Handler handler = new Handler() {

public void handleMessage(Message msg) {

// 在这里处理传入的消息

}

};

//开始消息循环

Looper.loop();

}

}

至此关于 Handler 的使用方法也就介绍完毕了,如果你想深入学习 Handler,了解其通信机制以及源码分析,请看博客 Android Handler深入学习(源码分析)

Copyright © 2088 炎龙游戏攻略网 - 活动副本全解析 All Rights Reserved.
友情链接
top