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

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

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


1.源码结构介绍


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

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

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

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

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

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

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

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

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


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

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

layout是布局页面

menu是地步菜单布局页面

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

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


[html]  ​​view plain​​ ​​copy​​



  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功能清单的配置文件,我们这里开放的权限并不多



[html]  ​​view plain​​ ​​copy​​



  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类


[java]  ​​view plain​​ ​​copy​​



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



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



2.SQLiteTemplate类


[java]  ​​view plain​​ ​​copy​​



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




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



3.XmppConnectionManager管理类


[java]  ​​view plain​​ ​​copy​​



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



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



3.演示效果

 



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

 


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

 


基于xmpp openfire smack开发之Android客户端开发[3]_系统消息_05

 




很像QQ吧,没错,这是2012年版本qq的安卓界面,只是界面元素一样,实现方式大不相同,下面简单列一下这个客户端实现的功能:


1.聊天


2.离线消息


3.添加,删除好友


4.添加,移动好友分组


5.设置昵称


6.监控好友状态


7.网络断开系统自动重连接


8.收到添加好友请求消息处理


9.收到系统广播消息处理


10.查看历史聊天记录


11.消息弹出提醒,和小气泡


....


因为时间关系不是很完美,主要用于学习研究,欢迎大家给我提bug和改进意见。




​​4.源码下载​​


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