要实现一对一通信,要解决两个问题:
1.发送消息时,要找到对方在服务器端的socket,然后在输出流输出消息。这样,对方的客户端输入流就会收到消息了。所以问题就是如何找到对方在服务器端的socket。
解决方法:在客户端登录时将服务器端socket和账号关联起来,用一个HashMap存储起来。当发送消息时,同时发送对方的账号就可以了。
2.接受消息时,要把消息显示在相应的窗口中。群聊的消息不能出现在通信双方的聊天窗口那里,而通信双方的消息也不能出现在群聊的窗口里。当消息送到对方的客户端时,对方可能已经打开了多个聊天窗口,那么就要解决消息要送到哪个窗口。
解决方法:每条聊天消息包含三部分内容,fromID,content,toID,表示发送方,内容,接受方,而每个聊天窗口有一个toID,且在打开时初始化,表示在这个窗口输入的东西会发给谁。这样就可以让聊天窗口挑选信息进行显示了。
同时,还有注意的是,因为app中有多个聊天窗口,在退出某个聊天窗口时,消息记录肯定是不能删除的,所以这里我选择了fragment,退出聊天窗口时隐藏即可。
由于截图不能说明些啥,所以没了!这里给出核心代码:
服务器端:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import org.json.JSONException;
import org.json.JSONObject;
public class ChatSocket extends Thread{
private Socket socket;
private BufferedWriter bWriter;
private BufferedReader bReader;
public ChatSocket (Socket socket) {
this.socket = socket;
try {
bWriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(),"UTF-8"));
bReader = new BufferedReader(new InputStreamReader(socket.getInputStream(),"UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
public void out(String s)
{
s += "\n";
try {
bWriter.write(s);
bWriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run()
{
try
{
String s;
while ((s = bReader.readLine()) != null)
{
try
{
JSONObject root = new JSONObject(s);
if(root.has("content"))
{
String toID = root.getString("toID");
if(toID.equalsIgnoreCase("Group"))
{
ChatManager.getChatManager().publish(this, s);
}
else
{
ChatSocket cs = ChatManager.getChatManager().hashMap.get(toID);
if(cs != null)
{
cs.out(s);
}
else
{
System.out.println("没有该用户" + toID);
}
}
}
else if(root.has("user") && root.has("password"))
{
JSONObject a = new JSONObject();
if(root.getString("user").equalsIgnoreCase(root.getString("password")))
{
if(!ChatManager.getChatManager().hashMap.containsKey("user"))
{
a.put("result", true);
ChatManager.getChatManager().hashMap.put(root.getString("user"), this);
System.out.println("用户" + root.getString("user") + "上线了!!");
}
else
{
a.put("result", false);
}
}
else
{
a.put("result", false);
}
out(a.toString());
}
}
catch (JSONException e)
{
e.printStackTrace();
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
客户端:
package com.example.mysocketclient;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
import com.example.mysocketclient.LoginActivity.MessageReceiver;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.os.Bundle;
import android.app.FragmentManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.TextView;
import android.widget.Toast;
public class ChooseChatFragment extends Fragment implements OnClickListener,OnItemClickListener{
private TextView textView;
private EditText editText;
private Button buttonAdd;
private Button buttonGroup;
private ListView listView;
private FriendEntityViewAdapter adapter;
private List<FriendEntity> list;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_choose_chat, container, false);
textView = (TextView)view.findViewById(R.id.titleText);
editText = (EditText)view.findViewById(R.id.editText);
buttonAdd = (Button)view.findViewById(R.id.buttonAdd);
buttonGroup = (Button)view.findViewById(R.id.buttonGroup);
listView = (ListView)view.findViewById(R.id.listview);
textView.setText(UserInfo.getID() + "的通讯录");
buttonAdd.setOnClickListener(this);
buttonGroup.setOnClickListener(this);
listView.setOnItemClickListener(this);
list = new ArrayList<FriendEntity>();
adapter = new FriendEntityViewAdapter(getActivity(), list);
listView.setAdapter(adapter);
return view;
}
@Override
public void onClick(View v) {
if(v.getId() == R.id.buttonAdd)
{
if(editText.getText().toString() != null)
{
FriendEntity fe = new FriendEntity();
fe.setID(editText.getText().toString());
list.add(fe);
adapter.notifyDataSetChanged();//通知ListView,数据已发生改变
listView.setSelection(listView.getCount() - 1);//发送一条消息时,ListView显示选择最后一项
editText.setText("");
}
}
else if(v.getId() == R.id.buttonGroup)
{
toChatFragment("Group");
}
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id)
{
toChatFragment(list.get(position).getID());
}
private void toChatFragment(String tag)
{
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.hide(this);
//保证只初始化一次
//这里ChatFragment作为一个Fragment,有一个tag,方便Fragment之间的切换
//同时ChatFragment作为一个聊天窗口,也有一个tag,表示跟谁的聊天,如tag为11,表示跟11的聊天
//这样ChatFragment在接受信息时只接受跟自己tag相等的,就可以将对应的信息显示在对应的窗口
if(fm.findFragmentByTag(tag) == null)
{
tx.add(R.id.content, new ChatFragment(tag), tag);
}
else
{
tx.show(fm.findFragmentByTag(tag));
}
tx.addToBackStack(null);
tx.commit();
}
}
package com.example.mysocketclient;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import org.json.JSONException;
import org.json.JSONObject;
import android.R.bool;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class ChatFragment extends Fragment implements OnClickListener{
private EditText editText;
private Button sendButton;
private Button backButton;
private TextView titleText;
private ListView listView;
private ChatMsgListViewAdapter adapter;
private List<ChatMsgEntity> list;
private String toID;//表示跟谁聊天的窗口
private MessageReceiver messageReceiver;
public ChatFragment(String toID)
{
this.toID = toID;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_chat, container, false);
editText = (EditText)view.findViewById(R.id.editText);
sendButton = (Button)view.findViewById(R.id.btn_send);
backButton = (Button)view.findViewById(R.id.btn_back);
titleText = (TextView)view.findViewById(R.id.title);
listView = (ListView)view.findViewById(R.id.listview);
sendButton.setOnClickListener(this);
backButton.setOnClickListener(this);
titleText.setText("跟" + toID + "的聊天");
list = new ArrayList<ChatMsgEntity>();
adapter = new ChatMsgListViewAdapter(getActivity(), list);
listView.setAdapter(adapter);
initMessageReceiver();
return view;
}
@Override
public void onClick(View v) {
if(v.getId() == R.id.btn_send) {
send();
}
else if(v.getId() == R.id.btn_back) {
FragmentManager fm = getFragmentManager();
FragmentTransaction tx = fm.beginTransaction();
tx.hide(this);
tx.show(fm.findFragmentByTag("ChooseChatFragment"));
tx.commit();
}
}
private void send()
{
String content = editText.getText().toString();
if(content.length() > 0) {
ChatMsgEntity entity = new ChatMsgEntity();
entity.setName(UserInfo.getID());
entity.setDate(getDate());
entity.setMessage(content);
entity.setMsgType(true);
list.add(entity);
adapter.notifyDataSetChanged();//通知ListView,数据已发生改变
listView.setSelection(listView.getCount() - 1);//发送一条消息时,ListView显示选择最后一项
editText.setText("");
try {
JSONObject root = new JSONObject();
root.put("content", content);
root.put("fromID", UserInfo.getID());
root.put("toID", toID);
SocketService.send(root.toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
}
private String getDate()
{
long time = System.currentTimeMillis();;
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
Date d = new Date(time);
return format.format(d);
}
private void initMessageReceiver()
{
messageReceiver = new MessageReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction("SocketClient");
getActivity().registerReceiver(messageReceiver,filter);
}
// @Override
// public void onDestroy() {
// super.onDestroy();
// unregisterReceiver(messageReceiver);
// stopService(new Intent(this,SocketService.class));
// }
public class MessageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String content = intent.getStringExtra("jsonString");
try
{
JSONObject root = new JSONObject(content);
//错误:root.getString("toID").equalsIgnoreCase(UserInfo.getID())
//因为对于每一个窗口来说,UserInfo.getID()都一样
//toID表示发信息给谁
boolean a = (root.getString("toID").equalsIgnoreCase("Group")) && (toID.equalsIgnoreCase("Group"));
boolean b = (!root.getString("toID").equalsIgnoreCase("Group")) && (toID.equalsIgnoreCase(root.getString("fromID")));
if(a || b)
{
if(content != null)
{
ChatMsgEntity entity = new ChatMsgEntity();
entity.setName(root.getString("fromID"));
entity.setDate(getDate());
entity.setMessage(root.getString("content"));
entity.setMsgType(false);
list.add(entity);
adapter.notifyDataSetChanged();//通知ListView,数据已发生改变
listView.setSelection(listView.getCount() - 1);//发送一条消息时,ListView显示选择最后一项
}
}
} catch (JSONException e) {
e.printStackTrace();
}
}
}
}
eclipse工程下载:
http://pan.baidu.com/s/1kT7yTH9
由于app的上交时间已经越来越近了,聊天的功能也勉强实现了,所以这个聊天app就这样了!后面本人肯定还会回来的!接下来打算添加地图功能,高德地图好像挺强大的!