用Socket.io打造你的聊天和消息推送

你還在用第三方的框架實現(xiàn)聊天和消息推送?快快試試吧


Socket.IO(官網(wǎng))介紹

是一個跨平臺的聊天框架,可以實現(xiàn) web 端和移動端的實時聊天,簡單說就是用來做聊天和消息推送的。最初以為項目做消息推送會直接使用第三方的,比如小米、極光啥的,但是,架構(gòu)師說我們不用第三方的,自己要搭建消息推送,Socket.io技術(shù)很成熟了。所以就這樣開始了Socket.io的學(xué)習(xí)之路。第一次接觸Socket.io以為很難做,因為不了解,加上能找的資源有限(上網(wǎng)查找的都是英文資料,有關(guān)于Android端的Socke.io更是少之又少,除非你去stackoverflow里面去找,英語還得好),所以寫篇文章記錄下實現(xiàn)該功能流程以及遇到的問題。
官方Demo,Github的Demo,這兩個 demo 都是一個聊天室,可以在里面聊天,剛開始弄這個的時候發(fā)現(xiàn)有好多哥們在那里,還找?guī)讉€一起在做聊天功能的小伙伴,快快下載試試吧。

本文不說服務(wù)端的搭建和web端的實現(xiàn),只是來說說 Android 端如何使用Socket.io實現(xiàn)消息推送功能。

(1)導(dǎo)包

