使用mina實現Android長連線
阿新 • • 發佈:2019-01-01
一.概述
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);
}
}
介面的效果圖如下:
- 服務的具體程式碼
/**
* 開啟長連線的服務
* 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<Context> mContext;
private NioSocketConnector mConnection;
private IoSession mSessioin;
private InetSocketAddress mAddress;
public ConnectManager(ConnectConfig mConfig) {
this.mConfig = mConfig;
this.mContext = new WeakReference<Context>(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