即時聊天IM之四 Android客戶端IM幫助類編寫


圖文無關一起娛樂:

程序員egojit

這一篇我們開始寫Android端的Smack版主類,后面Android的IM功能都是通過這個幫助類實現的

引用類庫:

因為我用的是IDE是Android Studio,所以我通過gradle進行jar包管理了,非常方便,jar包如下:

compile 'org.igniterealtime.smack:smack-core:4.1.4'
compile 'org.igniterealtime.smack:smack-tcp:4.1.4'
compile 'org.igniterealtime.smack:smack-extensions:4.1.4'
compile 'org.igniterealtime.smack:smack-android:4.1.4'
compile 'org.igniterealtime.smack:smack-android-extensions:4.1.4'
compile 'org.igniterealtime.smack:smack-experimental:4.1.4'
compile 'org.igniterealtime.smack:smack-bosh:4.1.4'
compile 'org.igniterealtime.smack:smack-resolver-dnsjava:4.1.4'
compile 'org.igniterealtime.smack:smack-legacy:4.1.4'

 加上這些jar包后我們就可以使用Smack庫了,大家很容易看到,我這個是4.1.4的包。它的最新包是4.2.0不過是alpha版本,所以我就用這個4.1.4最新正式包了。它和它的一些老版本差別還是比較大的。所以如果你是第一次使用還是和我一樣,防止出現一些問題。更新gradle后就可以使用了。

寫Smack幫助類:

我這里先定義一個ISmack接口,這種命名方式是我從C#那邊帶過來的,別糾結。本人很久之前是.NET平台的,.NET平台還是有很多東西是非常不錯的。這個接口就是一些約束協議。然后再寫了一個Smack 類,它繼承並且實現了ISmack接口。

ISmack接口代碼如下:

/**
 *
 * @備注:samck操作接口協議
 * @作者:高露
 * @時間:2015-10-24
 * @QQ:408365330
 *
 */
public interface ISmack {


    /**
     * 登錄
     * @param name 賬號
     * @param pwd 密碼
     * @return
     */
    public boolean login(String name,String pwd);


    /**
     * 消息發送
     * @param from 誰發送
     * @param to 發送給誰
     * @param content 發送內容
     */
    public void sendMessage(String from,String to,String content) throws SmackException.NotConnectedException;


    /**
     *添加好友
     * @param user 用戶jid
     * @param name 添加好友備注名稱
     * @param groups  好友添加到的分組,可以過個組
     */
    public void addRosterItem(String user,String name,String[] groups) throws Exception;

    /**
     * 刪除好友
     * @param user 好友jid
     */
    public void removeRoster(String user);


}

可以通過這份協議知道我這里定義了 登錄,消息發送,添加好友,刪除好友 這些方法,后面還是會添加的,每個方法備注還是比較詳細的,看看就知道是什么意思了。

然后我們再看看和分析Smack代碼

Smack代碼分析:

靜態構造函數:

首先定義了一個java靜態構造函數

static {
        if (connection == null) {
            XMPPTCPConnectionConfiguration configuration = XMPPTCPConnectionConfiguration.builder()
                    .setConnectTimeout(Constant.IM_TIMEOUT)
                    .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
                    .setHost(Constant.IM_SERVER)//ip
                    .setPort(Constant.IM_SERVER_PORT)//端口號設置一般式5222
                    .setServiceName(Constant.IM_SERVER_DOMAIN)//服務器名稱
                    .setDebuggerEnabled(true)//設置開啟調試
                    .setSendPresence(true)//設置是否發送Presece信息
                    .build();
            connection = new XMPPTCPConnection(configuration);

            connection.addConnectionListener(new ConnectionListener() {
                @Override
                public void connected(XMPPConnection connection) {
                    LogHelper.i(TAG, "connected");
                }

                @Override
                public void authenticated(XMPPConnection connection, boolean resumed) {
                    LogHelper.i(TAG, "authenticated");
                }

                @Override
                public void connectionClosed() {
                    LogHelper.i(TAG, "connectionClosed");
                }

                @Override
                public void connectionClosedOnError(Exception e) {
                    LogHelper.i(TAG, "connectionClosedOnError");
                }

                @Override
                public void reconnectionSuccessful() {
                    LogHelper.i(TAG, "reconnectionSuccessful");
                }

                @Override
                public void reconnectingIn(int seconds) {
                    LogHelper.i(TAG, "reconnectingIn");
                }

                @Override
                public void reconnectionFailed(Exception e) {
                    LogHelper.i(TAG, "reconnectionFailed");
                }
            });

            ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(connection);
            reconnectionManager.setFixedDelay(Constant.IM_RE_CONNET_TIME);//重聯間隔
            reconnectionManager.enableAutomaticReconnection();//開啟重聯機制
            try {
                connection.connect();
            } catch (SmackException | IOException | XMPPException ex) {
                ex.printStackTrace();
            }


        }
    }
