天天看點

基于xmpp openfire smack開發之Android用戶端開發[3]

在上兩篇文章中,我們依次介紹openfire部署以及smack常用api的使用,這一節中我們着力介紹如何基于asmack開發一個android的用戶端,本篇的重點在實踐,講解和原理環節,大家可以參考前兩篇的文章

基于xmpp openfire smack開發之openfire介紹和部署[1]

基于xmpp openfire smack開發之smack類庫介紹和使用[2]

基于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伺服器

[html] view

plaincopy

<!-- 預設的伺服器配置 -->   

  <integer name="xmpp_port">5222</integer>   

  <string name="xmpp_host">talk.google.com</string>   

  <string name="xmpp_service_name">gmail.com</string>  

  <bool name="is_remember">true</bool>  

  <bool name="is_autologin">false</bool>  

  <bool name="is_novisible">false</bool>   

androidmanifest.xml為android功能清單的配置檔案,我們這裡開放的權限并不多

    <!-- 通路internet -->  

<uses-permission android:name="android.permission.internet" />  

<!--- 通路網絡狀态 -->  

    <uses-permission android:name="android.permission.access_network_state" />  

    <!-- 往sdcard寫入資料權限 -->  

    <uses-permission android:name="android.permission.write_external_storage"/>  

   <span style="white-space: pre">  </span><!-- 在sdcard中建立與删除檔案權限 -->  

   <span style="white-space: pre">  </span><uses-permission android:name="android.permission.mount_unmount_filesystems"/>  

   <span style="white-space: pre">  </span><!-- 往sdcard寫入資料權限 -->  

   <span style="white-space: pre">  </span><uses-permission android:name="android.permission.write_external_storage"/>  

1.activitysupport類

[java] view

package csdn.shimiso.eim.activity;  

import android.app.activity;  

import android.app.alertdialog;  

import android.app.notification;  

import android.app.notificationmanager;  

import android.app.pendingintent;  

import android.app.progressdialog;  

import android.content.context;  

import android.content.dialoginterface;  

import android.content.intent;  

import android.content.sharedpreferences;  

import android.location.locationmanager;  

import android.net.connectivitymanager;  

import android.net.networkinfo;  

import android.os.bundle;  

import android.os.environment;  

import android.provider.settings;  

import android.view.inputmethod.inputmethodmanager;  

import android.widget.toast;  

import csdn.shimiso.eim.r;  

import csdn.shimiso.eim.comm.constant;  

import csdn.shimiso.eim.model.loginconfig;  

import csdn.shimiso.eim.service.imchatservice;  

import csdn.shimiso.eim.service.imcontactservice;  

import csdn.shimiso.eim.service.imsystemmsgservice;  

import csdn.shimiso.eim.service.reconnectservice;  

/** 

 * actity 工具支援類 

 *  

 * @author shimiso 

 */  

public class activitysupport extends activity implements iactivitysupport {  

    protected context context = null;  

    protected sharedpreferences preferences;  

    protected eimapplication eimapplication;  

    protected progressdialog pg = null;  

    protected notificationmanager notificationmanager;  

    @override  

    protected void oncreate(bundle savedinstancestate) {  

        super.oncreate(savedinstancestate);  

        context = this;  

        preferences = getsharedpreferences(constant.login_set, 0);  

        notificationmanager = (notificationmanager) getsystemservice(notification_service);  

        pg = new progressdialog(context);  

        eimapplication = (eimapplication) getapplication();  

        eimapplication.addactivity(this);  

    }  

