1. 程式人生 > >Android開發之基於MINA框架的聊天通訊功能實現

Android開發之基於MINA框架的聊天通訊功能實現

博主近端時間在做一個專案,裡面有個需求需要實現點對點可選擇聊天,就像一個QQ一樣。但是需求又沒那麼高大尚。似乎就只是一個簡單的聊天。網上找了很多資訊,最後決定使用MINA框架來實現。現在的IM通訊協議有一個叫XMPP的,博主一看,好像專案需求不需要那麼複雜,於是博主根據實際需要自己定了一個簡單的傳輸協議來實現。專案之前用的是openfire,但是聊天只是一個其中一個基礎功能,還有其他需求,而且openfire有各種限制,不支援JDK1.7等。於是只能放棄使用它,自己來寫一個通訊伺服器了。博主是做Android開發的,伺服器端理應該是做伺服器的人負責,無奈公司人手不夠,專案又催的緊,伺服器端還有很多其他的業務需要那個專業的人去做,於是,博主只好硬著頭皮上了。查看了一下好像OPENFIRE好像就是在MINA的框架上基於XMPP協議寫出來的,於是博主果斷就去研究了MINA框架,搞了那麼2天,還真被博主搞出來了。好了,廢話不說了,言歸正傳。

MINA框架,是Apache組織應用程式(Multipurpose Infrastructure for Network Applications) 是 Apache 組織一個較新的專案,它為開發高效能和高可用性的網路應用程式提供了非常便利的框架。在這不在詳細描述MINA框架的底層工作原理,直接進入例項開發,如果想看MINA框架的底層實現原理,請轉接(博主就是看這篇文章學的):http://blog.csdn.net/w13770269691/article/details/8614584

一、MINA框架服務端開發

博主的專案的服務端是一個web專案,還有的業務,在這裡博主將MINA框架部分剝離了出來,重新新建了一個專案。

1、MINA伺服器編寫:

package com.freedom.mina;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.Charset;

import org.apache.log4j.Logger;
import org.apache.mina.core.service.IoAcceptor;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.filter.codec.ProtocolCodecFilter;
import org.apache.mina.filter.codec.textline.LineDelimiter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;

/**
 * @ClassName: Server
 * @author victor_freedom (
[email protected]
) * @createddate Dec 14, 2014 11:25:40 PM * @Description: TODO */ public class Server implements Runnable { private final Logger logger = Logger.getLogger(Server.class); public Thread start; public Server() { start = new Thread(this); } public void startService() { System.out.println("MINA啟動"); start.start(); } public void stopService(){ System.out.println("MINA停止"); start.interrupt(); } public void run() { IoAcceptor acceptor = new NioSocketAcceptor(); acceptor.getFilterChain().addLast( "codec", new ProtocolCodecFilter(new TextLineCodecFactory(Charset .forName("UTF-8"), LineDelimiter.WINDOWS.getValue(), LineDelimiter.WINDOWS.getValue()))); acceptor.setHandler(new ServerHandler()); acceptor.getSessionConfig().setReadBufferSize(1024); acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); try { acceptor.bind(new InetSocketAddress(9090)); } catch (IOException e) { logger .error("Server start failed, Please check the network or port!"); } logger.info("Server start up!"); } }

2、MINA服務訊息Handler編寫(這裡需要大家注意看,在android配合MINA框架使用的時候,在中文訊息的處理上,需要特別的注意,詳情請注意看程式碼註釋)

package com.freedom.mina;

import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Date;

import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;

/**
 * @ClassName: ServerHandler
 * @author victor_freedom ([email protected])
 * @createddate Dec 15, 2014 12:34:44 AM
 * @Description:
 */
public class ServerHandler extends IoHandlerAdapter {

	/**
	 * 用於存放使用者資訊的集合
	 */
	private final UserSessionMap userSessionMap = UserSessionMap.getInstance();

	@Override
	public void messageSent(IoSession session, Object message) throws Exception {
		super.messageSent(session, message);
	}

	@Override
	public void sessionOpened(IoSession session) throws Exception {
	}

	@Override
	public void sessionClosed(IoSession session) throws Exception {
	}

	@Override
	public void exceptionCaught(IoSession session, Throwable cause)
			throws Exception {
		super.exceptionCaught(session, cause);
	}

