編輯:關於Android編程
那麼我們就來用Handler制作一個簡易的網絡請求框架。
如下圖:

解釋一下:UI在request的時候傳入UI中的Handler,同時將請求的Runnable推入到工作線程對應中的Handler,在工作線程中的Handler調用完畢之後,有通過傳遞過來的UI的Handler將數據傳送到UI,更新頁面。
其實核心就是UI中的Handler和工作線程中的Handler,UI中的Handler負責數據的傳遞,工作線程中的Handler負責請求的隊列和調度。那麼來看具體的代碼:
首先是工作線程:
public class SvrBgThread extends Thread {
private static String TAG = "SvrBgThread";
private Handler mBgTaskHandler;
public static final class TaskCmd {
/**
* 接收後台消息
*/
public static final int ACCEPT_MSG = 1;
/**
* 拒絕後台消息
*/
public static final int REFUSE_MSG = ACCEPT_MSG + 1;
/**
* 退出後台線程
*/
public static final int EXIT_TASK = REFUSE_MSG + 1;
}
private Runnable currentRunnable;
@Override
public void run() {
synchronized (SvrBgThread.this) {
Looper.prepare();
mBgTaskHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
int what = msg.what;
switch (what) {
case TaskCmd.ACCEPT_MSG:
break;
case TaskCmd.REFUSE_MSG:
cancelTask(currentRunnable);
break;
case TaskCmd.EXIT_TASK:
Looper.getMainLooper().quit();
break;
}
}
@Override
public void dispatchMessage(Message msg) {
if (msg.getCallback() != null) {
currentRunnable = msg.getCallback();
}
super.dispatchMessage(msg);
}
};
Looper.loop();
}
}
private void cancelTask(Runnable runnable) {
if (runnable == null) {
return;
}
}
public synchronized void cancelCurrentTask() {
cancelTask(currentRunnable);
}
public synchronized void cancelAllTask() {
if (mBgTaskHandler == null)
return;
//移除所有命令
mBgTaskHandler.removeCallbacksAndMessages(null);
cancelCurrentTask();
}
public synchronized Handler getBgTaskHandler() {
return mBgTaskHandler;
}
public synchronized void exit() {
if (mBgTaskHandler == null)
return;
mBgTaskHandler.sendEmptyMessage(TaskCmd.EXIT_TASK);
}
}
這裡在Thread中實例化Handler,這時,Handler中的消息隊列就准備好了,在Looper.loop()後,Looper就不斷從消息隊列中取出Message來執行。那麼這種耗時的網絡請求的執行就在工作線程中了(在主線程會ANR),並且請求的隊列也有了,請求的調度方式也有了,利用Android系統的Handler機制,我們免去了自己控制消息隊列的各種麻煩。
看請求的接口。
public interface IStudentProvider {
String URL_STUDENT = "datainfo/getStudentInfo";
/**
* 統一的學生信息接口
*
* @param reqType
* @param uiHandler
* @param requestUrl
* @param statisticTime
* @param userName
*/
boolean requestStudentInfo(Handler uiHandler,String url,int msgType);
}
看到接口中定義了請求一個學生的request的定義。
其中uiHandler就是UI中的Handler,負責數據傳回UI,msgType是該請求的標識,用來匹配請求(在ListView中Item的復用導致的數據變動,解決方法就是setTag後在匹配Tag,一樣的道理)
看實現
public class StudentProvider implements IStudentProvider {
public static final String TAG = "StudentProvider";
private SvrBgThread mSvrBgThread;
private StudentProvider() {
super();
mSvrBgThread = new SvrBgThread();
}
private static class InstanceHolder {
static final StudentProvider INSTANCE = new StudentProvider();
}
/**
* 單例模式,延遲加載
*/
public static StudentProvider getInstance() {
return InstanceHolder.INSTANCE;
}
public void init() {
mSvrBgThread.setName(TAG);
mSvrBgThread.start();
}
@Override
public boolean requestStudentInfo(Handler uiHandler, String url, int msgType) {
if (uiHandler == null || url == null) {
return false
}
HttpGet httpGet = null;
try {
// 獲取Post對象,輸入參數以JSON格式放置在body中
httpGet = HttpClientProxy.getJsonHttpGet(requestUrl);
} catch (UnsupportedEncodingException e) {
Log.e(TAG, "Exception", e);
return false;
} catch (JSONException e) {
Log.e(TAG, "Exception", e);
return false;
}
Student student = new Student();
// 創建http請求對象
HttpRequestRunnable requestRunnable = new HttpRequestRunnable(uiHandler, student, msgType, httpGet );
if (mSvrBgThread == null || !mSvrBgThread.isAlive()) {
Log.e(TAG, "svrBgThread not initialized !");
return false;
}
// 發送http請求
return mSvrBgThread.getBgTaskHandler().post(requestRunnable);
}
}
注意在init()方法中對工作線程的實例化,在requestStudentInfo方法中通過構造HttpRequestRunnable 具體的請求,隨後將該具體請求 推入到了工作線程中對應的Handler(mSvrBgThread.getBgTaskHandler().post(requestRunnable))中,當Handler取出該Runnable執行後就得到了請求的結果,其中HttpClientProxy提供具體的請求方式,比如post,get,put等
具體的HttpRequestRunnable :
public class HttpRequestRunnable implements Runnable {
private Handler mMsgHandler;
private IUserDataBuilder mUserData;
private int msgType;
private HttpUriRequest httpUriRequest;
public HttpRequestRunnable(Handler msgHandler, IUserDataBuilder userData, int msgType, HttpUriRequest httpUriRequest) {
this.mMsgHandler = msgHandler;
this.mUserData = userData;
this.msgType = msgType;
this.httpUriRequest = httpUriRequest;
}
/**
* 處理 服務端響應數據
*
* @param databuilder
* 用戶數據構造器,解析數據後的結果會填入該類中
* @param response
* http 響應結果
*/
private void handleResponse(IUserDataBuilder databuilder, HttpResponse response) throws Exception
{
int StatusCode = response.getStatusLine().getStatusCode();
switch (StatusCode)
{
case HttpStatus.SC_NOT_FOUND:
Log.e(TAG, "Response StatusCode:" + StatusCode);
databuilder.setServerRet(ServerRet.NOTFOUND);
throw new Exception();
case HttpStatus.SC_UNAUTHORIZED:
Log.e(TAG, "Response StatusCode:" + StatusCode);
databuilder.setServerRet(ServerRet.UNAUTHORIZED);
throw new Exception();
case HttpStatus.SC_OK:
Log.d(TAG, "Response StatusCode:" + StatusCode);
// 設置默認值,有的接口不會攜帶retCode字段,平台統一添加該字段
databuilder.setServerRet(ServerRet.OK);
// 解析返回數據
JSONObject retJsonObject = createJSONFromHttpEntity(response.getEntity());
if (retJsonObject == null)
{
// 創建默認的JSON數據,保證解析框架正常
Log.e(TAG, "Create default Json data.");
retJsonObject = HttpUtil.createHttpJson(ServerRet.ILLEGAL_STATE_EXCEPTION);
databuilder.setServerRet(ServerRet.ILLEGAL_STATE_EXCEPTION);
}
databuilder.parseJson(retJsonObject);
break;
default:
Log.e(TAG, "Response StatusCode:" + StatusCode);
throw new Exception();
}
}
/**
* 提取HTTPEntity中的JSON對象
*
* @param httpEntity
* @return
*/
private JSONObject createJSONFromHttpEntity(HttpEntity httpEntity)
{
JSONObject jsonObj = null;
String entity = null;
try
{
entity = EntityUtils.toString(httpEntity, HttpClientProxy.ENCODING);
jsonObj = new JSONObject(entity);
}
catch(Exception e)
{
Log.e(TAG,"Exception",e);
}
return jsonObj;
}
@Override
public void run() {
if (!isConditionMet()) {
Log.e(TAG, "Invalid RequestRunnable:" + toString());
return;
}
try {
// 向遠程服務端發送數據請求並獲取請求結果
HttpClient client = HttpClientProxy.httpClientBuilder();
HttpResponse response = client.execute(mHttpUriRequest);
handleResponse(mUserDatabuilder, response);
client.getConnectionManager().shutdown();
} catch (Exception e) {
if (httpUriRequest.isAborted()) {
mUserData.setServerRet(ServerRet.CLIENT_ABORT_REQUEST);
} else {
mUserData.setServerRet(transformException(e));
}
Log.e(TAG, "Exception", e);
} finally {
synchronized (mMsgHandler) {
Message message = Message.obtain(mMsgHandler, msgType, mUserData);
boolean success = mMsgHandler.sendMessage(message);
if (!success) {
Log.e(TAG, "send message back to user fail,"
+ "usually because the looper processing the message queue is exiting");
}
}
}
}
private boolean isConditionMet() {
if (mUserData == null) {
return false;
}
if (mMsgHandler == null) {
return false;
}
if (httpUriRequest == null) {
return false;
}
return true;
}
}
在HttpRequestRunnable 中其構造函數的參數分別是UI中的Handler,請求結果對象,請求的標志,和請求的方式。該類主要負責請求的執行,在執行結束後將數據設置到UI的Handler中( Message message = Message.obtain(mMsgHandler, msgType, mUserData);)。
數據是如何解析的:
IUserDataBuilder :
public interface IUserDataBuilder {
/**
* 將JSON數據解析成用戶期望的數據類型
*
* @param jsonObject
* JSON對象
* @return 解析成功返回true,否則返回時報
* @throws Exception
*/
boolean parseJson(JSONObject jsonObject) throws Exception;
ServerRet getServerRet();
void setServerRet();
}
public enum ServerRet {
OK(000,"ok");
private int retCode;
private String msg;
ServerRet(int retCode,String msg) {
this.retCode = retCode;
this.msg = msg;
}
}
可以看到IUserDataBuilder ,所有實體類需要實現的接口,都必須有對解析Json的過程,還有設置與獲取請求的結果(成功與否)
ServerRet:請求結果的枚舉。
Student:
public class Student implements IUserDataBuilder {
private ServerRet mServerRet;
private String name;
private int age;
private String mAddr;
@Override
public boolean parseJson(JSONObject jsonObject) throws Exception {
if (jsonObject == null){
return false;
}
Student temp = HttpUtils.fromJson(jsonObject.toString(),Student.class);
name = temp.getName();
age = temp.getAge();
mAddr = temp.getAddr();
return true;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String getAddr() {
return mAddr;
}
@Override
public ServerRet getServerRet() {
return null;
}
@Override
public void setServerRet() {
}
@Override
public String toString() {
return "Student{" +
"mServerRet=" + mServerRet +
", name='" + name + '\'' +
", age=" + age +
", mAddr='" + mAddr + '\'' +
'}';
}
}
可以實體類中的parseJson使用Gson來解析json,fromJson如下:
public static T fromJson(String jsonStr, Class mClass)
{
Gson mGson = new Gson();
T mt = mGson.fromJson(jsonStr, mClass);
return mt;
}
至此解析結束,實體類數據已填充。
看看使用:
public class StudentInfoActivity extends Activity {
public static final String TAG = "StudentInfoActivity";
public static final String URL = "http://10.10.12.158:8080";
public static final int MSGTYPE = 1;
private static Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
int what = msg.what;
if (what == MSGTYPE) {
Student student = (Student) msg.obj;
if (student.getServerRet() == ServerRet.OK) {
Log.i(TAG, student.toString())
} else {
Log.i(TAG, "student parse failed");
}
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
IStudentProvider studentProvider = StudentProvider.getInstance();
studentProvider.requestStudentInfo(handler, URL, MSGTYPE);
}
}
最後記得將StudentProvider .getInstance().init()方法放在Application的onCreate方法中,在程序啟動時將其初始化了。在退出是cancel。
總結:
在UI發送請求時,將UI中的Handler等傳入到具體的請求方法中(StudentProvider 的requestStudent方法中),在該方法中實現了具體的請求HttpRequestRunnable ,隨後將HttpRequestRunnable推入到工作線程的Handler對應的MessageQueue中,在Handler輪訓執行到HttpRequestRunnable後,將執行具體的請求同時將傳遞過來的JsonObject解析為具體的實體類,然後傳遞給UI的Handler,返回給UI。
弊端:相比較Volley,OkHttp,XUtuis等開源庫,使用Handler的請求麻煩,不易擴展,同時當連續執行多個請求時,該請求是在工作線程中串型執行的,並發性不好。
這裡旨在體會Handler的另外一種不常見的用法。深刻體會Handler機制的原理。
華為榮耀note8怎麼預約 榮耀note8預約購買攻略
華為榮耀於8月1號下午正式發布了6.6吋大屏手機華為榮耀NOTE8,那麼想要購買新機的朋友是不是很想知道華為榮耀note8怎麼預約購買呢?下面小編就馬上帶來
Android JNI學習筆記(四)-數據類型映射以及native調用java
1. 前言前幾篇學習了jni開發的基本流程、動態注冊native函數以及相關編譯文件的編寫,咱們也算是知道了jni開發,但是還不夠,今天咱們來學習下,java和jni的數
直接應用項目中的Android圖片緩存技術
前不久搞的Android圖片緩存,剛開始引入開源的框架,用著還行,但是在開發中遇到問題,就比如universal-image-loader-1.9.5.jar這個框架吧,
Android開發中include控件用法分析
本文實例講述了Android開發中include控件用法。分享給大家供大家參考,具體如下:我們知道,基於Android系統的應用程序的開發,界面設計是非常重要的,它關系著