背景

在Android中,只有在UIThread中才能直接更新界面
在Android中,长时间的工作(联网)都需要在workerThread中执行
在分线程获得服务器数据后, 需要立即到主线程去更新界面显示数据(这就需要通信)
如何实现线程间通信呢?
下面我们来了解:Android里面的消息机制和异步任务

API

Message :消息
可理解为线程间通讯的数据单元, 可通过message携带需要的数据
创建对象: Message.obtain(what)
封装数据
public int what //id 标识
public int arg1
public int arg2
public
Handler : 处理器
Handler是Message的处理器,同时也负责消息的发送和移除的工作
发送即时消息: sendMessage(Message msg)
发送延时消息: sendMessageDelayed(Message msg, long time)//延迟处理,不是延迟发送,也会立即发送的
处理消息: handleMessage(Message msg) (回调方法)
移除还未处理的消息: removeMessages(int what)
MessageQueue :
Looper(钩子) : 循环器
负责循环取出Message Queue里面的当前需要处理的Message
交给对应的Handler进行处理
处理完后, 将Message缓存到消息池中,

消息机制原理

图解

Android的线程通信:消息机制原理(Message,Handler,MessageQueue,Looper),异步任务AsyncTask,使用JSON_消息队列


Android的线程通信:消息机制原理(Message,Handler,MessageQueue,Looper),异步任务AsyncTask,使用JSON_消息队列_02

Message

/*
* Message类:
* public int what; id标识
* public int arg1; 保存int的数据
* public int arg2; 保存int的数据
* public Object obj;保存任意数据
* long when; 记录应该被处理的时间值
* Handler target; 用来处理消息的Handler对象,就是保存用来发送消息的Handler
* Runnable callback; 用来处理消息的回调器
* Message next; 下一个Message,形成链表的结构
* private static Message sPool; 用来缓存处理过的Message,用来复用
*
* Runnable对象的run()什么时候在分线程执行?
* 将Runnable传给Thread的构造方法的时候,比如:
* new Thread(new Runnable(){
* public void run(){} //这个就是在分线程执行
* }).start();
*/

Handler

/*
* Handler类:
* 作用:发送消息,处理消息,移除消息
* sendMessage(Message msg) 调用sendMessageDelayed(msg, 0);
*
* sendEmptyMessage(int what) 调用 sendEmptyMessageDelayed(what, 0);
* 进入里面看
* public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
Message msg = Message.obtain();
msg.what = what;
return sendMessageDelayed(msg, delayMillis);
}
其实发送空消息就是发送不带数据的消息

上面调用最多的就是sendMessageDelayed(Message msg, long delayMillis),进入查看一下

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
所以发送延迟消息就是发送的当前时间加上延迟的时间
进入sendMessageAtTime这个方法查看
public final boolean sendMessageAtFrontOfQueue(Message msg) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, 0);
}
进入enqueueMessage(queue, msg, 0);
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//这个就是将消息的target设置成当前的Handler,就是发送消息的Handler
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//然后调用了消息队列里面的方法将消息加入消息队列
return queue.enqueueMessage(msg, uptimeMillis);
}

移除消息
public final void removeMessages(int what) {
mQueue.removeMessages(this, what, null);
}
处理消息,需要重写的
public void handleMessage(Message msg) {
}

派发消息
public void dispatchMessage(Message msg) {
//如果消息自己可以处理,就让消息自己处理,就是看消息有没有重写callback方法
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果Handler对象中有回调监听器,调用回调监听器来处理消息,
//回调监听器如果返回的是true,就会处理完毕,比如又会继续调用handleMessage
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
*/

MessageQueue

