公司让我研究websocket,利用这个时间写了个仿微信的demo。
服务器端功能比较简单:转发给所有的client (类似与群聊)
android端功能:service后台接受消息;broadcastreceived监测网络连接断:断网时给出提示,重连时重新执行service中的websocket方法;接受到消息时如果activity没有在最前端发送notification通知用户。
要点:service将消息传递给activity使用的是activity中的全局handler
activity通service是否在屏幕最前时使用的binder对象,activity的onResum方法绑定service,onpause方法解绑service,来通知service是否需要保存消息并且发送notification。每次绑定后都查询一下service是否存着消息没有显示出来。
service中动态注册一个广播接收器来监听网络连接的状态
先看服务器端代码:
/**
* @author mace
* @email [email protected]
*/
public class ChatServer extends WebSocketServer {
public ChatServer(int port) throws UnknownHostException {
super(new InetSocketAddress(port));
}
public ChatServer(InetSocketAddress address) {
super(address);
}
@Override
public void onOpen(WebSocket conn, ClientHandshake handshake) {
sendToAll(conn.getRemoteSocketAddress().getAddress().getHostAddress()
+ " 进入房间 !");
System.out.println(conn.getRemoteSocketAddress().getAddress()
.getHostAddress()
+ " 进入房间 !");
}
@Override
public void onClose(WebSocket conn, int code, String reason, boolean remote) {
sendToAll(conn.getRemoteSocketAddress().getAddress().getHostAddress()
+ " 离开房间 !");
System.out.println(conn.getRemoteSocketAddress().getAddress()
.getHostAddress()
+ " 离开房间 !");
}
@Override
public void onMessage(WebSocket conn, String message) {
sendToAll("["
+ conn.getRemoteSocketAddress().getAddress().getHostAddress()
+ "]" + message);
System.out.println("["
+ conn.getRemoteSocketAddress().getAddress().getHostAddress()
+ "]" + message);
}
@Override
public void onError(WebSocket conn, Exception e) {
e.printStackTrace();
if (conn != null) {
System.out.println( String.valueOf(e));
conn.close();
}
}
// 发送给所有的聊天者
private void sendToAll(String text) {
Collection conns = connections();
synchronized (conns) {
for (WebSocket client : conns) {
client.send(text);
}
}
}
// 测试
public static void main(String[] args) throws InterruptedException,
IOException {
int port = 8887;
ChatServer server = new ChatServer(port);
server.start();
System.out.println("房间已开启,等待客户端接入,端口号: " + server.getPort());
BufferedReader webSocketIn = new BufferedReader(new InputStreamReader(
System.in));
while (true) {
String stringIn = webSocketIn.readLine();
server.sendToAll(stringIn);
}
}
}
下面详解android端的代码
service:service在Application中start,从程序一启动就开始运行
/** * @author mace * @email [email protected] */public class MyService extends Service { public static WebSocketClient client; private ConnectivityManager connectivityManager; private NetworkInfo info; private ArrayList
msgQueen = new ArrayList (); private boolean isSaveInSrevice = true; private MyBinder mBinder = new MyBinder(); private boolean iscon = true;//用于在broadcast中判断是否是需要重新连接的 private NotificationManager manager; private NotificationCompat.Builder notifyBuilder; private Vibrator vibrator; @Override public void onCreate() { super.onCreate(); manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); IntentFilter mFilter = new IntentFilter(); mFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); registerReceiver(mReceiver, mFilter); } @Override public int onStartCommand(Intent intent, int flags, int startId) { String address = "ws://192.168.31.184:8887"; try { WebSocketImpl.DEBUG = true; System.setProperty("java.net.preferIPv6Addresses", "false"); System.setProperty("java.net.preferIPv4Stack", "true"); client = new WebSocketClient(new URI(address), new Draft_17()) { @Override public void onOpen(final ServerHandshake serverHandshakeData) { iscon = true; if (!isSaveInSrevice) { Message msg = new Message(); msg.what = ChatClientActivity.ONOPEN; ChatClientActivity.uihandler.sendMessage(msg); } else { Log.e("Mace", "没有打开activity是的Onopen+1"); } } @Override public void onMessage(final String message) { //接受到消息就发送震动 openVibrator(); //判断activity是否是在最前端,如果是就将消息直接传给activity,如果不是就保存在service中 //并且发送 if (!isSaveInSrevice) { Message msg = new Message(); msg.what = ChatClientActivity.ONMESSEGE; Bundle b = new Bundle(); b.putString("msg", message); msg.setData(b); ChatClientActivity.uihandler.sendMessage(msg); } else { msgQueen.add(message); //发送通知 sendNotification(); Log.e("Mace", message); } } @Override public void onClose(final int code, final String reason, final boolean remote) { iscon = false; if (!isSaveInSrevice) { Message msg = new Message(); msg.what = ChatClientActivity.ONCLOSE; ChatClientActivity.uihandler.sendMessage(msg); } else { Log.e("Mace", "没有activity时的" + " onClose"); } } @Override public void onError(final Exception e) { iscon = false; client.close(); Log.e("Mace", String.valueOf(e)); } }; } catch (URISyntaxException e) { e.printStackTrace(); } client.connect(); return super.onStartCommand(intent, flags, startId); } private void openVibrator() { vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE); long[] pattern = {100, 400, 100, 400}; // 停止 开启 停止 开启 vibrator.vibrate(pattern, -1); //重复两次上面的pattern 如果只想震动一次,index设为-1 } @Override public boolean onUnbind(Intent intent) { mBinder.setIsSaveInSer(); return super.onUnbind(intent); } @Override public void onDestroy() { super.onDestroy(); Log.e("Mace", "+++++++++++Srevice Destroy++++++++++"); } @Override public IBinder onBind(Intent intent) { return mBinder; } /** * 接受到网络重连的广播时重新执行startcommoned方法让client重连 */ private BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) { connectivityManager = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); info = connectivityManager.getActiveNetworkInfo(); if (info != null && info.isAvailable() && iscon == false) { //断网的时候client会被close (调用了onclose方法) Intent serviceIntent = new Intent(context, MyService.class); context.startService(serviceIntent); // client.connect(); //不能用client直接connect,具体原因期待有人指出,可能是classnotfind? Log.e("Mace", "StartService"); } } } }; /** * 实现1.act到屏幕最前方时将保存在service中的msg向UI发送 * 2.act不再屏幕最前方时通知service保存消息 */ class MyBinder extends Binder { public void sendToUI() { Message msg = new Message(); msg.what = ChatClientActivity.ONMESSEGELIST; Bundle b = new Bundle(); ArrayList l = new ArrayList(); l.addAll(msgQueen); b.putParcelableArrayList("list", l); msg.setData(b); ChatClientActivity.uihandler.sendMessage(msg); isSaveInSrevice = false; msgQueen.clear(); manager.cancel(121); } public void setIsSaveInSer() { isSaveInSrevice = true; } } //发送notification private void sendNotification() { //点击的意图ACTION是跳转到Intent Log.e("Mace", "sendNotification"); Intent resultIntent = new Intent(this, ChatClientActivity.class); resultIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT); Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.a); notifyBuilder = new NotificationCompat.Builder(this) /*设置large icon*/ .setLargeIcon(bitmap) /*设置small icon*/ .setSmallIcon(R.mipmap.a) /*设置title*/ .setContentTitle("您收到了" + String.valueOf(msgQueen.size()) + "条消息") /*设置详细文本*/ .setContentText(msgQueen.get(msgQueen.size() - 1)) /*设置发出通知的时间为发出通知时的系统时间*/ .setWhen(System.currentTimeMillis()) /*设置发出通知时在status bar进行提醒*/ .setTicker("收到新消息") /*setOngoing(boolean)设为true,notification将无法通过左右滑动的方式清除 * 可用于添加常驻通知,必须调用cancle方法来清除 */ .setOngoing(false) /*设置点击后通知消失*/ .setAutoCancel(true) /*设置通知数量的显示类似于QQ那种,用于同志的合并*/ // .setNumber(3) /*点击跳转到MainActivity*/ .setContentIntent(pendingIntent); manager.notify(121, notifyBuilder.build()); } }
activity中代码:
/** * @author mace * @email [email protected] */ public class ChatClientActivity extends AppCompatActivity implements OnClickListener { public static int ONMESSEGE = 1; public static int ONMESSEGELIST = 2; public static int ONCLOSE = -1; public static int ONOPEN = 0; private ScrollView svChat; private EditText etDetails; private EditText etName; private EditText etMessage; private Button btnSend; private TextView closetv; private long exitTime = 0; private MyService.MyBinder myBinder; private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { myBinder = (MyService.MyBinder) service; myBinder.sendToUI(); Log.e("Mace", "onServiceConnected"); } @Override public void onServiceDisconnected(ComponentName name) { } }; public static MyHandler uihandler; //activity回到最前端时连接service进行通信 @Override protected void onResume() { super.onResume(); Intent bindIntent = new Intent(this, MyService.class); bindService(bindIntent, connection, BIND_AUTO_CREATE); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); uihandler = new MyHandler(); setContentView(R.layout.activity_chat_client); svChat = (ScrollView) findViewById(R.id.svChat); etDetails = (EditText) findViewById(R.id.etDetails); etName = (EditText) findViewById(R.id.etName); etMessage = (EditText) findViewById(R.id.etMessage); btnSend = (Button) findViewById(R.id.btnSend); btnSend.setOnClickListener(this); closetv = (TextView) findViewById(R.id.onClose); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.btnSend: try { if (MyService.client != null) { Log.e("Mace", "btnSend" + String.valueOf(Thread.currentThread().getId())); MyService.client.send(etName.getText().toString().trim() + "说:" + etMessage.getText().toString().trim()); svChat.post(new Runnable() { @Override public void run() { svChat.fullScroll(View.FOCUS_DOWN); etMessage.setText(""); etMessage.requestFocus(); } }); } } catch (Exception e) { e.printStackTrace(); } break; } } @Override protected void onPause() { super.onPause(); unbindService(connection); } public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { exit(); return false; } return super.onKeyDown(keyCode, event); } public void exit() { if ((System.currentTimeMillis() - exitTime) > 2000) { Toast.makeText(getApplicationContext(), "再按一次退出程序", Toast.LENGTH_SHORT).show(); exitTime = System.currentTimeMillis(); } else { finish(); } } //接受service传来的内容,并相应的修改UI class MyHandler extends Handler { @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (msg.what == ONMESSEGE) { addChatMessage(String.valueOf(msg.getData().get("msg"))); } else if (msg.what == ONCLOSE) { setConClose(); } else if (msg.what == ONOPEN) { setConOpen(); } else if (msg.what == ONMESSEGELIST) { ArrayList msgList = msg.getData().getParcelableArrayList("list"); String s = ""; for (int i = 0; i < msgList.size(); i++) { s = (String) msgList.get(i); addChatMessage(s); } } } } //添加message进入对话框 public void addChatMessage(String msg) { etDetails.append(msg + "\n"); } //连接关闭时的界面 public void setConClose() { btnSend.setEnabled(false); closetv.setVisibility(View.VISIBLE); } //连接可用时的界面 public void setConOpen() { btnSend.setEnabled(true); closetv.setVisibility(View.GONE); } }
manifest文件欢迎各路大神指出不足,大家共同提高\(^o^)/
demo实例:
http://download.csdn.net/detail/mace_android/9558242