zoukankan      html  css  js  c++  java
  • 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() +

    " ");

    }

    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(" ");

    }

    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() +

    " ");

    }

    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(" ");

    }

    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+" ");

    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-->
  • 相关阅读:
    ASP.NET Core – Web API Versioning
    ASP.NET Core – Handle Error on Web API
    ASP.NET Core – MVC vs Razor Page
    EF Core – Library use EF
    ASP.NET Core – Filter
    GeoServer地图开发解决方案
    参透人生的一道计算题
    iscroll4 精讲
    各种分割线Html代码
    iBatis入门教程
  • 原文地址:https://www.cnblogs.com/xiaochao1234/p/4335438.html
Copyright © 2011-2022 走看看