/*
MessageQueue类:这是储存消息的队列,以message的when排序的优先级队列
enqueueMessage(Message msg, long when)//将message添加到队列中
里面的队列排序的原理也很简单,可以查看一下
boolean enqueueMessage(Message msg, long when) {
if (msg.isInUse()) {
throw new AndroidRuntimeException(msg + " This message is already in use.");
}
if (msg.target == null) {
throw new AndroidRuntimeException("Message must have a target.");
}

boolean needWake;
synchronized (this) {
if (mQuiting) {
RuntimeException e = new RuntimeException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
return false;
}

msg.when = when;
Message p = mMessages;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}
}
if (needWake) {
nativeWake(mPtr);
}
return true;
}

取出一个Message对象,但是可能不会立即返回
Message next() 里面取出消息队列中的第一个消息,然后返回
里面调用了nativePollOnce(mPtr, nextPollTimeoutMillis);方法
这个本地方法会导致处于处理等待状态,但是不会阻塞主线程
*/

Looper

:这个方法在系统启动的时候就启动了
作用是从MessageQueue中获取当前需要处理的消息,并交给Handler处理

public static void loop() {
//获取当前的钩子
final Looper me = myLooper();
//获取消息队列
final MessageQueue queue = me.mQueue;
for (;;) {
//这里虽然是无限for循环,但是这个获取下一个消息可能会处于阻塞状态
Message msg = queue.next(); // might block
//调用Handler去分发消息并且处理消息
msg.target.dispatchMessage(msg);
//回收利用消息
msg.recycle();
}
}

异步任务AsyncTask

简介

什么是异步任务?
逻辑上: 以多线程的方式完成的功能需求
API上: 指AsyncTask类

AsyncTask的理解
在没有AsyncTask之前, 我们用Handler+Thread就可以实现异步任务的功能需求
AsyncTask是对Handler和Thread的封装, 使用它更编码更简洁,更高效
AsyncTask封装了ThreadPool,

API

AsyncTask: 简化Handler处理多线程通信的问题
AsyncTask<Params, Progress, Result>
Params 启动任务执行的输入参数,比如HTTP请求的URL。
Progress 后台任务执行的百分比。
Result 后台执行任务最终返回的结果,比如String。
execute(Params... params)
启动任务, 开始任务的执行流程
void onPreExecute()
在分线程工作开始之前在UIThread中执行,一般用来显示提示视图
Result doInBackground(Params... params)
在workerThread中执行, 完成任务的主要工作,通常需要较长的时间
void onPostExecute(Result result)
在doInBackground()执行完后在UIThread中执行,一般用来更新界面
publishProgress(Progress... values) : 在分线程中, 发布当前进度
void onProgressUpdate(Progress... values) :

例子

package com.example.myhandler;

import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.SystemClock;
import android.view.View;

public class AsyncTaskTestActivity extends Activity
{

@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_async);
}

private File apkFile;
private ProgressDialog dialog;

