乐趣区

Android简易聊天室软件HTTP实现

服务器端

我在本机搭建了 Tomcat 服务器作为 Web 应用的容器,使用 Servlet 来实现聊天的业务逻辑。

由于使用到了 JSON 数据,所以需要在相应的 Web 应用项目中的 WEB-INF\lib 文件夹下导入 6 个 jar 包定义了一个 DiffServlet

package com.chatroom;

import java.io.IOException;

import java.io.PrintWriter;

import javax.servlet.ServletException;

import javax.servlet.annotation.WebServlet;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import net.sf.json.JSONArray;

import net.sf.json.JSONObject;

@WebServlet(“/DiffServlet”)

public class DiffServlet extends HttpServlet {

private static final long serialVersionUID = 1L;

private static JSONArray messageList = new JSONArray();

public DiffServlet() {

super();

// TODO Auto-generated constructor stub

}

public void init()throws ServletException{ // 初始化,创建一个 JSON 对象列表,

if(messageList.isEmpty()) {// 用于存储聊天记录

JSONObject first = new JSONObject();

first.put(“name”, “Server”);

first.put(“message”, “Server Gets Ready”);

messageList.add(first);

}

}

protected void doGet(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

// 对 http 请求的 Get 方法进行响应,为客户端返回所有聊天记录

request.setCharacterEncoding(“UTF-8”);

response.setContentType(“application/json;charset=UTF-8”);

PrintWriter out = response.getWriter();

out.write(messageList.toString());

out.flush();

out.close();

}

protected void doPost(HttpServletRequest request, HttpServletResponse response)

throws ServletException, IOException {

// 对 http 请求的 Post 方法进行响应,将客户端发来的信息添加到聊天记录列表当中

request.setCharacterEncoding(“UTF-8”);

response.setContentType(“application/json;charset=UTF-8”);

String username = request.getParameter(“name”);

String message = request.getParameter(“message”);

JSONObject obj = new JSONObject();

obj.put(“name”, username);

obj.put(“message”, message);

messageList.add(obj);

}

}

在 web.xml 对该 Servlet 进行注册

DiffServlet

com.ChatRoom.DiffServlet

DiffServlet

/ChatRoom/DiffServlet

客户端

Android 客户端包含两个 Activity,一个是登录界面 MainActivity,一个是聊天界面 ChatRoom。所有的活动要在 AndroidManifest.xml 文件中进行注册,为了使用户在应用中使用输入法使界面的背景图不会被压缩,所以在每个活动标签中加入

android:windowSoftInputMode=”adjustPan”

这样一条代码,就可以解决背景图片被压缩的问题了。

由于项目中要使用网络,所以要在 AndroidManifest.xml 文件中对授权对网络的访问,添加如下代码:
亨达代理申请 http://www.kaifx.cn/broker/ha…

注意

如果我们使用的是 http 协议的域名,使用 Android Studio 开发的应用可能会与主机连接不上,出现这种情况可参考 6 号楼下的大懒喵的博客 OkHttp 请求 http 链接失败的问题

登录界面:MainActivity

布局文件:

android:orientation=”vertical”

android:layout_width=”match_parent”

android:layout_height=”match_parent”

android:background=”@drawable/picture”>

android:id=”@+id/et_name”

android:layout_width=”match_parent”

android:layout_height=”wrap_content”

android:layout_gravity=”center”

android:gravity=”center”

android:hint=” 用户名 ”/>

android:id=”@+id/btn_cnt”

android:layout_width=”100dp”

android:layout_height=”wrap_content”

android:background=”#07D0F3″

android:gravity=”center”

android:layout_gravity=”center”

android:text=” 连接 ”

android:textColor=”#ffffff” />

源代码:

package com.example.chatroom;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;

import android.os.Bundle;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

private Button btn_cnt;

private EditText et_name;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

btn_cnt = (Button) findViewById(R.id.btn_cnt);

et_name = findViewById(R.id.et_name);

btn_cnt.setOnClickListener(MainActivity.this);

}

