上篇文章Android socket 编程 实现消息推送(一)讲到socket编程的消息推送原理,现在我们来看看基于Android客户机socket编程实现消息推送的过程。


根据消息推送的原理图,我们的实现过程主要分为Server端和Client端,Server端采用Java的编程,而Client端则用Android编程。

所以在这里也分别创建了两个工程SocketServerSocketClient

1.SocketServer工程


我们先来看一下SocketMessage.java类:

public class SocketMessage {public int to;//socketID,指发送给谁public int from;//socketID,指谁发送过来的public String msg;//消息内容public String time;//接收时间public SocketThread thread;//socketThread下面有介绍}

该类是一个消息类,用于表示消息是由谁发给谁的、消息内容是什么、接收时间是多少,只有几个属性,比较简单。

而MyServer.java类就相对比较多一些代码:

package com.jimstin.server;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.InputStreamReader;import java.io.OutputStreamWriter;import java.net.ServerSocket;import java.net.Socket;import java.text.SimpleDateFormat;import java.util.ArrayList;import java.util.Date;import org.json.JSONObject;import com.jimstin.msg.SocketMessage;public class MyServer {private boolean isStartServer;private ServerSocket mServer;/** * 消息队列,用于保存SocketServer接收来自于客户机(手机端)的消息 */private ArrayList mMsgList = new ArrayList();/** * 线程队列,用于接收消息。每个客户机拥有一个线程,每个线程只接收发送给自己的消息 */private ArrayList mThreadList = new ArrayList();/** * 开启SocketServer */private void startSocket() {try {isStartServer = true;int prot = 2000;//端口可以自己设置,但要和Client端的端口保持一致mServer = new ServerSocket(prot);//创建一个ServerSocketSystem.out.println("启动server,端口:"+prot);Socket socket = null;int socketID = 0;//Android(SocketClient)客户机的唯一标志,每个socketID表示一个Android客户机//开启发送消息线程startSendMessageThread();//用一个循环来检测是否有新的客户机加入while(isStartServer) {//accept()方法是一个阻塞的方法,调用该方法后,//该线程会一直阻塞,直到有新的客户机加入,代码才会继续往下走socket = mServer.accept();//有新的客户机加入后,则创建一个新的SocketThread线程对象SocketThread thread = new SocketThread(socket, socketID++);thread.start();//将该线程添加到线程队列mThreadList.add(thread);}    } catch (Exception e) {e.printStackTrace();}}/** * 开启推送消息线程,如果mMsgList中有SocketMessage,则把该消息推送到Android客户机 */public void startSendMessageThread() {new Thread(){@Overridepublic void run() {super.run();try {/*如果isStartServer=true,则说明SocketServer已启动,用一个循环来检测消息队列中是否有消息,如果有,则推送消息到相应的客户机*/while(isStartServer) {//判断消息队列中的长度是否大于0,大于0则说明消息队列不为空if(mMsgList.size() > 0) {//读取消息队列中的第一个消息SocketMessage from = mMsgList.get(0);for(SocketThread to : mThreadList) {if(to.socketID == from.to) {BufferedWriter writer = to.writer;JSONObject json = new JSONObject();json.put("from", from.from);json.put("msg", from.msg);json.put("time", from.time);//writer写进json中的字符串数据,末尾记得加换行符:"\n",否则在客户机端无法识别//因为BufferedReader.readLine()方法是根据换行符来读取一行的writer.write(json.toString()+"\n");//调用flush()方法,刷新流缓冲,把消息推送到手机端writer.flush();System.out.println("推送消息成功:"+from.msg+">> to socketID:"+from.to);break;}}//每推送一条消息之后,就要在消息队列中移除该消息mMsgList.remove(0);}Thread.sleep(200);}} catch (Exception e) {e.printStackTrace();}}}.start();}/** * 定义一个SocketThread类,用于接收消息 * */public class SocketThread extends Thread {public int socketID;public Socket socket;//Socket用于获取输入流、输出流public BufferedWriter writer;//BufferedWriter 用于推送消息public BufferedReader reader;//BufferedReader 用于接收消息public SocketThread(Socket socket, int count) {socketID = count;this.socket = socket;System.out.println("新增一台客户机,socketID:"+socketID);}@Overridepublic void run() {super.run();try {//初始化BufferedReaderreader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "utf-8"));//初始化BufferedWriterwriter = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "utf-8"));//如果isStartServer=true,则说明SocketServer已经启动,//现在需要用一个循环来不断接收来自客户机的消息,并作其他处理while(isStartServer) {//先判断reader是否已经准备好if(reader.ready()) {/*读取一行字符串,读取的内容来自于客户机reader.readLine()方法是一个阻塞方法,从调用这个方法开始,该线程会一直处于阻塞状态,直到接收到新的消息,代码才会往下走*/String data = reader.readLine();//讲data作为json对象的内容,创建一个json对象JSONObject json = new JSONObject(data);//创建一个SocketMessage对象,用于接收json中的数据SocketMessage msg = new SocketMessage();msg.to = json.getInt("to");msg.msg = json.getString("msg");msg.from = socketID;msg.time = getTime(System.currentTimeMillis());//接收到一条消息后,将该消息添加到消息队列mMsgListmMsgList.add(msg);System.out.println("收到一条消息:"+json.getString("msg")+" >>>> to socketID:"+json.getInt("to"));}//睡眠100ms,每100ms检测一次是否有接收到消息Thread.sleep(100);}} catch (Exception e) {e.printStackTrace();}              }}/** * 获取指定格式的时间字符串,通过毫秒转换日期 * @param millTime */private String getTime(long millTime) {Date d = new Date(millTime);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return sdf.format(d);}public static void main(String[] args) {MyServer server = new MyServer();server.startSocket();}}

2.SocketClient工程

该工程是一个Android的工程,只有一个MainActivity.java和activity_main.xml文件,

先看一下activity_main.xml布局文件:

                                

效果图:



MainActivity.java类:

package com.jimstin.socketclient;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.text.SimpleDateFormat;import java.util.Date;import org.json.JSONObject;import com.tencent.stat.MtaSDkException;import com.tencent.stat.StatConfig;import com.tencent.stat.StatService;import android.R.integer;import android.os.AsyncTask;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.util.Log;import android.view.View;import android.view.View.OnClickListener;import android.widget.EditText;import android.widget.TextView;import android.widget.Toast;import android.app.Activity;public class MainActivity extends Activity implements OnClickListener {private EditText mIPEdt, mPortEdt, mSocketIDEdt, mMessageEdt;private static TextView mConsoleTxt;private static StringBuffer mConsoleStr = new StringBuffer();private Socket mSocket;private boolean isStartRecieveMsg;private SocketHandler mHandler;protected BufferedReader mReader;//BufferedWriter 用于推送消息protected BufferedWriter mWriter;//BufferedReader 用于接收消息    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView();    }private void initView() {mIPEdt = (EditText) findViewById(R.id.ip_edt);mPortEdt = (EditText) findViewById(R.id.port_edt);mSocketIDEdt = (EditText) findViewById(R.id.socket_id_edt);mMessageEdt = (EditText) findViewById(R.id.msg_edt);mConsoleTxt = (TextView) findViewById(R.id.console_txt);findViewById(R.id.start_btn).setOnClickListener(this);findViewById(R.id.send_btn).setOnClickListener(this);findViewById(R.id.clear_btn).setOnClickListener(this);mHandler = new SocketHandler();}/** * 初始化socket */private void initSocket() {//新建一个线程,用于初始化socket和检测是否有接收到新的消息Thread thread = new Thread(new Runnable() {@Overridepublic void run() {String ip = mIPEdt.getText().toString();//IPint port = Integer.parseInt(mPortEdt.getText().toString());//Sockettry {isStartRecieveMsg = true;mSocket = new Socket(ip, port);mReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream(), "utf-8"));mWriter = new BufferedWriter(new OutputStreamWriter(mSocket.getOutputStream(), "utf-8"));while(isStartRecieveMsg) {if(mReader.ready()) {/*读取一行字符串,读取的内容来自于客户机reader.readLine()方法是一个阻塞方法,从调用这个方法开始,该线程会一直处于阻塞状态,直到接收到新的消息,代码才会往下走*/String data = mReader.readLine();//handler发送消息,在handleMessage()方法中接收        mHandler.obtainMessage(0, data).sendToTarget();}Thread.sleep(200);}mWriter.close();mReader.close();mSocket.close();} catch (Exception e) {e.printStackTrace();} }});thread.start();}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.send_btn:send();break;case R.id.clear_btn:mConsoleStr.delete(0, mConsoleStr.length());mConsoleTxt.setText(mConsoleStr.toString());break;case R.id.start_btn:if(!isStartRecieveMsg) {initSocket();}break;default:break;}}/** * 发送 */private void send() {new AsyncTask() {@Overrideprotected String doInBackground(String... params) {sendMsg();return null;}}.execute();}/** * 发送消息 */protected void sendMsg() {try {String socketID = mSocketIDEdt.getText().toString().trim();String msg = mMessageEdt.getText().toString().trim();JSONObject json = new JSONObject();json.put("to", socketID);json.put("msg", msg);mWriter.write(json.toString()+"\n");mWriter.flush();mConsoleStr.append("我:" +msg+"   "+getTime(System.currentTimeMillis())+"\n");mConsoleTxt.setText(mConsoleStr);} catch (Exception e) {e.printStackTrace();}}static class SocketHandler extends Handler {@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubsuper.handleMessage(msg);switch (msg.what) {case 0:try {//将handler中发送过来的消息创建json对象JSONObject json = new JSONObject((String)msg.obj);mConsoleStr.append(json.getString("from")+":" +json.getString("msg")+"   "+getTime(System.currentTimeMillis())+"\n");//将json数据显示在TextView中mConsoleTxt.setText(mConsoleStr);} catch (Exception e) {e.printStackTrace();}break;default:break;}}}@Overridepublic void onBackPressed() {super.onBackPressed();isStartRecieveMsg = false;}private static String getTime(long millTime) {Date d = new Date(millTime);SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");return sdf.format(d);}}

以上代码的注释都比较详细,就不再多说了。


资源下载地址:http://download.csdn.net/detail/zhangjm_123/8258019


注意:先把Android端的apk分别安装到两台手机上面,再运行SocketServer,点击SocketClient的Start,在SocketServer的控制台上面就可以看到有新的客户机增加了(确保IP地址和端口的正确前提下)。然后输入socketID(socketID表示Android客户机的一个账号,就像QQ号一样,从0开始,一个socketID表示一个客户机)和content,点击send就可以发送消息了。

效果图请见Android socket 编程 实现消息推送(一)

有问题的欢迎留言。

更多相关文章

  1. 浅析Android中的消息机制-解决:Only the original thread that cr
  2. Android异步消息机制之Handler
  3. Android开发之消息处理机制(一)——Handler
  4. [置顶] Android(安卓)跨进程通信Aidl的使用及注意事项
  5. Titanium 使用刘明星的Jpush module做android端的消息推送
  6. Android(安卓)中的 Looper 对象
  7. android中handler的使用
  8. android之HandlerThread类的实现
  9. Android消息处理机制1——Handler

随机推荐

  1. android 通过Intent action 跳转到系统页
  2. Android(安卓)Kotlin入门-属性和字段
  3. Android编译SDL2和demo展示(2.0.5)
  4. Android通用初始化Activity模板
  5. Android Studio 中报错 程序包org.apache
  6. Android把Bitmap保存为bmp图片
  7. [Android]使用permission保护数据
  8. Android的触摸(Touch)机制
  9. android 通过经纬度获取地址信息
  10. 应用布局说明