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

你还在用第三方的框架实现聊天和消息推送?快快试试吧


Socket.IO(官网)介绍

是一个跨平台的聊天框架,可以实现 web 端和移动端的实时聊天,简单说就是用来做聊天和消息推送的。最初以为项目做消息推送会直接使用第三方的,比如小米、极光啥的,但是,架构师说我们不用第三方的,自己要搭建消息推送,Socket.io技术很成熟了。所以就这样开始了Socket.io的学习之路。第一次接触Socket.io以为很难做,因为不了解,加上能找的资源有限(上网查找的都是英文资料,有关于Android端的Socke.io更是少之又少,除非你去stackoverflow里面去找,英语还得好),所以写篇文章记录下实现该功能流程以及遇到的问题。
官方DemoGithub的Demo,这两个 demo 都是一个聊天室,可以在里面聊天,刚开始弄这个的时候发现有好多哥们在那里,还找几个一起在做聊天功能的小伙伴,快快下载试试吧。

本文不说服务端的搭建和web端的实现,只是来说说 Android 端如何使用Socket.io实现消息推送功能。

(1)导包

Android Studio 导包,一共有两种情况(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'
     }
    

注意:两个包的区别,如果你的项目没有用到 Https ,那么你可以使用两个当中的一个。如果有用到 Https ,那么你就要用二个包,不然你会连接不上 Https 的,具体的连接方式,以下会介绍。

第一种情况的导包获取到的 JAR 形式

第二种情况的导包获取到的 JAR 形式

PSAndroid Studio 用户直接跳过(如果你想看看你的Studio下载的 JAR 放在那里也可以看看)。Eclipse 用户获取 Jar,如果你会去远程仓库下载 Jar,那么你就去吧,也就是几个 JAR 而已,不然的话你还是得借助 Android Studio 来获取 Jar

用Studio导入包后找到你的 External Libraries ,选中你的 JAR ,如 engine.io-client-0.7.0 点击右键,点击 Library Properties ,会弹出一个对话框,Copy 这个URL,打开我的电脑,粘贴到导航栏点击确定就可以看到你的 JAR了,但是,这个 JAR 是一个资源文件的 JAR ,里面有源码的,我们不需要这个,点击back后退,一般会有三个文件夹,其中的一个就是你需要的 JAR了。流程图如下:



(2)代码使用

导完包剩下的就是代码的使用了。由于 Socket.io 封装得很好,所以我们能用到的类和方法不多,也就几个而已。

  • 获取 Socket 和设置 url : mSocket = IO.socket( "http://192.168.205.125:10443" );
  • 连接 mSocket.mSocket.connect();
  • 发送消息 mSocket.emit( "newMessage", data );,这里需要注意的是: data 是 JSONObject 的类。
  • 消息监听 mSocket.on( Socket.EVENT_CONNECT, onConnect );// 连接成功
  • 断开连接 mSocket.disconnect();
  • 断开消息监听 mSocket.off( Socket.EVENT_CONNECT, onConnect );

就是这么几个方法就可以实现消息推送或者实现聊天了。由于本文主要是实现消息推送的功能,所以把主要的代码放在了 Service,顺便把流程写一下,方便初学者学习。

    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", "小王" ); // 这里一般是设置登录名
                mSocket.emit( "loginName", jsonObject ); // 发送登录人
            } 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 );// 监听消息事件回调
        }
        
        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 );// 监听消息事件回调
        }
        
        private Emitter.Listener onConnectMsg = new Emitter.Listener() {
            @Override
            public void call( final Object... args ) {
                // 在这里处理你的消息
                Log.e( TAG, "服务器返回来的消息 : " + args[0] );
            }
        };
        
        /**
         * 实现消息回调接口
         */
        private Emitter.Listener onConnect = new Emitter.Listener() {
            @Override
            public void call( final Object... args ) {
                Log.e( TAG, "连接成功 " + args[0] );
                if (!isConnected) { // 如果已经断开,重新发送
                    try {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put( "userName", "小王" ); // 这里一般是设置登录名
                        mSocket.emit( "loginName", jsonObject ); // 发送登录人
                    } 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;
        }
    }
    

就这样完成了一个消息推送,不需要集成什么东西,也不用受制于人。

总结:刚开始做这个的时候找不到各种 JAR (项目还是在使用Eclipse开发中,所以还是需要去找到 JAR),那会自己才倒腾 Android Studio 没多久,把 Demo 下载下来运行没问题,然后就去找 这个资源的 JAR,准备集成到 项目里面去,一开始以为只要 engine.io-client JAR 和 socket.io-client JAR ,结果就报错了,然后不断的查资料找包(这时候还不知道 Android Studio 已经把JAR 下载到电脑了),最后才知道 Android Studio 下载的JAR在哪里(这才有了前面的找JAR),等到所有的JAR找到后,集成到了项目里面去,最后还是不断的报 NoClassDefFoundError ,把这些JAR全部放到 Android Studio 集成运行却没有问题,这才发现是我的 Eclipse 问题,等找到这些原因,时间已经过去两天了,悲剧呀,然后快速的集成到项目,最终运行一切Ok啦!但是,如果你觉得故事就这样没了,那就太没意思了,这个时候公司的接口全部转成 https ,悲剧呀,项目运行不起来了。花时间找源码,看看怎样实现,最终在stackoverflow找到了关于加载证书的方式,但是翻源码的时候却发现没有这个 opts.hostnameVerifier 属性,再一次悲剧(这个时候,我用的是上面的第一种JAR,也就是官网介绍的: compile 'com.github.nkzawa:socket.io-client:0.3.0')。这个时候时候发现了 Socket.io 官网的介绍和 Github 的介绍是两种不同的 JAR,最后导入 githubJAR(第二种导包方式,所以你还是直接用这个包吧) 确实可以运行了。满满的坑呀!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容