public void downloadApk(View v)
{
// 启动异步任务处理
//这里需要写三个泛型,我们还不知道要写什么,就先写Void了
//这里Void对应基本数据类型void,代表什么也不是,就是空的
new AsyncTask<Void, Integer, Void>()
{

// 1. 主线程, 显示提示视图
protected void onPreExecute()
{
dialog = new ProgressDialog(AsyncTaskTestActivity.this);
dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
dialog.show();

// 准备用于保存APK文件的File对象 :
// /storage/sdcard/Android/package_name/files/xxx.apk
apkFile = new File(getExternalFilesDir(null), "update.apk");

}

//2. 分线程, 联网请求
@Override
protected Void doInBackground(Void... params)
{
try
{
// 1. 得到连接对象
String path = "http://192.168.80.1:8080/Android/AppHouse.apk";
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
//2. 设置
//connection.setRequestMethod("GET");
connection.setConnectTimeout(5000);
connection.setReadTimeout(10000);
//3. 连接
connection.connect();
//4. 请求并得到响应码200
int responseCode = connection.getResponseCode();
if (responseCode == 200)
{
// 设置dialog的最大进度
dialog.setMax(connection.getContentLength());

//5. 得到包含APK文件数据的InputStream
InputStream is = connection.getInputStream();
//6. 创建指向apkFile的FileOutputStream
FileOutputStream fos = new FileOutputStream(apkFile);
//7. 边读边写
byte[] buffer = new byte[1024];
int len = -1;
while ((len = is.read(buffer)) != -1)
{
fos.write(buffer, 0, len);
// 8. 显示下载进度
// dialog.incrementProgressBy(len);
// 在分线程中, 发布当前进度
publishProgress(len);

// 休息一会(模拟网速慢)
// Thread.sleep(50);
SystemClock.sleep(5);
}

fos.close();
is.close();
}
//9. 下载完成, 关闭, 进入3)
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

//3. 主线程, 更新界面
protected void onPostExecute(Void result)
{
dialog.dismiss();
installAPK();
}

//在主线程中更新进度(在publishProgress()之后)
protected void onProgressUpdate(Integer[] values)
{
dialog.incrementProgressBy(values[0]);
}
}.execute();

}

private void installAPK()
{
Intent intent = new Intent("android.intent.action.INSTALL_PACKAGE");
intent.setDataAndType(Uri.fromFile(apkFile),
"application/vnd.android.package-archive");
startActivity(intent);
}
}

执行过程

​启动异步任务: new AsyncTask<Params, Progress,Result>().execute()​

Android的线程通信:消息机制原理(Message,Handler,MessageQueue,Looper),异步任务AsyncTask,使用JSON_android_03

使用JSON

简介

JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式
本质就是具有特定格式的字符串
JSON数据已经是客户端与服务器端交互的最常用的选择,
已经很少使用xml来进行数据交互了

解析技术:
Android原生API : 编程相对麻烦
Gson框架 : 编码简洁, 项目首选

解析方向:
将java对象(包含集合)转换为json格式字符串(服务器)
将json格式字符串转换为java对象(包含集合)

格式

整体结构:
Json数组 : [ ]
Json对象: { }
Json数组的结构: [value1, value2, value3]
Json对象的结构: {key1:value1, key2:value2, key3:value3}
key的数据类型: 字符串
value的数据类型:
数值
字符串
null
json数组 []
json对象 {}

API

Android原生API:
JsonObject : json对象 { }
JSONObject(String json) : 将json字符串解析为json对象
Xxx getXxx(String name) : 根据name, 在json对象中得到对应的Value
JsonArray : json数组 []
JSONArray(String json) : 将json字符串解析为json数组
int length() : 得到json数组中元素的个数
Xxx getXxx(int index) : 根据下标得到json数组中对应的元素数据

Gson框架API
Gson : 能解析json数据的类
Gson() : 构造对象的方法
String toJson(Object src) : 将对象转换为对应格式的json字符串
T fromJson(String json, Type typeOfT) : 解析Json字符串, 得到对象
TypeToken<T> : 用来得到Type的类
protected TypeToken() : 受保存的构造方法
Type getType() :

例子

package com.example.myhandler;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.test.AndroidTestCase;
import android.util.Log;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;

/*
1. 将json格式的字符串{}转换为Java对象, 使用原生API
2. 将json格式的字符串{}转换为Java对象, 使用GSON
3. 将json格式的字符串[]转换为Java对象的List, 使用原生API
4. 将json格式的字符串[]转换为Java对象的List, 使用GSON

5. 将Java对象转换为json字符串{}, 使用GSON
6. 将Java对象的List转换为json字符串[], 使用GSON
*/
public class JsonTest extends AndroidTestCase
{
/*
* 1. 将json格式的字符串{}转换为Java对象, 使用原生API
*/
public void testJsonToObject() throws JSONException
{
String jsonString = "{\"id\":2, \"name\":\"大虾\", \"price\":12.3,\"imagePath\":\"http://192.168.10.165:8080/L05_Server/images/f1.jpg\"}";

//将json字符串封装为JSONObject对象
JSONObject jsonObject = new JSONObject(jsonString);
//从对象中根据key得到对应的value
int id = jsonObject.getInt("id");
String name = jsonObject.getString("name");
double price = jsonObject.getDouble("price");
String imagePath = jsonObject.getString("imagePath");
//封装ShopInfo对象
ShopInfo shopInfo = new ShopInfo(id, name, price, imagePath);

Log.e("TAG", shopInfo.toString());
}

/*
* 1. 将json格式的字符串{}转换为Java对象, 使用GSON
*/
public void testJsonToObject2()
{
String jsonString = "{\"id\":3, \"name\":\"大虾\", \"price\":12.3,\"imagePath\":\"http://192.168.10.165:8080/L05_Server/images/f1.jpg\"}";

ShopInfo shopInfo = new Gson().fromJson(jsonString, ShopInfo.class);

Log.e("TAG", shopInfo.toString());
}


/*
* 3. 将json格式的字符串[]转换为Java对象的List, 使用原生API
*/
public void testJsonToList() throws JSONException
{
String jsonString = "[{\"id\":3, \"name\":\"大虾\", \"price\":12.3,\"imagePath\":\"http://192.168.10.165:8080/L05_Server/images/f1.jpg\"},"
+ "{\"id\":5, \"name\":\"大虾2\", \"price\":128.3,\"imagePath\":\"http://192.168.10.165:8080/L05_Server/images/f2.jpg\"}]";
List<ShopInfo> list = new ArrayList<ShopInfo>();

//1. 将json字符串包装JSONArray对象
JSONArray jsonArray = new JSONArray(jsonString);
//2. 遍历JSONArray对象所有元素(JSONObject), 并将每个元素封装为shopInfo, 并添加到List
for (int i = 0; i < jsonArray.length(); i++)
{
JSONObject jsonObject = jsonArray.getJSONObject(i);
// 从对象中根据key得到对应的value
int id = jsonObject.getInt("id");
String name = jsonObject.getString("name");
double price = jsonObject.getDouble("price");
String imagePath = jsonObject.getString("imagePath");
// 封装ShopInfo对象
ShopInfo shopInfo = new ShopInfo(id, name, price, imagePath);
list.add(shopInfo);
}

Log.e("TAG", list.toString());
}

/*
* 4. 将json格式的字符串[]转换为Java对象的List, 使用GSON
*/
public void testJsonToList2() throws JSONException
{
String jsonString = "[{\"id\":4, \"name\":\"大虾\", \"price\":12.3,\"imagePath\":\"http://192.168.10.165:8080/L05_Server/images/f1.jpg\"},"
+ "{\"id\":6, \"name\":\"大虾2\", \"price\":128.3,\"imagePath\":\"http://192.168.10.165:8080/L05_Server/images/f2.jpg\"}]";

//new TypeToken<List<ShopInfo>>(){}.getType()
//这种方式的写法是new一个TypeToken对象,但是这对象里面的方法都是受保护的,
//所以必须{}(代表现在是这个类本身自己)继承才可以访问里面的方法
List<ShopInfo> list = new Gson().fromJson(jsonString, new TypeToken<List<ShopInfo>>(){}.getType());

Log.e("TAG", list.toString());
}

/*
5. 将Java对象转换为json字符串{}, 使用GSON
*/
public void testObjectToJson()
{
ShopInfo info = new ShopInfo(3, "KK", 1000, "http://www.sina.com");
String json = new Gson().toJson(info);
Log.e("TAG", json);
}


/*
6. 将Java对象的List转换为json字符串[], 使用GSON
*/

public void testListToJson()
{
List<ShopInfo> list = new ArrayList<ShopInfo>();
list.add(new ShopInfo(3, "KK", 1000, "http://www.sina.com"));
list.add(new ShopInfo(4, "KK2", 2000, "http://www.sina.com222"));

String json = new Gson().toJson(list);

Log.e("TAG", json);
}

/*
* 如果JSON里面的key有点特殊,比如my name,数字之类的
* 在java中无法为这个key定义一个类,那么就只能将它转化成map数据类型了
*/
public void testJsonToMap()
{
String jsonString = "{\"my name\":\"大虾\", \"1\":12}";
Map<String, Object> map = new Gson().fromJson(jsonString, new TypeToken<Map<String, Object>>(){}.getType());
Log.e("TAG", map.toString());
}
}