yy1717
2019-12-30 9e1097ca3eff1c25df69990fc1098144c7cbcb56
Merge branch 'master' of https://gitee.com/endian11/DriveJudge

# Conflicts:
# im_lib/src/main/java/com/anyun/im_lib/ExecutorServiceFactory.java
# im_lib/src/main/java/com/anyun/im_lib/IMSConfig.java
# im_lib/src/main/java/com/anyun/im_lib/LoginAuthRespHandler.java
# im_lib/src/main/java/com/anyun/im_lib/MsgDispatcher.java
# im_lib/src/main/java/com/anyun/im_lib/interf/IMSClientInteface.java
# im_lib/src/main/java/com/anyun/im_lib/listener/IMSConnectStatusCallback.java
# im_lib/src/main/java/com/anyun/im_lib/listener/OnEventListener.java
# im_lib/src/main/java/com/anyun/im_lib/netty/NettyTcpClient.java
# im_lib/src/main/java/com/anyun/im_lib/netty/TCPChannelInitializerHandler.java
# im_lib/src/main/java/com/anyun/im_lib/netty/TCPReadHandler.java
5个文件已修改
17个文件已添加
1095 ■■■■■ 已修改文件
app/build.gradle 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/DB/Student.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/DB/WorkRoomDataBase.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/bean/DriveExamProtocol.java 132 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/bean/GainStuMessage.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/bean/KeepaliveMessage.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/bean/RegisterMessage.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/bean/StartExamMessage.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/cEventCenter/CEvent.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/cEventCenter/ObjectPool.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/cEventCenter/PooledObject.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/fragment/TcpFragment.java 48 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/fragment/TrainFragment.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/im/IMSClientBootstrap.java 109 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/im/IMSClientFactory.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/im/IMSConnectStatusListener.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/im/IMSEventListener.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/im/IMessageProcessor.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/im/MessageProcessor.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/im/handler/AbstractMessageHandler.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/im/handler/IMessageHandler.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/src/main/java/safeluck/drive/evaluation/util/CThreadPoolExecutor.java 332 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
app/build.gradle
@@ -55,4 +55,5 @@
    implementation project(path: ':lib')
    implementation 'com.google.code.gson:gson:2.8.6'
    implementation 'com.facebook.stetho:stetho:1.5.0'
    implementation project(path: ':im_lib')
}
app/src/main/java/safeluck/drive/evaluation/DB/Student.java
@@ -30,6 +30,16 @@
    @ColumnInfo(name = "sex")
    private int sex;
    public long getBegin_time() {
        return begin_time;
    }
    public void setBegin_time(long begin_time) {
        this.begin_time = begin_time;
    }
    private long begin_time;
    public Student(long stu_id, @NonNull String name, String ID, int sex) {
        this.stu_id = stu_id;
        this.name = name;
app/src/main/java/safeluck/drive/evaluation/DB/WorkRoomDataBase.java
@@ -7,6 +7,7 @@
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;
@@ -31,7 +32,7 @@
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
@Database(entities = {Student.class, CriteriaForI.class, FailedProj.class, CriteriaForII.class},version = 1,exportSchema = false)
@Database(entities = {Student.class, CriteriaForI.class, FailedProj.class, CriteriaForII.class},version = 2,exportSchema = false)
public abstract class WorkRoomDataBase extends RoomDatabase {
    private static final String TAG = "WorkRoomDataBase";
    public abstract StudentDao getstudentDao();
@@ -46,6 +47,14 @@
    private static final int NUMBER_OF_THREADS = 4;
    public static final ExecutorService dataBaseWriteExecutor = Executors.newFixedThreadPool(NUMBER_OF_THREADS);
    static Migration migration = new Migration(1,2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL("alter table student_table ADD  COLUMN begin_time INTEGER NOT NULL DEFAULT 0");
        }
    };
private static final Migration[] ALL_MIGRATIONS = new Migration[]{migration};
     public static WorkRoomDataBase getWorkRoomDataBase(final Context mContext){
         Log.i(TAG, "getWorkRoomDataBase");
@@ -54,6 +63,7 @@
                if (workRoomDataBase == null){
                    Log.i(TAG, "getWorkRoomDataBase==null ");
                    workRoomDataBase = Room.databaseBuilder(mContext.getApplicationContext(),WorkRoomDataBase.class,"work_database")
                            .addMigrations(ALL_MIGRATIONS)
                            .addCallback(new Callback() {
                                @Override
                                public void onCreate(@NonNull SupportSQLiteDatabase db) {
app/src/main/java/safeluck/drive/evaluation/bean/DriveExamProtocol.java
New file
@@ -0,0 +1,132 @@
package safeluck.drive.evaluation.bean;
import android.util.Log;
import com.anyun.im_lib.util.ByteUtil;
/**
 * MyApplication2
 * Created by lzw on 2019/12/17. 15:39:23
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public abstract class DriveExamProtocol {
    private static final String TAG = "DriveExamProtocol";
    /***========================
                消息结构表
    标识位    消息头    消息体    校验码    标识位
     0x7E                                                       0x7E
    ======================**/
   // 标识位(字节流开始)
    private byte MESSAGE_HEAD = 0x7e;
    //标识位(字节流结束)
    private byte MESSAGE_TAIL = 0x7e;
    //校验码指从消息头开始,同后一字节异或,直到校验码前一个字节,占用一个字节
//校验码  先暂时写死 todo
    private byte checkCode = 0x78;
/***===========以下是消息头=============***/
    //协议版本号    BYTE    235,固定
    private short protocol_version = 235;
//消息ID
    private short msg_id;
   /** 消息体属性格式结构表
15    14    13   |    12    11    10    | 9    8    7    6    5    4    3    2    1    0
    保留          数据加密方式         消息体长度
**/
    //消息体属性
    private short msg_property  =2;
    /**
       * 终端手机号 字符串长度必须为16
    **/
    private String phoneOnTerminal = "0008618513021245";
    /**
     *
     * 13    消息流水号    WORD    按发送顺序从0开始循环累加
     *
     */
    public static short msg_serial_num=0;
    //15    预留    BYTE    预留
    private byte reserve = 0x00;
    /***===========消息头结束=============***/
    /**
     * 消息体 需要子类实现
     */
    protected abstract byte[] createMessageBody();
    /**
     * 构造函数
     * @param msg_id 消息ID
     */
    public DriveExamProtocol(short msg_id) {
        this.msg_id = msg_id;
    }
    /**
     * 消息转为byte数组 7E......7E
     * @return
     */
    public byte[] toBytes(){
        byte[] desBytes = new byte[1+16+2+1+1];
        int pos = 0;
        //标识位
        desBytes[pos] = MESSAGE_HEAD;
        pos++;
        //协议版本号
        byte[] protoVersion = ByteUtil.shortGetByte(protocol_version);
        System.arraycopy(protoVersion,0,desBytes,pos,protoVersion.length);
        pos +=protoVersion.length;
        //消息ID
        byte[] msgIdBytes = ByteUtil.shortGetBytes(msg_id);
        System.arraycopy(msgIdBytes,0,desBytes,pos,msgIdBytes.length);
        pos+=msgIdBytes.length;
        //消息体属性
        byte[] msg_pro_bytes = ByteUtil.shortGetBytes(msg_property);
        System.arraycopy(msg_pro_bytes,0,desBytes,pos,msg_pro_bytes.length);
        pos+=msg_pro_bytes.length;
        //终端手机号
        byte[] phoneBytes = ByteUtil.str2Bcd(phoneOnTerminal);
        System.arraycopy(phoneBytes,0,desBytes,pos,phoneBytes.length);
        pos+=phoneBytes.length;
        //消息流水号
        byte[] msg_serialNum = ByteUtil.shortGetBytes(msg_serial_num++);
        System.arraycopy(msg_serialNum,0,desBytes,pos,msg_serialNum.length);
        pos+=msg_serialNum.length;
        //保留byte
        desBytes[pos] = reserve;
        pos++;
        //消息体
        byte[] messageBodyBytes = createMessageBody();
        System.arraycopy(messageBodyBytes,0,desBytes,pos,messageBodyBytes.length);
        pos+=messageBodyBytes.length;
        //校验码
        // TODO: 2019/12/18 校验码需要计算   还有转义需要处理
        desBytes[pos] = checkCode;
        pos++;
        //末尾结束标识位
        desBytes[pos] = MESSAGE_TAIL;
        Log.i(TAG, "包长度="+(pos+1));
        Log.i(TAG, "包内容: "+ByteUtil.byte2HexStr(desBytes));
        return desBytes;
    }
}
app/src/main/java/safeluck/drive/evaluation/bean/GainStuMessage.java
New file
@@ -0,0 +1,26 @@
package safeluck.drive.evaluation.bean;
/**
 * 获取学员信息消息(发送身份证ID上去给平台)
 * MyApplication2
 * Created by lzw on 2019/12/19. 18:26:35
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class GainStuMessage extends DriveExamProtocol {
    // TODO: 2019/12/19
    /**
     * 构造函数
     *
     * @param msg_id 消息ID
     */
    public GainStuMessage(short msg_id) {
        super(msg_id);
    }
    @Override
    protected byte[] createMessageBody() {
        return new byte[0];
    }
}
app/src/main/java/safeluck/drive/evaluation/bean/KeepaliveMessage.java
New file
@@ -0,0 +1,22 @@
package safeluck.drive.evaluation.bean;
/**
 * 心跳消息
 * MyApplication2
 * Created by lzw on 2019/12/19. 18:24:47
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class KeepaliveMessage extends DriveExamProtocol {
// TODO: 2019/12/19
    public KeepaliveMessage(short msg_id) {
        super(msg_id);
    }
    @Override
    protected byte[] createMessageBody() {
        return new byte[0];
    }
}
app/src/main/java/safeluck/drive/evaluation/bean/RegisterMessage.java
New file
@@ -0,0 +1,27 @@
package safeluck.drive.evaluation.bean;
/**
 * 注册消息
 * MyApplication2
 * Created by lzw on 2019/12/17. 17:51:44
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class RegisterMessage extends DriveExamProtocol {
    /**
     * 构造函数
     *
     * @param msg_id 消息ID
     */
    public RegisterMessage(short msg_id) {
        super(msg_id);
    }
    @Override
    protected byte[] createMessageBody() {
        byte[] messageBody = new byte[2];
        messageBody[0] = 0x65;
        messageBody[1] = 0x66;
        return messageBody;
    }
}
app/src/main/java/safeluck/drive/evaluation/bean/StartExamMessage.java
New file
@@ -0,0 +1,25 @@
package safeluck.drive.evaluation.bean;
/**
 * 给平台发送开始考试消息
 * MyApplication2
 * Created by lzw on 2019/12/19. 18:28:47
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class StartExamMessage extends DriveExamProtocol {
// TODO: 2019/12/19
    /**
     * 构造函数
     *
     * @param msg_id 消息ID
     */
    public StartExamMessage(short msg_id) {
        super(msg_id);
    }
    @Override
    protected byte[] createMessageBody() {
        return new byte[0];
    }
}
app/src/main/java/safeluck/drive/evaluation/cEventCenter/CEvent.java
New file
@@ -0,0 +1,70 @@
package safeluck.drive.evaluation.cEventCenter;
/**
 * 事件模型,需要传递的消息事件对象
 * MyApplication2
 * Created by lzw on 2019/12/30. 17:00:54
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class CEvent implements PooledObject {
    //主题
    private String topic;
    private int msgCode;//消息类型
    private int resultCode;//预留参数
    private Object obj;//回调返回数据
    public CEvent() {
    }
    public CEvent(String topic, int msgCode, int resultCode, Object obj) {
        this.topic = topic;
        this.msgCode = msgCode;
        this.resultCode = resultCode;
        this.obj = obj;
    }
    public String getTopic() {
        return topic;
    }
    public void setTopic(String topic) {
        this.topic = topic;
    }
    public int getMsgCode() {
        return msgCode;
    }
    public void setMsgCode(int msgCode) {
        this.msgCode = msgCode;
    }
    public int getResultCode() {
        return resultCode;
    }
    public void setResultCode(int resultCode) {
        this.resultCode = resultCode;
    }
    public Object getObj() {
        return obj;
    }
    public void setObj(Object obj) {
        this.obj = obj;
    }
    @Override
    public void reset() {
        this.obj = null;
        this.msgCode = 0;
        this.resultCode = 0;
        this.topic = null;
    }
}
app/src/main/java/safeluck/drive/evaluation/cEventCenter/ObjectPool.java
New file
@@ -0,0 +1,55 @@
package safeluck.drive.evaluation.cEventCenter;
/**
 * 自定义的对象池
 * MyApplication2
 * Created by lzw on 2019/12/30. 17:06:49
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public abstract class ObjectPool<T extends PooledObject> {
    private T[] mContainer;//对象容器
    private final Object LOCK = new Object();//对象锁
    private int length;//每次返回对象都放到数据末端,length表示前面可用对象数
    public ObjectPool(int capacity) {
        mContainer = createObjPool(capacity);
    }
    /**
     * 创建对象池
     * @param capacity 最大限度容量
     * @return
     */
    protected abstract T[] createObjPool(int capacity) ;
    /**
     * 创建一个新的对象
     * @return
     */
    protected abstract T createNewObj();
    public final T get(){
        //先从池中找到空闲的对象,如果没有,则重新创建一个对象
        T obj = findFreeObject();
        if (null == obj){
            obj = createNewObj();
        }else{
            obj.reset();
        }
        return obj;
    }
    /**
     * 从池中找到空闲的对象
     * @return
     */
    private T findFreeObject() {
        return null;
    }
}
app/src/main/java/safeluck/drive/evaluation/cEventCenter/PooledObject.java
New file
@@ -0,0 +1,15 @@
package safeluck.drive.evaluation.cEventCenter;
/**
 * 对象池
 * MyApplication2
 * Created by lzw on 2019/12/30. 16:59:40
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public interface PooledObject {
    /**
     * 恢复到默认状态
     */
    void reset();
}
app/src/main/java/safeluck/drive/evaluation/fragment/TcpFragment.java
@@ -10,6 +10,7 @@
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.widget.AppCompatEditText;
@@ -31,6 +32,8 @@
import safeluck.drive.evaluation.DB.failitems.FailedProj;
import safeluck.drive.evaluation.DB.failitems.FailedProj_select;
import safeluck.drive.evaluation.R;
import safeluck.drive.evaluation.im.IMSClientBootstrap;
import safeluck.drive.evaluation.im.MessageProcessor;
import safeluck.drive.evaluation.tcp.ConnectThread;
/**
@@ -45,9 +48,9 @@
    private static final String TAG = TcpFragment.class.getSimpleName();
    private TextInputEditText ip;
    private TextInputEditText port;
    private TextView tv_content;
    private Button btn_connect;
    private Button btn_send;
    private      ConnectThread connectThread;
    private AppCompatEditText sendEditText;
    private CriteriaIViewModel workViewModel;
    private int item_id=0;
@@ -61,7 +64,7 @@
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.layout_tcpclient, container, false);
        initView(view);
        workViewModel =ViewModelProviders.of(this).get(CriteriaIViewModel.class);
//        workViewModel =ViewModelProviders.of(this).get(CriteriaIViewModel.class);
  
        return view;
@@ -71,6 +74,7 @@
        ip = view.findViewById(R.id.input_ip);
        port = view.findViewById(R.id.input_port);
        sendEditText = view.findViewById(R.id.sendtxt);
        tv_content = view.findViewById(R.id.content);
        btn_connect = view.findViewById(R.id.btn_connect);
        btn_send = view.findViewById(R.id.btn_send);
@@ -83,22 +87,37 @@
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_connect:
//                String serverIp = ip.getText().toString().trim();
//                String serverPort = port.getText().toStrirng().trim();
//            connectThread = new ConnectThread(serverIp,Integer.parseInt(serverPort));
//            connectThread.start();
                    Random random = new Random();
                r = random.nextInt(30)+1;
                Log.i(TAG, "onClick: 随机数="+r);
                WorkRoomDataBase.dataBaseWriteExecutor.execute(new Runnable() {
                    @Override
                    public void run() {
                WorkRoomDataBase.getWorkRoomDataBase(getContext().getApplicationContext()).getFailProjDao().insert(new FailedProj(flag?1:2, r,flag?1001:1000));
                flag = !flag;
                    }
                });
                /**======================以下用于测试数据库==================*/
