使用mina实现Android长连接

一.概述
android长连接的实现有很多种,最常用的是使用第三方的长连接,比如推送服务的实现.使用第三方的长连接虽然在实现上最简单,但是扩展性缺少最差,要受限于三方的api,所以在这里介绍使用mina来实现android的长连接服务.

二.服务端的实现
首先来说说服务端的实现,这里只是举个简单的实例,目的只是告诉大家如何实现.

  • 导入需要的jar包
    这里写图片描述

  • 具体的代码实现

import java.net.InetSocketAddress;
import java.util.Date;

import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.serialization.ObjectSerializationCodecFactory;
import org.apache.mina.filter.logging.LoggingFilter;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

/**
 * mina服务端
 */
public class MainService {

    public static void main(String[] args) {
        IoAcceptor acceptor = new NioSocketAcceptor();
        //添加日志过滤
        acceptor.getFilterChain().addLast("logger", new LoggingFilter());
        acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
        //设置回调监听
        acceptor.setHandler(new DemoServerHandler());
        //设置读取大小
        acceptor.getSessionConfig().setReadBufferSize(2048);
        //设置超时时间
        acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10);

        try {
            //开启监听
            acceptor.bind(new InetSocketAddress(9123));

        } catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
        }
    }

    private static class  DemoServerHandler extends  IoHandlerAdapter{

        @Override
        public void sessionCreated(IoSession session) throws Exception {
            // TODO Auto-generated method stub
            super.sessionCreated(session);


        }

        /**
         * 一个客户端连接时回调的方法
         * @param session
         * @throws Exception
         */
        @Override
        public void sessionOpened(IoSession session) throws Exception {
            // TODO Auto-generated method stub
            super.sessionOpened(session);

        }

        /**
         * 接收到消息时回调的方法
         * @param session
         * @param message
         * @throws Exception
         */
        @Override
        public void messageReceived(IoSession session, Object message)
                throws Exception {
            // TODO Auto-generated method stub
            super.messageReceived(session, message);
            //接收客户端的消息
            String msg = message.toString();
            Date date = new Date();
            //发送消息给客户端
            session.write(msg+date.toString());
        }

        /**
         * 发送消息时回调的方法
         * @param session
         * @param message
         * @throws Exception
         */
        @Override
        public void messageSent(IoSession session, Object message)
                throws Exception {
            // TODO Auto-generated method stub
            super.messageSent(session, message);
        }

        /**
         * 一个客户端断开时回调的方法
         * @param session
         * @throws Exception
         */
        @Override
        public void sessionClosed(IoSession session) throws Exception {
            // TODO Auto-generated method stub
            super.sessionClosed(session);

        }

    }

}

服务端代码非常简单,只是简单的开启,然后接收客户端的信息,在将进行回传给客户端.

三.Android客户端的实现
Android客户端的实现流程就是一个用户界面,用来开启服务,发送消息和显示消息->一个服务,用来完成长连接->一个长连接的管理类,用来完成长连接具体实现->一个配置文件类,用来配置长连接实现需要的参数.

  • 用户界面的具体代码
public class MainActivity extends AppCompatActivity {
    private TextView tv;
    private Button btn,btn2;
    private MyBroadcaseRedceiver mBroadcaseRedceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.tv);
        btn = (Button) findViewById(R.id.btn);
        btn2 = (Button) findViewById(R.id.btn2);

        //注册广播 用来接收服务器返回的信息
        IntentFilter filter = new IntentFilter(ConnectManager.BROADCAST_ACTION);
        mBroadcaseRedceiver = new MyBroadcaseRedceiver();
        registerReceiver(mBroadcaseRedceiver, filter);
        //设置按钮的点击事件
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                SessionManager.getmInstance().writeToServer("发给服务器的消息 ");
            }
        });
        btn2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //开启服务
                Intent intent = new Intent(MainActivity.this, ConnectService.class);
                startService(intent);
            }
        });
    }





    public class MyBroadcaseRedceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
            //更新界面
            tv.setText(intent.getStringExtra(ConnectManager.MESSAGE));
        }
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        unregisterReceiver(mBroadcaseRedceiver);
    }
}