	@Override
	public void messageReceived(IoSession session, Object message)
			throws Exception {
		// 將收到的資訊解碼
		String data = URLDecoder.decode(message.toString(), "UTF-8").trim();
		// 這是博主自定義的一個傳輸協議,採用json資料格式傳輸
		ChatMessage receiveData = GsonUtils.jsonToBean(data, ChatMessage.class);
		if ("LOGIN".equals(receiveData.getType())) {
			userSessionMap.put(receiveData.getSender(), session);
			for (String key : userSessionMap.keySet()) {
				if (key.equals(receiveData.getSender()))
					continue;
				IoSession ioSession = userSessionMap.get(key);
				ChatMessage sendData = new ChatMessage();
				sendData.setContent(receiveData.getSender() + "上線了");
				sendData.setSender(receiveData.getSender());
				sendData.setType("LOGIN");
				sendData.setReceiver(key);
				sendData.setSendTime(new Date().getTime());
				sendData.setReceiveTime(new Date().getTime());
				String datas = GsonUtils.beanToJson(sendData).trim();
//				System.out.println(datas);
				//在Android端,涉及到中文傳輸的時候,總會出現亂碼或者是收不到訊息的問題,試了各種辦法,最後只能通過URLEncoder轉碼的方式來實現中文傳輸,
				//其他方式統統都不行,在服務端,記得轉碼完了之後加上換行符,不然android端收不到訊息。
				ioSession.write(URLEncoder.encode(datas, "UTF-8") + "\r\n");
			}
		} else if ("OFFLINE".equals(receiveData.getType())) {
			userSessionMap.remove(receiveData.getSender());
			for (String key : userSessionMap.keySet()) {
				IoSession ioSession = userSessionMap.get(key);
				ChatMessage sendData = new ChatMessage();
				sendData.setContent(receiveData.getSender() + "下線了");
				sendData.setSender(receiveData.getSender());
				sendData.setType("OFFLINE");
				sendData.setReceiver(key);
				sendData.setSendTime(new Date().getTime());
				sendData.setReceiveTime(new Date().getTime());
				String datas = GsonUtils.beanToJson(sendData).trim();
//				System.out.println(datas);
				ioSession.write(URLEncoder.encode(datas, "UTF-8") + "\r\n");
			}

		} else {
			System.out.println(data);
			IoSession ioSession = userSessionMap.get(receiveData.getReceiver());
			ioSession.write(URLEncoder.encode(data, "UTF-8") + "\r\n");
		}
	}

	@Override
	public void sessionCreated(IoSession session) throws Exception {
		super.sessionCreated(session);
	}

	@Override
	public void sessionIdle(IoSession session, IdleStatus status)
			throws Exception {
		super.sessionIdle(session, status);
	}

}

WEB端設定主要資訊講解完畢,詳情可以下載DEMO檢視測試,接下來講解Android端

二、MINA框架客戶端開發

1、Android後臺接受訊息服務編寫:

/**   
 * @Company: Batways
 * @Project:Tnt 
 * @Title: UpdateService.java
 * @Package com.batways.tnt.service
 * @Description: TODO
 * @author victor_freedom ([email protected])
 * @date 2014年9月6日 下午2:16:08
 * @version V1.0   
 */

package com.example.minatest.service;

import java.net.InetSocketAddress;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.Charset;
import java.util.Date;

import org.apache.mina.core.future.ConnectFuture;
import org.apache.mina.core.service.IoConnector;
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.textline.LineDelimiter;
import org.apache.mina.filter.codec.textline.TextLineCodecFactory;
import org.apache.mina.transport.socket.nio.NioSocketConnector;

import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;

import com.example.minatest.LoggerUtils;
import com.example.minatest.bean.ChatMessage;
import com.example.minatest.gloable.ConstantValues;
import com.example.minatest.utils.GsonUtils;

/**
 * @ClassName: UpdateService
 * @author victor_freedom ([email protected])
 * @createddate 2014年9月6日 下午2:16:08
 * @Description: TODO
 * 
 */
public class UpdateService extends Service {
	private DataHandler dataHandler;
	private IoSession session;
	private String TAG = "UpdateService";
	private BroadcastReceiver sendMessage = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			try {
				ChatMessage messge = new ChatMessage();
				messge.setType("NORMAL");
				String data = intent.getStringExtra("data");
				messge.setContent(data);
				messge.setSendTime(new Date().getTime());
				messge.setReceiveTime(new Date().getTime());
				//傳送者
				messge.setSender("justice");
				//接受者
				messge.setReceiver("freedom");
				//這裡傳送訊息的時候需要特別注意,這裡和服務端有點不同,換行符需要和實體內容一起進行編碼,不然服務端接受不到訊息。
				String info = GsonUtils.beanToJson(messge) + "\r\n";
				session.write(URLEncoder.encode(info, "UTF-8"));

			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	};

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

