乾貨:android實現hessian通信

轉載請註明出處: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結構一致,字段名/屬性名

源碼下載:http://download.csdn.net/detail/sctu_vroy/9207769

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章