界面的效果图如下:
使用mina实现Android长连接_第1张图片

  • 服务的具体代码

/**
 * 开启长连接的服务
 * Created by lyf on 2017/5/20.
 */

public class ConnectService extends Service {
    private ConnectThred connectThred;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //使用子线程开启连接
        connectThred = new ConnectThred("mina",getApplicationContext());
        connectThred.start();
    }



    @Override
    public void onDestroy() {
        super.onDestroy();
        connectThred.disConnection();
        connectThred=null;
    }

    /**
     * 负责调用connectmanager类来完成与服务器的连接
     */
    class ConnectThred extends HandlerThread {

        boolean isConnection;
        ConnectManager mManager;
        public ConnectThred(String name,Context context) {
            super(name);
            //创建配置文件类
            ConnectConfig config = new ConnectConfig.Builder(context)
                    .setIp("192.168.0.113")
                    .setPort(9123)
                    .setReadBufferSize(10240)
                    .setConnectionTimeout(10000)
                    .bulid();
            //创建连接的管理类
            mManager = new ConnectManager(config);
        }

        @Override
        protected void onLooperPrepared() {
            //利用循环请求连接
            while (true) {
                isConnection = mManager.connect();
                if (isConnection) {
                    //当请求成功的时候,跳出循环
                    break;
                }
                try {
                    Thread.sleep(3000);
                } catch (Exception e) {

                }
            }
        }

        /**
         *断开连接
         */
        public void disConnection(){
            mManager.disConnect();
        }

    }


因为长连接一般都是在后台运行的,所以推荐用服务开启,为了保证长连接的开启,我们在这里利用子线程开启死循环请求连接,直到成功为止.

  • 长连接实现的管理类的代码

/**
 * 连接的管理类
 * Created by lyf on 2017/5/20.
 */

public class ConnectManager {
    public static final String BROADCAST_ACTION="com.example.lyf.longconnect";
    public static final String MESSAGE="message";

    private ConnectConfig mConfig;//配置文件
    private WeakReference mContext;
    private NioSocketConnector mConnection;
    private IoSession mSessioin;
    private InetSocketAddress mAddress;

    public ConnectManager(ConnectConfig mConfig) {
        this.mConfig = mConfig;
        this.mContext = new WeakReference(mConfig.getmContext());
        init();
    }

    private void init() {
        mAddress = new InetSocketAddress(mConfig.getIp(),mConfig.getPort());
        //创建连接对象
        mConnection = new NioSocketConnector();
        //设置连接地址
        mConnection.setDefaultRemoteAddress(mAddress);
        mConnection.getSessionConfig().setReadBufferSize(mConfig.getReadBufferSize());
        //设置过滤
        mConnection.getFilterChain().addLast("logger",new LoggingFilter());
        mConnection.getFilterChain().addLast("codec",new ProtocolCodecFilter(new ObjectSerializationCodecFactory()));
        //设置连接监听
        mConnection.setHandler(new DefaultHandler(mContext.get()));
    }

    private static class DefaultHandler extends IoHandlerAdapter{
        private Context context;

        public DefaultHandler(Context context) {
            this.context = context;
        }

        /**
         * 连接成功时回调的方法
         * @param session
         * @throws Exception
         */
        @Override
        public void sessionOpened(IoSession session) throws Exception {
            //当与服务器连接成功时,将我们的session保存到我们的sesscionmanager类中,从而可以发送消息到服务器
            SessionManager.getmInstance().setIoSession(session);
        }

        /**
         * 接收到消息时回调的方法
         * @param session
         * @param message
         * @throws Exception
         */
        @Override
        public void messageReceived(IoSession session, Object message) throws Exception {

            if (context != null) {
                //将接收到的消息利用广播发送出去
                Intent intent = new Intent(BROADCAST_ACTION);
                intent.putExtra(MESSAGE,message.toString());
                context.sendBroadcast(intent);
            }
        }
    }

