Android的socket通信的长连接,有心跳检测

来源:互联网 发布:淘宝买房子 编辑:程序博客网 时间:2024/05/06 01:20

        在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() {@Overridepublic boolean sendMessage(String message) throws RemoteException {return sendMsg(message);}};@Overridepublic IBinder onBind(Intent arg0) {return (IBinder) iBackService;}@Overridepublic void onCreate() {super.onCreate();new InitSocketThread().start();}// 发送心跳包private Handler mHandler = new Handler();private Runnable heartBeatRunnable = new Runnable() {@Overridepublic void run() {if (System.currentTimeMillis() - sendTime >= HEART_BEAT_RATE) {boolean isSuccess = sendMsg("");// 就发送一个\r\n过去, 如果发送失败,就重新初始化一个socketif (!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;}// 初始化socketprivate 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);// 初始化成功后,就准备发送心跳包}// 释放socketprivate 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 {@Overridepublic 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")@Overridepublic 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;@Overrideprotected 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() {@Overridepublic 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();}}});}@Overrideprotected void onStart() {super.onStart();bindService(mServiceIntent, conn, BIND_AUTO_CREATE);// 开始服务registerReceiver();}@Overrideprotected void onResume() {super.onResume();// 注册广播 最好在onResume中注册// registerReceiver();}@Overrideprotected 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() {@Overridepublic 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() {@Overridepublic void onServiceDisconnected(ComponentName name) {// 未连接为空iBackService = null;}@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 已连接iBackService = IBackService.Stub.asInterface(service);}};}


</pre><p></p><pre>
好了,我们可以运行项目了。


这是前台显示的界面,正在发送心跳检测,发送给服务器的是空字符,返回的是ok字符,前台显示心跳正常,说明连接正常。


这是后台接收到的空字符,发送ok字符给前台。


现在我在前台界面输入内容发送给后台



后台收到前台消息,并返回一个数据:



前台收到后台的数据并改变UI


并且他们的连接是一直保持的,客户端会一直发送心跳判断是否还是连接中的,亲测demo是没有问题的。

demo下载地址:http://download.csdn.net/download/zh724738989/8274647

0 2
原创粉丝点击