public void onClick(View view) {

String name = et_name.getText().toString();

if (“”.equals(name)) {

Toast.makeText(this, “ 请输入用户名:”, Toast.LENGTH_SHORT).show();

// 如果输入的用户名为空的话,那么下端会出现提示

} else {

Intent intent=new Intent(MainActivity.this,ChatRoom.class);

intent.putExtra(“username”,name);

startActivity(intent);

}

}

}

聊天界面:ChatRoom

布局文件:

xmlns:app=”http://schemas.android.com/apk/res-auto”

android:orientation=”vertical”

android:background=”#d8e0e8″

android:layout_width=”match_parent”

android:layout_height=”match_parent”>

android:id=”@+id/msg_recycler_view”

android:layout_width=”match_parent”

android:layout_height=”0dp”

android:layout_weight=”1″

android:background=”@drawable/background” />

android:orientation=”horizontal”

android:layout_width=”match_parent”

android:layout_height=”wrap_content”>

android:id=”@+id/input_text”

android:layout_width=”0dp”

android:layout_height=”match_parent”

android:layout_weight=”1″

android:background=”#ffffff”

/>

android:id=”@+id/send”

android:layout_width=”wrap_content”

android:layout_height=”50dp”

android:background=”#07D0F3″

android:text=” 发送 ”

android:textColor=”#ffffff” />

源代码:

package com.example.chatroom;

import androidx.appcompat.app.AppCompatActivity;

import androidx.recyclerview.widget.LinearLayoutManager;

import androidx.recyclerview.widget.RecyclerView;

import android.annotation.SuppressLint;

import android.content.Intent;

import android.os.Bundle;

import android.os.Handler;

import android.os.Looper;

import android.os.Message;

import android.view.View;

import android.widget.Button;

import android.widget.EditText;

import android.widget.Toast;

import org.json.JSONArray;

import org.json.JSONObject;

import java.text.SimpleDateFormat;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

import okhttp3.FormBody;

import okhttp3.OkHttpClient;

import okhttp3.Request;

import okhttp3.RequestBody;

import okhttp3.Response;

public class ChatRoom extends AppCompatActivity implements View.OnClickListener{

private List msgList = new ArrayList<>();

private EditText inputText;

private Button send;

private RecyclerView msgRecyclerView;

private MsgAdapter adapter;

boolean isRunning = false;

private boolean isSend=false;

private String myName;

private String responseData;

private int curr; // 当前显示的消息条数

private int jsonLen; // 获取到的 json 列表长度

private Handler handler = new Handler(Looper.myLooper()){

// 获取当前进程的 Looper 对象传给 handler

// 在目前的 Android 开发中,子线程不能改变 UI,

// 所以子线程要对 UI 进行操作需要交给一个 Handler 对象来执行

@Override

public void handleMessage(Message message){

String message_Name = message.getData().getString(“name”);

String message_msgC = message.getData().getString(“msgContent”);

if(!message_msgC.equals(“”)){

if(message_Name.equals(myName))

addNewMessage(message_msgC, Msg.TYPE_SENT);

else

addNewMessage(message_msgC,Msg.TYPE_RECEIVED);

}

}

};

public void addNewMessage(String msg,int type){

Msg message = new Msg(msg,type);

msgList.add(message);

adapter.notifyItemInserted(msgList.size()-1);

msgRecyclerView.scrollToPosition(msgList.size()-1);

}

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_chat_room);

Intent intent =getIntent();

myName=intent.getStringExtra(“username”);

curr=0;

jsonLen=1;

isRunning=true;

inputText = findViewById(R.id.input_text);

send=findViewById(R.id.send);

send.setOnClickListener(this);

runOnUiThread(new Runnable() {

@Override

public void run() {

LinearLayoutManager layoutManager = new

LinearLayoutManager(ChatRoom.this);

msgRecyclerView= findViewById(R.id.msg_recycler_view);

msgRecyclerView.setLayoutManager(layoutManager);

adapter = new MsgAdapter(msgList);

msgRecyclerView.setAdapter(adapter);

}

});

