欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-22 17:48:22
背景在Android中,只有在UIThread中才能直接更新界面在Android中,长时间的工作(联网)都需要在workerThread中执行在分线程获得服务器数据后, 需要立即到主线程去更新界面显示数据(这就需要通信)如何实现线程间通信呢?下面我们来了解:Android里面的消息机制和异步任务APIMessage :消息 可理解为线程间通讯的数据单元, 可通过message携带需要的数据创建对象: Message.obtain(what)封装数据public in...

背景

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

API

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

消息机制原理

图解

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

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

	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, 比直接使用Thread效率要高

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

使用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());
	}
}

本文地址:https://blog.csdn.net/qq_43416157/article/details/110006809

相关标签: Android