//                Random random = new Random();
//                r = random.nextInt(30)+1;
//                Log.i(TAG, "onClick: 随机数="+r);
//                WorkRoomDataBase.dataBaseWriteExecutor.execute(new Runnable() {
//                    @Override
//                    public void run() {
//
//                WorkRoomDataBase.getWorkRoomDataBase(getContext().getApplicationContext()).getFailProjDao().insert(new FailedProj(flag?1:2, r,flag?1001:1000));
//                flag = !flag;
//                    }
//                });
                /**======================测试数据库结束==================*/
                String userId = "100002";
                String token = "token_" + userId;
                String hosts = "[{\"host\":\"192.168.10.234\", \"port\":8855}]";
                IMSClientBootstrap.getInstance().init(userId,token,hosts,1);
                break;
            case R.id.btn_send:
//                if (connectThread != null){
@@ -106,6 +125,11 @@
////                    sendEditText.getText().clear();
////                }
                MessageProcessor.getInstance().sendMessage(sendEditText.getText().toString().trim());
                sendEditText.getText().clear();
                break;
        }
    }
app/src/main/java/safeluck/drive/evaluation/fragment/TrainFragment.java
@@ -67,7 +67,7 @@
        FailedProjViewModel failedProjViewModel =ViewModelProviders.of(this).get(FailedProjViewModel.class);
        failedProjViewModel.getFailedProjectsForI(1000).observe(this, new Observer<List<FailedProj_select>>() {
        failedProjViewModel.getFailedProjectsForI(1001).observe(this, new Observer<List<FailedProj_select>>() {
            @Override
            public void onChanged(List<FailedProj_select> failedProj_selects) {
                item_id = 0;
app/src/main/java/safeluck/drive/evaluation/im/IMSClientBootstrap.java
New file
@@ -0,0 +1,109 @@
package safeluck.drive.evaluation.im;
import android.util.Log;
import com.anyun.im_lib.interf.IMSClientInteface;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.reflect.TypeToken;
import java.lang.reflect.Type;
import java.util.Vector;
/**
 * MyApplication2
 * Created by lzw on 2019/12/12. 16:05:30
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class IMSClientBootstrap {
    private static final String TAG = "IMSClientBootstrap";
    private static final IMSClientBootstrap INSTANCE= new  IMSClientBootstrap();
    private IMSClientInteface imsClient;
    /**标记IMSClientBootstrap是否已经初始化**/
    private boolean isActive;
    private IMSClientBootstrap(){
    }
    public static IMSClientBootstrap getInstance(){
        return  INSTANCE;
    }
    /**
     *
     * @param userId
     * @param token
     * @param hosts
     * @param appStatus
     */
    public synchronized void init(String userId,String token,String hosts,int appStatus){
        if (!isActive){
            Vector<String> serverUrlList = convertHosts(hosts);
            if (serverUrlList == null || serverUrlList.size() ==0){
                Log.i(TAG, "init IMLibClientBootstrap error,ims hosts is null");
                return;
            }
            isActive = true;
            Log.i(TAG, "init IMLibClientBootstrap ,server="+hosts);
            if (null != imsClient){
                imsClient.close();
            }
            //初始化IMSClientInteface
            imsClient = IMSClientFactory.getIMSClient();
            updateAppStatus(appStatus);
            imsClient.init(serverUrlList,new IMSEventListener(userId,token),new IMSConnectStatusListener());
        }
    }
public boolean isActive(){
        return isActive;
}
    public void updateAppStatus(int appStatus) {
        if (imsClient == null){
            return;
        }
        imsClient.setAppStatus(appStatus);
    }
    private Vector<String> convertHosts(String hosts) {
        Log.i(TAG, "convertHosts: "+hosts);
        if (hosts != null && hosts.length() > 0) {
            Vector<String> serverUrlList = new Vector<>();
            JsonArray jsonArray =JsonParser.parseString(hosts).getAsJsonArray();
            for (int i = 0; i < jsonArray.size(); i++) {
                JsonObject host = jsonArray.get(i).getAsJsonObject();
                String hostName = host.get("host").getAsString();
                int port = host.get("port").getAsInt();
                Log.i(TAG, "convertHosts: hostname="+hostName+"  port="+port);
                serverUrlList.add(hostName+" "+port);
            }
                return serverUrlList;
        }
        return null;
    }
    public void sendMessage(String message){
        if (isActive){
            imsClient.sendMsg(message);
        }
    }
}
app/src/main/java/safeluck/drive/evaluation/im/IMSClientFactory.java
New file
@@ -0,0 +1,16 @@
package safeluck.drive.evaluation.im;
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();
    }
}
app/src/main/java/safeluck/drive/evaluation/im/IMSConnectStatusListener.java
New file
@@ -0,0 +1,31 @@
package safeluck.drive.evaluation.im;
import android.util.Log;
import com.anyun.im_lib.listener.IMSConnectStatusCallback;
/**
 * MyApplication2
 * Created by lzw on 2019/12/12. 16:30:33
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class IMSConnectStatusListener implements IMSConnectStatusCallback {
    private static final String TAG = IMSConnectStatusListener.class.getSimpleName();
    @Override
    public void onConnecting() {
        Log.i(TAG, "onConnecting: ");
    }
    @Override
    public void onConnected() {
        Log.i(TAG, "onConnected: ");
    }
    @Override
    public void onConnectFailed() {
        Log.i(TAG, "onConnectFailed: ");
    }
}
app/src/main/java/safeluck/drive/evaluation/im/IMSEventListener.java
New file
@@ -0,0 +1,76 @@
package safeluck.drive.evaluation.im;
import com.anyun.im_lib.listener.OnEventListener;
import safeluck.drive.evaluation.bean.RegisterMessage;
/**
 * MyApplication2
 * Created by lzw on 2019/12/12. 16:12:40
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class IMSEventListener implements OnEventListener {
    private String userId;
    private String token;
    public IMSEventListener(String userId, String token) {
        this.userId = userId;
        this.token = token;
    }
    @Override
    public void dispatchMsg(Object message) {
        MessageProcessor.getInstance().receiveMsg((String)message);
    }
    @Override
    public boolean isNetWorkAvailable() {
        return true;
    }
    /**
     * 连接超时时长
     * @return  ms
     */
    @Override
    public int getConnectTimeout() {
        return 3000;
    }
    @Override
    public int getForegroundHeartbeatInterval() {
        return 0;
    }
    @Override
    public int getBackgroundHeartbeatInterval() {
        return 0;
    }
    @Override
    public int getServerSentReportMsgType() {
        return 0;
    }
    @Override
    public int getResendCount() {
        return 0;
    }
    @Override
    public int getResendInterval() {
        return 0;
    }
    @Override
    public int getReConnectInterval() {
        return 0;
    }
    @Override
    public byte[] getRegisterMessage() {
        return new RegisterMessage((short) 0x802).toBytes();
    }
}
app/src/main/java/safeluck/drive/evaluation/im/IMessageProcessor.java
New file
@@ -0,0 +1,12 @@
package safeluck.drive.evaluation.im;
/**
 * MyApplication2
 * Created by lzw on 2019/12/12. 16:14:57
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public interface IMessageProcessor {
    void receiveMsg(String message);
    void sendMessage(String msg);
}
app/src/main/java/safeluck/drive/evaluation/im/MessageProcessor.java
New file
@@ -0,0 +1,48 @@
package safeluck.drive.evaluation.im;
import android.util.Log;
import safeluck.drive.evaluation.util.CThreadPoolExecutor;
/**
 * MyApplication2
 * Created by lzw on 2019/12/12. 16:14:33
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public class MessageProcessor implements IMessageProcessor {
    private static final String TAG = MessageProcessor.class.getSimpleName();
    private MessageProcessor(){
    }
    private static class MessageProcessorInstance{
        private static final IMessageProcessor INSTANCE = new MessageProcessor();
    }
    public static IMessageProcessor getInstance(){
        return MessageProcessorInstance.INSTANCE;
    }
    @Override
    public void receiveMsg(String message) {
        Log.i(TAG, "receiveMsg: "+message);
    }
    @Override
    public void sendMessage(final String msg) {
        CThreadPoolExecutor.runInBackground(new Runnable() {
            @Override
            public void run() {
                if (IMSClientBootstrap.getInstance().isActive()){
                    IMSClientBootstrap.getInstance().sendMessage(msg);
                }else{
                    Log.e(TAG, "run: 发送消息失败,未初始化连接NettyTcp");
                }
            }
        });
    }
}
app/src/main/java/safeluck/drive/evaluation/im/handler/AbstractMessageHandler.java
New file
@@ -0,0 +1,15 @@
package safeluck.drive.evaluation.im.handler;
/**
 * MyApplication2
 * Created by lzw on 2019/12/12. 16:09:02
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public abstract class AbstractMessageHandler implements IMessageHandler {
    @Override
    public void execute(String msg) {
        action(msg);
    }
    protected abstract void action(String msg);
}
app/src/main/java/safeluck/drive/evaluation/im/handler/IMessageHandler.java
New file
@@ -0,0 +1,11 @@
package safeluck.drive.evaluation.im.handler;
/**
 * MyApplication2
 * Created by lzw on 2019/12/12. 16:04:29
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 */
public interface IMessageHandler {
    void execute(String msg);
}
app/src/main/java/safeluck/drive/evaluation/util/CThreadPoolExecutor.java
New file
@@ -0,0 +1,332 @@
package safeluck.drive.evaluation.util;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * MyApplication2
 * Created by lzw on 2019/12/12. 16:22:49
 * 邮箱:632393724@qq.com
 * All Rights Saved! Chongqing AnYun Tech co. LTD
 *
 *  *
 *  * <p>@ClassName:       CThreadPoolExecutor.java</p>
 *  * <b>
 *  * <p>@Description:     自定义固定大小的线程池
 *  * 每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。
 *  * 线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
 *  * <p>
 *  * 合理利用线程池能够带来三个好处:
 *  * 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
 *  * 第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
 *  * 第三:提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
 *  * 我们可以通过ThreadPoolExecutor来创建一个线程池:
 *  * new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, milliseconds,runnableTaskQueue, handler);
 *  * <p>
 *  * corePoolSize(线程池的基本大小):
 *  * 当提交一个任务到线程池时,线程池会创建一个线程来执行任务,
 *  * 即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。
 *  * 如果调用了线程池的prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。
 *  * <p>
 *  * runnableTaskQueue(任务队列):用于保存等待执行的任务的阻塞队列。 可以选择以下几个阻塞队列。
 *  * ArrayBlockingQueue:是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。
 *  * <p>
 *  * LinkedBlockingQueue:一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。
 *  * 静态工厂方法Executors.newFixedThreadPool()使用了这个队列。
 *  * <p>
 *  * SynchronousQueue:一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,
 *  * 吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。
 *  * <p>
 *  * PriorityBlockingQueue:一个具有优先级的无限阻塞队列。
 *  * <p>
 *  * maximumPoolSize(线程池最大大小):
 *  * 线程池允许创建的最大线程数。如果队列满了,并且已创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。值得注意的是如果使用了无界的任务队列这个参数就没什么效果。
 *  * <p>
 *  * ThreadFactory:
 *  * 用于设置创建线程的工厂,可以通过线程工厂给每个创建出来的线程设置更有意义的名字。
 *  * <p>
 *  * RejectedExecutionHandler(饱和策略):当队列和线程池都满了,说明线程池处于饱和状态,那么必须采取一种策略处理提交的新任务。
 *  * 这个策略默认情况下是AbortPolicy,表示无法处理新任务时抛出异常。以下是JDK1.5提供的四种策略。
 *  * AbortPolicy:直接抛出异常。
 *  * CallerRunsPolicy:只用调用者所在线程来运行任务。
 *  * DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
 *  * DiscardPolicy:不处理,丢弃掉。
 *  * 当然也可以根据应用场景需要来实现RejectedExecutionHandler接口自定义策略。如记录日志或持久化不能处理的任务。
 *  * <p>
 *  * keepAliveTime(线程活动保持时间):线程池的工作线程空闲后,保持存活的时间。
 *  * 所以如果任务很多,并且每个任务执行的时间比较短,可以调大这个时间,提高线程的利用率。
 *  * <p>
 *  * TimeUnit(线程活动保持时间的单位):可选的单位有天(DAYS),小时(HOURS),分钟(MINUTES),
 *  * 毫秒(MILLISECONDS),微秒(MICROSECONDS, 千分之一毫秒)和毫微秒(NANOSECONDS, 千分之一微秒)。</p>
 *  * </b>
 *  * <p>@author:          FreddyChen</p>
 *  * <p>@date:            2019/2/3 15:35</p>
 *  * <p>@email:           chenshichao@outlook.com</p>
 *  *
 *  * @see http://www.infoq.com/cn/articles/java-threadPool
 *
 */