    protected void onstart() {  

        super.onstart();  

    protected void onresume() {  

        super.onresume();  

    protected void onpause() {  

        super.onpause();  

    protected void onstop() {  

        super.onstop();  

    public void ondestroy() {  

        super.ondestroy();  

    public progressdialog getprogressdialog() {  

        return pg;  

    public void startservice() {  

        // 好友聯系人服務  

        intent server = new intent(context, imcontactservice.class);  

        context.startservice(server);  

        // 聊天服務  

        intent chatserver = new intent(context, imchatservice.class);  

        context.startservice(chatserver);  

        // 自動恢複連接配接服務  

        intent reconnectservice = new intent(context, reconnectservice.class);  

        context.startservice(reconnectservice);  

        // 系統消息連接配接服務  

        intent imsystemmsgservice = new intent(context,  

                imsystemmsgservice.class);  

        context.startservice(imsystemmsgservice);  

    /** 

     *  

     * 銷毀服務. 

     * @author shimiso 

     * @update 2012-5-16 下午12:16:08 

     */  

    public void stopservice() {  

        context.stopservice(server);  

        context.stopservice(chatserver);  

        context.stopservice(reconnectservice);  

        context.stopservice(imsystemmsgservice);  

    public void isexit() {  

        new alertdialog.builder(context).settitle("确定退出嗎?")  

                .setneutralbutton("确定", new dialoginterface.onclicklistener() {  

                    @override  

                    public void onclick(dialoginterface dialog, int which) {  

                        stopservice();  

                        eimapplication.exit();  

                    }  

                })  

                .setnegativebutton("取消", new dialoginterface.onclicklistener() {  

                        dialog.cancel();  

                }).show();  

    public boolean hasinternetconnected() {  

        connectivitymanager manager = (connectivitymanager) context  

                .getsystemservice(context.connectivity_service);  

        if (manager != null) {  

            networkinfo network = manager.getactivenetworkinfo();  

            if (network != null && network.isconnectedorconnecting()) {  

                return true;  

            }  

        }  

        return false;  

    public boolean validateinternet() {  

        if (manager == null) {  

            openwirelessset();  

            return false;  

        } else {  

            networkinfo[] info = manager.getallnetworkinfo();  

            if (info != null) {  

                for (int i = 0; i < info.length; i++) {  

                    if (info[i].getstate() == networkinfo.state.connected) {  

                        return true;  

                }  

        openwirelessset();  

    public boolean haslocationgps() {  

        locationmanager manager = (locationmanager) context  

                .getsystemservice(context.location_service);  

        if (manager  

                .isproviderenabled(android.location.locationmanager.gps_provider)) {  

            return true;  

    public boolean haslocationnetwork() {  

                .isproviderenabled(android.location.locationmanager.network_provider)) {  

    public void checkmemorycard() {  

        if (!environment.media_mounted.equals(environment  

                .getexternalstoragestate())) {  

            new alertdialog.builder(context)  

                    .settitle(r.string.prompt)  

                    .setmessage("請檢查記憶體卡")  

                    .setpositivebutton(r.string.menu_settings,  

                            new dialoginterface.onclicklistener() {  

                                @override  

                                public void onclick(dialoginterface dialog,  

                                        int which) {  

                                    dialog.cancel();  

                                    intent intent = new intent(  

                                            settings.action_settings);  

                                    context.startactivity(intent);  

                                }  

                            })  

                    .setnegativebutton("退出",  

                                    eimapplication.exit();  

                            }).create().show();  

    public void openwirelessset() {  

        alertdialog.builder dialogbuilder = new alertdialog.builder(context);  

        dialogbuilder  

                .settitle(r.string.prompt)  

                .setmessage(context.getstring(r.string.check_connection))  

                .setpositivebutton(r.string.menu_settings,  

                        new dialoginterface.onclicklistener() {  

                            @override  

                            public void onclick(dialoginterface dialog,  

                                    int which) {  

                                dialog.cancel();  

                                intent intent = new intent(  

                                        settings.action_wireless_settings);  

                                context.startactivity(intent);  

                            }  

                        })  

                .setnegativebutton(r.string.close,  

                                    int whichbutton) {  

                        });  

        dialogbuilder.show();  

     * 顯示toast 

     * @param text 

     * @param longint 

     * @update 2012-6-28 下午3:46:18 

    public void showtoast(string text, int longint) {  

        toast.maketext(context, text, longint).show();  

    public void showtoast(string text) {  

        toast.maketext(context, text, toast.length_short).show();  

     * 關閉鍵盤事件 

     * @update 2012-7-4 下午2:34:34 

    public void closeinput() {  

        inputmethodmanager inputmethodmanager = (inputmethodmanager) getsystemservice(context.input_method_service);  

        if (inputmethodmanager != null && this.getcurrentfocus() != null) {  

            inputmethodmanager.hidesoftinputfromwindow(this.getcurrentfocus()  

                    .getwindowtoken(), inputmethodmanager.hide_not_always);  

     * 發出notification的method. 

     * @param iconid 

     *            圖示 

     * @param contenttitle 

     *            标題 

     * @param contenttext 

     *            你内容 

     * @param activity 

     * @update 2012-5-14 下午12:01:55 

    public void setnotitype(int iconid, string contenttitle,  

            string contenttext, class activity, string from) {  

        /* 

         * 建立新的intent,作為點選notification留言條時, 會運作的activity 

         */  

        intent notifyintent = new intent(this, activity);  

        notifyintent.putextra("to", from);  

        // notifyintent.setflags(intent.flag_activity_new_task);  

        /* 建立pendingintent作為設定遞延運作的activity */  

        pendingintent appintent = pendingintent.getactivity(this, 0,  

                notifyintent, 0);  

        /* 建立notication,并設定相關參數 */  

        notification mynoti = new notification();  

        // 點選自動消失  

        mynoti.flags = notification.flag_auto_cancel;  

        /* 設定statusbar顯示的icon */  

        mynoti.icon = iconid;  

        /* 設定statusbar顯示的文字資訊 */  

        mynoti.tickertext = contenttitle;  

        /* 設定notification發生時同時發出預設聲音 */  

        mynoti.defaults = notification.default_sound;  

        /* 設定notification留言條的參數 */  

        mynoti.setlatesteventinfo(this, contenttitle, contenttext, appintent);  

        /* 送出notification */  

        notificationmanager.notify(0, mynoti);  

    public context getcontext() {  

        return context;  

    public sharedpreferences getloginusersharedpre() {  

        return preferences;  

    public void saveloginconfig(loginconfig loginconfig) {  

        preferences.edit()  

                .putstring(constant.xmpp_host, loginconfig.getxmpphost())  

                .commit();  

                .putint(constant.xmpp_port, loginconfig.getxmppport()).commit();  

        preferences  

                .edit()  

                .putstring(constant.xmpp_seivice_name,  

                        loginconfig.getxmppservicename()).commit();  

                .putstring(constant.username, loginconfig.getusername())  

                .putstring(constant.password, loginconfig.getpassword())  

                .putboolean(constant.is_autologin, loginconfig.isautologin())  

                .putboolean(constant.is_novisible, loginconfig.isnovisible())  

                .putboolean(constant.is_remember, loginconfig.isremember())  

                .putboolean(constant.is_online, loginconfig.isonline())  

                .putboolean(constant.is_firststart, loginconfig.isfirststart())  

    public loginconfig getloginconfig() {  

        loginconfig loginconfig = new loginconfig();  

        string a = preferences.getstring(constant.xmpp_host, null);  

        string b = getresources().getstring(r.string.xmpp_host);  

        loginconfig.setxmpphost(preferences.getstring(constant.xmpp_host,  

                getresources().getstring(r.string.xmpp_host)));  

        loginconfig.setxmppport(preferences.getint(constant.xmpp_port,  

                getresources().getinteger(r.integer.xmpp_port)));  

        loginconfig.setusername(preferences.getstring(constant.username, null));  

        loginconfig.setpassword(preferences.getstring(constant.password, null));  

        loginconfig.setxmppservicename(preferences.getstring(  

                constant.xmpp_seivice_name,  

                getresources().getstring(r.string.xmpp_service_name)));  

        loginconfig.setautologin(preferences.getboolean(constant.is_autologin,  

                getresources().getboolean(r.bool.is_autologin)));  

        loginconfig.setnovisible(preferences.getboolean(constant.is_novisible,  

                getresources().getboolean(r.bool.is_novisible)));  

        loginconfig.setremember(preferences.getboolean(constant.is_remember,  

                getresources().getboolean(r.bool.is_remember)));  

        loginconfig.setfirststart(preferences.getboolean(  

                constant.is_firststart, true));  

        return loginconfig;  

    public boolean getuseronlinestate() {  

        // preferences = getsharedpreferences(constant.login_set,0);  

        return preferences.getboolean(constant.is_online, true);  

    public void setuseronlinestate(boolean isonline) {  

        preferences.edit().putboolean(constant.is_online, isonline).commit();  

    public eimapplication geteimapplication() {  

        return eimapplication;  

}  

大家寫android程式會發現,不同的activity之間經常需要調用一些公共的資源,這裡的資源不僅包括android自身的,還有我們自己的管理服務類,甚至互相之間傳遞一些參數,這裡我仿照struts2的設計,提煉出一個activitysupport類,同時抽取一個接口,讓所有的activity都內建這個類,因為有了接口,我們便可以采用回調模式,非常友善的傳遞資料和使用公共的資源,這種好處相信大家使用之後都能有深刻的體會,通過接口回調傳遞參數和互相調用的方式無疑是最優雅的,spring和hibernate源碼中曾經大量使用這種結構。

2.sqlitetemplate類

package csdn.shimiso.eim.db;  

import java.util.arraylist;  

import java.util.list;  

import android.content.contentvalues;  

import android.database.cursor;  

import android.database.sqlite.sqlitedatabase;  

 * sqlite資料庫模闆工具類 

 * 該類提供了資料庫操作常用的增删改查,以及各種複雜條件比對,分頁,排序等操作 

 * @see sqlitedatabase 

public class sqlitetemplate {  

     * default primary key 

    protected string mprimarykey = "_id";  

     * dbmanager 

    private dbmanager dbmanager;  

     * 是否為一個事務 

    private boolean istransaction = false;  

     * 資料庫連接配接 

    private sqlitedatabase database = null;  

    private sqlitetemplate() {  

    private sqlitetemplate(dbmanager dbmanager, boolean istransaction) {  

        this.dbmanager = dbmanager;  

        this.istransaction = istransaction;  

     * istransaction 是否屬于一個事務 注:一旦istransaction設為true 

     * 所有的sqlitetemplate方法都不會自動關閉資源,需在事務成功後手動關閉 

     * @return 

    public static sqlitetemplate getinstance(dbmanager dbmanager,  

            boolean istransaction) {  

        return new sqlitetemplate(dbmanager, istransaction);  

     * 執行一條sql語句 

     * @param name 

     * @param tel 

    public void execsql(string sql) {  

        try {  

            database = dbmanager.opendatabase();  

            database.execsql(sql);  

        } catch (exception e) {  

            e.printstacktrace();  

        } finally {  

            if (!istransaction) {  

                closedatabase(null);  

    public void execsql(string sql, object[] bindargs) {  

            database.execsql(sql, bindargs);  

     * 向資料庫表中插入一條資料 

     * @param table 

     *            表名 

     * @param content 

     *            字段值 

    public long insert(string table, contentvalues content) {  

            // insert方法第一參數:資料庫表名,第二個參數如果content為空時則向表中插入一個null,第三個參數為插入的内容  

            return database.insert(table, null, content);  

        return 0;  

     * 批量删除指定主鍵資料 

     * @param ids 

    public void deletebyids(string table, object... primarykeys) {  

            if (primarykeys.length > 0) {  

                stringbuilder sb = new stringbuilder();  

                for (@suppresswarnings("unused")  

                object id : primarykeys) {  

                    sb.append("?").append(",");  

                sb.deletecharat(sb.length() - 1);  

                database = dbmanager.opendatabase();  

                database.execsql("delete from " + table + " where "  

                        + mprimarykey + " in(" + sb + ")",  

                        (object[]) primarykeys);  

     * 根據某一個字段和值删除一行資料, 如 name="jack" 

     * @param field 

     * @param value 

     * @return 傳回值大于0表示删除成功 

    public int deletebyfield(string table, string field, string value) {  

            return database.delete(table, field + "=?", new string[] { value });  

     * 根據條件删除資料 

     * @param whereclause 

     *            查詢語句 參數采用? 

     * @param whereargs 

     *            參數值 

    public int deletebycondition(string table, string whereclause,  

            string[] whereargs) {  

            return database.delete(table, whereclause, whereargs);  

     * 根據主鍵删除一行資料 

     * @param id 

    public int deletebyid(string table, string id) {  

            return deletebyfield(table, mprimarykey, id);  

     * 根據主鍵更新一行資料 

     * @param values 

     * @return 傳回值大于0表示更新成功 

    public int updatebyid(string table, string id, contentvalues values) {  

            return database.update(table, values, mprimarykey + "=?",  

                    new string[] { id });  

     * 更新資料 

    public int update(string table, contentvalues values, string whereclause,  

            return database.update(table, values, whereclause, whereargs);  

     * 根據主鍵檢視某條資料是否存在 

    public boolean isexistsbyid(string table, string id) {  

            return isexistsbyfield(table, mprimarykey, id);  

        return null;  

     * 根據某字段/值檢視某條資料是否存在 

     * @param status 

    public boolean isexistsbyfield(string table, string field, string value) {  

        stringbuilder sql = new stringbuilder();  

        sql.append("select count(*) from ").append(table).append(" where ")  

                .append(field).append(" =?");  

            return isexistsbysql(sql.tostring(), new string[] { value });  

     * 使用sql語句檢視某條資料是否存在 

     * @param sql 

     * @param selectionargs 

    public boolean isexistsbysql(string sql, string[] selectionargs) {  

        cursor cursor = null;  

            cursor = database.rawquery(sql, selectionargs);  

            if (cursor.movetofirst()) {  

                return (cursor.getint(0) > 0);  

            } else {  

                return false;  

                closedatabase(cursor);  

     * 查詢一條資料 

     * @param rowmapper 

     * @param args 

    public <t> t queryforobject(rowmapper<t> rowmapper, string sql,  

            string[] args) {  

        t object = null;  

            cursor = database.rawquery(sql, args);  

                object = rowmapper.maprow(cursor, cursor.getcount());  

        return object;  

     * 查詢 

     * @param startresult 

     *            開始索引 注:第一條記錄索引為0 

     * @param maxresult 

     *            步長 

    public <t> list<t> queryforlist(rowmapper<t> rowmapper, string sql,  

            string[] selectionargs) {  

        list<t> list = null;  

            list = new arraylist<t>();  

            while (cursor.movetonext()) {  

                list.add(rowmapper.maprow(cursor, cursor.getposition()));  

        return list;  

     * 分頁查詢 

            int startresult, int maxresult) {  

            cursor = database.rawquery(sql + " limit ?,?", new string[] {  

                    string.valueof(startresult), string.valueof(maxresult) });  

     * 擷取記錄數 

    public integer getcount(string sql, string[] args) {  

            cursor = database.rawquery("select count(*) from (" + sql + ")",  

                    args);  

            if (cursor.movetonext()) {  

                return cursor.getint(0);  

     *            檢索的表 

     * @param columns 

     *            由需要傳回列的列名所組成的字元串數組,傳入null會傳回所有的列。 

     * @param selection 

     *            查詢條件子句,相當于select語句where關鍵字後面的部分,在條件子句允許使用占位符"?" 

     *            對應于selection語句中占位符的值,值在數組中的位置與占位符在語句中的位置必須一緻,否則就會有異常 

     * @param groupby 

     *            對結果集進行分組的group by語句(不包括group by關鍵字)。傳入null将不對結果集進行分組 

     * @param having 

     *            對查詢後的結果集進行過濾,傳入null則不過濾 

     * @param orderby 

     *            對結果集進行排序的order by語句(不包括order by關鍵字)。傳入null将對結果集使用預設的排序 

     * @param limit 

     *            指定偏移量和擷取的記錄數,相當于select語句limit關鍵字後面的部分,如果為null則傳回所有行 

    public <t> list<t> queryforlist(rowmapper<t> rowmapper, string table,  

            string[] columns, string selection, string[] selectionargs,  

            string groupby, string having, string orderby, string limit) {  

            cursor = database.query(table, columns, selection, selectionargs,  

                    groupby, having, orderby, limit);  

     * get primary key 

    public string getprimarykey() {  

        return mprimarykey;  

     * set primary key 

     * @param primarykey 

    public void setprimarykey(string primarykey) {  

        this.mprimarykey = primarykey;  

     * @param <t> 

    public interface rowmapper<t> {  

        /** 

         *  

         * @param cursor 

         *            遊标 

         * @param index 

         *            下标索引 

         * @return 

        public t maprow(cursor cursor, int index);  

     * 關閉資料庫 

    public void closedatabase(cursor cursor) {  

        if (null != database) {  

            database.close();  

        if (null != cursor) {  

            cursor.close();  

我們希望在android操作資料庫是優雅的一種方式,這裡不必關注事務,也不用擔心分頁,更不用為了封裝傳遞對象煩惱,總之一切就像面向對象那樣,簡單,模闆類的出現正是解決這個問題,雖然它看上去可能不是那麼完美有待提高,這裡我封裝了很多sqlite常用的工具,大家可以借鑒使用。

3.xmppconnectionmanager管理類

package csdn.shimiso.eim.manager;  

import org.jivesoftware.smack.connection;  

import org.jivesoftware.smack.connectionconfiguration;  

import org.jivesoftware.smack.roster;  

import org.jivesoftware.smack.xmppconnection;  

import org.jivesoftware.smack.provider.providermanager;  

import org.jivesoftware.smackx.groupchatinvitation;  

import org.jivesoftware.smackx.privatedatamanager;  

import org.jivesoftware.smackx.packet.chatstateextension;  

import org.jivesoftware.smackx.packet.lastactivity;  

import org.jivesoftware.smackx.packet.offlinemessageinfo;  

import org.jivesoftware.smackx.packet.offlinemessagerequest;  

import org.jivesoftware.smackx.packet.sharedgroupsinfo;  

import org.jivesoftware.smackx.provider.dataformprovider;  

import org.jivesoftware.smackx.provider.delayinformationprovider;  

import org.jivesoftware.smackx.provider.discoverinfoprovider;  

import org.jivesoftware.smackx.provider.discoveritemsprovider;  

import org.jivesoftware.smackx.provider.mucadminprovider;  

import org.jivesoftware.smackx.provider.mucownerprovider;  

import org.jivesoftware.smackx.provider.mucuserprovider;  

import org.jivesoftware.smackx.provider.messageeventprovider;  

import org.jivesoftware.smackx.provider.multipleaddressesprovider;  

import org.jivesoftware.smackx.provider.rosterexchangeprovider;  

import org.jivesoftware.smackx.provider.streaminitiationprovider;  

import org.jivesoftware.smackx.provider.vcardprovider;  

import org.jivesoftware.smackx.provider.xhtmlextensionprovider;  

import org.jivesoftware.smackx.search.usersearch;  

 * xmpp伺服器連接配接工具類. 

public class xmppconnectionmanager {  

    private xmppconnection connection;  

    private static connectionconfiguration connectionconfig;  

    private static xmppconnectionmanager xmppconnectionmanager;  

    private xmppconnectionmanager() {  

    public static xmppconnectionmanager getinstance() {  

        if (xmppconnectionmanager == null) {  

            xmppconnectionmanager = new xmppconnectionmanager();  

        return xmppconnectionmanager;  

    // init  

    public xmppconnection init(loginconfig loginconfig) {  

        connection.debug_enabled = false;  

        providermanager pm = providermanager.getinstance();  

        configure(pm);  

        connectionconfig = new connectionconfiguration(  

                loginconfig.getxmpphost(), loginconfig.getxmppport(),  

                loginconfig.getxmppservicename());  

        connectionconfig.setsaslauthenticationenabled(false);// 不使用sasl驗證,設定為false  

        connectionconfig  

                .setsecuritymode(connectionconfiguration.securitymode.enabled);  

        // 允許自動連接配接  

        connectionconfig.setreconnectionallowed(false);  

        // 允許登陸成功後更新線上狀态  

        connectionconfig.setsendpresence(true);  

        // 收到好友邀請後manual表示需要經過同意,accept_all表示不經同意自動為好友  

        roster.setdefaultsubscriptionmode(roster.subscriptionmode.manual);  

        connection = new xmppconnection(connectionconfig);  

        return connection;  

     * 傳回一個有效的xmpp連接配接,如果無效則傳回空. 

     * @update 2012-7-4 下午6:54:31 

    public xmppconnection getconnection() {  

        if (connection == null) {  

            throw new runtimeexception("請先初始化xmppconnection連接配接");  

     * 銷毀xmpp連接配接. 

     * @update 2012-7-4 下午6:55:03 

    public void disconnect() {  

        if (connection != null) {  

            connection.disconnect();  

    public void configure(providermanager pm) {  

        // private data storage  

        pm.addiqprovider("query", "jabber:iq:private",  

                new privatedatamanager.privatedataiqprovider());  

        // time  

            pm.addiqprovider("query", "jabber:iq:time",  

                    class.forname("org.jivesoftware.smackx.packet.time"));  

        } catch (classnotfoundexception e) {  

        // xhtml  

        pm.addextensionprovider("html", "http://jabber.org/protocol/xhtml-im",  

                new xhtmlextensionprovider());  

        // roster exchange  

        pm.addextensionprovider("x", "jabber:x:roster",  

                new rosterexchangeprovider());  

        // message events  

        pm.addextensionprovider("x", "jabber:x:event",  

                new messageeventprovider());  

        // chat state  

        pm.addextensionprovider("active",  

                "http://jabber.org/protocol/chatstates",  

                new chatstateextension.provider());  

        pm.addextensionprovider("composing",  

        pm.addextensionprovider("paused",  

        pm.addextensionprovider("inactive",  

        pm.addextensionprovider("gone",  

        // filetransfer  

        pm.addiqprovider("si", "http://jabber.org/protocol/si",  

                new streaminitiationprovider());  

        // group chat invitations  

        pm.addextensionprovider("x", "jabber:x:conference",  

                new groupchatinvitation.provider());  

        // service discovery # items  

        pm.addiqprovider("query", "http://jabber.org/protocol/disco#items",  

                new discoveritemsprovider());  

        // service discovery # info  

        pm.addiqprovider("query", "http://jabber.org/protocol/disco#info",  

                new discoverinfoprovider());  

        // data forms  

        pm.addextensionprovider("x", "jabber:x:data", new dataformprovider());  

        // muc user  

        pm.addextensionprovider("x", "http://jabber.org/protocol/muc#user",  

                new mucuserprovider());  

        // muc admin  

        pm.addiqprovider("query", "http://jabber.org/protocol/muc#admin",  

                new mucadminprovider());  

        // muc owner  

        pm.addiqprovider("query", "http://jabber.org/protocol/muc#owner",  

                new mucownerprovider());  

        // delayed delivery  

        pm.addextensionprovider("x", "jabber:x:delay",  

                new delayinformationprovider());  

        // version  

            pm.addiqprovider("query", "jabber:iq:version",  

                    class.forname("org.jivesoftware.smackx.packet.version"));  

        // vcard  

        pm.addiqprovider("vcard", "vcard-temp", new vcardprovider());  

        // offline message requests  

        pm.addiqprovider("offline", "http://jabber.org/protocol/offline",  

                new offlinemessagerequest.provider());  

        // offline message indicator  

        pm.addextensionprovider("offline",  

                "http://jabber.org/protocol/offline",  

                new offlinemessageinfo.provider());  

        // last activity  

        pm.addiqprovider("query", "jabber:iq:last", new lastactivity.provider());  

        // user search  

        pm.addiqprovider("query", "jabber:iq:search", new usersearch.provider());  

        // sharedgroupsinfo  

        pm.addiqprovider("sharedgroup",  

                "http://www.jivesoftware.org/protocol/sharedgroup",  

                new sharedgroupsinfo.provider());  

        // jep-33: extended stanza addressing  

        pm.addextensionprovider("addresses",  

                "http://jabber.org/protocol/address",  

                new multipleaddressesprovider());  

這個類是xmpp連接配接的管理類,如果大家使用smack的api對這個應該不會陌生,asmack對xmpp連接配接的管理,與smack的差别不大,但是部分細微差別也有,我們在使用中如果遇到問題,還要多加注意,我們這裡将其設計成單例,畢竟重複建立連接配接是個非常消耗的過程。

基于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和改進意見。

分數比較大,不是為了坑大家,是怕有伸手黨出現,拿了源碼出去招搖撞騙,請尊重作者原創!

http://download.csdn.net/detail/shimiso/6224163

參閱文獻

openfirehttp://www.igniterealtime.org/

push-notificationhttp://www.push-notification.org/

claros chathttp://www.claros.org/

androidpnsourceforgehttp://sourceforge.net/projects/androidpn/

android消息推送解決方案http://www.cnblogs.com/hanyonglu/archive/2012/03/04/2378971.html

xmpp協定實作原理介紹 http://www.cnblogs.com/hanyonglu/archive/2012/03/04/2378956.html