日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

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

發布時間:2023/12/18 编程问答 54 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android 局域网聊天工具(可发送文字/语音) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

最近比較有空,花了點時間寫了個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();}@Overridepublic void run() {// TODO Auto-generated method stubLog.d(TAG, "線程開始運行,run()");// 用于裝入接收到的數據ByteBuffer buffer = ByteBuffer.allocate(1024);while (mIsRun) {int events = 0;try {events = mSelector.select();} catch (IOException e1) {// TODO Auto-generated catch blocke1.printStackTrace();mHandler.obtainMessage(EVENT_ERROR, "Selector IOException").sendToTarget();// 出錯break;}if (events <= 0) {// 走到這里,只能說明被wakeup了,應該是別的地方需要,因此這里暫停100msLog.d(TAG, "sleep 100 ms >>>");try {sleep(100);} catch (InterruptedException e) {// interrupt! ignore thise.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();// 代表連接成功后的socketSocketChannel 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 blocke1.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 blocke1.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設為capacitybuffer.clear();int a = 0;try {a = socketChannel.read(buffer);} catch (Exception e) {e.printStackTrace();try {socketChannel.close();} catch (IOException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}hasException = true;mHandler.obtainMessage(EVENT_CLOSED, socketChannel).sendToTarget();break;}Log.d(TAG, "a=" + a);if (a == 0) {break;}if (a == -1) {try {socketChannel.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}hasException = true;mHandler.obtainMessage(EVENT_CLOSED, socketChannel).sendToTarget();Log.w(TAG, "讀取到EOS,我們關閉了一個連接!");break;}if (a > 0) {buffer.flip();try {byteOutput.write(buffer.array());} catch (IOException e) {// TODO Auto-generated catch blockhasException = true;e.printStackTrace();}}}if (!hasException) {byte[] b = byteOutput.toByteArray();TcpArgs args = new TcpArgs();args.sc = socketChannel;args.content = SerialUtil.toObject(b);mHandler.obtainMessage(EVENT_RECEIVED, args).sendToTarget();}iter.remove();}}}// 關閉資源try {mSelector.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}Log.d(TAG, "線程結束運行");mHandler.sendEmptyMessage(EVENT_STOPPED);} }

一般我們需要對socket處理的事件應該有三個:來自客戶端的連接(accept),接收消息(read),發送消息(write)。TcpWorkerThread完成了前兩件事,至于發送消息,我在另外一個類里面完成,也是新起一個線程,不過消息發送完后,發送線程也就停止了。

以下TcpManager類有幾個特點:

1,靜態單例

2,擁有一個TcpWorkerThread對象

3,擁有當前所有連接成功的socket

4,具有“新建服務端”“新建客戶端”“發送消息”方法

5,采用register/notify機制,提供注冊監聽的方法

6,處理兩個特殊的TCP事件( socket連接后,還需要雙方互發昵稱,才算聊天建立成功;如果接收到音頻文件,需要保存到SD卡中;)

Class : TcpManager

package com.yarkey.tcp;import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.ArrayList;import android.os.Handler; import android.os.Message; import android.util.Log;import com.yarkey.groupchat.Global; import com.yarkey.tcp.TcpWorkerThread.TcpArgs; import com.yarkey.utils.RegistrantList;/*** 擁有TcpWorkerThread* * @author yeqi.zhang* */ public class TcpManager {private static final String TAG = "TcpManager";private static TcpManager mInstance;// 單例private static final String mRecFolder = TcpConfig.TCP_REC_FOLDER;// 保存文件的路徑private TcpWorkerThread mThread;// 異步線程private ServerSocketChannel mServerSocketChannel;private ArrayList<SocketChannel> mSocketChannelList;// 保存連接的socketChannelprivate ArrayList<String> mNameList;// 保存對方名字public static class MessageArgs {public String name;// 對方的昵稱public String content;// 文件路徑,或者消息內容}protected RegistrantList mRegistrantListConnect = new RegistrantList();protected RegistrantList mRegistrantListReceiveAudio = new RegistrantList();protected RegistrantList mRegistrantListReceiveText = new RegistrantList();protected RegistrantList mRegistrantListError = new RegistrantList();public ServerSocketChannel getServerSocketChannel() {return mServerSocketChannel;}public ArrayList<SocketChannel> getSocketChannelList() {return mSocketChannelList;}public ArrayList<String> getNameList() {return mNameList;}/** 連接建立成功, obj=name */public void registerForConnect(Handler h, int what, Object obj) {mRegistrantListConnect.addUnique(h, what, obj);}public void unRegisterForConnect(Handler h) {mRegistrantListConnect.remove(h);}/** 收到音頻消息, obj=MessageArgs */public void registerForReceiveAudio(Handler h, int what, Object obj) {mRegistrantListReceiveAudio.addUnique(h, what, obj);}public void unRegisterForReceiveAudio(Handler h) {mRegistrantListReceiveAudio.remove(h);}/** 收到文字消息, obj=MessageArgs */public void registerForReceiveText(Handler h, int what, Object obj) {mRegistrantListReceiveText.addUnique(h, what, obj);}public void unRegisterForReceiveText(Handler h) {mRegistrantListReceiveText.remove(h);}/** 有錯誤發生, obj=errorReason */public void registerForError(Handler h, int what, Object obj) {mRegistrantListError.addUnique(h, what, obj);}public void unRegisterForError(Handler h) {mRegistrantListError.remove(h);}/*** 如果TcpWorkerThread沒有初始化成功,返回null !* * @return*/public static TcpManager getInstance() {Log.d(TAG, "getInstance");if (mInstance == null) {mInstance = new TcpManager();}if (mInstance.mThread == null) {Log.e(TAG, "TcpWorkerThread 初始化不成功,這是致命的錯誤!");return null;}return mInstance;}public static void release() {Log.d(TAG, "release");mInstance.mThread.stopWorkerThread();// 釋放監聽的socketif (mInstance.mServerSocketChannel != null) {try {mInstance.mServerSocketChannel.close();Log.d(TAG, "release ServerSocketChannel!");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}// 釋放連接的socketint i = 0;for (; i < mInstance.mSocketChannelList.size(); i++) {try {mInstance.mSocketChannelList.get(i).close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}Log.i(TAG, "release " + (i - 1) + " SocketChannels!");}private TcpManager() {Log.d(TAG, "TcpManager private contructor");mSocketChannelList = new ArrayList<SocketChannel>();mNameList = new ArrayList<String>();try {mThread = new TcpWorkerThread(mThreadEventHandler);mThread.start();} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();mThread = null;// 這個會導致getInstance 返回null}}// public void setHandler(Handler h) {// Log.d(TAG, "setHandler,h=" + h);// mHandler = h;// }/*** 處理TcpWorkerThread*/private Handler mThreadEventHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {Log.d(TAG, "mThreadEventHandler, handleMessage");// TODO Auto-generated method stubswitch (msg.what) {case TcpWorkerThread.EVENT_ACCEPTED:Log.d(TAG, "TcpWorkerThread.EVENT_ACCEPTED");sendName((SocketChannel) msg.obj);break;case TcpWorkerThread.EVENT_RECEIVED:Log.d(TAG, "TcpWorkerThread.EVENT_RECEIVED");TcpWorkerThread.TcpArgs argsRec = (TcpArgs) msg.obj;SocketChannel socketch = argsRec.sc;FileSerial objSerial = (FileSerial) argsRec.content;// 準備發送一個MessageArgs對象MessageArgs msgargs = null;if (objSerial.getType() != FileSerial.TYPE_NAME) {msgargs = new MessageArgs();int index = 0;for (; index < mSocketChannelList.size(); index++) {if (socketch.equals(mSocketChannelList.get(index))) {break;}}msgargs.name = mNameList.get(index);}switch (objSerial.getType()) {case FileSerial.TYPE_AUDIO:Log.d(TAG, "收到音頻信息");FileOutputStream fileOut;try {fileOut = new FileOutputStream(mRecFolder + objSerial.getFileName());fileOut.write(objSerial.getFileContent(), 0, (int) objSerial.getFileLength());fileOut.close();Log.d(TAG, "音頻文件已保存到本地!");} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}// if (mHandler != null) {// mHandler.obtainMessage(EVENT_REC_AUDIO, mRecFolder +// objSerial.getFileName()).sendToTarget();// }msgargs.content = mRecFolder + objSerial.getFileName();mRegistrantListReceiveAudio.notifyResult(msgargs);break;case FileSerial.TYPE_TEXT:Log.d(TAG, "收到文字信息");// if (mHandler != null) {// mHandler.obtainMessage(EVENT_REC_TEXT,// objSerial.getFileName()).sendToTarget();// }msgargs.content = objSerial.getFileName();Log.d(TAG, "message=" + objSerial.getFileName());mRegistrantListReceiveText.notifyResult(msgargs);break;case FileSerial.TYPE_NAME:Log.d(TAG, "收到對方名稱,連接正式成功!");mSocketChannelList.add(argsRec.sc);mNameList.add(objSerial.getFileName());// if (mHandler != null) {// mHandler.obtainMessage(EVENT_CONNECT,// objSerial.getFileName()).sendToTarget();// }mRegistrantListConnect.notifyResult(objSerial.getFileName());break;}break;case TcpWorkerThread.EVENT_ERROR:Log.w(TAG, "error:" + (String) msg.obj);break;case TcpWorkerThread.EVENT_STOPPED:Log.w(TAG, "EVENT_STOPPED");break;// ---------------------------------------------case TcpAsyncClient.EVENT_CONNECTED:TcpAsyncClient.TcpArgs args = (TcpAsyncClient.TcpArgs) msg.obj;try {mThread.registToSelector(args.result);Log.d(TAG, "客戶端連接服務端成功,發送昵稱...");sendName(args.result);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();this.sendEmptyMessage(TcpAsyncClient.EVENT_ERROR);}break;case TcpAsyncClient.EVENT_ERROR:Log.e(TAG, "客戶端發起連接,發生錯誤!");// mHandler.obtainMessage(EVENT_ERROR,// "客戶端發起連接失敗").sendToTarget();mRegistrantListError.notifyResult("客戶端發起連接失敗");break;}}};/*** 作為客戶端,連接到指定的地址。如果返回false,表示連接沒成功,如果返回true,那么需要等待* * @deprecated android較高的版本,不允許在主線程訪問network* @param ip* @param port* @return*/public boolean newConnection(String ip, int port) {TcpClient client = new TcpClient(ip, port);SocketChannel sc = client.connect();try {mThread.registToSelector(sc);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();return false;}return true;}/*** 作為客戶端,異步創建連接* * @param ip* @param port*/public void newAsyncConnection(String ip, int port) {Log.d(TAG, "newAsyncConnection, ip=" + ip + ",port=" + port);TcpAsyncClient client = new TcpAsyncClient();TcpAsyncClient.TcpArgs args = new TcpAsyncClient.TcpArgs();args.handler = mThreadEventHandler;args.ip = ip;args.port = port;client.connect(args);}/*** 作為服務端,啟動服務監聽* * @param port* @return*/public boolean newServer(int port) {Log.d(TAG, "newServer, port=" + port);if (mServerSocketChannel != null) {// serversocketchannel 不一定注冊到selector成功,但是這里暫時只能允許存在一個Log.w(TAG, "can only have one server for accepting !");} else {TcpServer server = new TcpServer(port);mServerSocketChannel = server.accepting();}try {mThread.registToSelector(mServerSocketChannel);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();return false;}return true;}/*** 發送給對方,昵稱/文字消息/文件* * @param sc* SocketChannel* @param content* name/message/filepath* @param fileName* if type==AUDIO, you should set fileName* @param type* {@link FileSerial#TYPE_TEXT}, {@link FileSerial#TYPE_NAME},* {@link FileSerial#TYPE_AUDIO}*/private void send(final SocketChannel sc, String content, String fileName, int type) {Log.d(TAG, "content=" + content + ",fileName=" + fileName + ",type=" + type);FileSerial fpo = new FileSerial();// typefpo.setType(type);switch (type) {case FileSerial.TYPE_AUDIO:// namefpo.setFileName(fileName);// lengthFile f = new File(content);long fileLength = f.length();fpo.setFileLength(fileLength);// contentFileInputStream fis = null;byte[] fileContent = new byte[(int) fileLength];try {fis = new FileInputStream(content);fis.read(fileContent, 0, (int) fileLength);fis.close();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}fpo.setFileContent(fileContent);break;case FileSerial.TYPE_TEXT:case FileSerial.TYPE_NAME:// namefpo.setFileName(content);// lengthfpo.setFileLength(content.length());// content// nullbreak;}// toByte -> sendbyte[] bytes = SerialUtil.toByte(fpo);final ByteBuffer buffer = ByteBuffer.wrap(bytes);// E/AndroidRuntime(18606): android.os.NetworkOnMainThreadExceptionnew Thread() {@Overridepublic void run() {// TODO Auto-generated method stubLog.d(TAG, "send, async ! 異步發送 ... ");try {// 此處在主線程調用, 有可能會阻塞!sc.write(buffer);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}.start();}/*** 發送音頻文件* * @param index* @param content* @param fileName*/public void sendAudioFile(int index, String content, String fileName) {Log.d(TAG, "sendAudioFile, index=" + index + ",content=" + content + ",fileName=" + fileName);SocketChannel sc = mSocketChannelList.get(index);send(sc, content, fileName, FileSerial.TYPE_AUDIO);}/*** 發送音頻文件* * @param name* @param content* @param fileName*/public void sendAudioFile(String name, String content, String fileName) {Log.d(TAG, "sendAudioFile, name=" + name + ",content=" + content + ",fileName=" + fileName);int index = -1;for (int i = 0; i < mNameList.size(); i++) {if (mNameList.get(i).equals(name)) {index = i;break;}}if (index != -1) {sendAudioFile(index, content, fileName);} else {Log.w(TAG, "sendAudioFile, name=" + name + " not found !");}}/*** 發送文字消息* * @param index* @param msg*/public void sendMessage(int index, String msg) {Log.d(TAG, "sendMessage, index=" + index + ",msg=" + msg);SocketChannel sc = mSocketChannelList.get(index);send(sc, msg, null, FileSerial.TYPE_TEXT);}/*** 發送文字消息* * @param name* @param msg*/public void sendMessage(String name, String msg) {Log.d(TAG, "sendMessage, name=" + name + ",msg=" + msg);int index = -1;for (int i = 0; i < mNameList.size(); i++) {if (mNameList.get(i).equals(name)) {index = i;break;}}if (index != -1) {sendMessage(index, msg);} else {Log.w(TAG, "sendMessage, name=" + name + " not found !");}}/*** TCP連接后,需要雙方相互發送昵稱,才能算連接正式成立!* * @param sc* @param name*/private void sendName(SocketChannel sc) {Log.d(TAG, "sendName, name=" + Global.NAME_LOCAL);send(sc, Global.NAME_LOCAL, null, FileSerial.TYPE_NAME);} }

請注意類里面每個方法的權限(private,protected,public),我把這個類作為整個聊天工具TCP的核心類,具有所有需要的TCP操作方法。

TcpManager中還用到TcpAsyncClient,TcpServer,這兩個類完成ServerSocketChannel, SocketChannel的“打開”,打開完成后便可以將他們注冊到Selector了。

Class : TcpAsyncClient

主要方法:由于較新版本的android,不允許在主線程做網絡操作,所以以下代碼在一個新的線程中執行!

SocketChannel sc = SocketChannel.open(); sc.connect(new InetSocketAddress(args.ip, args.port));

Class : TcpServer

主要方法:

/*** 如果初始化不成功,返回null* * @param port* @return*/protected ServerSocketChannel accepting(int port) {Log.d(TAG, "accepting,port=" + port);ServerSocketChannel ssc = null;try {ssc = ServerSocketChannel.open();ssc.configureBlocking(false);ServerSocket ss = ssc.socket();ss.bind(new InetSocketAddress(port));} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();return null;}return ssc;}

TCP通信功能大概就這么多吧,另外有一個關于序列化的類FileSerial,在“語音發送與接收”再說一下。

2,聊天記錄 (AsyncQueryHandler)

聊天記錄使用android自帶的Sqlite數據庫做持久化。由于數據庫訪問可能會花很多時間,不宜在主線程操作,所以在這里主要關鍵考慮如果完成“異步”訪問數據庫。

android的AsyncQueryHandler類寫得很好,我只是在它的基礎上做了一點小修改就可以用了。拿來主義^_^

Class : ChatAsyncQueryHandler

package com.yarkey.database;import android.content.ContentValues; import android.database.Cursor; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.util.Log;public abstract class ChatAsyncQueryHandler extends Handler {private static final String TAG = "ChatAsyncQueryHandler";private ChatDatabase mDatabase;private static Looper mLooper = null;private Handler mWorkerHandler = null;private static final int EVENT_QUERY = 0;private static final int EVENT_INSERT = 1;private static final int EVENT_UPDATE = 2;private static final int EVENT_DELECT = 3;public ChatAsyncQueryHandler(ChatDatabase database) {mDatabase = database;synchronized (ChatAsyncQueryHandler.class) {if (mLooper == null) {HandlerThread thread = new HandlerThread("ChatAsyncQueryHandler");thread.start();mLooper = thread.getLooper();}}mWorkerHandler = new WorkerHandler(mLooper);}protected static final class WorkerArgs {public Handler handler;public String[] projection;public String selection;public ContentValues values;public Object result;}/*** 查詢。通過兩個字段,查詢ID,DATA,TIME,TYPE,SELF,CONTENT* * @param token* @param localName* @param remoteName*/public void startQuery(int token, String localName, String remoteName) {Log.d(TAG, "startQuery, token=" + token + ",localName=" + localName + ",remoteName=" + remoteName);Message msg = mWorkerHandler.obtainMessage(token);msg.arg1 = EVENT_QUERY;WorkerArgs args = new WorkerArgs();args.handler = this;args.projection = new String[] { ChatLog.C_ID, ChatLog.C_DATE, ChatLog.C_TIME, ChatLog.C_TYPE, ChatLog.C_SELF,ChatLog.C_CONTENT };args.selection = ChatLog.C_LOCAL + "=\"" + localName + "\" AND " + ChatLog.C_REMOTE + "=\"" + remoteName + "\"";msg.obj = args;mWorkerHandler.sendMessage(msg);}public void startInsert(int token, ContentValues values) {Log.d(TAG, "startInsert, values=" + values);Message msg = mWorkerHandler.obtainMessage(token);msg.arg1 = EVENT_INSERT;WorkerArgs args = new WorkerArgs();args.handler = this;args.values = values;msg.obj = args;mWorkerHandler.sendMessage(msg);}protected class WorkerHandler extends Handler {public WorkerHandler(Looper looper) {super(looper);// TODO Auto-generated constructor stub}@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubWorkerArgs args = (WorkerArgs) msg.obj;int token = msg.what;int event = msg.arg1;switch (event) {case EVENT_QUERY:args.result = mDatabase.query(args.projection, args.selection);break;case EVENT_INSERT:mDatabase.insert(args.values);break;case EVENT_UPDATE:break;case EVENT_DELECT:break;}Message reply = args.handler.obtainMessage(token);reply.obj = args;reply.arg1 = event;reply.sendToTarget();}}protected void onQueryCompleted(int token, Cursor cursor) {// empty}protected void onInsertCompleted(int token) {// empty}@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubWorkerArgs args = (WorkerArgs) msg.obj;int token = msg.what;int event = msg.arg1;switch (event) {case EVENT_QUERY:onQueryCompleted(token, (Cursor) args.result);break;case EVENT_INSERT:onInsertCompleted(token);break;case EVENT_UPDATE:break;case EVENT_DELECT:break;}} }


至于ChatDatabase類,繼承于SQLiteOpenHelper,就不多說了。

3,語音發送與接收 (ObjectOutputStream)

本聊天工具通過發送音頻文件,來實現語音聊天的。其他真正的聊天工具怎么實現的就不得而知了,有知道的網友也請分享一下^^。在這里主要需要解決幾個問題:

1,錄音與錄音播放 (MediaRecorder, MediaPlayer)

MediaRecorder 與 MediaPlayer 的例子網上有很多了,本人也是一知半解,不敢在這里說太多。主要是通過MediaRecorder調用錄音方法,結束后,我們得到一個存放在SD卡中的.3gp文件,有了這個文件,便可以調用MediaPlayer來播放它了!

可能需要注意的地方有幾個:MediaRecorder 錄音超時,系統是有一個上限的,設計程序的時候需要注意一下;另外,是關于MediaRecorder什么時候釋放,在本程序里面,每次用的時候就new一個,每次用完都調用release方法釋放它。不過,這里似乎需要考慮一些問題,就沒有做過多了解了。

2,錄音文件的發送與接收

TCP發送文件,一開始用java.io的時候,我用的是ObjectOutputStream, ObjectInputStream來序列化一個類然后發出去( 即writeObject方法 )。

writeObject 方法的輸入參數是一個FileSerial對象,實現Serializable接口

Class : FileSerial

package com.yarkey.tcp;import java.io.Serializable;public class FileSerial implements Serializable {private static final long serialVersionUID = 1L;private String fileName; // 文件名稱private long fileLength; // 文件長度private byte[] fileContent; // 文件內容private int type;/** name保存在filename字段里面! */protected static final int TYPE_NAME = 0;// 連接成功后,向對方發出自己的名字/** text保存在filename字段里面! */protected static final int TYPE_TEXT = 1;// 文字protected static final int TYPE_AUDIO = 2;// 音頻// public static final int TYPE_PICTURE = 3;// 圖片public int getType() {return type;}public void setType(int t) {type = t;}public String getFileName() {return fileName;}public void setFileName(String fileName) {this.fileName = fileName;}public long getFileLength() {return fileLength;}public void setFileLength(long fileLength) {this.fileLength = fileLength;}public byte[] getFileContent() {return fileContent;}public void setFileContent(byte[] fileContent) {this.fileContent = fileContent;} }

java.io發送FileSerial對象源碼:其中:

ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());

/*** * @param filePath* C:/haha.java* @param fileName* haha.java*/public void sendFile(String filePath, String fileName) {Log.d(TAG, fileName);if (out == null) {return;}try {FileSerial fpo = new FileSerial();// typefpo.setType(FileSerial.TYPE_AUDIO);// namefpo.setFileName(fileName);// lengthFile f = new File(filePath);long fileLength = f.length();fpo.setFileLength(fileLength);// contentFileInputStream fis = new FileInputStream(filePath);byte[] fileContent = new byte[(int) fileLength];fis.read(fileContent, 0, (int) fileLength);fis.close();fpo.setFileContent(fileContent);// sendlong start = System.currentTimeMillis();out.writeObject(fpo);long end = System.currentTimeMillis();System.out.println("It takes " + (end - start) + "ms");out.flush();out.reset();} catch (FileNotFoundException e) {// TODO Auto-generated catch blocke.printStackTrace();} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}


可是,用java.nio的時候,就會出錯了!我們需要將需要發送的內容裝在一個Buffer( ByteBuffer )中,然后通過SocketChannel的 write 方法發送出去。

// toByte -> sendbyte[] bytes = SerialUtil.toByte(fpo);final ByteBuffer buffer = ByteBuffer.wrap(bytes);// E/AndroidRuntime(18606): android.os.NetworkOnMainThreadExceptionnew Thread() {@Overridepublic void run() {// TODO Auto-generated method stubLog.d(TAG, "send, async ! 異步發送 ... ");try {// 此處不能在主線程調用!sc.write(buffer);} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}.start();


用到一個工具類SerialUtil,轉換對象 FileSerial -> byte[] , byte[] -> FileSerial

class : SerialUtil

package com.yarkey.tcp;import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream;import android.util.Log;public class SerialUtil {private static final String TAG = "SerialUtil";public static byte[] toByte(Object obj) {Log.d(TAG, "toByte");ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = null;try {oos = new ObjectOutputStream(baos);oos.writeObject(obj);byte[] bytes = baos.toByteArray();return bytes;} catch (IOException ex) {throw new RuntimeException(ex.getMessage(), ex);} finally {try {oos.close();} catch (Exception e) {}}}/** 此方法byte[]數組長度確定 */public static Object toObject(byte[] bytes) {Log.d(TAG, "toObject");ByteArrayInputStream bais = new ByteArrayInputStream(bytes);ObjectInputStream ois = null;try {ois = new ObjectInputStream(bais);Object object = ois.readObject();return object;} catch (IOException ex) {throw new RuntimeException(ex.getMessage(), ex);} catch (ClassNotFoundException ex) {throw new RuntimeException(ex.getMessage(), ex);} finally {try {ois.close();} catch (Exception e) {}}} }

音頻文件的發送大概情況就是上面說的了,但是接收有另外一個問題。我們不知道音頻文件的大小,所以不知道需要多大的ByteBuffer來接收它。

所以,在TcpWorkerThread中,處理接收到的消息時,還用到一個

ByteArrayOutputStream byteOutput = new ByteArrayOutputStream();

for( 循環 ){

??? byteOutput.write(buffer.array()); // 分次保存byteBuffer中的數據

}

byte[] b = byteOutput.toByteArray(); //最后一口氣弄出來


我們申請的bytebuffer就1024個字節的空間,超過1024個字節,所以我們分次“存”到這個ByteArrayOutputStream中,最后一口氣全部弄出來。然后調用SerialUtil將byte[]裝換成FileSerial類對象。

總結

以上是生活随笔為你收集整理的android 局域网聊天工具(可发送文字/语音)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

波多野结衣视频一区 | 日本高清免费中文字幕 | 狠狠色丁香久久婷婷综合五月 | 亚洲免费国产视频 | 91亚洲夫妻| 极品久久久 | 亚洲色图激情文学 | 国产无遮挡又黄又爽在线观看 | wwwav视频| 日韩午夜av | 欧美电影在线观看 | 国产中文字幕视频在线 | 欧美日韩国语 | 欧美亚洲国产日韩 | 中文视频一区二区 | 亚一亚二国产专区 | 麻豆91在线播放 | 久草在线播放视频 | 在线黄网站 | 91福利视频一区 | 91试看| 免费看在线看www777 | 婷婷五月在线视频 | 婷婷 中文字幕 | 丝袜护士aⅴ在线白丝护士 天天综合精品 | 午夜私人影院 | 日韩一区二区三区观看 | 操老逼免费视频 | 久久久久成人精品 | 婷婷视频导航 | 日韩成人黄色av | 一区二区欧美在线观看 | 国产手机精品视频 | 久草电影在线观看 | 欧美日韩在线播放一区 | 色综合天天色 | 日本黄色大片免费看 | 国产一区二区免费 | 亚洲国产中文在线观看 | 伊人资源视频在线 | 欧美在线观看视频一区二区 | 日韩av一区在线观看 | 亚洲人成影院在线 | 日本久久久久久久久久 | 色婷婷国产精品一区在线观看 | 免费高清在线观看电视网站 | av资源中文字幕 | 成人免费在线视频观看 | 久久女同性恋中文字幕 | 日韩动漫免费观看高清完整版在线观看 | av免费网站观看 | 国内精品久久久久久久久久久 | 欧美成人按摩 | 91成熟丰满女人少妇 | 97色在线观看 | 日韩av一区二区在线 | 国产精品a久久 | 日韩天堂在线观看 | 四虎影视av | 免费能看的黄色片 | 国产18精品乱码免费看 | 99视| 中文av一区二区 | www.少妇| 亚洲精品中文字幕在线观看 | 999久久国精品免费观看网站 | 亚洲色影爱久久精品 | 996久久国产精品线观看 | 成人永久在线 | 天天操夜夜操夜夜操 | 日韩综合一区二区 | 国产一二三区av | 午夜精品电影 | 日韩视频精品在线 | 黄色毛片网站在线观看 | 天天干天天弄 | 在线免费高清一区二区三区 | 91香蕉国产在线观看软件 | 色婷婷午夜 | 国产精品美女久久久久久 | 1024在线看片 | 久久任你操| 久草视频中文 | 玖玖在线免费视频 | 天天色天天干天天色 | 精品国产一区二区三区不卡 | 日韩电影在线观看一区二区三区 | 欧美激情综合色综合啪啪五月 | 操一草 | 欧美片一区二区三区 | 久久精品国产第一区二区三区 | 国产精品一区二区久久久 | 午夜视频亚洲 | 欧美九九视频 | 国产亚洲精品成人av久久ww | 亚洲精品理论 | 91精品国自产在线观看 | 国产精品欧美久久久久久 | 精品国产a | 免费涩涩网站 | 成人啪啪18免费游戏链接 | av大全在线观看 | 日韩激情在线视频 | 又黄又爽的免费高潮视频 | 色噜噜在线观看 | 欧美日韩在线免费视频 | 伊人va| 欧美一级爽 | 国产精品青草综合久久久久99 | 黄在线免费观看 | 久久综合狠狠综合久久综合88 | 欧美日一级片 | 日本精品一区二区三区在线观看 | 国产福利在线免费 | 97av在线视频免费播放 | 日韩av免费一区 | 日韩精品一区二区三区在线播放 | 久久视影 | 麻豆视频在线看 | 超薄丝袜一二三区 | 国产精品破处视频 | 干狠狠| 91探花国产综合在线精品 | 国产最新91| 国产69精品久久99的直播节目 | 日韩中文字幕在线观看 | 日韩.com | 久久99深爱久久99精品 | 日韩欧美在线高清 | 人人爽人人爽人人片 | 日本中文字幕一二区观 | 欧美在线free | 国产精品 中文字幕 亚洲 欧美 | 国产69精品久久久久久久久久 | 国产成人一区二区精品非洲 | 色a综合| 国产福利一区二区三区在线观看 | 日本中文字幕在线视频 | 成人久久18免费网站图片 | 亚洲五月综合 | 96国产精品视频 | 黄色小网站免费看 | 日韩av手机在线观看 | www.888.av| 一个色综合网站 | 欧美成人中文字幕 | 天天干天天搞天天射 | 精品字幕 | 日韩高清免费电影 | 欧美日韩二区在线 | 激情综合网在线观看 | 精品亚洲欧美一区 | 18久久久| 麻豆免费在线视频 | 黄色精品免费 | 狠狠的干 | 国产在线观看,日本 | 国产精品videossex国产高清 | 国产日韩欧美视频在线观看 | 二区视频在线观看 | 嫩小bbbb摸bbb摸bbb | 99久久精 | www.xxxx变态.com| 成人小视频在线观看免费 | 亚洲最新在线 | 欧美少妇xx | 国产成人一二三 | 国内久久久 | 久久成人高清 | 五月天久久狠狠 | 国产精品一码二码三码在线 | 中文在线亚洲 | 日韩成人在线免费观看 | 在线免费观看不卡av | 在线观看免费一级片 | 国产精品女人久久久 | 超碰人人在线观看 | 热久久最新地址 | 国产v亚洲v | 日本在线视频一区二区三区 | 日韩中文字幕免费 | 五月综合久久 | 麻豆成人网 | 国产成人精品一二三区 | 九色在线 | 亚洲国产小视频在线观看 | 免费在线观看a v | 国产人成精品一区二区三 | www国产一区 | 久久久久亚洲精品成人网小说 | 久久久久久久看片 | 国产涩涩网站 | 天天射天天射 | 欧美日韩在线观看一区 | 福利一区二区三区四区 | 成人h在线观看 | 亚洲精品影视在线观看 | 欧美综合在线视频 | 网站在线观看日韩 | 成人av电影免费在线观看 | 国产黄色片免费 | 国产成人无码AⅤ片在线观 日韩av不卡在线 | 亚在线播放中文视频 | 久久99国产精品久久99 | 丁香花中文在线免费观看 | 亚洲美女免费精品视频在线观看 | 狠狠干婷婷 | 天天爱天天干天天爽 | 高清av免费观看 | 日日射天天射 | 久久情网 | 亚洲精色| 成人在线一区二区 | 亚洲91精品 | 久久精彩免费视频 | 日韩网站在线 | 精品在线亚洲视频 | 日韩精品一区二区三区免费观看视频 | 日韩在线观看一区二区三区 | 亚洲女同ⅹxx女同tv | 欧美精品一二三 | 在线观看免费观看在线91 | 亚洲高清久久久 | 天天射天天搞 | 国产精品久久免费看 | 久草在线国产 | 二区三区在线 | 狂野欧美激情性xxxx欧美 | 中文字幕免费高清在线观看 | 国产中文欧美日韩在线 | 最近中文字幕免费av | 中文av资源站 | 国产精品免费在线观看视频 | 久草视频在线播放 | 国产精品久久久久久久久久了 | 久久国产经典视频 | 日本亚洲国产 | 91久久精品日日躁夜夜躁国产 | 狠狠干成人综合网 | 五月天婷婷丁香花 | 丁香 婷婷 激情 | 99久久99久国产黄毛片 | 亚洲欧美偷拍另类 | 香蕉视频在线免费 | 国产黄色高清 | 国产精品久久久一区二区三区网站 | av先锋中文字幕 | 亚洲一区免费在线 | av黄色大片 | 人人干人人艹 | 亚洲国产精品视频在线观看 | 欧美一级激情 | 伊人影院99 | 香蕉视频在线免费 | 国产永久免费 | 国产亚洲午夜高清国产拍精品 | 中文字幕之中文字幕 | 九九综合久久 | 99自拍视频在线观看 | 国产精品久久久久久久久久ktv | 欧美日韩国产精品一区二区亚洲 | 久久99国产精品久久99 | 日韩成人免费在线观看 | 国产精品嫩草影院123 | 狠狠操狠狠干天天操 | 五月激情久久久 | 能在线看的av | 亚洲精品小视频在线观看 | 久久久久久久久久久久久久电影 | 日韩精品在线免费观看 | 成人性生交大片免费观看网站 | 91精品久久久久久综合乱菊 | 欧美少妇xxx | 丁五月婷婷 | 97在线视频免费 | 九月婷婷人人澡人人添人人爽 | 在线观看一级片 | 中文在线www | 国产99久久九九精品免费 | 亚洲欧美国产精品18p | 亚洲免费在线播放视频 | 久久一区二区三区四区 | 91黄色免费看 | 亚洲九九九在线观看 | 亚洲综合色视频在线观看 | 天天插天天色 | 99r国产精品 | 国产精品伦一区二区三区视频 | 最近中文字幕视频完整版 | 九九国产精品视频 | 亚洲专区免费观看 | 97超级碰碰碰视频在线观看 | 97操操| 欧美日韩高清一区二区三区 | 久久精品老司机 | 欧美乱码精品一区二区 | 亚洲影视九九影院在线观看 | 日本资源中文字幕在线 | 欧美精品被 | 九九热视频在线 | 国产美女视频黄a视频免费 久久综合九色欧美综合狠狠 | 久草资源免费 | 狠狠的干 | 国产精品乱码久久久久久1区2区 | 中文字幕欧美日韩va免费视频 | 蜜臀久久99精品久久久无需会员 | ,午夜性刺激免费看视频 | 中文字幕一区在线观看视频 | 亚洲综合国产精品 | 精品一区免费 | 日韩美精品视频 | 久久久免费精品国产一区二区 | 日日爱夜夜爱 | 亚洲精品永久免费视频 | 色婷婷婷 | 久久99精品一区二区三区三区 | 91国内在线视频 | 久久综合久久综合久久综合 | 色综合久久精品 | 久久久精品欧美 | www好男人| 黄色三级免费看 | 韩日三级在线 | 日韩在线观看视频一区二区三区 | 91精品久久久久久久91蜜桃 | 天天爱天天干天天爽 | 日韩在线无 | 亚洲欧洲精品久久 | 九九九毛片 | 99热这里只有精品8 久久综合毛片 | 中中文字幕av在线 | 国产日韩欧美中文 | 一区二区三区在线视频111 | 日日干网址 | 欧美精品亚洲二区 | 欧美精品一区二区蜜臀亚洲 | 日韩av高清 | 天堂网一区二区三区 | 97看片吧 | 亚洲九九精品 | 亚洲a在线观看 | 国产日韩精品一区二区 | 久久福利国产 | 91在线日韩 | 国产精品久久中文字幕 | 久草视频在线免费播放 | 国产中文字幕视频 | 日本中文在线播放 | 麻豆免费观看视频 | 久久精品视频在线观看 | 国产尤物在线视频 | 欧美不卡视频在线 | 韩国av免费看 | 中文亚洲欧美日韩 | 欧美一区二区三区免费观看 | 美女久久久久久久久久久 | 国产精品18久久久久久不卡孕妇 | 欧美性生活免费看 | 91精品久久久久久综合乱菊 | 国产欧美精品一区二区三区 | 国产精品一区二区三区观看 | 999久久a精品合区久久久 | 97夜夜澡人人双人人人喊 | 国产一二区在线观看 | 伊人首页| 久久久免费高清视频 | 一区二区三区四区免费视频 | 国产亚洲免费的视频看 | 亚洲精品国产视频 | 999色视频 | 青草视频在线免费 | 东方av在 | 91黄色视屏| 69国产盗摄一区二区三区五区 | 久久综合中文色婷婷 | 美女搞黄国产视频网站 | 国产精品久久网 | 91av在线视频播放 | 色婷婷国产精品一区在线观看 | 看av免费网站 | 中文字幕视频一区二区 | 中文字幕在线观看资源 | 精品国产大片 | 成人国产精品 | 国产亚洲精品成人av久久影院 | 日本性高潮视频 | 就要干b| 人人澡超碰碰97碰碰碰软件 | 色综合久久久久综合 | 在线激情小视频 | 国产不卡一区二区视频 | 亚洲精品国产成人 | 在线观看久 | 国产高清日韩欧美 | 中文字幕中文字幕在线中文字幕三区 | 在线黄网站 | 国产精品第52页 | 波多野结衣在线观看一区 | www.天天成人国产电影 | 亚洲精品视频在线免费 | 激情 一区二区 | 91精品国产自产老师啪 | 中文字幕av免费在线观看 | 亚洲在线成人精品 | 国产一区二区三区免费在线 | 国产在线观看h | 成年人国产精品 | 九九电影在线 | av电影免费在线看 | 亚州视频在线 | 玖玖爱免费视频 | 欧美国产精品久久久久久免费 | 精品一区91| 中文成人字幕 | 伊人色**天天综合婷婷 | 91伊人久久大香线蕉蜜芽人口 | 国产无区一区二区三麻豆 | 欧美精品色 | av在线精品 | 日韩视频一区二区三区 | 中文字幕4 | 中文字幕在线观看视频免费 | 九色最新网址 | 国产精品美女在线 | 久久久久久久久久电影 | 午夜精品久久一牛影视 | 狠狠操在线 | 亚洲三级黄 | 亚洲视频高清 | 国产一级一片免费播放放a 一区二区三区国产欧美 | 亚洲男人天堂2018 | 欧美做受xxx | 久久精品亚洲一区二区三区观看模式 | 在线韩国电影免费观影完整版 | 国产精品免费久久久久久 | 中文日韩在线 | 日韩免费精品 | 亚洲天堂毛片 | 亚洲精品av中文字幕在线在线 | 久久免费看a级毛毛片 | 中文字幕乱视频 | 99免在线观看免费视频高清 | 天天干天天操 | 四虎在线观看视频 | 亚洲好视频 | 在线亚洲小视频 | 欧美精品久久久久久久久老牛影院 | 久久久综合 | 色婷婷激情综合 | www.在线观看av| 日韩欧美一区二区三区黑寡妇 | 成人小视频在线播放 | 97免费在线观看 | 久久精品99国产精品亚洲最刺激 | 日本婷婷色| 久久大视频 | 超黄视频网站 | 日韩一区二区三区高清免费看看 | 97超碰免费在线观看 | 激情综合色播五月 | 人人添人人| 欧美日韩xxxxx | 96香蕉视频 | 一区二区成人国产精品 | 手机在线观看国产精品 | 精品无人国产偷自产在线 | 久久久久久久久久网 | 99久久婷婷国产一区二区三区 | 国产不卡精品 | 天堂视频一区 | 日本在线免费看 | av一级一片| 夜夜视频| 色婷婷av在线 | 天天色天天干天天色 | 日韩欧美在线观看一区 | 色综合久久久久久久久五月 | 免费看的视频 | 国产又粗又猛又黄又爽视频 | 91日韩在线专区 | 久久久九九 | 成人精品国产 | 日日夜夜91 | 一区二区三区手机在线观看 | 色妞色视频一区二区三区四区 | 日韩av视屏| 国产精品美女久久久久久久网站 | 麻豆传媒视频观看 | 久草精品电影 | 国产成人av网站 | 就要干b | 亚洲综合爱 | 国产一级黄色av | 成年人在线免费看 | 亚洲一级片 | 亚洲第一区在线观看 | 成人a毛片 | 在线观看免费91 | 99中文字幕 | 久久久久久毛片精品免费不卡 | 在线播放视频一区 | 国产精品淫 | 久久久久久久久久久久影院 | 国产主播大尺度精品福利免费 | 99久久精品免费一区 | 超碰在线资源 | 成人av手机在线 | 日日碰狠狠添天天爽超碰97久久 | 久久午夜视频 | 精品国产欧美 | 毛片美女网站 | 在线va视频 | 国产视频在 | 免费观看一级成人毛片 | 久久一线 | 亚洲天堂网视频 | 天天干人人干 | 国产精品久久久久久久av电影 | 国产成人久久精品77777综合 | 国产亚洲精品久 | 欧美日韩在线网站 | 狠狠色狠狠色综合系列 | 久草精品免费 | 天天天天天干 | 亚洲国产精久久久久久久 | aa级黄色大片| 日韩免费专区 | www最近高清中文国语在线观看 | 日韩视频一区二区 | 毛片一区二区 | 色综合亚洲精品激情狠狠 | 欧美日韩免费观看一区二区三区 | 在线日韩 | 日本护士三级少妇三级999 | 亚洲综合视频在线播放 | 伊人婷婷色 | 精品福利国产 | 久久久91精品国产一区二区精品 | 不卡日韩av | 国产成人精品一区在线 | 91精品国产一区二区在线观看 | 久久蜜臀av | 成人国产精品一区二区 | 日本中文字幕免费观看 | 国产精品久久久久久久久免费看 | 91九色蝌蚪视频网站 | 国产精品原创视频 | 一本一本久久aa综合精品 | 久久精品久久99精品久久 | 成人动漫一区二区 | 玖玖视频免费在线 | 97超碰在线久草超碰在线观看 | 青草视频在线 | 日韩免费观看一区二区 | 超碰伊人网 | 五月天婷婷丁香花 | 免费观看一级成人毛片 | 日本成人a | 久久精品视频观看 | 久久国内免费视频 | 综合精品久久久 | 久热香蕉视频 | 在线成人免费电影 | 91精品国产91久久久久福利 | 婷婷亚洲五月色综合 | 国产精品av免费在线观看 | 欧美中文字幕第一页 | 日韩中文字幕免费在线观看 | 99超碰在线播放 | 欧美色图亚洲图片 | 国产精品日韩久久久久 | 久草在线视频资源 | 免费在线| 天天干夜夜擦 | 手机av电影在线 | 夜夜干夜夜 | 亚洲欧美日韩国产精品一区午夜 | 天天射天天爱天天干 | 黄色福利网 | 免费在线观看黄 | 91热在线| 黄色国产在线观看 | 能在线观看的日韩av | 色婷婷成人网 | 超碰在线最新地址 | 精品亚洲二区 | 在线观看www视频 | 视频一区二区视频 | av综合在线观看 | 国产区久久 | 久色网 | 欧美精品一二三 | 精品国产aⅴ一区二区三区 在线直播av | 四虎成人av | 综合久久精品 | 色婷婷国产 | 日韩专区在线 | 精品亚洲在线 | 视频一区视频二区在线观看 | 亚洲精品理论 | av再线观看 | 久久精品一区二区三区四区 | 美国av片在线观看 | 婷婷国产v亚洲v欧美久久 | 欧美日韩国产精品一区 | 成人国产精品久久久久久亚洲 | 九九影视理伦片 | 色婷婷播放 | 波多野结衣在线观看一区二区三区 | 日韩欧美视频免费在线观看 | 中文字幕在线免费看 | 免费看片在线观看 | 中文字幕在线网 | 亚洲va综合va国产va中文 | 国内综合精品午夜久久资源 | 午夜精品视频免费在线观看 | 中文字幕超清在线免费 | 在线三级av | 九九电影在线 | freejavvideo日本免费| 亚洲精品在线观看的 | 国产在线精品视频 | 欧美精品久久久久a | 国产视频久 | 国产精品视频线看 | 中文理论片 | 精品视频在线播放 | 人人艹视频 | 久久久久久国产精品久久 | 欧美激情视频三区 | 黄色在线观看网站 | 婷婷久久综合网 | 久操操 | 亚洲二区精品 | 国产一区欧美一区 | 中文字幕二区 | 久久精品中文字幕一区二区三区 | 91麻豆精品国产午夜天堂 | 九九久久久久久久久激情 | 精品久久福利 | 最新国产精品拍自在线播放 | 又污又黄网站 | 亚洲综合一区二区精品导航 | 在线免费黄| 国产又粗又猛又黄又爽的视频 | 叶爱av在线 | 四虎免费在线观看视频 | 国产精品岛国久久久久久久久红粉 | 有码中文字幕在线观看 | 欧美日韩成人 | 久久久网页 | 久久资源总站 | 色婷婷综合久久久久 | 国产成人三级 | 国产在线a | 欧美aa一级片 | 91 在线视频 | 久久天堂亚洲 | 97色婷婷成人综合在线观看 | 青青河边草手机免费 | 亚洲成人精品在线 | 在线观看av小说 | 国产欧美日韩精品一区二区免费 | 在线观看视频亚洲 | av成人亚洲| 粉嫩av一区二区三区四区 | 99久久99久久 | 欧美一区二区三区四区夜夜大片 | 五月天.com| 成人免费毛片aaaaaa片 | 在线久热 | 免费欧美高清视频 | 精品一区二区三区久久 | 在线观看亚洲成人 | 中文字幕在线观看视频一区二区三区 | 五月婷婷在线综合 | 久久精品国产精品亚洲 | 日韩91av| 波多野结衣网址 | 免费手机黄色网址 | 97在线视频观看 | 97综合在线 | 亚洲精品福利在线观看 | 在线观看视频在线 | 永久免费毛片在线观看 | 亚洲经典视频在线观看 | 久久露脸国产精品 | 六月色播 | 日韩中文字幕视频在线观看 | 91看毛片 | 不卡电影免费在线播放一区 | 一区二区三区四区在线 | av高清不卡 | 五月婷婷欧美视频 | 黄色小说视频网站 | 四虎www com| 日本字幕网 | 国产日韩欧美视频在线观看 | 国产一区二区在线免费播放 | 国产传媒中文字幕 | 国产精品18久久久久久久久久久久 | 免费观看成人网 | 黄色av电影免费观看 | 综合国产在线观看 | 亚洲精品色婷婷 | 国产精品久久久久久久久久久免费看 | 国产亚洲精品久久久久久久久久 | 最新午夜电影 | 久久艹国产 | 狠狠综合| 成人久久毛片 | 欧美日韩中文字幕综合视频 | 欧美日韩一区二区在线 | 婷婷激情五月综合 | 在线视频观看成人 | 黄色小视频在线观看免费 | 久草电影在线观看 | 国产99爱| 精品久久电影 | 精品亚洲视频在线 | 人人精久 | 99产精品成人啪免费网站 | 欧美久久99 | 中文永久字幕 | 久久精精品视频 | 欧美一区二区在线看 | 欧美另类v| 日韩av手机在线观看 | 国产成人精品综合久久久 | 国产在线小视频 | 韩国一区二区三区在线观看 | 米奇影视7777| 国产 日韩 中文字幕 | 国产不卡在线视频 | 丝袜护士aⅴ在线白丝护士 天天综合精品 | 国内精品视频在线播放 | 五月婷婷操| 国产精品久久电影网 | 久久久鲁 | 日韩精品视频免费在线观看 | 欧美一级网站 | 特黄一级毛片 | 欧美动漫一区二区三区 | 国产欧美日韩一区 | 狠狠操天天操 | 在线观看视频一区二区三区 | 国产黄在线 | 狠狠狠狠狠狠 | 久久艹国产 | 国产99一区视频免费 | 精品国产诱惑 | 日日草av| 亚洲精品国产自产拍在线观看 | 天天色天天草天天射 | 国产精品久久久99 | 久久久久久久福利 | 欧美精品在线一区 | 99久久久国产精品美女 | 在线中文字幕一区二区 | 一区二区三区国产欧美 | 久久精品视频99 | 天天射天天爽 | 日p视频| 精品国内自产拍在线观看视频 | 91传媒免费观看 | 欧美精品久 | 91九色视频| 狠狠色2019综合网 | 久久国产精品电影 | 婷婷久久丁香 | 亚洲日本三级 | 国产片免费在线观看视频 | 成人av在线观 | 国产色综合天天综合网 | 韩国三级一区 | 香蕉色综合 | 丁香av| 久草99| 少妇bbbb搡bbbb桶 | 在线观看视频你懂的 | 亚洲一区二区精品视频 | japanese黑人亚洲人4k | 国产一级一片免费播放放 | 天天做天天干 | 亚洲精品视频在线播放 | 亚洲男男gaygay无套 | 中日韩三级视频 | 色综合天天综合网国产成人网 | 色婷婷亚洲婷婷 | 最近中文字幕视频完整版 | 国产999在线观看 | 国产精品综合av一区二区国产馆 | 国内精品久久久久影院一蜜桃 | 91成年视频 | 午夜视频在线网站 | 天天爱天天爽 | 中文字幕在线观看完整版 | 国产精品av免费观看 | aⅴ精品av导航 | 韩国一区二区三区在线观看 | 国产精品手机看片 | 国产精品毛片久久久久久久久久99999999 | 国产精品黄色影片导航在线观看 | 精品视频在线播放 | 亚洲精品www. | av三级在线免费观看 | 一区二区三区在线观看免费视频 | 97精品在线 | 成人午夜网址 | 日本在线观看一区二区三区 | 激情婷婷亚洲 | 亚洲三级在线免费观看 | 国产无遮挡猛进猛出免费软件 | 国产精品色婷婷视频 | 久久精品久久国产 | 操操操综合 | 免费a视频在线观看 | 国产一区视频免费在线观看 | 麻豆一二 | 美女视频黄频大全免费 | 国产99视频在线观看 | 视频成人永久免费视频 | 99精品在线免费 | 久久久免费毛片 | 在线免费观看成人 | 成人免费视频播放 | 最近日本mv字幕免费观看 | 久久黄色网页 | 亚洲精品tv| 久久婷婷一区二区三区 | 久久精品在线免费观看 | 久久久久二区 | 成人av电影免费观看 | 日韩视频欧美视频 | 精品嫩模福利一区二区蜜臀 | 日韩一级电影网站 | 2023国产精品自产拍在线观看 | 亚洲永久免费av | 国产 视频 高清 免费 | 中文字幕在线播放日韩 | 中文字幕999 | 国产欧美精品在线观看 | 国产又粗又长又硬免费视频 | 黄色小说在线免费观看 | 国产综合香蕉五月婷在线 | 四虎在线观看 | 综合网久久 | 久久国产精品99久久人人澡 | 亚洲一区久久 | 久久人人爽人人片av | 久久国产精品视频观看 | 国产精品久久人 | 日日干 天天干 | 在线精品观看 | 精品视频免费久久久看 | 2019av在线视频 | 伊人五月天综合 | 青草视频在线 | 国产亚洲精品久久久久久无几年桃 | 99视频网站 | 久久久精品 | 久久综合婷婷 | 亚洲综合欧美激情 | 日韩在线视频免费看 | 超碰免费久久 | 狠狠色狠狠色综合日日小说 | 美女久久久久久 | 久要激情网 | 久久免费视频播放 | 在线视频麻豆 | 在线观看亚洲精品 | 日韩精品久久中文字幕 | 丁香六月天| 免费麻豆 | 久久女教师 | 色哟哟国产精品 | 狠狠躁18三区二区一区ai明星 | 免费成人在线观看视频 | 黄色三级免费看 | .国产精品成人自产拍在线观看6 | 日韩av一卡二卡三卡 | 久久久精品免费看 | 在线欧美国产 | 中文字幕在线字幕中文 | 久久99精品一区二区三区三区 | 精品免费观看 | 亚洲成av人片一区二区梦乃 | 天天干天天操天天干 | 麻豆久久 | 午夜久久久精品 | 色婷婷综合五月 | 国产精品成人aaaaa网站 | 国产麻豆传媒 | 久久在线观看视频 | 午夜精品久久久久久久99无限制 | 久久成人黄色 | 国产九色91 | 天天操天天是 | av网站在线免费观看 | 欧美性生活久久 | 久99热| 欧美色图另类 | 福利视频在线看 | 婷婷在线视频观看 | 日韩乱码中文字幕 | 91精品夜夜 | 欧美日bb | 黄色软件在线观看 | 日韩在线观看高清 | 亚洲年轻女教师毛茸茸 | 午夜av在线播放 | 久久久久国产成人精品亚洲午夜 | 亚洲精品高清一区二区三区四区 | 国产专区视频在线 | 99久久精品国产系列 | 精品在线观看国产 | 日韩一区二区三区高清在线观看 | 亚洲婷婷伊人 | 久久夜色电影 | 日韩,中文字幕 | 综合网伊人 | 国产清纯在线 | 国产美女免费观看 | 午夜三级影院 | 成年人免费在线观看网站 | 91在线国产观看 | 久久久av电影 | 国产福利网站 | av视屏在线 | 日韩欧美视频在线观看免费 | 中日韩在线视频 | 亚洲精品在线电影 | 色久综合 | 中文字幕在线观看资源 | 日日干激情五月 | 亚洲免费永久精品国产 | 国产精品久久久久久久久毛片 | 国产原创在线视频 | 国产精品一区二区三区免费视频 | 国产真实在线 | 成人在线视频你懂的 | 亚洲国产成人在线观看 | av日韩国产 | 国产免费国产 | 午夜视频一区二区 | 国产精品视频app | 婷婷天天色 | 亚洲精品一区二区在线观看 | 国产精品免费视频观看 | www国产一区 | 国产午夜精品久久久久久久久久 | 一区二区三区在线观看中文字幕 | 在线观看免费一级片 | 日韩特黄一级欧美毛片特黄 | 国产日韩欧美精品在线观看 | 日韩欧美一区二区不卡 | 成人黄色在线视频 | 日韩亚洲国产中文字幕 | 黄色app网站在线观看 | 久久国产一区二区三区 | 特级黄色片免费看 | 欧美成人xxxxx | 亚洲欧美国产精品 | 91精品视频免费在线观看 | 日韩大片免费观看 | 国产免费观看高清完整版 | 98超碰人人 | 天天操天天爽天天干 | 欧美日韩中文在线视频 | 波多野结衣综合网 | 丁香六月av| 国产成人1区| 日韩美一区二区三区 | 欧美精品久久人人躁人人爽 | 国内成人精品2018免费看 | 激情五月开心 | 久久96国产精品久久99漫画 | 色先锋av资源中文字幕 | 91视频在线免费下载 | 免费激情网 | 精品国产91亚洲一区二区三区www | 黄色三级视频片 | 国产精品日韩精品 | 久久中文欧美 | 欧美日韩高清一区二区 国产亚洲免费看 | 欧美色噜噜噜 | 97国产在线观看 | 国产精品一区二区三区四 | 一二三区高清 | 国产不卡免费视频 | 久久久久一区 | 成人久久久电影 | 国产综合在线观看视频 | 欧美午夜激情网 | 天天操天天射天天插 | 天天狠狠操 | 91在线免费观看网站 | 亚洲国产成人精品在线观看 |