当我们执行一些耗时操作, 比如发起一条网络请求时, 考虑到网速等其他因素, 服务器未必会立刻响应我们的请求, 那么就必须将这类操作放在子线程中运行, 这就需要实现多线程编程.
1 启动线程
Android 多线程编程与 Java 多线程编程语法相同, 因为就是 Android 就是基于 Java 语言的嘛 O(_)O 哈哈~
1.1 继承 Thread 类
新建一个类继承自 Thread, 然后重写父类的 run() 方法:
- public class CustomThread extends Thread{
- @Override
- public void run() {
- // 处理耗时逻辑
- }
- }
启动线程:
new CustomThread().start();
1.2 实现 Runnable 接口
一般采用此方法实现多线程, 这样耦合度更低, 而且可以实现类的扩展性更好, 因为 Java 类支持实现多接口.
- public class CustomThread implements Runnable{
- @Override
- public void run() {
- // 处理耗时逻辑
- }
- }
启动线程:
new Thread(new CustomThread()).start();
也可以使用匿名类来实现 Runnable 接口:
- // 使用匿名类来实现 Runnable 接口
- new Thread(new Runnable() {
- @Override
- public void run() {
- // 处理耗时逻辑
- }
- });
2 更新 UI
Android 的 UI 是线程不安全的, 所以如果想要更新应用程序的 UI 元素, 就必须在主线程中进行, 否则会抛出异常(CalledFromWrongThreadException) .
假设, 我们在子线程中更新 UI:
- findViewById(R.id.change).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- textView.setText("饭吃了吗?");
- }
- }).start();
- }
- });
执行结果:
APP 崩溃, 我们看一下日志, 果然抛出了异常:
在子线程中, 必须使用异步消息处理机制来更新 UI.
首先, 新建异步消息实例:
- private Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case CHANGE_CONTENT:
- textView.setText("饭吃了吗?");
- break;
- default:
- break;
- }
- }
- };
使用异步消息来更新 UI:
- findViewById(R.id.change).setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- Message message = new Message();
- message.what = CHANGE_CONTENT;
- handler.sendMessage(message);
- }
- }).start();
- }
- });
3 异步消息机制
异步消息处理由 4 个部分组成.
3.1 Message
Message 可以在线程之间传递消息. 可以在它的内部携带少量数据, 用于在不同线程之间进行数据交换. 除了 what 字段, 还可以使用 arg1 和 arg2 来携带整型数据, 使用 obj 来携带 Object 数据.
3.2 Handler
Handler 用于发送 (sendMessage 系列方法) 与处理消息(handleMessage 方法).
3.2 MessageQueue
MessageQueue 用于存放所有通过 Handler 发送的消息 . 这部分消息会一直存放在消息队列中, 直到被处理 . 每个线程中只会有一个 MessageQueue 对象 .
3.3 Looper
Looper 用于管理 MessageQueue 队列, 调用 Looper.loop() 方法之后, 就会进入无限循环中, 每当发现 MessageQueue 存在一条消息, 就会将它取出, 并传递到 Handler 的 handleMessage() 方法中. 每个线程中只会有一个 Looper 对象.
3.4 异步消息处理流程
在主线程中创建 Handler 对象, 并重写 handleMessage() 方法.
子线程进行 UI 操作时, 创建 Message 对象, 通过 Handler 发送这条消息.
Looper 从 MessageQueue 中取出待处理消息.
最后分发回 Handler 的 handleMessage() 方法中.
Message 经过一系列流转调用后, 也就从子线程进入到主线程, 这样就可以执行更新 UI 的操作啦 O(_)O 哈哈~
4 AsyncTask
为了更方便地在子线程中进行更新 UI 操作, Android 基于异步处理消息机制封装了一个工具类 AsyncTask.
AsyncTask 是个抽象类, 所以要使用它必须创建一个子类 . 在继承时可以指定 3 个泛型参数:
泛型参数 | 说明 |
---|---|
Params | 执行 AsyncTask 时传入的参数,用于后台任务。 |
Progress | 执行后台任务时,如果需要在界面中显示当前进度,那么可以在这里指定进度单位。 |
Result | 任务执行后,如果需要返回结果,那么可以在这里指定返回值类型。 |
继承后, 还需要重写以下方法:
方法 | 说明 |
---|---|
void onPreExecute() | 后台任务执行前被调用,一般用于界面初始化操作。 |
Result doInBackground(Params... params) | 这个方法内的代码都会在子线程中运行,我们可以在此执行一些耗时操作。如果在子类中指定了 AsyncTask 的第三个泛型参数,那么可以在此直接 return xxx 返回执行结果。 注意: 在此不可进行 UI 操作,UI 操作请在 publishProgress(Progress...) 方法中完成。 |
void onProgressUpdate(Progress... values) | 当后台任务调用了 publishProgress(Progress...) 方法之后就会调用该方法。该方法所携带的参数就是后台任务传递过来的参数。可在此进行 UI 操作。 |
void onPostExecute(Result result) | 当后台任务通过 return 语句返回时,就会调用该方法。可在此利用返回的数据,进行 UI 更新操作。 |
来源: http://www.jianshu.com/p/f97dbaa9ac60