Socket长连接Android端心跳机制实现

来源:互联网 发布:redis可视化工具 知乎 编辑:程序博客网 时间:2024/06/05 19:13

前面用golang写的socket服务端请移步:http://blog.csdn.net/u010072711/article/details/76082176

1. 把socket链接和心跳功能都放在一个Service中,为什么要放在Service中?

一般我们这种socket几乎是跟app的生命周期一样长,甚至更长。不管在不在Service中去完成操作,我们都得开异步线程,虽然Service并不是异步操作,但是为了提升我们任务的优先级,我们最好是放在Service中,因为Service是由Android系统管理的,并且拥有比较高的优先级,线程是java中的异步任务载体,可以说Android系统不太认识线程。放在Service中可以很大程度上避免任务被回收或者关闭

2. 为什么需要心跳机制?

由于移动设备的网络的复杂性,经常会出现网络断开,如果没有心跳包的检测,客户端只会在需要发送数据的时候才知道自己已经断线,会延误,甚至丢失服务器发送过来的数据。我们可以每隔3秒钟或者每间隔1秒钟去判断一下socket是否断开,没断开就读取数据,断开了就重新连接socket。

3. Service与Activity之间怎么通信?

3.1 Activity调用bindService (Intent service, ServiceConnection conn, int flags)方法,得到Service对象的一个引用,这样Activity可以直接调用到Service中的方法,如果要主动通知Activity,我们可以利用回调方法

3.2 Service向Activity发送消息,可以使用广播,当然Activity要注册相应的接收器。比如Service要向多个Activity发送同样的消息的话,用这种方法就更好

4.源代码

4.1 Service代码