	@Override
	public void onCreate() {
		IntentFilter filter = new IntentFilter();
		filter.addAction(ConstantValues.SENDMESSGE);
		filter.addCategory(Intent.CATEGORY_DEFAULT);
		dataHandler = new DataHandler();
		registerReceiver(sendMessage, filter);
		new Thread(new Runnable() {

			@Override
			public void run() {
				IoConnector connector = new NioSocketConnector();
				connector.getFilterChain().addLast(
						"textProtocol",
						new ProtocolCodecFilter(new TextLineCodecFactory(
								Charset.forName("UTF-8"), LineDelimiter.WINDOWS
										.getValue(), LineDelimiter.WINDOWS
										.getValue())));

				connector.getSessionConfig().setReadBufferSize(1024);
				connector.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE,
						10);

				connector.setHandler(dataHandler);
				// 這裡是非同步操作 連線後立即返回
				ConnectFuture future = connector.connect(new InetSocketAddress(
						"192.168.1.111", 9090));
				future.awaitUninterruptibly();// 等待連線建立完成
				session = future.getSession();

				session.getCloseFuture().awaitUninterruptibly();// 等待連線斷開
				connector.dispose();
			}
		}).start();

		super.onCreate();
	}

	@Override
	public void onDestroy() {
		unregisterReceiver(sendMessage);
		sendMessage = null;
		super.onDestroy();
	}

	private class DataHandler extends IoHandlerAdapter {

		@Override
		public void sessionCreated(IoSession session) throws Exception {
		}

		@Override
		public void sessionOpened(IoSession session) throws Exception {
			//上線與伺服器建立連線之後,告訴伺服器上線了
			ChatMessage messge = new ChatMessage();
			messge.setType("LOGIN");
			//傳送者
			messge.setSender("justice");
			messge.setContent("login");
			//接受者(這裡接受者可以不需要設定)
			messge.setReceiver("freedom");
			messge.setSendTime(new Date().getTime());
			messge.setReceiveTime(new Date().getTime());
			//這裡傳送訊息的時候需要特別注意,這裡和服務端有點不同,換行符需要和實體內容一起進行編碼,不然服務端接受不到訊息。
			session.write(URLEncoder.encode(GsonUtils.beanToJson(messge)
					+ "\r\n", "UTF-8"));
		}

		@Override
		public void sessionClosed(IoSession session) throws Exception {
		}

		@Override
		public void sessionIdle(IoSession session, IdleStatus status)
				throws Exception {
		}

		@Override
		public void exceptionCaught(IoSession session, Throwable cause)
				throws Exception {
		}

		@Override
		public void messageReceived(IoSession session, Object message)
				throws Exception {
			//處理從服務端接收到的訊息
			String result = URLDecoder.decode(message.toString(), "UTF-8");
			LoggerUtils.i(TAG, result);
			BroadcastHelper.sendBroadCast(getBaseContext(),
					ConstantValues.RECEIVEMESSGE, "data", result);
		}

		@Override
		public void messageSent(IoSession session, Object message)
				throws Exception {

		}
	}

}
2、主Activity類編寫:
package com.example.minatest;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.example.minatest.bean.ChatMessage;
import com.example.minatest.gloable.ConstantValues;
import com.example.minatest.service.BroadcastHelper;
import com.example.minatest.service.UpdateService;
import com.example.minatest.utils.GsonUtils;

/**
 * @ClassName: MainActivity
 * @author victor_freedom ([email protected])
 * @createddate 2014-12-15 上午12:49:29
 * @Description: TODO
 */