    /**
     * 与服务器连接的方法
     * @return
     */
    public boolean connect(){
       try{
           ConnectFuture future =mConnection.connect();
           future.awaitUninterruptibly();
           mSessioin = future.getSession();
       }catch (Exception e){
           e.printStackTrace();
            return false;
       }
        return mSessioin==null?false:true;
    }

    /**
     * 断开连接的方法
     */
    public void disConnect(){
        mConnection.dispose();
        mConnection=null;
        mSessioin=null;
        mAddress=null;
        mContext=null;
    }


这个管理类就是利用mina框架的api来实现的与服务器连接的方法.具体的分析看注释.

  • 配置文件类

/**
 * 连接的配置文件
 * Created by lyf on 2017/5/20.
 */

public class ConnectConfig {
    private Context mContext;
    private String ip;
    private int port;
    private int readBufferSize; //缓存大小
    private long connectionTimeout;//连接超时时间

    public Context getmContext() {
        return mContext;
    }



    public String getIp() {
        return ip;
    }



    public int getPort() {
        return port;
    }


    public int getReadBufferSize() {
        return readBufferSize;
    }



    public long getConnectionTimeout() {
        return connectionTimeout;
    }



    public static class Builder{
        private Context mContext;
        private String ip="";
        private int port=9123;
        private int readBufferSize=10240; //缓存大小
        private long connectionTimeout=10000;//连接超时时间


        public Builder(Context mContext) {
            this.mContext = mContext;
        }

        public Builder setConnectionTimeout(long connectionTimeout) {
            this.connectionTimeout = connectionTimeout;
            return this;
        }

        public Builder setIp(String ip) {
            this.ip = ip;
            return this;
        }

        public Builder setmContext(Context mContext) {
            this.mContext = mContext;
            return this;
        }

        public Builder setPort(int port) {
            this.port = port;
            return this;
        }

        public Builder setReadBufferSize(int readBufferSize) {
            this.readBufferSize = readBufferSize;
            return this;
        }

        public ConnectConfig bulid(){
            ConnectConfig connectConfig = new ConnectConfig();
            connectConfig.connectionTimeout = this.connectionTimeout;
            connectConfig.ip = this.ip;
            connectConfig.port = this.port;
            connectConfig.mContext = this.mContext;
            connectConfig.readBufferSize = this.readBufferSize;
            return  connectConfig;
        }
    }
}

这里就是利用Builder模型来方便创建配置文件对象,就是配置一些实现长连接需要的参数.

  • 与服务器通信的单例

/**
 * session管理类,通过ioSession与服务器通信
 * Created by lyf on 2017/5/21.
 */

public class SessionManager {
    private static  SessionManager mInstance = null;

    private IoSession ioSession;//最终与服务器 通信的对象

    public static SessionManager getmInstance(){
        if (mInstance == null) {
            synchronized (SessionManager.class) {
                if (mInstance == null) {
                    mInstance = new SessionManager();
                }
            }
        }
        return mInstance;
    }
    private SessionManager(){

    }

    public void setIoSession(IoSession ioSession) {
        this.ioSession = ioSession;
    }

    /**
     * 将对象写到服务器
     */
    public void writeToServer(Object msg) {
        if (ioSession != null) {
            ioSession.write(msg);
        }
    }

    /**
     * 关闭连接
     */
    public void closeSession() {
        if (ioSession != null) {
            ioSession.closeOnFlush();
        }
    }

    public void removeSession() {
        ioSession = null;
    }


}

通过这个单例的writeToServer方法就可以将信息发送到服务器了.

四.总结
以上就是通过mina框架实现的一套简单长连接,通过mina框架来实现长连接虽然比第三方的复杂,好处就是便于扩展,想要什么功能都可以实现.本文的代码我都放到了github

你可能感兴趣的:(android开发,android)