Andoird通信之简单聊天ChatApp
Android通信,大体上的逻辑是跟Java上的通信是差不多的,只是因为Android线程不能直接访问UI界面组件,所以在处理的时候有些麻烦,只要是通过Handle处理。
接下来是一个简单地手机聊天App,主要是手机终端与电脑之间的通信,比如发文字或者是发送图片。
因为涉及到发送不同类型的文件,所以需要制定以下协议:
1、消息类型,使用一个int表示,4个字节:
1、表示文字数据包;
2、表示鱼片数据包。
2、数据包的长度,使用一个int表示,4个字节
3、数据内容,使用字节数组进行传输。
?
关键性代码:
1、服务机与客户机之间的链接:
服务机:
ServerSocket?ss?=?new?ServerSocket(port);
Socket?socket?=?ss.accept();
客户机:
//这个地方不能填电脑的本机地址,一定要是局域网或公网的//IP地址,wifi
socket?=?new?Socket("10.96.23.74",?8888);
2、发送文字的代码:
//发送数据类型,?1-文本,?2-图片
dataOutputStream.writeInt(1);
//发送数据长度
dataOutputStream.writeInt(bytes.?length);
//发送数据内容
dataOutputStream.write(bytes);
3、发送图片的代码:
//把一个图片文件读取到一个字节数组中
FileInputStream?fis?=?new?FileInputStream("images/jobs.png");
int?len?=?fis.available();
byte[]?bytes?=?new?byte[len];
fis.read(bytes);
fis.close();
//发送数据类型,?1-文本,?2-图片
dataOutputStream.writeInt(2);
//数据长度
dataOutputStream.writeInt(bytes.?length);
//数据内容
dataOutputStream.write(bytes);
//刷新缓存
dataOutputStream.flush();
4、Handle处理UI界面:
在客户机线程里面,handler.sendMessage(msg);
在MainActivity里面:
//创建Handler对象
Handler?handler?=?new?Handler(
//回调接口,当handler接收到消息时调用该接口的handleMessage方法
new?Callback()?{
//出理线程发送过来的数据
public?boolean?handleMessage(Message?msg)?{
if(msg.?what?==?1){
//把服务器发送过来的内容显示出来
textMsgLog.append("?服务器:?"?+?msg.?obj?.toString()?+
"\n");
}
if(msg.?what?==?2){
//服务器发送过来的图片
Bitmap?bitmap?=?(Bitmap)msg.?obj?;
ImageSpan?imageSpan?=?new?ImageSpan(MainActivity.?this,
bitmap);
//创建一个SpannableString对象,以便插//入用ImageSpan对象封装的图像
SpannableString?ss?=?new?SpannableString("img");
//?用imageSpan对象替换img
ss.setSpan(imageSpan,?0,?3,
Spannable.?SPAN_EXCLUSIVE_EXCLUSIVE);
//把图片显示在界面上
textMsgLog.append("?服务器:?");
textMsgLog.append(ss);
textMsgLog.append("\n");
}
return?true;
}
});
5、在?Android?Manifest.xml文件中添加网络权限:
在?uses-sdk?标签后面添加
<uses-permission?android:name="android.permission.INTERNET"/>
?
所以,对于服务机
import?java.io.IOException;
import?java.net.ServerSocket;
import?java.net.Socket;
?
public?class?Server?{
public?void?setUp(int?port){
try?{
//监听port端口
ServerSocket?ss?=?new?ServerSocket(port);
//循环接收客户端的访问
while(true){
Socket?socket?=?ss.accept();
//把客户端的连接放到线程里处理
SocketThread?st?=?new?SocketThread(socket);
st.start();
}
}?catch?(IOException?e)?{
e.printStackTrace();
}
}
?
public?static?void?main(String[]?args)?{
//服务器监听8888端口
new?Server().setUp(8888);
}
}
?
?
import?java.io.DataInputStream;
import?java.io.DataOutputStream;
import?java.io.FileInputStream;
import?java.io.IOException;
import?java.io.InputStream;
import?java.net.Socket;
?
public?class?SocketThread?extends?Thread?{
private?Socket?socket;
private?DataOutputStream?dataOutputStream;
?
public?SocketThread(Socket?socket)?{
this.?socket?=?socket;
}
?
public?void?run(){
try?{
//得到网络连接的输入输出流对象
InputStream?inputStream?=?socket.getInputStream();
//用数据输入流对原输入流进行封装,用于读取不同类型的数据
DataInputStream?dis?=?new?DataInputStream(inputStream);
//用数据输出流对原输出流进行封装,用于发送不同类型的数据
dataOutputStream?=?new?DataOutputStream(socket.getOutputStream());
while(true){
//读取一个int,注意:这里是readInt方法
int?msgType?=?dis.readInt();
//文字消息
if(msgType?==?1){
//读取数据包长度
int?len?=?dis.readInt();
//读取数据内容
byte[]?bytes?=?new?byte[len];
dis.readFully(bytes);
//转成字符串
String?content?=?new?String(bytes,?"GB2312");
System.?out.println("?客户端说:?"+content);
//客户端发送过来的字符串为image的话就发送一张图片过去
if(content.equals("image")){
sendImage();
}?else?{
//向客户端回应信息
String?replyString?=?"?服务器收到了("+content+")";
bytes?=?replyString.getBytes("GB2312");
//发送数据类型,?1-文本,?2-图片
dataOutputStream.writeInt(1);
//发送数据长度
dataOutputStream.writeInt(bytes.?length);
//发送数据内容
dataOutputStream.write(bytes);
//刷新缓存
dataOutputStream.flush();
}
}
//图片数据包
if(msgType?==?2){
//读取数据包长度
int?len?=?dis.readInt();
//读取数据内容
byte[]?bytes?=?new?byte[len];
dis.readFully(bytes);
//在这里可以把客户端发送的图片保存到文件,也可以做个界面显示出来,具体代码就不写了
?
?
?
}
}
}?catch?(IOException?e)?{
e.printStackTrace();
}
}
?
//向客户端发送一张图片
public?void?sendImage()?throws?IOException{
//把一个图片文件读取到一个字节数组中
//src/wo.jpg,以文件夹ChatAppServer为起始目录
FileInputStream?fis?=?new?FileInputStream("images/wo.jpg");
int?len?=?fis.available();
byte[]?bytes?=?new?byte[len];
fis.read(bytes);
fis.close();
//发送数据类型,?1-文本,?2-图片
dataOutputStream.writeInt(2);
//数据长度
dataOutputStream.writeInt(bytes.?length);
//数据内容
dataOutputStream.write(bytes);
//刷新缓存
dataOutputStream.flush();
}
}
?
对于客户机
首先是Android页面:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
?android:layout_width="match_parent"
?android:layout_height="match_parent"
?android:orientation="vertical"?>
?
<TextView
?android:layout_width="wrap_content"
?android:layout_height="wrap_content"
?android:id="@+id/textMsgLog"
?android:text=""?/>
?
?<LinearLayout
?android:layout_width="match_parent"
?android:layout_height="match_parent"
?android:orientation="horizontal">
?
?<EditText
android:id="@+id/editInput"
?android:layout_width="220dp"
?android:layout_height="wrap_content"?/>
?
?<Button
?android:id="@+id/btnSend"
?android:layout_width="wrap_content"
?android:layout_height="wrap_content"
?android:onClick="send"
?android:text="发送"?/>
?
</LinearLayout>
</LinearLayout>
?
主页面:
package?com.cyw.chatapp;
?
import?android.app.Activity;
import?android.graphics.Bitmap;
import?android.os.Bundle;
import?android.os.Handler;
import?android.os.Handler.Callback;
import?android.os.Message;
import?android.text.Spannable;
import?android.text.SpannableString;
import?android.text.style.ImageSpan;
import?android.view.View;
import?android.widget.EditText;
import?android.widget.TextView;
import?android.widget.Toast;
?
public?class?MainActivity?extends?Activity?{
private?TextView?textMsgLog;
private?ClientThread?clientThread;
?
protected?void?onCreate(Bundle?savedInstanceState)?{
super.onCreate(savedInstanceState);
setContentView(R.layout.?activity_main);
?
//聊天记录组件
textMsgLog?=?(TextView)findViewById(R.id.?textMsgLog);
//创建Handler对象
Handler?handler?=?new?Handler(
//回调接口,当handler接收到消息时调用该接口的handleMessage方法
new?Callback()?{
//出理线程发送过来的数据
public?boolean?handleMessage(Message?msg)?{
if(msg.?what?==?1){
//把服务器发送过来的内容显示出来
textMsgLog.append("?服务器:?"?+?msg.?obj?.toString()?+
"\n");
}
if(msg.?what?==?2){
//服务器发送过来的图片
Bitmap?bitmap?=?(Bitmap)msg.?obj?;
ImageSpan?imageSpan?=?new?ImageSpan(MainActivity.?this,
bitmap);
//创建一个SpannableString对象,以便插入用ImageSpan对象封装的图像
SpannableString?ss?=?new?SpannableString("img");
//?用imageSpan对象替换img
ss.setSpan(imageSpan,?0,?3,
Spannable.?SPAN_EXCLUSIVE_EXCLUSIVE);
//把图片显示在界面上
textMsgLog.append("?服务器:?");
textMsgLog.append(ss);
textMsgLog.append("\n");
}
return?true;
}
});
?
//通信线程,通过handler对象与UI主现象发送数据
clientThread?=?new?ClientThread(handler);
clientThread.start();
}
?
//向服务器发送文本内容
public?void?send(View?v){
EditText?editInput?=?(EditText)findViewById(R.id.?editInput);
String?msg?=?editInput.getText().toString();
boolean?result?=?clientThread.sendText(msg);
if(result){
textMsgLog.append("?我:?"+msg+"\n");
editInput.setText("");
}?else?{
Toast.?makeText(this,?"?发送失败",?Toast.?LENGTH_LONG).show();
}
}
}
?
客户端处理线程:
package?com.cyw.chatapp;
?
import?java.io.DataInputStream;
import?java.io.DataOutputStream;
import?java.io.IOException;
import?java.net.Socket;
import?java.net.UnknownHostException;
import?android.graphics.Bitmap;
import?android.graphics.BitmapFactory;
import?android.os.Handler;
import?android.os.Message;
?
public?class?ClientThread?extends?Thread?{
//与UI主线程通信的中间代理
private?Handler?handler;
private?DataOutputStream?dataOutputStream;
private?DataInputStream?dataInputStream;
private?Socket?socket;
?
public?ClientThread(Handler?handler){
this.?handler?=?handler;
}
?
//一定记得要在线程里访问网络
public?void?run(){
try?{
//因为连接网络比较耗时间,会阻塞UI主线程
//所以连接到服务器的操作一定要放在线程的run里面
connect();
while(true){
//读取一个int,注意:这里是readInt方法
int?msgType?=?dataInputStream.readInt();
//文字消息
if(msgType?==?1){
//读取数据包长度
int?len?=?dataInputStream.readInt();
//读取数据内容
byte[]?bytes?=?new?byte[len];
dataInputStream.readFully(bytes);
//转成字符串
String?content?=?new?String(bytes,?"GB2312");
Message?msg?=?new?Message();
//消息类型
msg.?what?=?1;
//消息内容
msg.?obj?=?content;
//发送给UI主线程
handler.sendMessage(msg);
}
//图片数据包
if(msgType?==?2){
//读取数据包长度
int?len?=?dataInputStream.readInt();
//读取数据内容
byte[]?bytes?=?new?byte[len];
dataInputStream.readFully(bytes);
//把字节数组还原成一个图片
Bitmap?bitmap?=?BitmapFactory.?decodeByteArray(bytes,
0,?len);
Message?msg?=?new?Message();
//消息类型
msg.?what?=?2;
//消息内容
msg.?obj?=?bitmap;
//发送给UI主线程
handler.sendMessage(msg);
}
}
}?catch?(IOException?e)?{
e.printStackTrace();
}?finally?{
close();
}
}
?
//链接主机
private?void?connect()?throws?UnknownHostException,?IOException?{
//这个地方不能填电脑的本机地址,一定要是局域网或公网的IP地址,wifi
//IPv4?地址:?10.96.23.74??IPv6?地址:?2001:250:4400:d000::3:97b8
socket?=?new?Socket("10.96.23.74",?8888);
//得到输入输出流对象
dataInputStream?=?new?DataInputStream(socket.getInputStream());
dataOutputStream?=?new?DataOutputStream(socket.getOutputStream());
}
?
//关闭网络连接
private?void?close(){
if(dataInputStream?!=?null){
try?{
dataInputStream.close();
}?catch?(IOException?e)?{
}
}
if(dataOutputStream?!=?null){
try?{
dataOutputStream.close();
}?catch?(IOException?e)?{
}
}
if(socket?!=?null){
try?{
socket.close();
}?catch?(IOException?e)?{
}
}
}
?
//向服务器发送数据
public?boolean?sendText(String?content){
try?{
//把字符串转成字节数组
byte[]?bytes?=?content.getBytes("GB2312");
//消息类型
dataOutputStream.writeInt(1);
//数据长度
dataOutputStream.writeInt(bytes.?length);
//发送数据
dataOutputStream.write(bytes);
dataOutputStream.flush();
return?true;
}?catch?(IOException?e)?{
e.printStackTrace();
}
return?false;
}
}
?
<!--EndFragment-->原文:http://cyw.iteye.com/blog/2192049