基于xmpp openfire smack开发之Android客户端开发[3]

时间:2023-03-09 03:05:05
基于xmpp openfire smack开发之Android客户端开发[3]

在上两篇文章中,我们依次介绍openfire部署以及smack常用API的使用,这一节中我们着力介绍如何基于asmack开发一个Android的客户端,本篇的重点在实践,讲解和原理环节,大家可以参考前两篇的文章

基于xmpp openfire smack开发之openfire介绍和部署[1]

基于xmpp openfire smack开发之smack类库介绍和使用[2]

1.源码结构介绍

基于xmpp openfire smack开发之Android客户端开发[3]

activity包下存放一些android页面交互相关的控制程序,还有一个些公共帮助类

db包为sqlite的工具类封装,这里做了一些自定义的改造,稍微仿Spring的JdbcTemplate结构,使用起来更加方便一点

manager包留下主要是一些管理组件,包括联系人管理,消息管理,提醒管理,离线消息管理,用户管理,xmpp连接管理

model包中都是一些对象模型,传输介质

service中存放一些android后台的核心服务,主要包括聊天服务,联系人服务,系统消息服务,重连接服务

task包中存放一些耗时的异步操作

util中存放一些常用的工具类

view中一些和android的UI相关的显示控件

基于xmpp openfire smack开发之Android客户端开发[3]

anim中存放一些动画元素的配置

layout是布局页面

menu是地步菜单布局页面

values中存放一些字符,颜色,样式,参数的配置信息

其中strings.xml中,保存的缺省配置为gtalk的服务器信息,大家如果有谷歌gtalk的账号可以直接登录,否则需要更改这里的配置才可以使用其他的xmpp服务器

  1. <!-- 缺省的服务器配置 -->
  2. <integer name="xmpp_port">5222</integer>
  3. <string name="xmpp_host">talk.google.com</string>
  4. <string name="xmpp_service_name">gmail.com</string>
  5. <bool name="is_remember">true</bool>
  6. <bool name="is_autologin">false</bool>
  7. <bool name="is_novisible">false</bool>

AndroidManifest.xml为android功能清单的配置文件,我们这里开放的权限并不多

  1. <!-- 访问Internet -->
  2. <uses-permission android:name="android.permission.INTERNET" />
  3. <!--- 访问网络状态 -->
  4. <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  5. <!-- 往SDCard写入数据权限 -->
  6. <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
  7. <span style="WHITE-SPACE: pre">  </span><!-- 在SDCard中创建与删除文件权限 -->
  8. <span style="WHITE-SPACE: pre">  </span><uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
  9. <span style="WHITE-SPACE: pre">  </span><!-- 往SDCard写入数据权限 -->
  10. <span style="WHITE-SPACE: pre">  </span><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

2.核心类介绍

