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

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


Socket.IO(官网)介绍

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

用Socket.io打造你的聊天和消息推送_第1张图片

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

用Socket.io打造你的聊天和消息推送_第2张图片

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了。流程图如下:

用Socket.io打造你的聊天和消息推送_第3张图片

用Socket.io打造你的聊天和消息推送_第4张图片

用Socket.io打造你的聊天和消息推送_第5张图片

(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(第二种导包方式,所以你还是直接用这个包吧) 确实可以运行了。满满的坑呀!

你可能感兴趣的:(用Socket.io打造你的聊天和消息推送)