一.概述
android长连接的实现有很多种,最常用的是使用第三方的长连接,比如推送服务的实现.使用第三方的长连接虽然在实现上最简单,但是扩展性缺少最差,要受限于三方的api,所以在这里介绍使用mina来实现android的长连接服务.
二.服务端的实现
首先来说说服务端的实现,这里只是举个简单的实例,目的只是告诉大家如何实现.
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);
}
}
/**
* 开启长连接的服务
* 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