public class MainActivity extends Activity {
	private Button sendMessage;
	private TextView recevier;
	private EditText content;
	private Intent intent;
	protected String TAG = "MainActivity";
	// 接受訊息廣播
	private BroadcastReceiver contentRecevier = new BroadcastReceiver() {

		@Override
		public void onReceive(Context context, Intent intent) {
			String data = intent.getStringExtra("data");
			ChatMessage receiveData = GsonUtils.jsonToBean(data,
					ChatMessage.class);
			// 如果接受到空訊息時過濾掉
			if (null == receiveData) {
				return;
			}
			// 將訊息展現出來。
			recevier.setText(receiveData.getSender() + ":"
					+ receiveData.getContent());
		}
	};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		intent = new Intent(MainActivity.this, UpdateService.class);
		startService(intent);
		IntentFilter filter = new IntentFilter();
		filter.addAction(ConstantValues.RECEIVEMESSGE);
		filter.addCategory(Intent.CATEGORY_DEFAULT);
		registerReceiver(contentRecevier, filter);
		sendMessage = (Button) findViewById(R.id.send);
		content = (EditText) findViewById(R.id.content);
		recevier = (TextView) findViewById(R.id.recevier);
		recevier.setText("訊息");
		sendMessage.setOnClickListener(new OnClickListener() {

			@Override
			public void onClick(View v) {
				String data = content.getText().toString();
				if (data.isEmpty())
					return;
				// 點擊發送訊息到伺服器
				BroadcastHelper.sendBroadCast(MainActivity.this,
						ConstantValues.SENDMESSGE, "data", data);
				content.setText("");
			}
		});
	}

	@Override
	protected void onDestroy() {
		//退出處理,這裡應該在發個下線的訊息,時間太晚了,博主就不寫了。
		unregisterReceiver(contentRecevier);
		contentRecevier = null;
		stopService(intent);
		intent = null;
		super.onDestroy();
	}
}

附上幾張博主測試時候的測試圖,博主測試的時候寫死兩個使用者,一個freedom,一個justice:

終於寫完了,今晚不錯,寫了兩篇,前一篇JAVA檔案IO流詳解也寫了兩個小時,這個從11點開始寫,到現在瞬間就到1點了。

希望這篇部落格可以幫到所有運用MINA框架做Android開發的人。

DEMO下載地址(服務端加客戶端):http://download.csdn.net/detail/victorfreedom/8259969


相關推薦

Android開發基於MINA框架聊天通訊功能實現

博主近端時間在做一個專案,裡面有個需求需要實現點對點可選擇聊天,就像一個QQ一樣。但是需求又沒那麼高大尚。似乎就只是一個簡單的聊天。網上找了很多資訊,最後決定使用MINA框架來實現。現在的IM通訊協議有一個叫XMPP的,博主一看,好像專案需求不需要那麼複雜,於是博主根據實際

Android開發編寫精美的聊天介面

廢話少說,直接堆程式碼,是看著Android第一行程式碼練習的,大牛繞過,菜鳥勿噴 首先 編寫主介面,修改  activity_main.xml <LinearLayout xmlns:android="http://schemas.android.com/apk/

Android學習基於DrawerLayout的側邊欄實現

本例,是基於AndroidStudio開發,用到v7包下DrawerLayout,Toolbar,和android.support.design下的NavigationView,需要在gradle中配置 compile 'com.android.support:appco

Android開發,MapBox的使用及部分功能實現(一)----- 初始化、標記、定位、styleurl

近期,應公司要求,開始接觸MapBox For Android的開發。 經過初步的接觸,發現MapBox與我之前使用的Arcgis有很多不同,相比起來,MapBox更清潔,更輕便,也更容易使用,但是相對的,MapBox相對於Arcgis缺少了很多的功能實現,許多的東西都需要

Android開發藍芽(一)——基於SPP協議藍芽模組通訊

使用裝置 基本概念 基本流程 本文意在介紹藍芽開發的主要流程,學習使用藍芽開發一個星期了,寫寫一個星期以來遇到的一些小問題,還有介紹下流程。開發具有基本的通訊功能,本專案主要是用於與藍芽模組的串列埠讀寫功能。 下一篇文章還有Android開

Unity3D開發基於委託的訊息框架開發

我們在開發專案的時候會發現,我們經常要在不同模組不同類之間呼叫函式。比如:我們在點選場景一個物體後,就需要對UI介面做一定的變化。通常最簡單的方式是我們直接在面板繫結持有對更改介面的函式的物件。可是如果專案比較大,類比較多的情況下,我們會發現專案變得異常混亂。更改一點需求可能會牽一髮而動全身。其

Android開發AIDL實現遠端服務程序通訊(IPC)

首先什麼是AIDL呢,它是Android系統中的一種介面定義語言,用於約束兩個程序間的通訊規則,供編譯器生成程式碼。 實現Android裝置上的兩個程序間通訊(IPC),程序之間的通訊資訊首先會被轉換成AIDL協議資訊,然後傳送給對方;對方接收到AIDL協議資