XMPPTCPConnectionConfiguration類是連接的配置,連接相關配置都是通過這個類傳遞給XMPPTCPConnection對象的。配置信息的設置請看上面的代碼注釋,connection.addConnectionListener是給連接添加監聽,這里面我們可以監聽連接的各種狀態。然后做相應的處理。
ReconnectionManager這個類是重聯管理設置連接斷開后是否允許重新連接。這樣實現自動重聯
最后通過connection.connect()連接XMPP服務器
登錄:
/**
     * 登錄
     * @param name 賬號
     * @param pwd 密碼
     * @return
     */
    @Override
    public boolean login(String name, String pwd) {
        try {
            if (connection.isConnected()) {
                connection.login(name, pwd);

            } else {
                connection.connect();
                connection.login(name, pwd);
            }

        } catch (XMPPException | SmackException | IOException ex) {
            ex.printStackTrace();
        }

        /**
         * 消息監聽
         */
        registerMessageListener();
        /**
         * 通訊錄監聽
         */
        registerRosterListener();
        return false;
    }

通過用戶名和密碼進行登錄。登錄之前判斷是否連接了XMPP服務器。登錄后添加消息監聽處理消息,通訊錄監聽處理通訊錄。下面再看看這兩個監聽方法

通訊錄監聽:

/**
     * 通訊錄監聽
     */
    private void registerRosterListener() {
        Roster roster = Roster.getInstanceFor(connection);
        roster.setSubscriptionMode(Roster.SubscriptionMode.manual);//設置添加好友,需要對方確認
        roster.addRosterListener(new RosterListener() {
            @Override
            public void entriesAdded(Collection<String> collection) {
                LogHelper.i(TAG, "通訊錄用戶添加");
            }

            @Override
            public void entriesUpdated(Collection<String> collection) {
                LogHelper.i(TAG, "通訊錄用戶變更");
            }

            @Override
            public void entriesDeleted(Collection<String> collection) {
                LogHelper.i(TAG, "通訊錄用戶刪除");
            }

            @Override
            public void presenceChanged(Presence presence) {
                LogHelper.i(TAG, "通訊錄用戶presence變化");
            }
        });
    }
Roster類管理這通訊錄。給通訊錄添加了監聽這樣可以處理通訊里,請看上面的代碼。這里重點講解roster.setSubscriptionMode(Roster.SubscriptionMode.manual);這個設置通訊里添加好友的模式。這里設置了添加好友需要確認,而不是直接成為好友,一般都是這樣,但是這里也支持直接成為好友。我們通過源碼來看看SubscriptionMode這個枚舉。
/**
     * 好友請求訂閱的模式枚舉.
     */
    public enum SubscriptionMode {

        /**
         * 自動接收所有好友請求. This is
         * 這是默認的模式,適合簡單的客戶端. 更復雜的客戶端希望手動處理好友添加請求.
         */
        accept_all,

        /**
         * 自動拒絕所有請求
         */
        reject_all,

        /**
         * 好友請求被忽略,意味着必須手動注冊和處理presence監聽,然后處理類型是        
* Presence.Type.SUBSCRIBE類型或者是Presence.Type.UNSUBSCRIBE類型的 presence包
         */
        manual
    }