public class CThreadPoolExecutor {
    private static final String TAG = CThreadPoolExecutor.class.getSimpleName();
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();// CPU个数
    //    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;// 线程池中核心线程的数量
//    private static final int MAXIMUM_POOL_SIZE = 2 * CPU_COUNT + 1;// 线程池中最大线程数量
    private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));// 线程池中核心线程的数量
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;// 线程池中最大线程数量
    private static final long KEEP_ALIVE_TIME = 30L;// 非核心线程的超时时长,当系统中非核心线程闲置时间超过keepAliveTime之后,则会被回收。如果ThreadPoolExecutor的allowCoreThreadTimeOut属性设置为true,则该参数也表示核心线程的超时时长
    private static final int WAIT_COUNT = 128; // 最多排队个数,这里控制线程创建的频率
    private static ThreadPoolExecutor pool = createThreadPoolExecutor();
    private static ThreadPoolExecutor createThreadPoolExecutor() {
        if (pool == null) {
            pool = new ThreadPoolExecutor(
                    CORE_POOL_SIZE,
                    MAXIMUM_POOL_SIZE,
                    KEEP_ALIVE_TIME,
                    TimeUnit.SECONDS,
                    new LinkedBlockingQueue<Runnable>(WAIT_COUNT),
                    new CThreadFactory("CThreadPool", Thread.NORM_PRIORITY - 2),
                    new CHandlerException());
        }
        return pool;
    }
    public static class CThreadFactory implements ThreadFactory {
        private AtomicInteger counter = new AtomicInteger(1);
        private String prefix = "";
        private int priority = Thread.NORM_PRIORITY;
        public CThreadFactory(String prefix, int priority) {
            this.prefix = prefix;
            this.priority = priority;
        }
        public CThreadFactory(String prefix) {
            this.prefix = prefix;
        }
        public Thread newThread(Runnable r) {
            Thread executor = new Thread(r, prefix + " #" + counter.getAndIncrement());
            executor.setDaemon(true);
            executor.setPriority(priority);
            return executor;
        }
    }
    /**
     * 抛弃当前的任务
     */
    private static class CHandlerException extends ThreadPoolExecutor.AbortPolicy {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            Log.d(TAG, "rejectedExecution:" + r);
            Log.e(TAG, logAllThreadStackTrace().toString());
            //            Tips.showForce("任务被拒绝", 5000);
            if (!pool.isShutdown()) {
                pool.shutdown();
                pool = null;
            }
            pool = createThreadPoolExecutor();
        }
    }
    private static ExecutorService jobsForUI = Executors.newFixedThreadPool(
            CORE_POOL_SIZE, new CThreadFactory("CJobsForUI", Thread.NORM_PRIORITY - 1));
    /**
     * 启动一个消耗线程,常驻后台
     *
     * @param r
     */
    public static void startConsumer(final Runnable r, final String name) {
        runInBackground(new Runnable() {
            public void run() {
                new CThreadFactory(name, Thread.NORM_PRIORITY - 3).newThread(r).start();
            }
        });
    }
    /**
     * 提交到其他线程去跑,需要取数据的时候会等待任务完成再继续
     *
     * @param task
     * @return
     */
    public static <T> Future<T> submitTask(Callable<T> task) {
        return jobsForUI.submit(task);
    }
    /**
     * 强制清理任务
     *
     * @param task
     * @return
     */
    public static <T> void cancelTask(Future<T> task) {
        if (task != null) {
            task.cancel(true);
        }
    }
    /**
     * 从 Future 中获取值,如果发生异常,打日志
     *
     * @param future
     * @param tag
     * @param name
     * @return
     */
    public static <T> T getFromTask(Future<T> future, String tag, String name) {
        try {
            return future.get();
        } catch (Exception e) {
            Log.e(tag, (name != null ? name + ": " : "") + e.toString());
        }
        return null;
    }
    public static void runInBackground(Runnable runnable) {
        if (pool == null) {
            createThreadPoolExecutor();
        }
        pool.execute(runnable);
        //        Future future = pool.submit(runnable);
        //        try {
        //            future.get();
        //        } catch (InterruptedException e) {
        //            e.printStackTrace();
        //        } catch (ExecutionException e) {
        //            e.printStackTrace();
        //        }
    }
    private static Thread mainThread;
    private static Handler mainHandler;
    static {
        Looper mainLooper = Looper.getMainLooper();
        mainThread = mainLooper.getThread();
        mainHandler = new Handler(mainLooper);
    }
    public static boolean isOnMainThread() {
        return mainThread == Thread.currentThread();
    }
    public static void runOnMainThread(Runnable r) {
        if (isOnMainThread()) {
            r.run();
        } else {
            mainHandler.post(r);
        }
    }
    public static void runOnMainThread(Runnable r, long delayMillis) {
        if (delayMillis <= 0) {
            runOnMainThread(r);
        } else {
            mainHandler.postDelayed(r, delayMillis);
        }
    }
    // 用于记录后台等待的Runnable,第一个参数外面的Runnable,第二个参数是等待中的Runnable
    private static HashMap<Runnable, Runnable> mapToMainHandler = new HashMap<Runnable, Runnable>();
    public static void runInBackground(final Runnable runnable, long delayMillis) {
        if (delayMillis <= 0) {
            runInBackground(runnable);
        } else {
            Runnable mainRunnable = new Runnable() {
                @Override
                public void run() {
                    mapToMainHandler.remove(runnable);
                    pool.execute(runnable);
                }
            };
            mapToMainHandler.put(runnable, mainRunnable);
            mainHandler.postDelayed(mainRunnable, delayMillis);
        }
    }
    /**
     * 对runOnMainThread的,移除Runnable
     *
     * @param r
     */
    public static void removeCallbackOnMainThread(Runnable r) {
        mainHandler.removeCallbacks(r);
    }
    public static void removeCallbackInBackground(Runnable runnable) {
        Runnable mainRunnable = mapToMainHandler.get(runnable);
        if (mainRunnable != null) {
            mainHandler.removeCallbacks(mainRunnable);
        }
    }
    public static void logStatus() {
        StringBuilder sb = new StringBuilder();
        sb.append("getActiveCount");
        sb.append(pool.getActiveCount());
        sb.append("\ngetTaskCount");
        sb.append(pool.getTaskCount());
        sb.append("\ngetCompletedTaskCount");
        sb.append(pool.getCompletedTaskCount());
        Log.d(TAG, sb.toString());
    }
    public static StringBuilder logAllThreadStackTrace() {
        StringBuilder builder = new StringBuilder();
        Map<Thread, StackTraceElement[]> liveThreads = Thread.getAllStackTraces();
        for (Iterator<Thread> i = liveThreads.keySet().iterator(); i.hasNext(); ) {
            Thread key = i.next();
            builder.append("Thread ").append(key.getName())
                    .append("\n");
            StackTraceElement[] trace = liveThreads.get(key);
            for (int j = 0; j < trace.length; j++) {
                builder.append("\tat ").append(trace[j]).append("\n");
            }
        }
        return builder;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10000; i++) {
            final int index = i;
            System.out.println("index=" + index);
            CThreadPoolExecutor.runInBackground(new Runnable() {
                @Override
                public void run() {
                    System.out.println("正在运行第[" + (index + 1) + "]个线程.");
                }
            });
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}