android 局域网聊天工具(可发送文字/语音)

本文介绍了一个使用Java异步TCP通信实现的Android局域网聊天工具,包括核心功能如聊天、聊天记录存储、语音发送与接收。通过TcpWorkerThread处理连接和消息接收,TcpManager作为TCP核心类,利用AsyncQueryHandler实现异步数据库访问,MediaRecorder和MediaPlayer处理录音和播放。
摘要由CSDN通过智能技术生成

最近比较有空,花了点时间写了个android局域网聊天工具,使用java的异步tcp通信。基本功能实现(简单的界面,聊天记录,发送文字,发送语音),在此小结一下。

 

Java (非android)局域网聊天工具源码,跟android的差别不大,参考:

http://download.csdn.net/detail/yarkey09/7052573

 

0,整个程序源码结构

1,聊天功能 (ServerSocketChannel & SocketChannel)

实现这个功能的时候有一个非常大的感受,就是写java程序真是方便!因为自己以前就写过windows上的java异步socket通信程序,所以这次几乎不需要修改很多代码,就可以搬过来。颇有Write one, run everywhere的feel。

个人认为java.nio的核心就是Selector和Buffer吧。通过Selector轮询各个已注册的socket的事件。若没有事件,则阻塞,若有事件则返回。因为在android,主线程不能做太多事情,

所以我起了一个新的线程,让Selector自个儿跑去。

以下是TcpWorkerThread类的源码,主要完成三件事

1,"开启"一个Selector

2,提供registToSelector方法

3,处理客户端的连接事件,处理socket接收消息事件

Class : TcpWorkerThread

package com.yarkey.tcp;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
import java.util.Set;

import android.os.Handler;
import android.util.Log;

public class TcpWorkerThread extends Thread {

	private static final String TAG = "TcpWorkerThread";

	/** 出错!返回String,描述出错原因 */
	public static final int EVENT_ERROR = 0;
	/** 线程结束, 停止运行 */
	public static final int EVENT_STOPPED = 1;
	/** 收到来自客户端的tcp连接, 报告一个socketchannel */
	public static final int EVENT_ACCEPTED = 2;
	/** 收到来自客户端的tcp消息, 报告一个TcpArgs, content为FileSerial对象 */
	public static final int EVENT_RECEIVED = 3;
	/** SocketChannel,ServerSocketChannel关闭 */
	public static final int EVENT_CLOSED = 4;

	/** TCP线程往主线程通信 */
	private Handler mHandler;

	private Selector mSelector;
	private boolean mIsRun = true;

	protected static class TcpArgs {

		SocketChannel sc;
		Object content;// 接收消息
	}

	/**
	 * 如果抛出异常,不能进行异步通信了
	 * 
	 * @throws Exception
	 */
	public TcpWorkerThread(Handler handler) throws Exception {
		Log.d(TAG, "TcpWorkerThread contructor");
		if (handler == null) {
			throw new Exception("Handler is null!");
		} else {
			mHandler = handler;
		}
		mSelector = Selector.open();
	}

	/**
	 * 将一个服务端的ServerSocketChannel设置为非阻塞模式,并将其注册到selector中(OP_ACCEPT)
	 * 
	 * @param ssc
	 * @throws IOException
	 */
	public void registToSelector(ServerSocketChannel ssc) throws IOException {
		Log.d(TAG, "registToSelector, ServerSocketChannel");
		ssc.configureBlocking(false);
		mSelector.wakeup();
		ssc.register(mSelector, SelectionKey.OP_ACCEPT);
	}

	/**
	 * 将一个客户端的SocketChannel设置为非阻塞模式,并将其注册到selector中(OP_READ)
	 * 
	 * @param ss
	 * @throws IOException
	 */
	public void registToSelector(SocketChannel ss) throws IOException {
		Log.d(TAG, "registToSelector, SocketChannel");
		ss.configureBlocking(false);
		mSelector.wakeup();
		ss.register(mSelector, SelectionKey.OP_READ);
	}

	/**
	 * 停止线程运行
	 */
	public void stopWorkerThread() {
		Log.d(TAG, "stopWorkerThread");
		mIsRun = false;
		mSelector.wakeup();
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub
		Log.d(TAG, "线程开始运行,run()");

		// 用于装入接收到的数据
		ByteBuffer buffer = ByteBuffer.allocate(1024);

		while (mIsRun) {

			int events = 0;
			try {
				events = mSelector.select();
			} catch (IOException e1) {
				// TODO Auto-generated catch block
				e1.printStackTrace();
				mHandler.obtainMessage(EVENT_ERROR, "Selector IOException").sendToTarget();// 出错
				break;
			}
			if (events <= 0) {
				// 走到这里,只能说明被wakeup了,应该是别的地方需要,因此这里暂停100ms
				Log.d(TAG, "sleep 100 ms >>>");
				try {
					sleep(100);
				} catch (InterruptedException e) {
					// interrupt! ignore this
					e.printStackTrace();
				}
				Log.d(TAG, "sleep 100 ms <<< wake up.");
				continue;
			}
			Log.d(TAG, "mSelector.select(), events ===========================> " + events);
			Set<SelectionKey> selectionKeys = mSelector.selectedKeys();
			Iterator<SelectionKey> iter = selectionKeys.iterator();

			// 代表连接成功后的socket
			SocketChannel socketChannel;

			while (iter.hasNext()) {

				SelectionKey key = iter.next();
				socketChannel = null;

				// 服务端收到连接
				if ((key.readyOps() & SelectionKey.OP_ACCEPT) == SelectionKey.OP_ACCEPT) {
					ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
					try {
						socketChannel = ssc.accept();
						Log.d(TAG, "ssc.accept()");
					} catch (IOException e) {
						e.printStackTrace();
						try {
							ssc.close();
						} catch (IOException e1) {
							// TODO Auto-generated catch block
							e1.printStackTrace();
						}
						mHandler.obtainMessage(EVENT_CLOSED, ssc).sendToTarget();
					}
					if (socketChannel != null) {
						try {
							socketChannel.configureBlocking(false);
							socketChannel.register(mSelector, SelectionKey.OP_READ);
							Log.d(TAG, "来自客户端的新连接");
							mHandler.obtainMessage(EVENT_ACCEPTED, socketChannel).sendToTarget();
						} catch (IOException e) {
							e.printStackTrace();
							try {
								socketChannel.close();
							} catch (IOException e1) {
								// TODO Auto-generated catch block
								e1.printStackTrace();
							}
							mHandler.obtainMessage(EVENT_CLOSED, socketChannel).sendToTarget();
						}
					} else {
						Log.e(TAG, "socketChannel is null !");
					}
					iter.remove();
				}
				// 接收到客户的消息
				else if ((key.readyOps() & SelectionKey.OP_READ) == SelectionKey.OP_READ) {
					socketChannel = (SocketChannel) key.channel();
					Log.d(TAG, "接收到新消息");

					boolean hasException = false;
					ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();
					while (true) {
						// 把position设为0,把limit设为capacity
						buffer.clear();
						int a = 0;
						try {
							a = socketChannel.read(buffer);
						} catch (Exception e) {
							e.printStackTrace();

							try {
								socketChannel.close();
							} catch (IOException e1) {
								// TODO Auto-generated catch b
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值