前不久在项目中实现了Im功能,之前im功能使用使用第三方,这次用到了WebSocket。找到
https://github.com/TooTallNate/Java-WebSocket
compile "org.java-websocket:Java-WebSocket:1.3.7"
基于库实现过程遇到不少问题:
坑1:WebSocketClient objects are not reuseable
new WebSocketClient对象只能连接一次(调用connect时),要重新建立得再new 一次。
网络好的情况下,websoket会经常断开,重新连接,但要连接过程中又要考虑正在聊天的内容,不能让用户正在聊天感觉断网了,连接成功后把聊天的内容发送出去,所以要做一个聊天数据缓冲区。
坑2:发送的信息没有反馈是否送达?
首先要理解WebSocket是”心跳“机制,会”假“连接中状态,一但发送信息就会走onClose,信息则未发送到服务器;
WebSocketClient中有三个抽象方法:
public abstract void onOpen( ServerHandshake handshakedata );
public abstract void onMessage( String message );
public abstract void onClose( int code, String reason, boolean remote );
public abstract void onError( Exception ex );
public void onMessage( ByteBuffer bytes ) {
//To overwrite
}
一但发送信息时WebSocketClient.send( String text ),会在约200毫秒内回调onClose方法,则断定刚发送的信息不成功。我是设定发送出去后1秒内未走onclose则断定已发送出去。未发送成功则放在数据缓冲区,等连接成功后再把缓冲的数据再发送一次,当然缓冲区的每个数据都有个加入时间,超过一定时间后又未发送出去(断网)则反馈给UI知并更新db。
坑3:表情图
表情是使用[mj00]代表, 点击表情显示到输入框上:
gvMoji.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView> parent, View view, int position, long id) {
String emji = mMojiHAdapter.getItem(position);
int reId = getResources().getIdentifier(emji, "drawable", getPackageName());
emji = "[" + emji + "]";
SpannableString spannableString = new SpannableString(emji);
ImageSpan imageSpan = new ImageSpan(ImHActivity.this, reId);
spannableString.setSpan(imageSpan, 0, emji.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
int index = edtWrit.getSelectionStart();
Editable editable = edtWrit.getText();
editable.insert(index, spannableString);
}
});
聊天列表内容的转换(正则):
public static void setText(TextView tv, String content) {
tv.setText("");
if (null != content) {
Pattern pattern = Pattern.compile("\\[[^\\[\\]]*\\]");
Matcher matcher = pattern.matcher(content);
int start = -1;
int end = -1;
while (matcher.find()) {
start = matcher.start();
if (end != -1 && start > end) {
tv.append(content.substring(end, start));
}
if (start != 0 && end == -1) {
tv.append(content.substring(0, start));
}
end = matcher.end();
String emji = content.substring(start + 1, end - 1);
int reId = tv.getContext().getResources().getIdentifier(emji, "drawable",
tv.getContext().getPackageName());
if (reId == 0) { // 配对了,但不是表情
tv.append(content.substring(start, end));
} else {
emji = content.substring(start, end);
SpannableString spannableString = new SpannableString(emji);
ImageSpan imageSpan = new ImageSpan(tv.getContext(), reId);
spannableString.setSpan(imageSpan, 0, emji.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
tv.append(spannableString);
}
}
if (end == -1) {
tv.setText(content);
} else if (end != content.length()) {
tv.append(content.substring(end, content.length()));
}
}
}
其他小坑:
聊天列表
聊天有6种类型(ViewTypeCount),文字、语音、图片(发送与接收各三种)
坑在数据库没设计好,重构了二次表,因为需求说自己可以跟自己聊天。
聊天图片要处理在微信一样有个角:
代码实现合成 右图是ninePicId:
private Bitmap getPic(Bitmap bitmap_in, int ninePicId) {
Bitmap bitmap_bg = BitmapFactory.decodeResource(context.getResources(), ninePicId);
Bitmap roundConcerImage = Bitmap.createBitmap(bitmap_in.getWidth(), bitmap_in.getHeight(), Config.ARGB_8888);
Canvas canvas = new Canvas(roundConcerImage);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
Rect dst = new Rect(0, 0, bitmap_in.getWidth(), bitmap_in.getHeight());
Rect src = new Rect(0, 0, bitmap_in.getWidth(), bitmap_in.getHeight());
paint.setAntiAlias(true);
NinePatch patch = new NinePatch(bitmap_bg, bitmap_bg.getNinePatchChunk(), null);
patch.draw(canvas, dst);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas.drawBitmap(bitmap_in, src, dst, paint);
return roundConcerImage;
}