Android之Mina框架學習
- 什麼是Mina?
Apache MINA(Multipurpose Infrastructure for Network Applications) 是 Apache 組織一個較新的專案,它為開發高效能和高可用性的網路應用程式提供了非常便利的框架。當前發行的 MINA 版本支援基於 Java NIO 技術的 TCP/UDP 應用程式開發、串列埠通訊程式(只在最新的預覽版中提供),MINA 所支援的功能也在進一步的擴充套件中。
總之:我們簡單理解它是一個封裝底層IO操作,提供高階操作API的通訊框架!
- 什麼是NIO技術?
在JDK1.4以前,I/O輸入輸出處理,我們把它稱為舊I/O處理,在JDK1.4開始,java提供了一系列改進的輸入/輸出新特性,這些功能被稱為新I/O(NEW I/O),新添了許多用於處理輸入/輸出的類,這些類都被放在java.nio包及子包下,並且對原java.io包中的很多類以NIO為基礎進行了改寫,新添了滿足新I/O的功能。
Java NIO和IO的主要區別
面向緩衝(Buffer)
-
什麼是長連線、短連線、心跳包?請參考這裡
- 常見的Java NIO框架有哪些?
Mina、Netty、Grizzly、xSocket等,區別如下:
Mina
Mina(Multipurpose Infrastructure for Network Applications) 是 Apache組織一個較新的專案,它為開發高效能和高可用性的網路應用程式提供了非常便利的框架。當前發行的 Mina 版本2.04支援基於 JavaNIO 技術的 TCP/UDP 應用程式開發、串列埠通訊程式,Mina 所支援的功能也在進一步的擴充套件中。目前,正在使用Mina的應用包括:Apache Directory Project、AsyncWeb、AMQP(Advanced MessageQueuing Protocol)、RED5 Server(Macromedia? FlashMedia RTMP)、ObjectRADIUS、 Openfire等等。
Netty
Netty是一款非同步的事件驅動的網路應用框架和工具,用於快速開發可維護的高效能、高擴充套件性協議伺服器和客戶端。也就是說,Netty是一個NIO客戶端/伺服器框架,支援快速、簡單地開發網路應用,如協議伺服器和客戶端。它極大簡化了網路程式設計,如TCP和UDP套接字伺服器。
Grizzly
Grizzly是一種應用程式框架,專門解決編寫成千上萬使用者訪問伺服器時候產生的各種問題。使用JAVANIO作為基礎,並隱藏其程式設計的複雜性。容易使用的高效能的API。帶來非阻塞socketd到協議處理層。利用高效能的緩衝和緩衝管理使用高效能的執行緒池。
xSocket
xSocket:是一個輕量級的基於nio的伺服器框架用於開發高效能、可擴充套件、多執行緒的伺服器。該框架封裝了執行緒處理、非同步讀/寫等方面。(只是對Java的NIO做了最簡單的封裝,以便於開發使用。)
OK,我們現在可以看看三者的簡單對比了。
首先,從設計的理念上來看,Mina的設計理念是最為優雅的。當然,由於Netty的主導作者與Mina的主導作者是同一人,出自同一人之手的Netty在設計理念上與Mina基本上是一致的。而Grizzly在設計理念上就較差了點,幾乎是JavaNIO的簡單封裝。
其次,從專案的出身來看,Mina出身於開源界的大牛Apache組織,Netty出身於商業開源大亨Jboss,而Grizzly則出身於土鱉Sun公司。從其出身可以看到其應用的廣泛程式,到目前為止,我見到業界還是使用Mina多一些,而Netty也在慢慢的應用起來,而Grizzly則似乎只有Sun自已的專案使用了,如果還有其他的公司或開源專案在使用,那就算我孤陋寡聞。 最後,從入門的文件來說,由於Mina見世時間相對較長,官方以及民間的文件與入門示例都相當的多。Netty的官方文件也做得很好,而民間文件就要相對於Mina少一些了。至於Grizzly,不管是官方還是民間,都很少見到其文件。
- 常見的Mina使用場景有哪些?
如:客戶端和服務端進行socket通訊時,服務端需要知道客戶端的是否線上,這就要求客戶端要一直定時傳送心跳請求告知服務端;通訊服務中斷後,客戶端自動發起重新連線等等。。。有同學說:這功能幾個普通函式就能實現,沒啥特別的啊?開始我也是這樣想的,但慢慢了解Mina框架後,發現很多底層操作都封裝好了,簡單易用,真的比自己砌磚爽多了……自己實現請參考 - Mina如何實現通訊?
下面以Mina實現Android客戶端與伺服器端通訊為例,原文參考:二、伺服器端程式碼
MinaService.java package com.czhappy.mina; 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; public class MinaService { 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(9226)); } catch (Exception e) { e.printStackTrace(); } System.out.println("啟動服務"); } /** * @ClassName: DemoServerHandler * @Description: 負責session物件的建立和監聽以及訊息的建立和接收監聽 * @author chenzheng * @date 2016-12-9 下午3:57:11 */ private static class DemoServerHandler extends IoHandlerAdapter{ //伺服器與客戶端建立連線 @Override public void sessionCreated(IoSession session) throws Exception { System.out.println("伺服器與客戶端建立連線..."); super.sessionCreated(session); } @Override public void sessionOpened(IoSession session) throws Exception { System.out.println("伺服器與客戶端連線開啟..."); super.sessionOpened(session); } //訊息的接收處理 @Override public void messageReceived(IoSession session, Object message) throws Exception { // TODO Auto-generated method stub super.messageReceived(session, message); String str = message.toString(); Date date = new Date(); session.write(date.toString()); System.out.println("接收到的資料:"+str); } @Override public void messageSent(IoSession session, Object message) throws Exception { // TODO Auto-generated method stub super.messageSent(session, message); } @Override public void sessionClosed(IoSession session) throws Exception { // TODO Auto-generated method stub super.sessionClosed(session); } } }
三、Android客戶端程式碼
ConnectionConfig.java package com.czhappy.minaclient; import android.content.Context; /** * Description:構建者模式 */ public class ConnectionConfig { private Context context; private String ip; private int port; private int readBufferSize; private long connectionTimeout; public Context getContext() { return context; } 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 context; private String ip = "192.168.168.20"; private int port = 9226; private int readBufferSize = 10240; private long connectionTimeout = 10000; public Builder(Context context){ this.context = context; } public Builder setIp(String ip){ this.ip = ip; return this; } public Builder setPort(int port){ this.port = port; return this; } public Builder setReadBufferSize(int readBufferSize){ this.readBufferSize = readBufferSize; return this; } public Builder setConnectionTimeout(long connectionTimeout){ this.connectionTimeout = connectionTimeout; return this; } private void applyConfig(ConnectionConfig config){ config.context = this.context; config.ip = this.ip; config.port = this.port; config.readBufferSize = this.readBufferSize; config.connectionTimeout = this.connectionTimeout; } public ConnectionConfig builder(){ ConnectionConfig config = new ConnectionConfig(); applyConfig(config); return config; } } } ConnectionManager.java package com.czhappy.minaclient; import android.content.Context; import android.content.Intent; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import org.apache.mina.core.future.ConnectFuture; import org.apache.mina.core.service.IoHandlerAdapter; 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.NioSocketConnector; import java.lang.ref.WeakReference; import java.net.InetSocketAddress; public class ConnectionManager { private static final String BROADCAST_ACTION = "com.commonlibrary.mina.broadcast"; private static final String MESSAGE = "message"; private ConnectionConfig mConfig; private WeakReference<Context> mContext; private NioSocketConnector mConnection; private IoSession mSession; private InetSocketAddress mAddress; public ConnectionManager(ConnectionConfig config){ this.mConfig = config; this.mContext = new WeakReference<Context>(config.getContext()); init(); } private void init() { mAddress = new InetSocketAddress(mConfig.getIp(), mConfig.getPort()); mConnection = new NioSocketConnector(); mConnection.getSessionConfig().setReadBufferSize(mConfig.getReadBufferSize()); mConnection.getFilterChain().addLast("logging", new LoggingFilter()); mConnection.getFilterChain().addLast("codec", new ProtocolCodecFilter(new ObjectSerializationCodecFactory())); mConnection.setHandler(new DefaultHandler(mContext.get())); mConnection.setDefaultRemoteAddress(mAddress); } /** * 與伺服器連線 * @return */ public boolean connnect(){ Log.e("tag", "準備連線"); try{ ConnectFuture future = mConnection.connect(); future.awaitUninterruptibly(); mSession = future.getSession(); SessionManager.getInstance().setSeesion(mSession); }catch (Exception e){ e.printStackTrace(); Log.e("tag", "連線失敗"); return false; } return mSession == null ? false : true; } /** * 斷開連線 */ public void disContect(){ mConnection.dispose(); mConnection=null; mSession=null; mAddress=null; mContext = null; Log.e("tag", "斷開連線"); } private static class DefaultHandler extends IoHandlerAdapter{ private Context mContext; private DefaultHandler(Context context){ this.mContext = context; } @Override public void sessionOpened(IoSession session) throws Exception { super.sessionOpened(session); } @Override public void messageReceived(IoSession session, Object message) throws Exception { Log.e("tag", "接收到伺服器端訊息:"+message.toString()); if(mContext!=null){ Intent intent = new Intent(BROADCAST_ACTION); intent.putExtra(MESSAGE, message.toString()); LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent); } } } } MinaService.java package com.czhappy.minaclient; import android.app.Service; import android.content.Context; import android.content.Intent; import android.os.HandlerThread; import android.os.IBinder; import android.support.annotation.Nullable; import android.util.Log; public class MinaService extends Service{ private ConnectionThread thread; @Override public void onCreate() { super.onCreate(); thread = new ConnectionThread("mina", getApplicationContext()); thread.start(); Log.e("tag", "啟動執行緒嘗試連線"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { super.onDestroy(); thread.disConnect(); thread=null; } @Nullable @Override public IBinder onBind(Intent intent) { return null; } class ConnectionThread extends HandlerThread{ private Context context; boolean isConnection; ConnectionManager mManager; public ConnectionThread(String name, Context context){ super(name); this.context = context; ConnectionConfig config = new ConnectionConfig.Builder(context) .setIp("192.168.168.20") .setPort(9226) .setReadBufferSize(10240) .setConnectionTimeout(10000).builder(); mManager = new ConnectionManager(config); } @Override protected void onLooperPrepared() { while(true){ isConnection = mManager.connnect(); if(isConnection){ Log.e("tag", "連線成功"); break; } try { Log.e("tag", "嘗試重新連線"); Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } } } public void disConnect(){ mManager.disContect(); } } } SessionManager.java package com.czhappy.minaclient; import android.util.Log; import org.apache.mina.core.session.IoSession; public class SessionManager { private static SessionManager mInstance=null; private IoSession mSession; public static SessionManager getInstance(){ if(mInstance==null){ synchronized (SessionManager.class){ if(mInstance==null){ mInstance = new SessionManager(); } } } return mInstance; } private SessionManager(){} public void setSeesion(IoSession session){ this.mSession = session; } public void writeToServer(Object msg){ if(mSession!=null){ Log.e("tag", "客戶端準備傳送訊息"); mSession.write(msg); } } public void closeSession(){ if(mSession!=null){ mSession.closeOnFlush(); } } public void removeSession(){ this.mSession=null; } } MinaTestActivity.java package com.czhappy.minaclient; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.support.v4.content.LocalBroadcastManager; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.TextView; public class MinaTestActivity extends AppCompatActivity implements View.OnClickListener{ private TextView start_service_tv, send_tv, receive_tv; private MessageBroadcastReceiver receiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_mina_test); initView(); registerBroadcast(); } private void registerBroadcast() { receiver = new MessageBroadcastReceiver(); IntentFilter filter = new IntentFilter("com.commonlibrary.mina.broadcast"); LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter); } private void initView() { receive_tv = (TextView) this.findViewById(R.id.receive_tv); start_service_tv = (TextView) this.findViewById(R.id.start_service_tv); start_service_tv.setOnClickListener(this); send_tv = (TextView) this.findViewById(R.id.send_tv); send_tv.setOnClickListener(this); } @Override public void onClick(View v) { switch(v.getId()){ case R.id.start_service_tv: Log.e("tag", "點選啟動服務"); Intent intent = new Intent(this, MinaService.class); startService(intent); break; case R.id.send_tv: Log.e("tag", "點選傳送訊息"); SessionManager.getInstance().writeToServer("hello123"); break; } } private void unregisterBroadcast(){ LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver); } @Override protected void onDestroy() { super.onDestroy(); stopService(new Intent(this, MinaService.class)); unregisterBroadcast(); } private class MessageBroadcastReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { receive_tv.setText(intent.getStringExtra("message")); } } }
四、執行結果
1.點選啟動服務,觀察客戶端是否與伺服器端連線成功;
2.點選傳送訊息,觀察訊息是否傳送成功,如果失敗判斷失敗在哪一步。客戶端執行結果:
伺服器端執行結果: - 我使用Mina遇到的坑
1)客戶端無法得到伺服器的返回值
解決方案:一定要正確設定編碼、解碼器,千萬不要直接搬別人的程式碼。
StringDecoder和StringEncoder報文內容格式,請與服務端約定好。
2)客戶端頻繁傳送心跳包,解決方法:參考這裡
- 利用Mina實現聊天室,可以參考這裡。
- 其他參考
相關文章
- NIO框架之MINA原始碼解析(二):mina核心引擎框架原始碼
- mina框架框架
- NIO框架之MINA原始碼解析(一):背景框架原始碼
- Android之Mina頻繁傳送心跳包Android
- Android 網路框架 OKHttp 學習Android框架HTTP
- Android之SurfaceView學習AndroidView
- 深度學習之Tensorflow框架深度學習框架
- 集合框架學習之List介面框架
- NIO框架之MINA原始碼解析(五):NIO超級陷阱和使用同步IO與MINA通訊框架原始碼
- Java之struts2框架學習Java框架
- 集合框架原始碼學習之ArrayList框架原始碼
- Netty-Mina深入學習與對比(二)Netty
- android開發之網路學習-Socket學習Android
- 駁 《駁 《駁 《駁 《停止學習框架》》》》、《駁 《駁 《停止學習框架》》》、《駁 《停止學習框架》》、《停止學習框架》框架
- 集合框架原始碼學習之LinkedList框架原始碼
- Java框架學習之Hibernate入門Java框架
- Android原始碼學習之handlerAndroid原始碼
- Android學習筆記之IntentAndroid筆記Intent
- Android學習之 屬性動畫Android動畫
- 深度學習學習框架深度學習框架
- 朝花夕拾之socket的基本使用以及mina框架簡單介紹框架
- SSM框架學習之Spring的AOP學習以及資料整理SSM框架Spring
- Python教程系列之Flask框架的學習PythonFlask框架
- java基礎學習之類集框架(十)Java框架
- Android AOP學習之:AspectJ實踐Android
- Android學習之 WebView使用小結AndroidWebView
- OS 學習框架框架
- 停止學習框架框架
- Koa框架學習框架
- Httprunner框架學習HTTP框架
- ThinkPhP 框架學習PHP框架
- 前端學習框架前端框架
- Play框架學習框架
- 集合框架原始碼學習之HashMap(JDK1.8)框架原始碼HashMapJDK
- 一起學習SSM框架之SpringMVC(五)SSM框架SpringMVC
- 學習心得之華為數字化轉型框架框架
- Android學習之活動的最佳實踐Android
- Android併發學習之阻塞佇列Android佇列