new Thread(new Receive(), “ 接收线程 ”).start();

new Thread(new Send(), “ 发送线程 ”).start();

}

public void parseJSONWithJSONObject(String jsonData) {// 解析 JSON 数据函数

try {

JSONArray jsonArray = new JSONArray(jsonData);

jsonLen = jsonArray.length();

for (; curr < jsonLen; curr++) {

JSONObject jsonObject = jsonArray.getJSONObject(curr);

String name = jsonObject.getString(“name”);

String msgContent = jsonObject.getString(“message”);

Message message = new Message();

Bundle bundle = new Bundle();

bundle.putString(“name”, name);

bundle.putString(“msgContent”, msgContent); // 往 Bundle 中存放数据

message.setData(bundle);//mes 利用 Bundle 传递数据

handler.sendMessage(message);// 用 activity 中的 handler 发送消息

}

} catch (Exception e) {

Looper.prepare();

Toast.makeText(ChatRoom.this, “ 解析 json 错误!”, Toast.LENGTH_SHORT).show();

Looper.loop();

}

}

String msgEntity;

@Override

public void onClick(View view){

String content = inputText.getText().toString();

@SuppressLint(“SimpleDateFormat”)

String date = new SimpleDateFormat(“yyyy-MM-dd hh:mm:ss”).format(new Date());

StringBuilder sb = new StringBuilder();

msgEntity = myName;

sb.append(msgEntity).append(“\n”+date+”\n”+content);

msgEntity = sb.toString();

if(!””.equals(msgEntity)){

Message message = new Message();

Bundle bundle = new Bundle();

bundle.putString(“name”, myName);

bundle.putString(“msgContent”, msgEntity); // 往 Bundle 中存放数据

message.setData(bundle);//mes 利用 Bundle 传递数据

handler.sendMessage(message);// 用 activity 中的 handler 发送消息

inputText.setText(“”);

isSend = true;

curr++;

}

sb.delete(0,sb.length());

}

class Send implements Runnable{

@Override

public void run(){ // 发送线程

while(isRunning){

if(isSend){

RequestBody requestBody = new FormBody.Builder()

.add(“name”,myName)

.add(“message”,msgEntity)

.build();

try {

OkHttpClient client = new OkHttpClient();

Request request2 = new Request.Builder()

// 指定访问的服务器地址

.url(Resource.DiffUrl).post(requestBody)

.build();

Response response = client.newCall(request2).execute();

String responseData = response.body().string();

isSend = false;

} catch (Exception e) {

Looper.prepare();

Toast.makeText(ChatRoom.this, “ 发送失败!”,

Toast.LENGTH_SHORT).show();

Looper.loop();

}

}

}

}

}

class Receive implements Runnable{

public void run(){

while(isRunning){

if(!isSend) {

try {

OkHttpClient client = new OkHttpClient();

Request request = new Request.Builder()

// 指定访问的服务器地址

.url(Resource.DiffUrl).get()

.build();

//Resource.DiffUrl 为 DiffSevlet 的 URL 地址

// 其需要根据你的服务端 Servlet 的 URL 地址进行修改

Response response = client.newCall(request).execute();

String responseData = response.body().string();

if (responseData != null && responseData.startsWith(“\ufeff”)){

responseData = responseData.substring(1);

}

parseJSONWithJSONObject(responseData);

} catch (Exception e) {

Looper.prepare();

Toast.makeText(ChatRoom.this, “ 连接服务器失败!!!”,

Toast.LENGTH_SHORT).show();

Looper.loop();

}

}

}

}

}

需要在 build.gradle 中添加库依赖

implementation ‘com.android.support:recyclerview-v7:28.0.0’

implementation ‘com.squareup.okhttp3:okhttp:4.5.0’

implementation ‘com.squareup.okio:okio:2.5.0’

1

2

Msg 实体类以及 MsgAdapter 类的相关代码参考衣侠客的博客 Android 聊天室 (客户端)

————————————————

退出移动版