Android開發手把手教你寫ButterKnife框架(一)

系列文章目錄導讀: 一、概述 JakeWharton我想在Android界無人不知,無人不曉的吧, ButterKnife這個框架就是出自他隻手。這個框架我相信很多人都用過,本系列部落格就是帶大家更加深入的認識這個框架,ButterKnife截至目前

Android開發BlueTooth--最簡單的Andorid傳統藍芽通訊Demo

又到了Android小白的開發之路上寫筆記的時間了~~ 開篇都不知道說什麼好... ...前兩個月寫了WIFI,後來也想寫一下藍芽的,可惜公司產品不給力,出現了很多BUG,一直在修啊修,最近終於有點空閒時間了。那就來簡單的嘗試一下 關於藍芽開發,優秀部落格一大片,你能來看我

Android IM基於Openfire+Smack的聊天伺服器的搭建與測試

XMPP協議(Extensible Messaging and PresenceProtocol,可擴充套件訊息處理現場協議)是一種基於XML的協議,目的是為了解決及時通訊標準而提出來的,最早是在Jabber上實現的。它繼承了在XML環境中靈活的發展性。因

Android開發圖片載入快取框架Picasso的領教

Picasso實現了圖片的非同步載入,並解決了Android中載入圖片時常見的一些問題,它有以下特點: 在Adapter中取消了不在檢視範圍內的ImageView的資源載入,因為可能會產生圖片錯

Android開發手把手教你寫ButterKnife框架(三)

系列文章目錄導讀: 一、概述 然後在Processor裡生成自己的程式碼,把要輸出的類,通過StringBuilder拼接字串,然後輸出。 try { // write the file JavaFileObject

Android軟體開發應用程式之間的通訊介紹

Android 開發中在程式之間通訊的介面做的還是非常豐富的本例主要向大家介紹程式之間是如何進行溝通,有哪幾種溝通方式如何來實現溝通。 1.      使用handler傳遞訊息     handler 大家可以把它想象成主執行緒(UI執行緒)的一個子執行緒,它可以給主執

android開發手機與微控制器藍芽模組通訊

之前兩篇都是在說與手機的連線,連線方法,和主動配對連線,都是手機與手機的操作,做起來還是沒問題的,但是最終的目的是與微控制器的藍芽模組的通訊。 下面是到目前為止嘗試的與微控制器的通訊方法,沒有成功,但是從思路上來說沒有問題,最大的問題是與微控制器配對的時候,微控制器的藍芽

Android開發聲網即時通訊與訊飛語音識別相結合

聲網是一家提供語音、視訊即時通訊服務的公司,他的服務大多基於WebRTC開源專案並進行一些優化和修改。而訊飛語音識別應該不用多說了,老羅在釋出會上介紹得已經夠詳細了。 那麼下面進入今天的主題,就是讓聲網和訊飛識別同時使用,之前可能有朋友沒遇到過這樣的需求,那先說一下讓兩者同

Android開發getX,getRawX,getWidth,getTranslationX等的區別

save string hlist getwidth sta 是我 touch 項目 寬度 轉載請註明出處:http://blog.csdn.net/dmk877/article/details/51550031 好久沒寫博客了,最近工作確實挺忙的,剛剛結束了一個

Android 開發Windows環境下Android Studio安裝和使用教程(圖文詳細步驟)

9.png 虛擬機 jdk版本 編寫 clip 開發平臺 集成開發 arc 電腦安裝 鑒於谷歌最新推出的Android Studio備受開發者的推崇,所以也跟著體驗一下。 一、介紹Android Studio Android Studio 是一個Android

Android開發AudioManager(音頻管理器)具體解釋

應該 數量 service eth out 開發 要求 type 路由 AudioManager簡單介紹: AudioManager類提供了訪問音量和振鈴器mode控制。使用Context.getSystemService(Context.AUDIO_SERVICE)

【入門篇】ANDROID開發BUG專講

world 自然 執行 類型 效率 str 積累 全部 href 話說諸葛亮是一個優秀的程序員,每個錦囊都是應對不同的case而編寫的。可是優秀的程序員也敵只是更優秀的bug。六出祈山。七進中原,鞠躬盡瘁,死而後已的諸葛亮僅僅由於有一

android開發merge結合include優化布局

ted com match clas you title example ews 文件的 merge結合include優化android布局,效果不知道。個人感覺使用上也有非常大的局限。只是還是了解一下。記錄下來。 布局文件都要有根節點,但androi