1.ActivitySupport类
  1. package ****.shimiso.eim.activity;
  2. import android.app.Activity;
  3. import android.app.AlertDialog;
  4. import android.app.Notification;
  5. import android.app.NotificationManager;
  6. import android.app.PendingIntent;
  7. import android.app.ProgressDialog;
  8. import android.content.Context;
  9. import android.content.DialogInterface;
  10. import android.content.Intent;
  11. import android.content.SharedPreferences;
  12. import android.location.LocationManager;
  13. import android.net.ConnectivityManager;
  14. import android.net.NetworkInfo;
  15. import android.os.Bundle;
  16. import android.os.Environment;
  17. import android.provider.Settings;
  18. import android.view.inputmethod.InputMethodManager;
  19. import android.widget.Toast;
  20. import ****.shimiso.eim.R;
  21. import ****.shimiso.eim.comm.Constant;
  22. import ****.shimiso.eim.model.LoginConfig;
  23. import ****.shimiso.eim.service.IMChatService;
  24. import ****.shimiso.eim.service.IMContactService;
  25. import ****.shimiso.eim.service.IMSystemMsgService;
  26. import ****.shimiso.eim.service.ReConnectService;
  27. /**
  28. * Actity 工具支持类
  29. *
  30. * @author shimiso
  31. *
  32. */
  33. public class ActivitySupport extends Activity implements IActivitySupport {
  34. protected Context context = null;
  35. protected SharedPreferences preferences;
  36. protected EimApplication eimApplication;
  37. protected ProgressDialog pg = null;
  38. protected NotificationManager notificationManager;
  39. @Override
  40. protected void onCreate(Bundle savedInstanceState) {
  41. super.onCreate(savedInstanceState);
  42. context = this;
  43. preferences = getSharedPreferences(Constant.LOGIN_SET, 0);
  44. notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
  45. pg = new ProgressDialog(context);
  46. eimApplication = (EimApplication) getApplication();
  47. eimApplication.addActivity(this);
  48. }
  49. @Override
  50. protected void onStart() {
  51. super.onStart();
  52. }
  53. @Override
  54. protected void onResume() {
  55. super.onResume();
  56. }
  57. @Override
  58. protected void onPause() {
  59. super.onPause();
  60. }
  61. @Override
  62. protected void onStop() {
  63. super.onStop();
  64. }
  65. @Override
  66. public void onDestroy() {
  67. super.onDestroy();
  68. }
  69. @Override
  70. public ProgressDialog getProgressDialog() {
  71. return pg;
  72. }
  73. @Override
  74. public void startService() {
  75. // 好友联系人服务
  76. Intent server = new Intent(context, IMContactService.class);
  77. context.startService(server);
  78. // 聊天服务
  79. Intent chatServer = new Intent(context, IMChatService.class);
  80. context.startService(chatServer);
  81. // 自动恢复连接服务
  82. Intent reConnectService = new Intent(context, ReConnectService.class);
  83. context.startService(reConnectService);
  84. // 系统消息连接服务
  85. Intent imSystemMsgService = new Intent(context,
  86. IMSystemMsgService.class);
  87. context.startService(imSystemMsgService);
  88. }
  89. /**
  90. *
  91. * 销毁服务.
  92. *
  93. * @author shimiso
  94. * @update 2012-5-16 下午12:16:08
  95. */
  96. @Override
  97. public void stopService() {
  98. // 好友联系人服务
  99. Intent server = new Intent(context, IMContactService.class);
  100. context.stopService(server);
  101. // 聊天服务
  102. Intent chatServer = new Intent(context, IMChatService.class);
  103. context.stopService(chatServer);
  104. // 自动恢复连接服务
  105. Intent reConnectService = new Intent(context, ReConnectService.class);
  106. context.stopService(reConnectService);
  107. // 系统消息连接服务
  108. Intent imSystemMsgService = new Intent(context,
  109. IMSystemMsgService.class);
  110. context.stopService(imSystemMsgService);
  111. }
  112. @Override
  113. public void isExit() {
  114. new AlertDialog.Builder(context).setTitle("确定退出吗?")
  115. .setNeutralButton("确定", new DialogInterface.OnClickListener() {
  116. @Override
  117. public void onClick(DialogInterface dialog, int which) {
  118. stopService();
  119. eimApplication.exit();
  120. }
  121. })
  122. .setNegativeButton("取消", new DialogInterface.OnClickListener() {
  123. @Override
  124. public void onClick(DialogInterface dialog, int which) {
  125. dialog.cancel();
  126. }
  127. }).show();
  128. }
  129. @Override
  130. public boolean hasInternetConnected() {
  131. ConnectivityManager manager = (ConnectivityManager) context
  132. .getSystemService(context.CONNECTIVITY_SERVICE);
  133. if (manager != null) {
  134. NetworkInfo network = manager.getActiveNetworkInfo();
  135. if (network != null && network.isConnectedOrConnecting()) {
  136. return true;
  137. }
  138. }
  139. return false;
  140. }
  141. @Override
  142. public boolean validateInternet() {
  143. ConnectivityManager manager = (ConnectivityManager) context
  144. .getSystemService(context.CONNECTIVITY_SERVICE);
  145. if (manager == null) {
  146. openWirelessSet();
  147. return false;
  148. } else {
  149. NetworkInfo[] info = manager.getAllNetworkInfo();
  150. if (info != null) {
  151. for (int i = 0; i < info.length; i++) {
  152. if (info[i].getState() == NetworkInfo.State.CONNECTED) {
  153. return true;
  154. }
  155. }
  156. }
  157. }
  158. openWirelessSet();
  159. return false;
  160. }
  161. @Override
  162. public boolean hasLocationGPS() {
  163. LocationManager manager = (LocationManager) context
  164. .getSystemService(context.LOCATION_SERVICE);
  165. if (manager
  166. .isProviderEnabled(android.location.LocationManager.GPS_PROVIDER)) {
  167. return true;
  168. } else {
  169. return false;
  170. }
  171. }
  172. @Override
  173. public boolean hasLocationNetWork() {
  174. LocationManager manager = (LocationManager) context
  175. .getSystemService(context.LOCATION_SERVICE);
  176. if (manager
  177. .isProviderEnabled(android.location.LocationManager.NETWORK_PROVIDER)) {
  178. return true;
  179. } else {
  180. return false;
  181. }
  182. }
  183. @Override
  184. public void checkMemoryCard() {
  185. if (!Environment.MEDIA_MOUNTED.equals(Environment
  186. .getExternalStorageState())) {
  187. new AlertDialog.Builder(context)
  188. .setTitle(R.string.prompt)
  189. .setMessage("请检查内存卡")
  190. .setPositiveButton(R.string.menu_settings,
  191. new DialogInterface.OnClickListener() {
  192. @Override
  193. public void onClick(DialogInterface dialog,
  194. int which) {
  195. dialog.cancel();
  196. Intent intent = new Intent(
  197. Settings.ACTION_SETTINGS);
  198. context.startActivity(intent);
  199. }
  200. })
  201. .setNegativeButton("退出",
  202. new DialogInterface.OnClickListener() {
  203. @Override
  204. public void onClick(DialogInterface dialog,
  205. int which) {
  206. dialog.cancel();
  207. eimApplication.exit();
  208. }
  209. }).create().show();
  210. }
  211. }
  212. public void openWirelessSet() {
  213. AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context);
  214. dialogBuilder
  215. .setTitle(R.string.prompt)
  216. .setMessage(context.getString(R.string.check_connection))
  217. .setPositiveButton(R.string.menu_settings,
  218. new DialogInterface.OnClickListener() {
  219. @Override
  220. public void onClick(DialogInterface dialog,
  221. int which) {
  222. dialog.cancel();
  223. Intent intent = new Intent(
  224. Settings.ACTION_WIRELESS_SETTINGS);
  225. context.startActivity(intent);
  226. }
  227. })
  228. .setNegativeButton(R.string.close,
  229. new DialogInterface.OnClickListener() {
  230. @Override
  231. public void onClick(DialogInterface dialog,
  232. int whichButton) {
  233. dialog.cancel();
  234. }
  235. });
  236. dialogBuilder.show();
  237. }
  238. /**
  239. *
  240. * 显示toast
  241. *
  242. * @param text
  243. * @param longint
  244. * @author shimiso
  245. * @update 2012-6-28 下午3:46:18
  246. */
  247. public void showToast(String text, int longint) {
  248. Toast.makeText(context, text, longint).show();
  249. }
  250. @Override
  251. public void showToast(String text) {
  252. Toast.makeText(context, text, Toast.LENGTH_SHORT).show();
  253. }
  254. /**
  255. *
  256. * 关闭键盘事件
  257. *
  258. * @author shimiso
  259. * @update 2012-7-4 下午2:34:34
  260. */
  261. public void closeInput() {
  262. InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
  263. if (inputMethodManager != null && this.getCurrentFocus() != null) {
  264. inputMethodManager.hideSoftInputFromWindow(this.getCurrentFocus()
  265. .getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS);
  266. }
  267. }
  268. /**
  269. *
  270. * 发出Notification的method.
  271. *
  272. * @param iconId
  273. *            图标
  274. * @param contentTitle
  275. *            标题
  276. * @param contentText
  277. *            你内容
  278. * @param activity
  279. * @author shimiso
  280. * @update 2012-5-14 下午12:01:55
  281. */
  282. public void setNotiType(int iconId, String contentTitle,
  283. String contentText, Class activity, String from) {
  284. /*
  285. * 创建新的Intent,作为点击Notification留言条时, 会运行的Activity
  286. */
  287. Intent notifyIntent = new Intent(this, activity);
  288. notifyIntent.putExtra("to", from);
  289. // notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
  290. /* 创建PendingIntent作为设置递延运行的Activity */
  291. PendingIntent appIntent = PendingIntent.getActivity(this, 0,
  292. notifyIntent, 0);
  293. /* 创建Notication,并设置相关参数 */
  294. Notification myNoti = new Notification();
  295. // 点击自动消失
  296. myNoti.flags = Notification.FLAG_AUTO_CANCEL;
  297. /* 设置statusbar显示的icon */
  298. myNoti.icon = iconId;
  299. /* 设置statusbar显示的文字信息 */
  300. myNoti.tickerText = contentTitle;
  301. /* 设置notification发生时同时发出默认声音 */
  302. myNoti.defaults = Notification.DEFAULT_SOUND;
  303. /* 设置Notification留言条的参数 */
  304. myNoti.setLatestEventInfo(this, contentTitle, contentText, appIntent);
  305. /* 送出Notification */
  306. notificationManager.notify(0, myNoti);
  307. }
  308. @Override
  309. public Context getContext() {
  310. return context;
  311. }
  312. @Override
  313. public SharedPreferences getLoginUserSharedPre() {
  314. return preferences;
  315. }
  316. @Override
  317. public void saveLoginConfig(LoginConfig loginConfig) {
  318. preferences.edit()
  319. .putString(Constant.XMPP_HOST, loginConfig.getXmppHost())
  320. .commit();
  321. preferences.edit()
  322. .putInt(Constant.XMPP_PORT, loginConfig.getXmppPort()).commit();
  323. preferences
  324. .edit()
  325. .putString(Constant.XMPP_SEIVICE_NAME,
  326. loginConfig.getXmppServiceName()).commit();
  327. preferences.edit()
  328. .putString(Constant.USERNAME, loginConfig.getUsername())
  329. .commit();
  330. preferences.edit()
  331. .putString(Constant.PASSWORD, loginConfig.getPassword())
  332. .commit();
  333. preferences.edit()
  334. .putBoolean(Constant.IS_AUTOLOGIN, loginConfig.isAutoLogin())
  335. .commit();
  336. preferences.edit()
  337. .putBoolean(Constant.IS_NOVISIBLE, loginConfig.isNovisible())
  338. .commit();
  339. preferences.edit()
  340. .putBoolean(Constant.IS_REMEMBER, loginConfig.isRemember())
  341. .commit();
  342. preferences.edit()
  343. .putBoolean(Constant.IS_ONLINE, loginConfig.isOnline())
  344. .commit();
  345. preferences.edit()
  346. .putBoolean(Constant.IS_FIRSTSTART, loginConfig.isFirstStart())
  347. .commit();
  348. }
  349. @Override
  350. public LoginConfig getLoginConfig() {
  351. LoginConfig loginConfig = new LoginConfig();
  352. String a = preferences.getString(Constant.XMPP_HOST, null);
  353. String b = getResources().getString(R.string.xmpp_host);
  354. loginConfig.setXmppHost(preferences.getString(Constant.XMPP_HOST,
  355. getResources().getString(R.string.xmpp_host)));
  356. loginConfig.setXmppPort(preferences.getInt(Constant.XMPP_PORT,
  357. getResources().getInteger(R.integer.xmpp_port)));
  358. loginConfig.setUsername(preferences.getString(Constant.USERNAME, null));
  359. loginConfig.setPassword(preferences.getString(Constant.PASSWORD, null));
  360. loginConfig.setXmppServiceName(preferences.getString(
  361. Constant.XMPP_SEIVICE_NAME,
  362. getResources().getString(R.string.xmpp_service_name)));
  363. loginConfig.setAutoLogin(preferences.getBoolean(Constant.IS_AUTOLOGIN,
  364. getResources().getBoolean(R.bool.is_autologin)));
  365. loginConfig.setNovisible(preferences.getBoolean(Constant.IS_NOVISIBLE,
  366. getResources().getBoolean(R.bool.is_novisible)));
  367. loginConfig.setRemember(preferences.getBoolean(Constant.IS_REMEMBER,
  368. getResources().getBoolean(R.bool.is_remember)));
  369. loginConfig.setFirstStart(preferences.getBoolean(
  370. Constant.IS_FIRSTSTART, true));
  371. return loginConfig;
  372. }
  373. @Override
  374. public boolean getUserOnlineState() {
  375. // preferences = getSharedPreferences(Constant.LOGIN_SET,0);
  376. return preferences.getBoolean(Constant.IS_ONLINE, true);
  377. }
  378. @Override
  379. public void setUserOnlineState(boolean isOnline) {
  380. // preferences = getSharedPreferences(Constant.LOGIN_SET,0);
  381. preferences.edit().putBoolean(Constant.IS_ONLINE, isOnline).commit();
  382. }
  383. @Override
  384. public EimApplication getEimApplication() {
  385. return eimApplication;
  386. }
  387. }

