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 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); mReadThread = new ReadThread(socket); mReadThread.start(); mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE); // 初始化成功后,就准备发送心跳包 }// 释放socket private void releaseLastSocket(WeakReference 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 mWeakSocket; private boolean isStart = true; public ReadThread(Socket socket) { mWeakSocket = new WeakReference(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); } }; }

【Android socket通信的长连接与心跳检测】好了,我们可以运行项目了。
Demo

    推荐阅读