yy1717
2019-12-30 13f7b5e09031ac94f10de3d4ced32143fb6fc326
首次加入ndk
25个文件已添加
1142 ■■■■■ 已修改文件
im_lib/.gitignore 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/build.gradle 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/consumer-rules.pro 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/libs/netty-all-4.1.43.jar 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/proguard-rules.pro 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/androidTest/java/com/anyun/im_lib/ExampleInstrumentedTest.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/AndroidManifest.xml 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/ExecutorServiceFactory.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/HeartbeatHandler.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/HeartbeatRespHandler.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/IMSClientFactory.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/IMSConfig.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/LoginAuthRespHandler.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/MsgDispatcher.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/MsgTimeOutTimerManager.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/MsgTimeoutTimer.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/im/IMSClientBootstrap.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/interf/IMSClientInteface.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/listener/IMSConnectStatusCallback.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/listener/OnEventListener.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/netty/NettyTcpClient.java 378 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/netty/TCPChannelInitializerHandler.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/java/com/anyun/im_lib/netty/TCPReadHandler.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/main/res/values/strings.xml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/src/test/java/com/anyun/im_lib/ExampleUnitTest.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
im_lib/.gitignore
New file
@@ -0,0 +1 @@
/build
im_lib/build.gradle
New file
@@ -0,0 +1,34 @@
apply plugin: 'com.android.library'
android {
    compileSdkVersion 29
    defaultConfig {
        minSdkVersion 21
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        consumerProguardFiles 'consumer-rules.pro'
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.0.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
}
im_lib/consumer-rules.pro
im_lib/libs/netty-all-4.1.43.jar
Binary files differ
im_lib/proguard-rules.pro
New file
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
#   http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
#   public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
im_lib/src/androidTest/java/com/anyun/im_lib/ExampleInstrumentedTest.java
New file
@@ -0,0 +1,27 @@
package com.anyun.im_lib;
import android.content.Context;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
 * Instrumented test, which will execute on an Android device.
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
    @Test
    public void useAppContext() {
        // Context of the app under test.
        Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
        assertEquals("com.anyun.im_lib.test", appContext.getPackageName());
    }
}
im_lib/src/main/AndroidManifest.xml
New file
@@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.anyun.im_lib" />
im_lib/src/main/java/com/anyun/im_lib/ExecutorServiceFactory.java
New file
@@ -0,0 +1,100 @@
package com.anyun.im_lib;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
 * MyApplication2
 * Created by lzw on 2019/12/2. 14:15:59
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class ExecutorServiceFactory {
    /*** 管理线程组,负责重连***/
    private ExecutorService bossPool;
    /*** 工作线程组,负责心跳***/
    private ExecutorService workPool;
    /**
     * 初始化Boss线程池
     */
    public synchronized void initBossLoopGroup(){
        initBossLoopGroup(1);
    }
    /**
     * 初始化work线程池
     */
    public synchronized void initWorkLoopGroup(){
        initWorkLoopGroup(1);
    }
    /**
     * 执行Boss任务
     * @param runnable
     */
    public void  execBossTask(Runnable runnable){
        if (bossPool == null){
            initBossLoopGroup();
        }
        bossPool.execute(runnable);
    }
    /**
     * 执行work任务
     * @param runnable
     */
    public void  execWorkTask(Runnable runnable){
        if (workPool == null){
            initWorkLoopGroup();
        }
        workPool.execute(runnable);
    }
    private void initWorkLoopGroup(int size) {
        destroyWorkLoopGroup();
        workPool = Executors.newFixedThreadPool(size);
    }
    private void initBossLoopGroup(int size) {
        destroyBossLoopGroup();
        bossPool = Executors.newFixedThreadPool(size);
    }
    /**
     * 释放boss线程池
     */
    private void destroyBossLoopGroup() {
        if (bossPool != null){
            try {
                bossPool.shutdownNow();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                bossPool = null;
            }
        }
    }
 /**
     * 释放work线程池
     */
 public void destroyWorkLoopGroup() {
        if (workPool != null){
            try {
                workPool.shutdownNow();
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                workPool = null;
            }
        }
    }
    /**
     * 释放所有线程
     */
    public void destroy() {
        destroyBossLoopGroup();
        destroyWorkLoopGroup();
    }
}
im_lib/src/main/java/com/anyun/im_lib/HeartbeatHandler.java
New file
@@ -0,0 +1,12 @@
package com.anyun.im_lib;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
 * MyApplication2
 * Created by lzw on 2019/12/4. 11:31:25
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class HeartbeatHandler extends ChannelInboundHandlerAdapter  {
}
im_lib/src/main/java/com/anyun/im_lib/HeartbeatRespHandler.java
New file
@@ -0,0 +1,22 @@
package com.anyun.im_lib;
import com.anyun.im_lib.interf.IMSClientInteface;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
 * MyApplication2
 * Created by lzw on 2019/12/4. 11:50:05
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class HeartbeatRespHandler extends ChannelInboundHandlerAdapter {
    private static final String TAG = HeartbeatRespHandler.class.getSimpleName();
    private IMSClientInteface imsClient;
    public HeartbeatRespHandler(IMSClientInteface imsClient) {
        this.imsClient = imsClient;
    }
}
im_lib/src/main/java/com/anyun/im_lib/IMSClientFactory.java
New file
@@ -0,0 +1,16 @@
package com.anyun.im_lib;
import com.anyun.im_lib.interf.IMSClientInteface;
import com.anyun.im_lib.netty.NettyTcpClient;
/**
 * MyApplication2
 * Created by lzw on 2019/12/2. 13:13:44
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class IMSClientFactory {
    public static IMSClientInteface getIMSClient() {
        return NettyTcpClient.getInstance();
    }
}
im_lib/src/main/java/com/anyun/im_lib/IMSConfig.java
New file
@@ -0,0 +1,28 @@
package com.anyun.im_lib;
/**
 * MyApplication2
 * Created by lzw on 2019/12/2. 14:16:22
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class IMSConfig {
    public static final int CONNECT_STATE_FAILURE = -1;
    public static final int DEFAULT_RECONNECT_BASE_DELAY_TIME = 3 * 1000;
    public static final int DEFAULT_CONNECT_TIMEOUT = 10 * 1000;
    public static final int DEFAULT_HEARTBEAT_INTERVAL_FOREGROUND = 3 * 1000;
    public static final int DEFAULT_HEARTBEAT_INTERVAL_BACKGROUND = 30 * 1000;
    /*** 应用在前台标识***/
    public static final int APP_STATUS_FOREGROUND = 0;
    /*** 应用在后台标识***/
    public static final int APP_STATUS_BACKGROUND = -1;
    /*** 默认消息发送失败重发次数 ***/
    public static final int DEFAULT_RESEND_COUNT = 3;
    public static final int DEFAULT_RESEND_INTERVAL = 8 * 1000;
    /******默认重连一个周期失败间隔时长******/
    public static final int DEFAULT_RECONNECT_INTERVAL = 3 * 1000;
    public static final int CONNECT_STATE_CONNECTING = 0;
    public static final int CONNECT_STATE_SUCCESSFUL = 1;
}
im_lib/src/main/java/com/anyun/im_lib/LoginAuthRespHandler.java
New file
@@ -0,0 +1,42 @@
package com.anyun.im_lib;
import android.util.Log;
import com.anyun.im_lib.interf.IMSClientInteface;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
 * MyApplication2
 * 设备鉴权
 * Created by lzw on 2019/12/4. 11:25:33
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class LoginAuthRespHandler extends ChannelInboundHandlerAdapter {
    private static final String TAG = LoginAuthRespHandler.class.getSimpleName();
    private IMSClientInteface imsClient;
    /**
     * 构造函数
     * @param imsClient
     */
    public LoginAuthRespHandler(IMSClientInteface imsClient) {
        this.imsClient = imsClient;
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
        Log.i(TAG, "channelReadComplete");
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        super.channelRead(ctx, msg);
        Log.i(TAG, "channelRead");
        // TODO: 2019/12/4
    }
}
im_lib/src/main/java/com/anyun/im_lib/MsgDispatcher.java
New file
@@ -0,0 +1,15 @@
package com.anyun.im_lib;
import com.anyun.im_lib.listener.OnEventListener;
/**
 * MyApplication2
 * Created by lzw on 2019/12/2. 14:02:14
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class MsgDispatcher {
    public void setOnEventListener(OnEventListener listener) {
    }
}
im_lib/src/main/java/com/anyun/im_lib/MsgTimeOutTimerManager.java
New file
@@ -0,0 +1,33 @@
package com.anyun.im_lib;
import com.anyun.im_lib.interf.IMSClientInteface;
import com.anyun.im_lib.netty.NettyTcpClient;
import java.util.HashMap;
import java.util.Map;
/**
 * MyApplication2
 * Created by lzw on 2019/12/2. 14:03:15
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class MsgTimeOutTimerManager {
    private IMSClientInteface imsClient;
    private Map<String,MsgTimeoutTimer> mMsgTimeoutMap = new HashMap<>();
    public MsgTimeOutTimerManager(NettyTcpClient nettyTcpClient) {
        this.imsClient = nettyTcpClient;
    }
    /**
     * 添加消息到发送超时管理器
     * @param message
     */
    public void add(String message){
        if (message == null ){
        }
    }
}
im_lib/src/main/java/com/anyun/im_lib/MsgTimeoutTimer.java
New file
@@ -0,0 +1,36 @@
package com.anyun.im_lib;
import android.util.Log;
import com.anyun.im_lib.interf.IMSClientInteface;
import java.util.Timer;
import java.util.TimerTask;
/**
 * MyApplication2
 * Created by lzw on 2019/12/2. 15:09:39
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class MsgTimeoutTimer extends Timer {
    private static final String TAG = "MsgTimeoutTimer";
    private IMSClientInteface imsClient;
    private Object message;//发送的消息
    private  int currentResendCount;//当前重发次数
    private  MsgTimeoutTask task;//消息发送超时任务
    public MsgTimeoutTimer(IMSClientInteface imsClient) {
        Log.i(TAG, "MsgTimeoutTimer: ");
        this.imsClient = imsClient;
        this.schedule(task,imsClient.getResendInterval(),imsClient.getResendInterval());
    }
    private class MsgTimeoutTask extends TimerTask{
        @Override
        public void run() {
        }
    }
}
im_lib/src/main/java/com/anyun/im_lib/im/IMSClientBootstrap.java
New file
@@ -0,0 +1,61 @@
package com.anyun.im_lib.im;
import android.util.Log;
import com.anyun.im_lib.IMSClientFactory;
import com.anyun.im_lib.interf.IMSClientInteface;
import java.util.Vector;
import static android.content.ContentValues.TAG;
/**
 * MyApplication2
 * Created by lzw on 2019/12/2. 11:56:55
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class IMSClientBootstrap {
    private static final IMSClientBootstrap INSTANCE= new IMSClientBootstrap();
    private IMSClientInteface imsClient;
    /**标记IMSClientBootstrap是否已经初始化**/
    private boolean isAlive;
    /**
     *
     * @param userId
     * @param token
     * @param hosts
     * @param appStatus
     */
    public synchronized void init(String userId,String token,String hosts,int appStatus){
        if (!isAlive){
            Vector<String> serverUrlList = convertHosts(hosts);
            if (serverUrlList == null || serverUrlList.size() ==0){
                Log.i(TAG, "init IMLibClientBootstrap error,ims hosts is null");
                return;
            }
            isAlive = true;
            Log.i(TAG, "init IMLibClientBootstrap ,server="+hosts);
        }
        if (null != imsClient){
            imsClient.close();
        }
        //初始化IMSClientInteface
        imsClient = IMSClientFactory.getIMSClient();
        updateAppStatus(appStatus);
//        imsClient.init(serverUrlList,new OnEventListener(userId,token),new IMSConnectStatusCallback());
    }
    private void updateAppStatus(int appStatus) {
    }
    private Vector<String> convertHosts(String hosts) {
        //TODO
        return null;
    }
}
im_lib/src/main/java/com/anyun/im_lib/interf/IMSClientInteface.java
New file
@@ -0,0 +1,114 @@
package com.anyun.im_lib.interf;
import com.anyun.im_lib.MsgDispatcher;
import com.anyun.im_lib.MsgTimeOutTimerManager;
import com.anyun.im_lib.listener.IMSConnectStatusCallback;
import com.anyun.im_lib.listener.OnEventListener;
import java.util.Vector;
/**
 * MyApplication2
 * Created by lzw on 2019/12/2. 11:58:51
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public interface IMSClientInteface {
    /**
     * 初始化
     * @param serverUrlList
     * @param listener
     * @param callback
     */
    void init(Vector<String> serverUrlList, OnEventListener listener, IMSConnectStatusCallback callback);
    /**
     * 重置连接,也就是重连
     * 首次连接也可认为是重连
     */
    void resetConnect();
    /**
     * 重置连接,也就是重连
     * 首次连接也可认为是重连
     * 重载
     * @param isFirst  是否首次连接
     */
    void resetConnect(boolean isFirst);
    /**
     * 关闭连接,同时释放资源
     */
    void close();
    /**
     * 表示ims是否已经关闭
     * @return
     */
    boolean isClosed();
    /**
     * 发送消息
     * @param msg
     */
    void sendMsg(String msg);
    /**
     * 发送消息
     * 重载
     * @param msg
     * @param isJoinTimeoutManager 是否加入超时管理器
     */
    void sendMsg(String msg, boolean isJoinTimeoutManager);
    /**
     * 获取重连间隔时长
     * @return
     */
    int getReconnectInterval();
    /**
     * 获取连间隔时长
     * @return
     */
    int getConnectTimeout();
    /**
     * 获取应用在前台时心跳间隔
     * @return
     */
    int getForegroundHeartbeatInterval();
    /**
     * 设置app前后台状态
     * @param appStatus
     */
    void setAppStatus(int appStatus);
    /**
     * 获取应用在后台时心跳间隔
     * @return
     */
    int getBackgroundHeartbeatInterval();
    /**
     * 获取应用层消息发送超时重发次数
     * @return
     */
    int getResendCount();
    /**
     * 获取应用层消息发送超时间隔
     * @return
     */
    int getResendInterval();
    /**
     * 获取消息转发器
     * @return
     */
    MsgDispatcher getMsgDispatcher();
    MsgTimeOutTimerManager getMsgTimeOutTimerManager();
}
im_lib/src/main/java/com/anyun/im_lib/listener/IMSConnectStatusCallback.java
New file
@@ -0,0 +1,20 @@
package com.anyun.im_lib.listener;
/**
 * MyApplication2
 * Created by lzw on 2019/12/2. 13:18:05
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public interface IMSConnectStatusCallback {
    /***ims连接中*/
    void connecting();
    /***ims连接成功**/
    void onConnected();
    /***ims连接失败**/
    void onConnectFailed();
}
im_lib/src/main/java/com/anyun/im_lib/listener/OnEventListener.java
New file
@@ -0,0 +1,51 @@
package com.anyun.im_lib.listener;
/**
 * MyApplication2
 * Created by lzw on 2019/12/2. 13:17:49
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public interface OnEventListener {
    /**
     * 分发消息到应用层
     * @param message
     */
    void dispatchMsg(Object  message);
    /**
     * 从应用层获取网络是否可用
     * @return
     */
    boolean isNetWorkAvailable();
    /**
     * 获取重连间隔时长
     * @return
     */
    int getConnectTimeout();
    /**
     * 获取应用在前台时心跳间隔
     * @return
     */
    int getForegroundHeartbeatInterval();
    /**
     * 获取应用在后台时心跳间隔
     * @return
     */
    int getBackgroundHeartbeatInterval();
    /**
     * 获取应用层消息发送超时重发次数
     * @return
     */
    int getResendCount();
    /**
     * 获取应用层消息发送超时间隔
     * @return
     */
    int getResendInterval();
}
im_lib/src/main/java/com/anyun/im_lib/netty/NettyTcpClient.java
New file
@@ -0,0 +1,378 @@
package com.anyun.im_lib.netty;
import android.text.TextUtils;
import android.util.Log;
import com.anyun.im_lib.ExecutorServiceFactory;
import com.anyun.im_lib.HeartbeatHandler;
import com.anyun.im_lib.IMSConfig;
import com.anyun.im_lib.MsgDispatcher;
import com.anyun.im_lib.MsgTimeOutTimerManager;
import com.anyun.im_lib.interf.IMSClientInteface;
import com.anyun.im_lib.listener.IMSConnectStatusCallback;
import com.anyun.im_lib.listener.OnEventListener;
import java.util.Vector;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.timeout.IdleStateHandler;
/**
 * MyApplication2
 * Created by lzw on 2019/12/2. 13:14:52
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class NettyTcpClient implements IMSClientInteface {
    private static final String TAG = NettyTcpClient.class.getSimpleName();
    private static volatile NettyTcpClient instance;
    private Bootstrap bootstrap;
    private Channel channel;
    /****标识IMS是否已关闭***/
    private boolean isClosed = false;
    /****ims服务器地址组***/
    private Vector<String> serverUrlList;
    /****与应用层交互的listener***/
    private OnEventListener mOnEventListener;
    /*** ims连接状态回调监听器**/
    private IMSConnectStatusCallback imsConnectStatusCallback;
    /****消息转发器***/
    private MsgDispatcher msgDispatcher;
    /***线程池工厂**/
    private ExecutorServiceFactory loopGroup;
    /****是否正在进行重连***/
    private boolean isReconnecting = false;
    /*** ims连接状态,初始化为连接失败 ***/
    private int connectStatus = IMSConfig.CONNECT_STATE_FAILURE;
    /*** 重连间隔时长 ***/
    private int reconnectInterval = IMSConfig.DEFAULT_RECONNECT_BASE_DELAY_TIME;
    /*** 连接超时时长 ***/
    private int connectTimeOut = IMSConfig.DEFAULT_CONNECT_TIMEOUT;
    private int heartBeatInterval = IMSConfig.DEFAULT_HEARTBEAT_INTERVAL_FOREGROUND;
    private int foregroundHeartBeatInterval = IMSConfig.DEFAULT_HEARTBEAT_INTERVAL_FOREGROUND;
    private int backgroundHeartBeatInterval = IMSConfig.DEFAULT_HEARTBEAT_INTERVAL_BACKGROUND;
    private int appStatus = IMSConfig.APP_STATUS_FOREGROUND;
    /*** 消息发送超时重发次数***/
    private int resendCount = IMSConfig.DEFAULT_RESEND_COUNT;
    /*** 消息发送失败重发间隔时长***/
    private int resendInterval = IMSConfig.DEFAULT_RESEND_INTERVAL;
    /*** 当前连接host***/
    private String currentHost = null;
    /*** 当前连接port***/
    private int currentPort = -1;
    /*** 消息发送超时定时管理器***/
    private MsgTimeOutTimerManager msgTimeOutTimerManager;
    private NettyTcpClient(){
    }
    public static IMSClientInteface getInstance() {
        if (null == instance){
            synchronized (NettyTcpClient.class){
                if (null==instance){
                    instance = new NettyTcpClient();
                }
            }
        }
        return instance;
    }
    /**
     * 初始化
     * @param serverUrlList 服务器地址列表
     * @param listener 与应用层交互的listener
     * @param callback ims连接状态回调
     */
    @Override
    public void init(Vector<String> serverUrlList, OnEventListener listener, IMSConnectStatusCallback callback) {
        close();
        isClosed = false;
        this.serverUrlList = serverUrlList;
        this.mOnEventListener = listener;
        this.imsConnectStatusCallback = callback;
        msgDispatcher = new MsgDispatcher();
        msgDispatcher.setOnEventListener(listener);
        loopGroup = new ExecutorServiceFactory();
        loopGroup.initBossLoopGroup();
        msgTimeOutTimerManager = new MsgTimeOutTimerManager(this);
        /*** 进行第一次连接***/
        resetConnect(true);
    }
    @Override
    public void resetConnect() {
        this.resetConnect(false);
    }
    @Override
    public void resetConnect(boolean isFirst) {
        if (!isFirst){
            try {
                Thread.sleep(IMSConfig.DEFAULT_RECONNECT_INTERVAL);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //只有第一个调用者才能赋值并调用重连
        if (!isClosed && !isReconnecting){
            synchronized (this){
                if (!isClosed && !isReconnecting){
                    //标识重连任务进行中...
                    isReconnecting = true;
                    onConnectStatusCallback(IMSConfig.CONNECT_STATE_CONNECTING);
                    closeChannel();
                    loopGroup.execBossTask(new ResetConnectRunnable(isFirst));
                }
            }
        }
    }
    private class ResetConnectRunnable implements Runnable{
        private boolean isFirst;
        public ResetConnectRunnable(boolean isFirst) {
            this.isFirst = isFirst;
        }
        @Override
        public void run() {
            if (!isFirst){
                onConnectStatusCallback(IMSConfig.CONNECT_STATE_FAILURE);
            }
            try {
                //重连时,释放工作组线程池,也就是停止心跳
                loopGroup.destroyWorkLoopGroup();
                while (!isClosed){
                    if (!isNetworkAvaliable()){
                        try {
                            Thread.sleep(2000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        continue;
                    }
                    //网络可用才进行连接
                    int status;
                    if ((status=reConnect()) == IMSConfig.CONNECT_STATE_SUCCESSFUL){
                        onConnectStatusCallback(status);
                        //连接成功,调出循环
                        break;
                    }
                    if (status == IMSConfig.CONNECT_STATE_FAILURE){
                        onConnectStatusCallback(status);
                        try {
                            Thread.sleep(IMSConfig.DEFAULT_RECONNECT_INTERVAL);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            } finally {
                //标识重连任务停止
                isReconnecting = false;
            }
        }
    }
    /**
     * 重连,首次连接也认为是第一次重连
     * @return
     */
    private int reConnect() {
        if (!isClosed){
            try {
                //先释放EventLoop线程组
                if (bootstrap != null){
                    bootstrap.group().shutdownGracefully();
                }
            } finally {
                    bootstrap = null;
            }
            initBootstrap();
            return connectServer();
        }
        return IMSConfig.CONNECT_STATE_FAILURE;
    }
    /***
     * 初始化Bootstrap
     */
    private void initBootstrap() {
        EventLoopGroup loopGroup = new NioEventLoopGroup(4);
        bootstrap = new Bootstrap();
        bootstrap.group(loopGroup).channel(NioSocketChannel.class);
        // 设置该项以后,如果在两小时内没有数据通信时,TCP会自动发送一个活动探测数据报文
        bootstrap.option(ChannelOption.SO_KEEPALIVE,true);
        bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,getConnectTimeout());
        bootstrap.handler(new TCPChannelInitializerHandler(this));
    }
    private int connectServer(){
        if (serverUrlList == null || serverUrlList.size() == 0){
            return IMSConfig.CONNECT_STATE_FAILURE;
        }
        for (int i = 0; i < serverUrlList.size(); i++) {
            String serverUrl = serverUrlList.get(i);
            if (TextUtils.isEmpty(serverUrl)){
                return IMSConfig.CONNECT_STATE_FAILURE;
            }
        }
    }
    private boolean isNetworkAvaliable() {
        if (mOnEventListener != null){
            return mOnEventListener.isNetWorkAvailable();
        }
        return false;
    }
    private void onConnectStatusCallback(int connectStateConnecting) {
    }
    @Override
    public void close() {
        if (isClosed){
            return;
        }
        isClosed = true;
        /*** 关闭channel***/
        try {
            closeChannel();
        } catch (Exception e) {
            e.printStackTrace();
        }
        /*** 关闭bootstrap ***/
        try {
            if (bootstrap != null){
                bootstrap.group().shutdownGracefully();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        /*** 释放线程池 ***/
        try {
            if (loopGroup != null){
                loopGroup.destroy();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (serverUrlList != null){
                    serverUrlList.clear();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            isReconnecting = false;
            channel = null;
            bootstrap = null;
        }
    }
    private void closeChannel() {
        if (channel != null){
            removeHandler(HeartbeatHandler.class.getSimpleName());
            removeHandler(TCPReadHandler.class.getSimpleName());
            removeHandler(IdleStateHandler.class.getSimpleName());
        }
    }
    /**
     * 移除指定handler
     * @param handlerName
     */
    private void removeHandler(String handlerName) {
        try {
            if (channel.pipeline().get(handlerName) != null){
                channel.pipeline().remove(handlerName);
            }
        } catch (Exception e) {
            e.printStackTrace();
            Log.i(TAG, "removeHandler fail,handlerName="+handlerName);
        }
    }
    @Override
    public boolean isClosed() {
        return false;
    }
    @Override
    public void sendMsg(String msg) {
    }
    @Override
    public void sendMsg(String msg, boolean isJoinTimeoutManager) {
    }
    @Override
    public int getReconnectInterval() {
        return 0;
    }
    @Override
    public int getConnectTimeout() {
        return 0;
    }
    @Override
    public int getForegroundHeartbeatInterval() {
        return 0;
    }
    @Override
    public void setAppStatus(int appStatus) {
    }
    @Override
    public int getBackgroundHeartbeatInterval() {
        return 0;
    }
    @Override
    public int getResendCount() {
        return 0;
    }
    @Override
    public int getResendInterval() {
        return 0;
    }
    @Override
    public MsgDispatcher getMsgDispatcher() {
        return null;
    }
    @Override
    public MsgTimeOutTimerManager getMsgTimeOutTimerManager() {
        return null;
    }
}
im_lib/src/main/java/com/anyun/im_lib/netty/TCPChannelInitializerHandler.java
New file
@@ -0,0 +1,44 @@
package com.anyun.im_lib.netty;
import com.anyun.im_lib.HeartbeatRespHandler;
import com.anyun.im_lib.LoginAuthRespHandler;
import com.anyun.im_lib.interf.IMSClientInteface;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
/**
 * MyApplication2
 * channel初始化
 * Created by lzw on 2019/12/2. 15:56:39
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class TCPChannelInitializerHandler  extends ChannelInitializer<Channel> {
    private IMSClientInteface imsClient;
    public TCPChannelInitializerHandler(NettyTcpClient nettyTcpClient) {
        this.imsClient = nettyTcpClient;
    }
    @Override
    protected void initChannel(Channel channel) throws Exception {
        ChannelPipeline pipeline = channel.pipeline();
        //netty提供的自定义长度解码器,解决TP拆包/粘包问题
        // TODO: 2019/12/4
        //握手认证消息相应处理handler
        pipeline.addLast(LoginAuthRespHandler.class.getSimpleName(), new LoginAuthRespHandler(imsClient));
        //心跳消息响应处理handler
        pipeline.addLast(HeartbeatRespHandler.class.getSimpleName(), new HeartbeatRespHandler(imsClient));
        //接收消息处理handler
        pipeline.addLast(TCPReadHandler.class.getSimpleName(),new TCPReadHandler(imsClient));
    }
}
im_lib/src/main/java/com/anyun/im_lib/netty/TCPReadHandler.java
New file
@@ -0,0 +1,65 @@
package com.anyun.im_lib.netty;
import android.util.Log;
import com.anyun.im_lib.interf.IMSClientInteface;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
/**
 * MyApplication2
 * 消息接收处理handler
 * Created by lzw on 2019/12/4. 10:58:43
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class TCPReadHandler extends ChannelInboundHandlerAdapter {
    private static final String TAG = TCPReadHandler.class.getSimpleName();
    private IMSClientInteface imsClient;
    public TCPReadHandler(IMSClientInteface imsClient) {
        this.imsClient = imsClient;
    }
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        Log.i(TAG, "channelInactive");
        Channel channel = ctx.channel();
        if (channel != null){
            channel.close();
            ctx.close();
        }
        //触发重连
        imsClient.resetConnect(false);
    }
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        super.exceptionCaught(ctx, cause);
        Log.i(TAG, "exceptionCaught: "+cause.getMessage());
        Channel channel = ctx.channel();
        if (channel != null){
            channel.close();
        }
        //触发重连
        imsClient.resetConnect(false);
    }
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        super.channelRead(ctx, msg);
        // TODO: 2019/12/4
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        super.channelReadComplete(ctx);
        Log.i(TAG, "channelReadComplete");
    }
}
im_lib/src/main/res/values/strings.xml
New file
@@ -0,0 +1,3 @@
<resources>
    <string name="app_name">im_lib</string>
</resources>
im_lib/src/test/java/com/anyun/im_lib/ExampleUnitTest.java
New file
@@ -0,0 +1,17 @@
package com.anyun.im_lib;
import org.junit.Test;
import static org.junit.Assert.*;
/**
 * Example local unit test, which will execute on the development machine (host).
 *
 * @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
 */
public class ExampleUnitTest {
    @Test
    public void addition_isCorrect() {
        assertEquals(4, 2 + 2);
    }
}