1. 程式人生 > >Android socket通訊的長連線與心跳檢測

Android socket通訊的長連線與心跳檢測

在Android開發中,我們可能需要和伺服器保持連線不斷開,這時需要用到socket通訊的長連線,並且定時傳送訊息檢測是否是連線狀態——心跳檢測。
我們需要一個客戶端和一個伺服器端的demo,現在我就貼出重要程式碼,主要是android客戶端的,伺服器端的demo供大家下載。
首先我們需要新建一個BackService類來繼承Service:

package com.example.sockettest;  

import java.io.IOException;  
import java.io.InputStream;  
import java.io.OutputStream;  
import
java.lang.ref.WeakReference; import java.net.Socket; import java.net.UnknownHostException; import java.util.Arrays; import android.annotation.SuppressLint; import android.app.Service; import android.content.Intent; import android.os.Handler; import android.os.IBinder; import android.os.RemoteException; import
android.util.Log; public class BackService extends Service { private static final String TAG = "BackService"; /** 心跳檢測時間 */ private static final long HEART_BEAT_RATE = 3 * 1000; /** 主機IP地址 */ private static final String HOST = "192.168.1.104"; /** 埠號 */ public static
final int PORT = 9800; /** 訊息廣播 */ public static final String MESSAGE_ACTION = "org.feng.message_ACTION"; /** 心跳廣播 */ public static final String HEART_BEAT_ACTION = "org.feng.heart_beat_ACTION"; private long sendTime = 0L; /** 弱引用 在引用物件的同時允許對垃圾物件進行回收 */ private WeakReference<Socket> mSocket; private ReadThread mReadThread; private IBackService.Stub iBackService = new IBackService.Stub() { @Override public boolean sendMessage(String message) throws RemoteException { return sendMsg(message); } }; @Override public IBinder onBind(Intent arg0) { return (IBinder) iBackService; } @Override public void onCreate() { super.onCreate(); new InitSocketThread().start(); } // 傳送心跳包 private Handler mHandler = new Handler(); private Runnable heartBeatRunnable = new Runnable() { @Override public void run() { if (System.currentTimeMillis() - sendTime >= HEART_BEAT_RATE) { boolean isSuccess = sendMsg("");// 就傳送一個\r\n過去, 如果傳送失敗,就重新初始化一個socket if (!isSuccess) { mHandler.removeCallbacks(heartBeatRunnable); mReadThread.release(); releaseLastSocket(mSocket); new InitSocketThread().start(); } } mHandler.postDelayed(this, HEART_BEAT_RATE); } }; public boolean sendMsg(String msg) { if (null == mSocket || null == mSocket.get()) { return false; } Socket soc = mSocket.get(); try { if (!soc.isClosed() && !soc.isOutputShutdown()) { OutputStream os = soc.getOutputStream(); String message = msg + "\r\n"; os.write(message.getBytes()); os.flush(); sendTime = System.currentTimeMillis();// 每次傳送成功資料,就改一下最後成功傳送的時間,節省心跳間隔時間 Log.i(TAG, "傳送成功的時間:" + sendTime); } else { return false; } } catch (IOException e) { e.printStackTrace(); return false; } return true; } // 初始化socket private void initSocket() throws UnknownHostException, IOException { Socket socket = new Socket(HOST, PORT); mSocket = new WeakReference<Socket>(socket); mReadThread = new ReadThread(socket); mReadThread.start(); mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);// 初始化成功後,就準備傳送心跳包 } // 釋放socket private void releaseLastSocket(WeakReference<Socket> mSocket) { try { if (null != mSocket) { Socket sk = mSocket.get(); if (!sk.isClosed()) { sk.close(); } sk = null; mSocket = null; } } catch (IOException e) { e.printStackTrace(); } } class InitSocketThread extends Thread { @Override public void run() { super.run(); try { initSocket(); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } } public class ReadThread extends Thread { private WeakReference<Socket> mWeakSocket; private boolean isStart = true; public ReadThread(Socket socket) { mWeakSocket = new WeakReference<Socket>(socket); } public void release() { isStart = false; releaseLastSocket(mWeakSocket); } @SuppressLint("NewApi") @Override public void run() { super.run(); Socket socket = mWeakSocket.get(); if (null != socket) { try { InputStream is = socket.getInputStream(); byte[] buffer = new byte[1024 * 4]; int length = 0; while (!socket.isClosed() && !socket.isInputShutdown() && isStart && ((length = is.read(buffer)) != -1)) { if (length > 0) { String message = new String(Arrays.copyOf(buffer, length)).trim(); Log.i(TAG, "收到伺服器傳送來的訊息:"+message); // 收到伺服器過來的訊息,就通過Broadcast傳送出去 if (message.equals("ok")) {// 處理心跳回復 Intent intent = new Intent(HEART_BEAT_ACTION); sendBroadcast(intent); } else { // 其他訊息回覆 Intent intent = new Intent(MESSAGE_ACTION); intent.putExtra("message", message); sendBroadcast(intent); } } } } catch (IOException e) { e.printStackTrace(); } } } } }

關鍵程式碼已經註釋了,相信大家應該可以看懂。在這個類中關聯了一個IBackService的類,新建一個IBackService.aidl。對,沒錯,就是新建一個IBackService.aidl,關於aidl的知識請查閱相關文件。程式碼如下:

package com.example.sockettest;  
interface IBackService{  
    boolean sendMessage(String message);  
}  

現在就是MainActivity了,這就是一個activity,接收廣播,改變UI,就不多說了:

package com.example.sockettest;  

import android.app.Activity;  
import android.content.BroadcastReceiver;  
import android.content.ComponentName;  
import android.content.Context;  
import android.content.Intent;  
import android.content.IntentFilter;  
import android.content.ServiceConnection;  
import android.os.Bundle;  
import android.os.IBinder;  
import android.os.RemoteException;  
import android.util.Log;  
import android.view.View;  
import android.view.View.OnClickListener;  
import android.widget.Button;  
import android.widget.EditText;  
import android.widget.TextView;  
import android.widget.Toast;  

public class MainActivity extends Activity {  
    private static final String TAG = "MainActivity";  

    private Intent mServiceIntent;  
    private IBackService iBackService;  
    private TextView tv;  
    private EditText et;  
    private Button btn;  

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

        initViews();  
        initData();  
    }  

    private void initViews() {  
        tv = (TextView) findViewById(R.id.tv);  
        et = (EditText) findViewById(R.id.editText1);  
        btn = (Button) findViewById(R.id.button1);  
    }  

    private void initData() {  
        mServiceIntent = new Intent(this, BackService.class);  
        btn.setOnClickListener(new OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                String string = et.getText().toString().trim();  
                Log.i(TAG, string);  
                try {  
                    Log.i(TAG, "是否為空:" + iBackService);  
                    if (iBackService == null) {  
                        Toast.makeText(getApplicationContext(),  
                                "沒有連線,可能是伺服器已斷開", Toast.LENGTH_SHORT).show();  
                    } else {  
                        boolean isSend = iBackService.sendMessage(string);  
                        Toast.makeText(MainActivity.this,  
                                isSend ? "success" : "fail", Toast.LENGTH_SHORT)  
                                .show();  
                        et.setText("");  
                    }  
                } catch (RemoteException e) {  
                    e.printStackTrace();  
                }  
            }  
        });  
    }  

    @Override  
    protected void onStart() {  
        super.onStart();  
        bindService(mServiceIntent, conn, BIND_AUTO_CREATE);  
        // 開始服務  
        registerReceiver();  
    }  

    @Override  
    protected void onResume() {  
        super.onResume();  
        // 註冊廣播 最好在onResume中註冊  
        // registerReceiver();  
    }  

    @Override  
    protected void onPause() {  
        super.onPause();  
        // 登出廣播 最好在onPause上登出  
        unregisterReceiver(mReceiver);  
        // 登出服務  
        unbindService(conn);  
    }  

    // 註冊廣播  
    private void registerReceiver() {  
        IntentFilter intentFilter = new IntentFilter();  
        intentFilter.addAction(BackService.HEART_BEAT_ACTION);  
        intentFilter.addAction(BackService.MESSAGE_ACTION);  
        registerReceiver(mReceiver, intentFilter);  
    }  

    private BroadcastReceiver mReceiver = new BroadcastReceiver() {  
        @Override  
        public void onReceive(Context context, Intent intent) {  
            String action = intent.getAction();  
            // 訊息廣播  
            if (action.equals(BackService.MESSAGE_ACTION)) {  
                String stringExtra = intent.getStringExtra("message");  
                tv.setText(stringExtra);  
            } else if (action.equals(BackService.HEART_BEAT_ACTION)) {// 心跳廣播  
                tv.setText("正常心跳");  
            }  
        }  
    };  

    private ServiceConnection conn = new ServiceConnection() {  
        @Override  
        public void onServiceDisconnected(ComponentName name) {  
            // 未連線為空  
            iBackService = null;  
        }  

        @Override  
        public void onServiceConnected(ComponentName name, IBinder service) {  
            // 已連線  
            iBackService = IBackService.Stub.asInterface(service);  
        }  
    };  
}  

好了,我們可以執行專案了。

Demo