Android Studio 導(dǎo)包,一共有兩種情況(PS: Eclipse 用戶也不要哭,下面會教你怎樣獲取到 JAR

  1. 第一種情況 compile 'com.github.nkzawa:socket.io-client:0.3.0'

  2. 第二種情況

     compile ('io.socket:socket.io-client:0.7.0') {
       // excluding org.json which is provided by Android
       exclude group: 'org.json', module: 'json'
     }
    

注意:兩個包的區(qū)別,如果你的項目沒有用到 Https ,那么你可以使用兩個當(dāng)中的一個。如果有用到 Https ,那么你就要用二個包,不然你會連接不上 Https 的,具體的連接方式,以下會介紹。

第一種情況的導(dǎo)包獲取到的 JAR 形式

第二種情況的導(dǎo)包獲取到的 JAR 形式

PSAndroid Studio 用戶直接跳過(如果你想看看你的Studio下載的 JAR 放在那里也可以看看)。Eclipse 用戶獲取 Jar,如果你會去遠(yuǎn)程倉庫下載 Jar,那么你就去吧,也就是幾個 JAR 而已,不然的話你還是得借助 Android Studio 來獲取 Jar。

用Studio導(dǎo)入包后找到你的 External Libraries ,選中你的 JAR ,如 engine.io-client-0.7.0 點擊右鍵,點擊 Library Properties ,會彈出一個對話框,Copy 這個URL,打開我的電腦,粘貼到導(dǎo)航欄點擊確定就可以看到你的 JAR了,但是,這個 JAR 是一個資源文件的 JAR ,里面有源碼的,我們不需要這個,點擊back后退,一般會有三個文件夾,其中的一個就是你需要的 JAR了。流程圖如下:



(2)代碼使用

導(dǎo)完包剩下的就是代碼的使用了。由于 Socket.io 封裝得很好,所以我們能用到的類和方法不多,也就幾個而已。

  • 獲取 Socket 和設(shè)置 url : mSocket = IO.socket( "http://192.168.205.125:10443" );
  • 連接 mSocket.mSocket.connect();。
  • 發(fā)送消息 mSocket.emit( "newMessage", data );,這里需要注意的是: data 是 JSONObject 的類。
  • 消息監(jiān)聽 mSocket.on( Socket.EVENT_CONNECT, onConnect );// 連接成功
  • 斷開連接 mSocket.disconnect();。
  • 斷開消息監(jiān)聽 mSocket.off( Socket.EVENT_CONNECT, onConnect );

就是這么幾個方法就可以實現(xiàn)消息推送或者實現(xiàn)聊天了。由于本文主要是實現(xiàn)消息推送的功能,所以把主要的代碼放在了 Service,順便把流程寫一下,方便初學(xué)者學(xué)習(xí)。

    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.support.annotation.Nullable;
    import android.util.Log;
    
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import java.net.URISyntaxException;
    import java.security.KeyManagementException;
    import java.security.NoSuchAlgorithmException;
    import java.security.cert.CertificateException;
    import java.security.cert.X509Certificate;
    
    import javax.net.ssl.HostnameVerifier;
    import javax.net.ssl.SSLContext;
    import javax.net.ssl.SSLSession;
    import javax.net.ssl.TrustManager;
    import javax.net.ssl.X509TrustManager;
    
    import io.socket.client.IO;
    import io.socket.client.Socket;
    import io.socket.emitter.Emitter;
    
    public class MessagePushService extends Service {
        
        private static final String TAG = MessagePushService.class.getSimpleName();
        
        private Socket mSocket;
        
        private boolean isConnected;
        
        /**
         * 初始化Socket,Https的連接方式
         */
        private void initSocketHttps() {
            SSLContext sc = null;
            TrustManager[] trustCerts = new TrustManager[] { new X509TrustManager() {
                
                @Override
                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
                
                @Override
                public void checkServerTrusted( X509Certificate[] chain, String authType )
                    throws CertificateException {
                }
                
                @Override
                public void checkClientTrusted( X509Certificate[] chain, String authType )
                    throws CertificateException {
                    
                }
            } };
            try {
                sc = SSLContext.getInstance( "TLS" );
                sc.init( null, trustCerts, null );
                IO.Options opts = new IO.Options();
                opts.sslContext = sc;
                opts.hostnameVerifier = new HostnameVerifier() {
                    @Override
                    public boolean verify( String s, SSLSession sslSession ) {
                        return true;
                    }
                };
                mSocket = IO.socket( "https://192.168.205.125:10443", opts );
            } catch ( NoSuchAlgorithmException e ) {
                e.printStackTrace();
            } catch ( KeyManagementException e ) {
                e.printStackTrace();
            } catch ( URISyntaxException e ) {
                e.printStackTrace();
            }
        }
        
        /**
         * 初始化Socket,Http的連接方式
         */
        private void initSocketHttp() {
            try {
                mSocket = IO.socket( "http://192.168.205.125:10443" ); // 初始化Socket
            } catch ( URISyntaxException e ) {
                e.printStackTrace();
            }
        }
        
        private void connectSocket() {
            try {
                mSocket.connect();
                JSONObject jsonObject = new JSONObject();
                jsonObject.put( "userName", "小王" ); // 這里一般是設(shè)置登錄名
                mSocket.emit( "loginName", jsonObject ); // 發(fā)送登錄人
            } catch ( JSONException e ) {
                e.printStackTrace();
            }
            
            mSocket.on( Socket.EVENT_CONNECT, onConnect );// 連接成功
            mSocket.on( Socket.EVENT_DISCONNECT, onDisconnect );// 斷開連接
            mSocket.on( Socket.EVENT_CONNECT_ERROR, onConnectError );// 連接異常
            mSocket.on( Socket.EVENT_CONNECT_TIMEOUT, onConnectTimeoutError );// 連接超時
            mSocket.on( "newMessage", onConnectMsg );// 監(jiān)聽消息事件回調(diào)
        }
        
        private void disConnectSocket() {
            mSocket.disconnect();
            
            mSocket.off( Socket.EVENT_CONNECT, onConnect );// 連接成功
            mSocket.off( Socket.EVENT_DISCONNECT, onDisconnect );// 斷開連接
            mSocket.off( Socket.EVENT_CONNECT_ERROR, onConnectError );// 連接異常
            mSocket.off( Socket.EVENT_CONNECT_TIMEOUT, onConnectTimeoutError );// 連接超時
            mSocket.off( "newMessage", onConnectMsg );// 監(jiān)聽消息事件回調(diào)
        }
        
        private Emitter.Listener onConnectMsg = new Emitter.Listener() {
            @Override
            public void call( final Object... args ) {
                // 在這里處理你的消息
                Log.e( TAG, "服務(wù)器返回來的消息 : " + args[0] );
            }
        };
        
        /**
         * 實現(xiàn)消息回調(diào)接口
         */
        private Emitter.Listener onConnect = new Emitter.Listener() {
            @Override
            public void call( final Object... args ) {
                Log.e( TAG, "連接成功 " + args[0] );
                if (!isConnected) { // 如果已經(jīng)斷開,重新發(fā)送
                    try {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put( "userName", "小王" ); // 這里一般是設(shè)置登錄名
                        mSocket.emit( "loginName", jsonObject ); // 發(fā)送登錄人
                    } catch ( JSONException e ) {
                        e.printStackTrace();
                    }
                    isConnected = true;
                }
            }
        };
        
        private Emitter.Listener onDisconnect = new Emitter.Listener() {
            @Override
            public void call( Object... args ) {
                Log.e( TAG, "斷開連接 " + args[0] );
                isConnected = false;
            }
        };
        
        private Emitter.Listener onConnectError = new Emitter.Listener() {
            @Override
            public void call( final Object... args ) {
                Log.e( TAG, "連接 失敗" + args[0] );
            }
        };
        
        private Emitter.Listener onConnectTimeoutError = new Emitter.Listener() {
            @Override
            public void call( final Object... args ) {
                Log.e( TAG, "連接 超時" + args[0] );
                
            }
        };
        
        @Nullable
        @Override
        public IBinder onBind( Intent intent ) {
            return null;
        }
    }
    

就這樣完成了一個消息推送,不需要集成什么東西,也不用受制于人。

總結(jié):剛開始做這個的時候找不到各種 JAR (項目還是在使用Eclipse開發(fā)中,所以還是需要去找到 JAR),那會自己才倒騰 Android Studio 沒多久,把 Demo 下載下來運行沒問題,然后就去找 這個資源的 JAR,準(zhǔn)備集成到 項目里面去,一開始以為只要 engine.io-client JAR 和 socket.io-client JAR ,結(jié)果就報錯了,然后不斷的查資料找包(這時候還不知道 Android Studio 已經(jīng)把JAR 下載到電腦了),最后才知道 Android Studio 下載的JAR在哪里(這才有了前面的找JAR),等到所有的JAR找到后,集成到了項目里面去,最后還是不斷的報 NoClassDefFoundError ,把這些JAR全部放到 Android Studio 集成運行卻沒有問題,這才發(fā)現(xiàn)是我的 Eclipse 問題,等找到這些原因,時間已經(jīng)過去兩天了,悲劇呀,然后快速的集成到項目,最終運行一切Ok啦!但是,如果你覺得故事就這樣沒了,那就太沒意思了,這個時候公司的接口全部轉(zhuǎn)成 https ,悲劇呀,項目運行不起來了。花時間找源碼,看看怎樣實現(xiàn),最終在stackoverflow找到了關(guān)于加載證書的方式,但是翻源碼的時候卻發(fā)現(xiàn)沒有這個 opts.hostnameVerifier 屬性,再一次悲?。ㄟ@個時候,我用的是上面的第一種JAR,也就是官網(wǎng)介紹的: compile 'com.github.nkzawa:socket.io-client:0.3.0')。這個時候時候發(fā)現(xiàn)了 Socket.io 官網(wǎng)的介紹和 Github 的介紹是兩種不同的 JAR,最后導(dǎo)入 githubJAR(第二種導(dǎo)包方式,所以你還是直接用這個包吧) 確實可以運行了。滿滿的坑呀!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容