package danxx.library.socket;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.app.Service;import android.content.Intent;import android.os.Handler;import android.os.IBinder;import android.os.RemoteException;import android.support.v4.content.LocalBroadcastManager;import android.util.Log;/** * Created by dawish on 2017/7/24. *  由于移动设备的网络的复杂性,经常会出现网络断开,如果没有心跳包的检测, *  客户端只会在需要发送数据的时候才知道自己已经断线,会延误,甚至丢失服务器发送过来的数据。 */public class BackService extends Service {    private static final String TAG = "danxx";    /**心跳频率*/    private static final long HEART_BEAT_RATE = 3 * 1000;    /**服务器ip地址*/    public static final String HOST = "192.168.123.27";// "192.168.1.21";//    /**服务器端口号*/    public static final int PORT = 9800;    /**服务器消息回复广播*/    public static final String MESSAGE_ACTION="message_ACTION";    /**服务器心跳回复广播*/    public static final String HEART_BEAT_ACTION="heart_beat_ACTION";    /**读线程*/    private ReadThread mReadThread;    private LocalBroadcastManager mLocalBroadcastManager;    /***/    private WeakReference<Socket> mSocket;    // For heart Beat    private Handler mHandler = new Handler();    /**心跳任务,不断重复调用自己*/    private Runnable heartBeatRunnable = new Runnable() {        @Override        public void run() {            if (System.currentTimeMillis() - sendTime >= HEART_BEAT_RATE) {                boolean isSuccess = sendMsg("HeartBeat");//就发送一个\r\n过去 如果发送失败,就重新初始化一个socket                if (!isSuccess) {                    mHandler.removeCallbacks(heartBeatRunnable);                    mReadThread.release();                    releaseLastSocket(mSocket);                    new InitSocketThread().start();                }            }            mHandler.postDelayed(this, HEART_BEAT_RATE);        }    };    private long sendTime = 0L;    /**     * aidl通讯回调     */    private IBackService.Stub iBackService = new IBackService.Stub() {        /**         * 收到内容发送消息         * @param message 需要发送到服务器的消息         * @return         * @throws RemoteException         */        @Override        public boolean sendMessage(String message) throws RemoteException {            return sendMsg(message);        }    };    @Override    public IBinder onBind(Intent arg0) {        return iBackService;    }    @Override    public void onCreate() {        super.onCreate();        new InitSocketThread().start();        mLocalBroadcastManager=LocalBroadcastManager.getInstance(this);    }    public boolean sendMsg(final String msg) {        if (null == mSocket || null == mSocket.get()) {            return false;        }        final Socket soc = mSocket.get();            if (!soc.isClosed() && !soc.isOutputShutdown()) {                new Thread(new Runnable() {                    @Override                    public void run() {                        try {                            OutputStream os = soc.getOutputStream();                            String message = msg + "\r\n";                            os.write(message.getBytes());                            os.flush();                        } catch (IOException e) {                            e.printStackTrace();                        }                    }                }).start();                sendTime = System.currentTimeMillis();//每次发送成数据,就改一下最后成功发送的时间,节省心跳间隔时间            } else {                return false;            }        return true;    }    private void initSocket() {//初始化Socket        try {            Socket so = new Socket(HOST, PORT);            mSocket = new WeakReference<Socket>(so);            mReadThread = new ReadThread(so);            mReadThread.start();            mHandler.postDelayed(heartBeatRunnable, HEART_BEAT_RATE);//初始化成功后,就准备发送心跳包        } catch (UnknownHostException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }    }    /**     * 心跳机制判断出socket已经断开后,就销毁连接方便重新创建连接     * @param mSocket     */    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();            initSocket();        }    }    // Thread to read content from Socket    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);        }        @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.e(TAG, message);                            //收到服务器过来的消息,就通过Broadcast发送出去                            if(message.equals("ok")){//处理心跳回复                                Intent intent=new Intent(HEART_BEAT_ACTION);                                mLocalBroadcastManager.sendBroadcast(intent);                            }else{                                //其他消息回复                                Intent intent=new Intent(MESSAGE_ACTION);                                intent.putExtra("message", message);                                mLocalBroadcastManager.sendBroadcast(intent);                            }                        }                    }                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }    @Override    public void onDestroy() {        super.onDestroy();        mHandler.removeCallbacks(heartBeatRunnable);        mReadThread.release();        releaseLastSocket(mSocket);    }}

4.2 Activity代码

package com.danxx.views;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.support.v4.content.LocalBroadcastManager;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import android.widget.EditText;import android.widget.TextView;import android.widget.Toast;import java.lang.ref.WeakReference;import java.util.concurrent.Executors;import danxx.library.socket.BackService;import danxx.library.socket.IBackService;/** * Created by daish on 2017/7/24. */public class ActivityHeartSocket extends AppCompatActivity {    private TextView mResultText;    private EditText mEditText;    private Intent mServiceIntent;    private IBackService iBackService;    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);        }    };    class MessageBackReciver extends BroadcastReceiver {        private WeakReference<TextView> textView;        public MessageBackReciver(TextView tv) {            textView = new WeakReference<TextView>(tv);        }        @Override        public void onReceive(Context context, Intent intent) {            String action = intent.getAction();            TextView tv = textView.get();            if (action.equals(BackService.HEART_BEAT_ACTION)) {                if (null != tv) {                    Log.i("danxx", "Get a heart heat");                    tv.setText("Get a heart heat");                }            } else {                Log.i("danxx", "Get a heart heat");                String message = intent.getStringExtra("message");                tv.setText("服务器消息:"+message);            }        };    }    private MessageBackReciver mReciver;    private IntentFilter mIntentFilter;    private LocalBroadcastManager mLocalBroadcastManager;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_heart_socket);        mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);        mResultText = (TextView) findViewById(R.id.resule_text);        mEditText = (EditText) findViewById(R.id.content_edit);        mReciver = new MessageBackReciver(mResultText);        mServiceIntent = new Intent(this, BackService.class);        mIntentFilter = new IntentFilter();        mIntentFilter.addAction(BackService.HEART_BEAT_ACTION);        mIntentFilter.addAction(BackService.MESSAGE_ACTION);    }    @Override    protected void onStart() {        super.onStart();        mLocalBroadcastManager.registerReceiver(mReciver, mIntentFilter);        bindService(mServiceIntent, conn, BIND_AUTO_CREATE);    }    @Override    protected void onStop() {        super.onStop();        unbindService(conn);        mLocalBroadcastManager.unregisterReceiver(mReciver);    }    public void onClick(View view) {        switch (view.getId()) {            case R.id.send:                String content = mEditText.getText().toString();                try {                    boolean isSend = iBackService.sendMessage(content);//Send Content by socket                    Toast.makeText(this, isSend ? "success" : "fail",                            Toast.LENGTH_SHORT).show();                    mEditText.setText("");                } catch (RemoteException e) {                    e.printStackTrace();                }                break;            default:                break;        }    }}

5.效果图

5.1 golang服务端

这里写图片描述

5.2 Android客户端

这里写图片描述

Golang服务端代码:/Dawish/GoStudy/blob/master/src/main/SocketServer.go
Android客户端代码:AppLibrary/src/main/java/danxx/library/socket/BackService.java