轉載請註明出處:http://blog.csdn.net/SCTU_vroy/article/details/49366853
本文將詳述如何在android(客戶端)上與服務端通過Hessian實現網絡通信、數據傳輸。
網上關於這方面的資料並不少,但是良莠不齊,讓筆者走了不少彎路。本文將先解析android端源代碼(ps:web端代碼各位度娘or谷歌),而後分享在android上實現hessian常見的錯誤和解決方法。請各位大神多多提意見,謝謝~
Step 1:新建android工程,添加權限,導入源碼工程中android_hessian的jar包
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
注意:
1)hessian官網提供的jar包適用於web端開發,不適用於android端;
2)code.google提供的hessdroid包親測是有問題的,大概是因爲裏面包含一些android不支持的類,
具體可見:http://hsrong.iteye.com/blog/1719996
Step 2:創建接口IHessian,前端與後端必須一致
public interface IHessian {
String sayHello();
void sendModel(HessianModel model);
}
Step 3:創建數據類HessianModel,前端與後端字段名(屬性)必須一致,而且包名(命名空間)也必須一致,並且需要實現序列化!(因爲Hessian 是一個基於 binary-RPC 實現的遠程通訊)
package com.example.androidhessian;
import java.io.Serializable;
/**
* 統一的數據包model
* 字段名必須統一
* 且必須實現序列化
* @author victor shi
* 2015/10/20
*/
public class HessianModel implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
private long id;
private String name;
private String date;
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
}
Step 4:回到MainActivity,獲取服務端ip,new一個子線程實現hessian請求。這裏可能有人會問,爲什麼不直接在主線程進行網絡通信?這個會在後面解答。
1)手機連接wifi熱點後,獲取服務端地址,記得在AndroidManifest.xml中添加網絡權限:
/*
* 獲取服務端地址
*/
public static String getHostIP(Context context) {
WifiManager wifi = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
DhcpInfo dhcpInfo = wifi.getDhcpInfo();
StringBuilder sb = new StringBuilder();
// sb.append("網絡信息:" + "\n");
// sb.append("本機ip:" + formatIP(dhcpInfo.ipAddress) + "\n");
// sb.append("子網掩碼:" + formatIP(dhcpInfo.netmask) + "\n");
// sb.append("網關ip:" + formatIP(dhcpInfo.gateway) + "\n");
sb.append(formatIP(dhcpInfo.serverAddress));
return sb.toString();
}
/*
* ip格式化
*/
private static String formatIP(int ipAddress) {
return (ipAddress & 0xff) + "." + ((ipAddress >> 8) & 0xff) + "."
+ ((ipAddress >> 16) & 0xff) + "." + ((ipAddress >> 24) & 0xff);
}
2)直接po出Activity的代碼:
public class MainActivity extends Activity {
private static final String TAG = "Android Hessian Test";
private static final String PORT = "8081"; //端口號
private static final long TIMEOUT = 5000L; //網絡請求時長
private Button button;
private TextView tv;
private HessianModel model = null;
private String url = "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
initView();
initData();
initEvent();
}
private void initView() {
button = (Button) findViewById(R.id.btnHessian);
tv = (TextView) findViewById(R.id.tvMsg);
}
private void initData() {
model = new HessianModel();
model.setDate("2015.10.20");
model.setId(1L);
model.setName("victor");
/*
* 獲取服務端ip地址
*/
url = "http://" + NetTools.getHostIP(getApplicationContext()) + ":" + PORT + "/test.hessian";
}
private void initEvent() {
button.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// Hessian 請求
new Thread(runnable).start();
}
});
}
/**
* 處理隊列信息
* 操作UI
*/
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
tv.setText(msg.obj.toString());
}
};
/**
* 開啓子線程進行網絡通信
* 自Android4.0版本以後,不支持在UI主線程中網絡通信,
* 否則會報運行異常android.os.NetworkOnMainTreadException
*/
Runnable runnable = new Runnable() {
@Override
public void run() {
HessianProxyFactory factory = new HessianProxyFactory();
Message msg = Message.obtain();
factory.setDebug(true);
factory.setReadTimeout(TIMEOUT);
try {
IHessian service = (IHessian) factory.create(IHessian.class, url, getClassLoader());
msg.obj = service.sayHello();
handler.sendMessage(msg);
// service.sendModel(model);
} catch (MalformedURLException e) {
e.printStackTrace();
}
}
};
}
代碼解析:
1)url中端口號PORT後面的字符串“/test.hessian”取決於後臺項目的路徑;
2)爲什麼需要new一個子線程來實現hessian通信呢?原因在於自Android4.0版本後,如果直接在主線程進行網絡通信,程序運行會拋出運行異常:android.os.NetworkOnMainThreadException。解決方法有幾種,可自行搜索,此處採用多線程的方法,也可直接使用AsyncTask異步處理更方便。
到這裏,源代碼的解析部分就結束了,最後分享一下在android上實現hessian常見的問題和解決方法:
(ps:基本都是運行異常)
問題一:java.lang.NoClassDefFoundError: com/caucho/hessian/client/HessianConnectionException
解決方法:問題出在hessian包的版本,導入本文源碼工程libs下的android_hessian.jar包即可解決。
問題二:android.os.NetworkOnMainThreadException
解決方法:如果是android4.0以下版本則不會報錯,4.0以上則不允許在主線程中實現網絡通信,只能new子線程,在子線程中實現hessian通信。
問題三:com.caucho.hessian.client.HessianConnectionException: 500: java.net.SocketException: recvfrom failed: ECONNRESET (Connection reset by peer)
解決方法:此異常是出在已經與服務端建立網絡連接的基礎上,問題還是出在hessian的jar包,即便jar包可以在web端使用,但是裏面包含一些android不支持的類。所以還是需要導入正確的jar包,並add to build path,可從本文源碼工程中獲取,或者打開鏈接下載:http://hsrong.iteye.com/blog/1719996
還有需要注意的地方就是:
1)傳遞的對象model包名(or命名空間)需要一致
2)傳遞的對象model需要序列化
3)傳遞的對象model結構一致,字段名/屬性名