大家写android程序会发现,不同的activity之间经常需要调用一些公共的资源,这里的资源不仅包括android自身的,还有我们自己的管理服务类,甚至相互之间传递一些参数,这里我仿照struts2的设计,提炼出一个ActivitySupport类,同时抽取一个接口,让所有的Activity都集成这个类,因为有了接口,我们便可以采用回调模式,非常方便的传递数据和使用公共的资源,这种好处相信大家使用之后都能有深刻的体会,通过接口回调传递参数和相互调用的方式无疑是最优雅的,spring和hibernate源码中曾经大量使用这种结构。

2.SQLiteTemplate类
  1. package ****.shimiso.eim.db;
  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import android.content.ContentValues;
  5. import android.database.Cursor;
  6. import android.database.sqlite.SQLiteDatabase;
  7. /**
  8. * SQLite数据库模板工具类
  9. *
  10. * 该类提供了数据库操作常用的增删改查,以及各种复杂条件匹配,分页,排序等操作
  11. *
  12. * @see SQLiteDatabase
  13. */
  14. public class SQLiteTemplate {
  15. /**
  16. * Default Primary key
  17. */
  18. protected String mPrimaryKey = "_id";
  19. /**
  20. * DBManager
  21. */
  22. private DBManager dBManager;
  23. /**
  24. * 是否为一个事务
  25. */
  26. private boolean isTransaction = false;
  27. /**
  28. * 数据库连接
  29. */
  30. private SQLiteDatabase dataBase = null;
  31. private SQLiteTemplate() {
  32. }
  33. private SQLiteTemplate(DBManager dBManager, boolean isTransaction) {
  34. this.dBManager = dBManager;
  35. this.isTransaction = isTransaction;
  36. }
  37. /**
  38. * isTransaction 是否属于一个事务 注:一旦isTransaction设为true
  39. * 所有的SQLiteTemplate方法都不会自动关闭资源,需在事务成功后手动关闭
  40. *
  41. * @return
  42. */
  43. public static SQLiteTemplate getInstance(DBManager dBManager,
  44. boolean isTransaction) {
  45. return new SQLiteTemplate(dBManager, isTransaction);
  46. }
  47. /**
  48. * 执行一条sql语句
  49. *
  50. * @param name
  51. * @param tel
  52. */
  53. public void execSQL(String sql) {
  54. try {
  55. dataBase = dBManager.openDatabase();
  56. dataBase.execSQL(sql);
  57. } catch (Exception e) {
  58. e.printStackTrace();
  59. } finally {
  60. if (!isTransaction) {
  61. closeDatabase(null);
  62. }
  63. }
  64. }
  65. /**
  66. * 执行一条sql语句
  67. *
  68. * @param name
  69. * @param tel
  70. */
  71. public void execSQL(String sql, Object[] bindArgs) {
  72. try {
  73. dataBase = dBManager.openDatabase();
  74. dataBase.execSQL(sql, bindArgs);
  75. } catch (Exception e) {
  76. e.printStackTrace();
  77. } finally {
  78. if (!isTransaction) {
  79. closeDatabase(null);
  80. }
  81. }
  82. }
  83. /**
  84. * 向数据库表中插入一条数据
  85. *
  86. * @param table
  87. *            表名
  88. * @param content
  89. *            字段值
  90. */
  91. public long insert(String table, ContentValues content) {
  92. try {
  93. dataBase = dBManager.openDatabase();
  94. // insert方法第一参数:数据库表名,第二个参数如果CONTENT为空时则向表中插入一个NULL,第三个参数为插入的内容
  95. return dataBase.insert(table, null, content);
  96. } catch (Exception e) {
  97. e.printStackTrace();
  98. } finally {
  99. if (!isTransaction) {
  100. closeDatabase(null);
  101. }
  102. }
  103. return 0;
  104. }
  105. /**
  106. * 批量删除指定主键数据
  107. *
  108. * @param ids
  109. */
  110. public void deleteByIds(String table, Object... primaryKeys) {
  111. try {
  112. if (primaryKeys.length > 0) {
  113. StringBuilder sb = new StringBuilder();
  114. for (@SuppressWarnings("unused")
  115. Object id : primaryKeys) {
  116. sb.append("?").append(",");
  117. }
  118. sb.deleteCharAt(sb.length() - 1);
  119. dataBase = dBManager.openDatabase();
  120. dataBase.execSQL("delete from " + table + " where "
  121. + mPrimaryKey + " in(" + sb + ")",
  122. (Object[]) primaryKeys);
  123. }
  124. } catch (Exception e) {
  125. e.printStackTrace();
  126. } finally {
  127. if (!isTransaction) {
  128. closeDatabase(null);
  129. }
  130. }
  131. }
  132. /**
  133. * 根据某一个字段和值删除一行数据, 如 name="jack"
  134. *
  135. * @param table
  136. * @param field
  137. * @param value
  138. * @return 返回值大于0表示删除成功
  139. */
  140. public int deleteByField(String table, String field, String value) {
  141. try {
  142. dataBase = dBManager.openDatabase();
  143. return dataBase.delete(table, field + "=?", new String[] { value });
  144. } catch (Exception e) {
  145. e.printStackTrace();
  146. } finally {
  147. if (!isTransaction) {
  148. closeDatabase(null);
  149. }
  150. }
  151. return 0;
  152. }
  153. /**
  154. * 根据条件删除数据
  155. *
  156. * @param table
  157. *            表名
  158. * @param whereClause
  159. *            查询语句 参数采用?
  160. * @param whereArgs
  161. *            参数值
  162. * @return 返回值大于0表示删除成功
  163. */
  164. public int deleteByCondition(String table, String whereClause,
  165. String[] whereArgs) {
  166. try {
  167. dataBase = dBManager.openDatabase();
  168. return dataBase.delete(table, whereClause, whereArgs);
  169. } catch (Exception e) {
  170. e.printStackTrace();
  171. } finally {
  172. if (!isTransaction) {
  173. closeDatabase(null);
  174. }
  175. }
  176. return 0;
  177. }
  178. /**
  179. * 根据主键删除一行数据
  180. *
  181. * @param table
  182. * @param id
  183. * @return 返回值大于0表示删除成功
  184. */
  185. public int deleteById(String table, String id) {
  186. try {
  187. dataBase = dBManager.openDatabase();
  188. return deleteByField(table, mPrimaryKey, id);
  189. } catch (Exception e) {
  190. e.printStackTrace();
  191. } finally {
  192. if (!isTransaction) {
  193. closeDatabase(null);
  194. }
  195. }
  196. return 0;
  197. }
  198. /**
  199. * 根据主键更新一行数据
  200. *
  201. * @param table
  202. * @param id
  203. * @param values
  204. * @return 返回值大于0表示更新成功
  205. */
  206. public int updateById(String table, String id, ContentValues values) {
  207. try {
  208. dataBase = dBManager.openDatabase();
  209. return dataBase.update(table, values, mPrimaryKey + "=?",
  210. new String[] { id });
  211. } catch (Exception e) {
  212. e.printStackTrace();
  213. } finally {
  214. if (!isTransaction) {
  215. closeDatabase(null);
  216. }
  217. }
  218. return 0;
  219. }
  220. /**
  221. * 更新数据
  222. *
  223. * @param table
  224. * @param values
  225. * @param whereClause
  226. * @param whereArgs
  227. * @return 返回值大于0表示更新成功
  228. */
  229. public int update(String table, ContentValues values, String whereClause,
  230. String[] whereArgs) {
  231. try {
  232. dataBase = dBManager.openDatabase();
  233. return dataBase.update(table, values, whereClause, whereArgs);
  234. } catch (Exception e) {
  235. e.printStackTrace();
  236. } finally {
  237. if (!isTransaction) {
  238. closeDatabase(null);
  239. }
  240. }
  241. return 0;
  242. }
  243. /**
  244. * 根据主键查看某条数据是否存在
  245. *
  246. * @param table
  247. * @param id
  248. * @return
  249. */
  250. public Boolean isExistsById(String table, String id) {
  251. try {
  252. dataBase = dBManager.openDatabase();
  253. return isExistsByField(table, mPrimaryKey, id);
  254. } catch (Exception e) {
  255. e.printStackTrace();
  256. } finally {
  257. if (!isTransaction) {
  258. closeDatabase(null);
  259. }
  260. }
  261. return null;
  262. }
  263. /**
  264. * 根据某字段/值查看某条数据是否存在
  265. *
  266. * @param status
  267. * @return
  268. */
  269. public Boolean isExistsByField(String table, String field, String value) {
  270. StringBuilder sql = new StringBuilder();
  271. sql.append("SELECT COUNT(*) FROM ").append(table).append(" WHERE ")
  272. .append(field).append(" =?");
  273. try {
  274. dataBase = dBManager.openDatabase();
  275. return isExistsBySQL(sql.toString(), new String[] { value });
  276. } catch (Exception e) {
  277. e.printStackTrace();
  278. } finally {
  279. if (!isTransaction) {
  280. closeDatabase(null);
  281. }
  282. }
  283. return null;
  284. }
  285. /**
  286. * 使用SQL语句查看某条数据是否存在
  287. *
  288. * @param sql
  289. * @param selectionArgs
  290. * @return
  291. */
  292. public Boolean isExistsBySQL(String sql, String[] selectionArgs) {
  293. Cursor cursor = null;
  294. try {
  295. dataBase = dBManager.openDatabase();
  296. cursor = dataBase.rawQuery(sql, selectionArgs);
  297. if (cursor.moveToFirst()) {
  298. return (cursor.getInt(0) > 0);
  299. } else {
  300. return false;
  301. }
  302. } catch (Exception e) {
  303. e.printStackTrace();
  304. } finally {
  305. if (!isTransaction) {
  306. closeDatabase(cursor);
  307. }
  308. }
  309. return null;
  310. }
  311. /**
  312. * 查询一条数据
  313. *
  314. * @param rowMapper
  315. * @param sql
  316. * @param args
  317. * @return
  318. */
  319. public <T> T queryForObject(RowMapper<T> rowMapper, String sql,
  320. String[] args) {
  321. Cursor cursor = null;
  322. T object = null;
  323. try {
  324. dataBase = dBManager.openDatabase();
  325. cursor = dataBase.rawQuery(sql, args);
  326. if (cursor.moveToFirst()) {
  327. object = rowMapper.mapRow(cursor, cursor.getCount());
  328. }
  329. } finally {
  330. if (!isTransaction) {
  331. closeDatabase(cursor);
  332. }
  333. }
  334. return object;
  335. }
  336. /**
  337. * 查询
  338. *
  339. * @param rowMapper
  340. * @param sql
  341. * @param startResult
  342. *            开始索引 注:第一条记录索引为0
  343. * @param maxResult
  344. *            步长
  345. * @return
  346. */
  347. public <T> List<T> queryForList(RowMapper<T> rowMapper, String sql,
  348. String[] selectionArgs) {
  349. Cursor cursor = null;
  350. List<T> list = null;
  351. try {
  352. dataBase = dBManager.openDatabase();
  353. cursor = dataBase.rawQuery(sql, selectionArgs);
  354. list = new ArrayList<T>();
  355. while (cursor.moveToNext()) {
  356. list.add(rowMapper.mapRow(cursor, cursor.getPosition()));
  357. }
  358. } finally {
  359. if (!isTransaction) {
  360. closeDatabase(cursor);
  361. }
  362. }
  363. return list;
  364. }
  365. /**
  366. * 分页查询
  367. *
  368. * @param rowMapper
  369. * @param sql
  370. * @param startResult
  371. *            开始索引 注:第一条记录索引为0
  372. * @param maxResult
  373. *            步长
  374. * @return
  375. */
  376. public <T> List<T> queryForList(RowMapper<T> rowMapper, String sql,
  377. int startResult, int maxResult) {
  378. Cursor cursor = null;
  379. List<T> list = null;
  380. try {
  381. dataBase = dBManager.openDatabase();
  382. cursor = dataBase.rawQuery(sql + " limit ?,?", new String[] {
  383. String.valueOf(startResult), String.valueOf(maxResult) });
  384. list = new ArrayList<T>();
  385. while (cursor.moveToNext()) {
  386. list.add(rowMapper.mapRow(cursor, cursor.getPosition()));
  387. }
  388. } finally {
  389. if (!isTransaction) {
  390. closeDatabase(cursor);
  391. }
  392. }
  393. return list;
  394. }
  395. /**
  396. * 获取记录数
  397. *
  398. * @return
  399. */
  400. public Integer getCount(String sql, String[] args) {
  401. Cursor cursor = null;
  402. try {
  403. dataBase = dBManager.openDatabase();
  404. cursor = dataBase.rawQuery("select count(*) from (" + sql + ")",
  405. args);
  406. if (cursor.moveToNext()) {
  407. return cursor.getInt(0);
  408. }
  409. } catch (Exception e) {
  410. e.printStackTrace();
  411. } finally {
  412. if (!isTransaction) {
  413. closeDatabase(cursor);
  414. }
  415. }
  416. return 0;
  417. }
  418. /**
  419. * 分页查询
  420. *
  421. * @param rowMapper
  422. * @param table
  423. *            检索的表
  424. * @param columns
  425. *            由需要返回列的列名所组成的字符串数组,传入null会返回所有的列。
  426. * @param selection
  427. *            查询条件子句,相当于select语句where关键字后面的部分,在条件子句允许使用占位符"?"
  428. * @param selectionArgs
  429. *            对应于selection语句中占位符的值,值在数组中的位置与占位符在语句中的位置必须一致,否则就会有异常
  430. * @param groupBy
  431. *            对结果集进行分组的group by语句(不包括GROUP BY关键字)。传入null将不对结果集进行分组
  432. * @param having
  433. *            对查询后的结果集进行过滤,传入null则不过滤
  434. * @param orderBy
  435. *            对结果集进行排序的order by语句(不包括ORDER BY关键字)。传入null将对结果集使用默认的排序
  436. * @param limit
  437. *            指定偏移量和获取的记录数,相当于select语句limit关键字后面的部分,如果为null则返回所有行
  438. * @return
  439. */
  440. public <T> List<T> queryForList(RowMapper<T> rowMapper, String table,
  441. String[] columns, String selection, String[] selectionArgs,
  442. String groupBy, String having, String orderBy, String limit) {
  443. List<T> list = null;
  444. Cursor cursor = null;
  445. try {
  446. dataBase = dBManager.openDatabase();
  447. cursor = dataBase.query(table, columns, selection, selectionArgs,
  448. groupBy, having, orderBy, limit);
  449. list = new ArrayList<T>();
  450. while (cursor.moveToNext()) {
  451. list.add(rowMapper.mapRow(cursor, cursor.getPosition()));
  452. }
  453. } finally {
  454. if (!isTransaction) {
  455. closeDatabase(cursor);
  456. }
  457. }
  458. return list;
  459. }
  460. /**
  461. * Get Primary Key
  462. *
  463. * @return
  464. */
  465. public String getPrimaryKey() {
  466. return mPrimaryKey;
  467. }
  468. /**
  469. * Set Primary Key
  470. *
  471. * @param primaryKey
  472. */
  473. public void setPrimaryKey(String primaryKey) {
  474. this.mPrimaryKey = primaryKey;
  475. }
  476. /**
  477. *
  478. * @author shimiso
  479. *
  480. * @param <T>
  481. */
  482. public interface RowMapper<T> {
  483. /**
  484. *
  485. * @param cursor
  486. *            游标
  487. * @param index
  488. *            下标索引
  489. * @return
  490. */
  491. public T mapRow(Cursor cursor, int index);
  492. }
  493. /**
  494. * 关闭数据库
  495. */
  496. public void closeDatabase(Cursor cursor) {
  497. if (null != dataBase) {
  498. dataBase.close();
  499. }
  500. if (null != cursor) {
  501. cursor.close();
  502. }
  503. }
  504. }