以上是我通過源碼中的英文轉譯的,鄙人英文就這水平見諒,見諒。這里說的presence包不懂的話看看第一篇XMPP協議簡析,這樣就懂了。我的原則是任何東西先懂原理(先修煉好《易筋經》然后再學招式,否則累也記不住,不懂硬記很累,有些學編程,語言沒學好然后直接上平台性的東西,發現容易出問題,同樣一個道理)。

消息監聽:

/**
     * 各種消息包監聽
     */
    private void registerMessageListener() {
        connection.addSyncStanzaListener(new StanzaListener() {
            @Override
            public void processPacket(Stanza stanza) throws SmackException.NotConnectedException {
                if (stanza instanceof Message) {//表示接收到是消息包
                    Message message = (Message) stanza;
                    if (message.getType() == Message.Type.chat) {//表示單聊

                    }
                    if (message.getType() == Message.Type.groupchat) {//表示群聊

                    }
                    if (message.getType() == Message.Type.error) {//表示錯誤信息

                    }
                }

                if (stanza instanceof Presence) {//表示接收到的是Presence包

                }

                if (stanza instanceof IQ) {//表示接收到的是IQ包

                }
            }
        }, new StanzaFilter() {
            @Override
            public boolean accept(Stanza stanza) {
                return true;
            }
        });

雖然還有其它監聽消息的方法,但是我選擇這種了,因為這種可以監聽所有各種消息包,看上面注釋能理解。如果不懂這些包神馬意思還是推薦你看前面第一篇XMPP協議簡析。那么久知道神馬是IQ,神馬是Message,什么是Presence包了

new StanzaFilter() {
            @Override
            public boolean accept(Stanza stanza) {
                return true;
            }
        }

這個是過濾包,我這里返回ture就是不過濾直接接受所有XMPP包。你還可以過濾特定的包比如IQ包  StanzaFilter filter=new StanzaTypeFilter(IQ.class) ,然后把這個作為 addSyncStanzaListener方法的第二個參數這樣就只能接受IQ消息了。

 

消息發送

 /**
     * 消息發送
     *
     * @param from    誰發送
     * @param to      發送給誰
     * @param content 發送內容
     */
    @Override
    public void sendMessage(String from, String to, String content) throws SmackException.NotConnectedException {


        Message msg = new Message(to, content);
        msg.setFrom(from);
        connection.sendStanza(msg);


//            ChatManager chatmanager = ChatManager.getInstanceFor(connection);
//            Chat newChat = chatmanager.createChat(to, new ChatMessageListener() {
//                @Override
//                public void processMessage(Chat chat, Message message) {
//                    LogHelper.i(TAG, "接收到消息:" + message);
//
//                }
//            });
//           newChat.sendMessage(content);


    }

我這里可以看到有兩種方式,一種直接原始的發送Message包,另一種就是被我注釋的代碼段,通過ChatManager來發送消息

添加好友:

/**
     * 添加好友
     * @param user 用戶jid
     * @param name 添加好友備注名稱
     * @param groups  好友添加到的分組,可以過個組
     * @throws Exception
     */
    @Override
    public void addRosterItem(String user, String name, String[] groups) throws Exception{
        Roster roster=Roster.getInstanceFor(connection);
        roster.createEntry(user,name,null);
    }

這里沒神馬好講的,因為上面設置了 roster.setSubscriptionMode(Roster.SubscriptionMode.manual);//設置添加好友,需要對方確認  這種模式,添加好友需要對方確認

最后:

今天就此結束,雖然只有這些方法,如果你結合前面第一篇XMPP協議簡介弄懂原理,我們就共同進步了:)。希望我們一起每天進步一點。

合肥程序員群:49313181。    合肥實名程序員群:128131462 (不願透露姓名和信息者勿加入)
Q  Q:408365330     E-Mail:egojit@qq.com
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM