这一次来说说第二种方案。

首先来分析一下:
1. 我们要做的是让客户端支持emoji表情,而且有多个不同的客户端,比如android,ios,还有网页。
2. 那么从服务端来解决我觉得不是一个很好的方案,虽然可以对不同客户端分别处理,但是要明白一点,不管服务器的存储方式,客户端还是需要显示的。
3. 第二种方案是,用图片代替这个表情字符,这样子会比较好看,用户体验好一些。所以客户端是必然需要做处理的,而转换成一些标记也只是需要一个转换函数和一个逆转函数。而且我认为客户端分治方法要比服务器全盘处理好一些。
所以这一种方案的结果是,所有内容都放在客户端处理。服务器不做任何调整,这样修改的东西少一些。主要是服务器修改起来会更加费事。

解决方案:
由于有githug上发现有人已经实现了,所以我也就拿来主义了,不过把源码看了一遍,写得很好,肯定比我写的好,不过实现的想法是一样的。
githug Emojicon : https://github.com/rockerhieu/emojicon 也可以在这里下载:http://pan.baidu.com/s/1sjlsRGL
里面有个demo可以直接使用。

简单来说原理:
一个emoji表情对应一个utf8mb4字符。
一个emoji表情对应一张图片。
那么我们要做的就是,当这个字符被输入的时候,用一张图片代替。在android中Spannable可以显示很多东西,显示图片是没有问题的。
这是个体力活。还好有人已经做完了。在此感谢!

再说说数据库存储方面。
刚才已经说了,服务端不做任何修改,那么就需要在客户端把utf8mb4的编码转换一下,转换成普通字符,至少是普通的utf8,比如emoji字符 0x1f604 可以转换成[emoji]0x1f604[/emoji],这样就可以存储了,显示的时候再转换回去。

下面是我修改的部分,即,存入数据库前的转换和显示的时候的逆转换

private static final String emojiSeparatorStart = String.valueOf(Character.toChars(1));
private static final String emojiSeparatorEnd = "/" + String.valueOf(Character.toChars(1));

/**
 * Convert emoji characters of the given Spannable to the according
 * emojiCode for save for database.
 * 
 * @author wuwang(xzpv444@gmail.com)
 * 
 * @param context
 * @param text
 */
public static String convertEmojis(Context context, Spannable text) {
	String result = "";

	int index = 0;
	int length = -1;

	int textLength = text.length();
	int textLengthToProcessMax = textLength - index;
	int textLengthToProcess = length < 0 || length >= textLengthToProcessMax ? textLength : (length + index);

	// remove spans throughout all text
	EmojiconSpan[] oldSpans = text.getSpans(0, textLength, EmojiconSpan.class);
	for (int i = 0; i < oldSpans.length; i++) {
		text.removeSpan(oldSpans[i]);
	}

	int skip;
	for (int i = index; i < textLengthToProcess; i += skip) {
		skip = 0;
		int icon = 0;
		char c = text.charAt(i);
		if (isSoftBankEmoji(c)) {
			icon = getSoftbankEmojiResource(c);
			skip = icon == 0 ? 0 : 1;
		}
		int unicode = 0;
		if (icon == 0) {
			unicode = Character.codePointAt(text, i);
			skip = Character.charCount(unicode);

			if (unicode > 0xff) {
				icon = getEmojiResource(context, unicode);
			}

			if (icon == 0 && i + skip < textLengthToProcess) {
				int followUnicode = Character.codePointAt(text, i + skip);
				if (followUnicode == 0x20e3) {
					int followSkip = Character.charCount(followUnicode);
					switch (unicode) {
					case 0x0031:
						icon = R.drawable.emoji_0031;
						break;
					case 0x0032:
						icon = R.drawable.emoji_0032;
						break;
					case 0x0033:
						icon = R.drawable.emoji_0033;
						break;
					case 0x0034:
						icon = R.drawable.emoji_0034;
						break;
					case 0x0035:
						icon = R.drawable.emoji_0035;
						break;
					case 0x0036:
						icon = R.drawable.emoji_0036;
						break;
					case 0x0037:
						icon = R.drawable.emoji_0037;
						break;
					case 0x0038:
						icon = R.drawable.emoji_0038;
						break;
					case 0x0039:
						icon = R.drawable.emoji_0039;
						break;
					case 0x0030:
						icon = R.drawable.emoji_0030;
						break;
					case 0x0023:
						icon = R.drawable.emoji_0023;
						break;
					default:
						followSkip = 0;
						break;
					}
					skip += followSkip;
				} else {
					int followSkip = Character.charCount(followUnicode);
					switch (unicode) {
					case 0x1f1ef:
						icon = (followUnicode == 0x1f1f5) ? R.drawable.emoji_1f1ef_1f1f5 : 0;
						break;
					case 0x1f1fa:
						icon = (followUnicode == 0x1f1f8) ? R.drawable.emoji_1f1fa_1f1f8 : 0;
						break;
					case 0x1f1eb:
						icon = (followUnicode == 0x1f1f7) ? R.drawable.emoji_1f1eb_1f1f7 : 0;
						break;
					case 0x1f1e9:
						icon = (followUnicode == 0x1f1ea) ? R.drawable.emoji_1f1e9_1f1ea : 0;
						break;
					case 0x1f1ee:
						icon = (followUnicode == 0x1f1f9) ? R.drawable.emoji_1f1ee_1f1f9 : 0;
						break;
					case 0x1f1ec:
						icon = (followUnicode == 0x1f1e7) ? R.drawable.emoji_1f1ec_1f1e7 : 0;
						break;
					case 0x1f1ea:
						icon = (followUnicode == 0x1f1f8) ? R.drawable.emoji_1f1ea_1f1f8 : 0;
						break;
					case 0x1f1f7:
						icon = (followUnicode == 0x1f1fa) ? R.drawable.emoji_1f1f7_1f1fa : 0;
						break;
					case 0x1f1e8:
						icon = (followUnicode == 0x1f1f3) ? R.drawable.emoji_1f1e8_1f1f3 : 0;
						break;
					case 0x1f1f0:
						icon = (followUnicode == 0x1f1f7) ? R.drawable.emoji_1f1f0_1f1f7 : 0;
						break;
					default:
						followSkip = 0;
						break;
					}
					skip += followSkip;
				}
			}
		}

		if (icon > 0) {
			result += emojiSeparatorStart + Integer.toHexString(unicode) + emojiSeparatorEnd;
		} else {
			result += c;
		}
	}
	return result;
}

/**
 * restore emojiCode to emoji character
 * 
 * @author wuwang(xzpv444@gmail.com)
 * 
 * @param context
 * @param text
 * @return
 */
public static CharSequence restoreEmojis(Context context, CharSequence text) {
	String regexp = emojiSeparatorStart + ".*?" + emojiSeparatorStart;
	Pattern p = Pattern.compile(regexp);
	Matcher m = p.matcher(text);
	while (true) {
		if (m.find()) {
			String x = String.valueOf(Character.toChars(Integer.valueOf(m.group(0).replace(emojiSeparatorEnd, "").replace(emojiSeparatorStart, ""), 16)));
			text = text.toString().replace(m.group(0), x);
		} else {
			break;
		}
	}
	return text;
}