我们希望在android操作数据库是优雅的一种方式,这里不必关注事务,也不用担心分页,更不用为了封装传递对象烦恼,总之一切就像面向对象那样,简单,模板类的出现正是解决这个问题,虽然它看上去可能不是那么完美有待提高,这里我封装了很多sqlite常用的工具,大家可以借鉴使用。

3.XmppConnectionManager管理类
  1. package ****.shimiso.eim.manager;
  2. import org.jivesoftware.smack.Connection;
  3. import org.jivesoftware.smack.ConnectionConfiguration;
  4. import org.jivesoftware.smack.Roster;
  5. import org.jivesoftware.smack.XMPPConnection;
  6. import org.jivesoftware.smack.provider.ProviderManager;
  7. import org.jivesoftware.smackx.GroupChatInvitation;
  8. import org.jivesoftware.smackx.PrivateDataManager;
  9. import org.jivesoftware.smackx.packet.ChatStateExtension;
  10. import org.jivesoftware.smackx.packet.LastActivity;
  11. import org.jivesoftware.smackx.packet.OfflineMessageInfo;
  12. import org.jivesoftware.smackx.packet.OfflineMessageRequest;
  13. import org.jivesoftware.smackx.packet.SharedGroupsInfo;
  14. import org.jivesoftware.smackx.provider.DataFormProvider;
  15. import org.jivesoftware.smackx.provider.DelayInformationProvider;
  16. import org.jivesoftware.smackx.provider.DiscoverInfoProvider;
  17. import org.jivesoftware.smackx.provider.DiscoverItemsProvider;
  18. import org.jivesoftware.smackx.provider.MUCAdminProvider;
  19. import org.jivesoftware.smackx.provider.MUCOwnerProvider;
  20. import org.jivesoftware.smackx.provider.MUCUserProvider;
  21. import org.jivesoftware.smackx.provider.MessageEventProvider;
  22. import org.jivesoftware.smackx.provider.MultipleAddressesProvider;
  23. import org.jivesoftware.smackx.provider.RosterExchangeProvider;
  24. import org.jivesoftware.smackx.provider.StreamInitiationProvider;
  25. import org.jivesoftware.smackx.provider.VCardProvider;
  26. import org.jivesoftware.smackx.provider.XHTMLExtensionProvider;
  27. import org.jivesoftware.smackx.search.UserSearch;
  28. import ****.shimiso.eim.model.LoginConfig;
  29. /**
  30. *
  31. * XMPP服务器连接工具类.
  32. *
  33. * @author shimiso
  34. */
  35. public class XmppConnectionManager {
  36. private XMPPConnection connection;
  37. private static ConnectionConfiguration connectionConfig;
  38. private static XmppConnectionManager xmppConnectionManager;
  39. private XmppConnectionManager() {
  40. }
  41. public static XmppConnectionManager getInstance() {
  42. if (xmppConnectionManager == null) {
  43. xmppConnectionManager = new XmppConnectionManager();
  44. }
  45. return xmppConnectionManager;
  46. }
  47. // init
  48. public XMPPConnection init(LoginConfig loginConfig) {
  49. Connection.DEBUG_ENABLED = false;
  50. ProviderManager pm = ProviderManager.getInstance();
  51. configure(pm);
  52. connectionConfig = new ConnectionConfiguration(
  53. loginConfig.getXmppHost(), loginConfig.getXmppPort(),
  54. loginConfig.getXmppServiceName());
  55. connectionConfig.setSASLAuthenticationEnabled(false);// 不使用SASL验证,设置为false
  56. connectionConfig
  57. .setSecurityMode(ConnectionConfiguration.SecurityMode.enabled);
  58. // 允许自动连接
  59. connectionConfig.setReconnectionAllowed(false);
  60. // 允许登陆成功后更新在线状态
  61. connectionConfig.setSendPresence(true);
  62. // 收到好友邀请后manual表示需要经过同意,accept_all表示不经同意自动为好友
  63. Roster.setDefaultSubscriptionMode(Roster.SubscriptionMode.manual);
  64. connection = new XMPPConnection(connectionConfig);
  65. return connection;
  66. }
  67. /**
  68. *
  69. * 返回一个有效的xmpp连接,如果无效则返回空.
  70. *
  71. * @return
  72. * @author shimiso
  73. * @update 2012-7-4 下午6:54:31
  74. */
  75. public XMPPConnection getConnection() {
  76. if (connection == null) {
  77. throw new RuntimeException("请先初始化XMPPConnection连接");
  78. }
  79. return connection;
  80. }
  81. /**
  82. *
  83. * 销毁xmpp连接.
  84. *
  85. * @author shimiso
  86. * @update 2012-7-4 下午6:55:03
  87. */
  88. public void disconnect() {
  89. if (connection != null) {
  90. connection.disconnect();
  91. }
  92. }
  93. public void configure(ProviderManager pm) {
  94. // Private Data Storage
  95. pm.addIQProvider("query", "jabber:iq:private",
  96. new PrivateDataManager.PrivateDataIQProvider());
  97. // Time
  98. try {
  99. pm.addIQProvider("query", "jabber:iq:time",
  100. Class.forName("org.jivesoftware.smackx.packet.Time"));
  101. } catch (ClassNotFoundException e) {
  102. }
  103. // XHTML
  104. pm.addExtensionProvider("html", "http://jabber.org/protocol/xhtml-im",
  105. new XHTMLExtensionProvider());
  106. // Roster Exchange
  107. pm.addExtensionProvider("x", "jabber:x:roster",
  108. new RosterExchangeProvider());
  109. // Message Events
  110. pm.addExtensionProvider("x", "jabber:x:event",
  111. new MessageEventProvider());
  112. // Chat State
  113. pm.addExtensionProvider("active",
  114. "http://jabber.org/protocol/chatstates",
  115. new ChatStateExtension.Provider());
  116. pm.addExtensionProvider("composing",
  117. "http://jabber.org/protocol/chatstates",
  118. new ChatStateExtension.Provider());
  119. pm.addExtensionProvider("paused",
  120. "http://jabber.org/protocol/chatstates",
  121. new ChatStateExtension.Provider());
  122. pm.addExtensionProvider("inactive",
  123. "http://jabber.org/protocol/chatstates",
  124. new ChatStateExtension.Provider());
  125. pm.addExtensionProvider("gone",
  126. "http://jabber.org/protocol/chatstates",
  127. new ChatStateExtension.Provider());
  128. // FileTransfer
  129. pm.addIQProvider("si", "http://jabber.org/protocol/si",
  130. new StreamInitiationProvider());
  131. // Group Chat Invitations
  132. pm.addExtensionProvider("x", "jabber:x:conference",
  133. new GroupChatInvitation.Provider());
  134. // Service Discovery # Items
  135. pm.addIQProvider("query", "http://jabber.org/protocol/disco#items",
  136. new DiscoverItemsProvider());
  137. // Service Discovery # Info
  138. pm.addIQProvider("query", "http://jabber.org/protocol/disco#info",
  139. new DiscoverInfoProvider());
  140. // Data Forms
  141. pm.addExtensionProvider("x", "jabber:x:data", new DataFormProvider());
  142. // MUC User
  143. pm.addExtensionProvider("x", "http://jabber.org/protocol/muc#user",
  144. new MUCUserProvider());
  145. // MUC Admin
  146. pm.addIQProvider("query", "http://jabber.org/protocol/muc#admin",
  147. new MUCAdminProvider());
  148. // MUC Owner
  149. pm.addIQProvider("query", "http://jabber.org/protocol/muc#owner",
  150. new MUCOwnerProvider());
  151. // Delayed Delivery
  152. pm.addExtensionProvider("x", "jabber:x:delay",
  153. new DelayInformationProvider());
  154. // Version
  155. try {
  156. pm.addIQProvider("query", "jabber:iq:version",
  157. Class.forName("org.jivesoftware.smackx.packet.Version"));
  158. } catch (ClassNotFoundException e) {
  159. }
  160. // VCard
  161. pm.addIQProvider("vCard", "vcard-temp", new VCardProvider());
  162. // Offline Message Requests
  163. pm.addIQProvider("offline", "http://jabber.org/protocol/offline",
  164. new OfflineMessageRequest.Provider());
  165. // Offline Message Indicator
  166. pm.addExtensionProvider("offline",
  167. "http://jabber.org/protocol/offline",
  168. new OfflineMessageInfo.Provider());
  169. // Last Activity
  170. pm.addIQProvider("query", "jabber:iq:last", new LastActivity.Provider());
  171. // User Search
  172. pm.addIQProvider("query", "jabber:iq:search", new UserSearch.Provider());
  173. // SharedGroupsInfo
  174. pm.addIQProvider("sharedgroup",
  175. "http://www.jivesoftware.org/protocol/sharedgroup",
  176. new SharedGroupsInfo.Provider());
  177. // JEP-33: Extended Stanza Addressing
  178. pm.addExtensionProvider("addresses",
  179. "http://jabber.org/protocol/address",
  180. new MultipleAddressesProvider());
  181. }
  182. }

这个类是xmpp连接的管理类,如果大家使用smack的api对这个应该不会陌生,asmack对xmpp连接的管理,与smack的差别不大,但是部分细微区别也有,我们在使用中如果遇到问题,还要多加注意,我们这里将其设计成单例,毕竟重复创建连接是个非常消耗的过程。

3.演示效果

基于xmpp openfire smack开发之Android客户端开发[3] 基于xmpp openfire smack开发之Android客户端开发[3] 基于xmpp openfire smack开发之Android客户端开发[3] 

很像QQ吧,没错,这是2012年版本qq的安卓界面,只是界面元素一样,实现方式大不相同,下面简单列一下这个客户端实现的功能:
1.聊天
2.离线消息
3.添加,删除好友
4.添加,移动好友分组
5.设置昵称
6.监控好友状态
7.网络断开系统自动重连接
8.收到添加好友请求消息处理
9.收到系统广播消息处理
10.查看历史聊天记录
11.消息弹出提醒,和小气泡
....
因为时间关系不是很完美,主要用于学习研究,欢迎大家给我提bug和改进意见。

4.源码下载

分数比较大,不是为了坑大家,是怕有伸手党出现,拿了源码出去招摇撞骗,请尊重作者原创!