├── .gitignore ├── JitPackUpload.gradle ├── README.md ├── apk └── xtcpdemo_1.0.apk ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── xuexiang │ │ └── xtcpdemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── xuexiang │ │ │ └── xtcpdemo │ │ │ ├── MyApp.java │ │ │ ├── activity │ │ │ └── MainActivity.java │ │ │ ├── fragment │ │ │ ├── MainFragment.java │ │ │ └── TestFragment.java │ │ │ ├── model │ │ │ ├── LoginInfo.java │ │ │ └── LoginInfoArray.java │ │ │ └── protocol │ │ │ ├── EmptyRequest.java │ │ │ ├── SettingRequest.java │ │ │ └── test │ │ │ ├── MessageTest.java │ │ │ ├── TestProtocolItem.java │ │ │ └── indefiniteArray │ │ │ ├── TestByteArray.java │ │ │ ├── TestIntArray.java │ │ │ ├── TestLongArray.java │ │ │ ├── TestShortArray.java │ │ │ └── TestStringItem.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── xuexiang │ └── xtcpdemo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img └── download.png ├── settings.gradle ├── versions.gradle ├── xtcp_annotation ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── xuexiang │ └── xtcp │ ├── annotation │ ├── Protocol.java │ └── ProtocolField.java │ ├── enums │ └── StorageMode.java │ └── model │ ├── IArrayItem.java │ ├── IProtocol.java │ ├── IProtocolCenter.java │ ├── IProtocolFieldCenter.java │ ├── IProtocolItem.java │ ├── ProtocolFieldInfo.java │ └── ProtocolInfo.java ├── xtcp_compiler ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── com │ └── xuexiang │ └── xtcp │ ├── compiler │ ├── ProtocolFieldProcessor.java │ └── ProtocolProcessor.java │ └── util │ ├── Consts.java │ └── Logger.java └── xtcp_runtime ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src └── main ├── AndroidManifest.xml ├── java └── com │ └── xuexiang │ └── xtcp │ ├── XTCP.java │ ├── _XTCP.java │ ├── core │ ├── XProtocolCenter.java │ ├── XTCPConstants.java │ ├── component │ │ ├── buffer │ │ │ ├── BufferException.java │ │ │ ├── IBuffer.java │ │ │ └── impl │ │ │ │ ├── CircularBuffer.java │ │ │ │ └── SimpleBuffer.java │ │ └── monitor │ │ │ ├── IMonitor.java │ │ │ ├── OnMonitorListener.java │ │ │ └── impl │ │ │ └── TimeoutMonitor.java │ ├── message │ │ ├── IMessage.java │ │ ├── MessageConstants.java │ │ └── template │ │ │ ├── XMessage.java │ │ │ └── XOrderlyMessage.java │ ├── model │ │ ├── AbstractArrayItem.java │ │ ├── BCD.java │ │ ├── ByteArray.java │ │ ├── FixedString.java │ │ ├── IntArray.java │ │ ├── LargeByteArray.java │ │ ├── LargeString.java │ │ ├── LongArray.java │ │ ├── ShortArray.java │ │ ├── StringField.java │ │ └── XProtocolItem.java │ └── parser │ │ ├── IProtocolParser.java │ │ └── impl │ │ └── DefaultProtocolParser.java │ ├── logs │ ├── ILogger.java │ ├── LogcatLogger.java │ └── XTLog.java │ └── utils │ ├── BCDUtils.java │ ├── ConvertUtils.java │ ├── MessageUtils.java │ └── ParserUtils.java └── res └── values └── strings.xml /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /LocalRepository 4 | /keystores 5 | /local.properties 6 | /.idea 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild -------------------------------------------------------------------------------- /JitPackUpload.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.github.dcendents.android-maven' 2 | 3 | // 指定group,com.github.<用户名>,这里我默认填写的是我的github账号,请换成你自己的。 4 | group='com.github.xuexiangjys' 5 | 6 | //--------------------------------------------- 7 | 8 | 9 | // 指定编码 10 | tasks.withType(JavaCompile) { 11 | options.encoding = "UTF-8" 12 | } 13 | 14 | tasks.withType(Javadoc) { 15 | options.encoding = 'UTF-8' 16 | } 17 | 18 | if (project.hasProperty("android")) { // Android libraries 19 | task sourcesJar(type: Jar) { 20 | classifier = 'sources' 21 | from android.sourceSets.main.java.srcDirs 22 | } 23 | 24 | task javadoc(type: Javadoc) { 25 | failOnError false 26 | source = android.sourceSets.main.java.srcDirs 27 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 28 | } 29 | } else { // Java libraries 30 | task sourcesJar(type: Jar, dependsOn: classes) { 31 | classifier = 'sources' 32 | from sourceSets.main.allSource 33 | } 34 | } 35 | 36 | javadoc { 37 | options { 38 | encoding "UTF-8" 39 | charSet 'UTF-8' 40 | author true 41 | version true 42 | links "http://docs.oracle.com/javase/7/docs/api" 43 | } 44 | } 45 | 46 | // 制作文档(Javadoc) 47 | task javadocJar(type: Jar, dependsOn: javadoc) { 48 | classifier = 'javadoc' 49 | from javadoc.destinationDir 50 | } 51 | 52 | artifacts { 53 | archives javadocJar 54 | archives sourcesJar 55 | } -------------------------------------------------------------------------------- /apk/xtcpdemo_1.0.apk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/XTCP/9016969bb256ea3846ca670454b5f882e9e82219/apk/xtcpdemo_1.0.apk -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'com.xuexiang.xaop' //引用xaop插件 3 | 4 | android { 5 | compileSdkVersion build_versions.target_sdk 6 | buildToolsVersion build_versions.build_tools 7 | 8 | defaultConfig { 9 | applicationId "com.xuexiang.xtcpdemo" 10 | minSdkVersion 19 11 | targetSdkVersion build_versions.target_sdk 12 | versionCode 1 13 | versionName "1.0" 14 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 15 | } 16 | 17 | if (isNeedPackage.toBoolean()) { 18 | signingConfigs { 19 | release { 20 | storeFile file(app_release.storeFile) 21 | storePassword app_release.storePassword 22 | keyAlias app_release.keyAlias 23 | keyPassword app_release.keyPassword 24 | } 25 | } 26 | } 27 | 28 | buildTypes { 29 | release { 30 | minifyEnabled true 31 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 32 | if (isNeedPackage.toBoolean()) { 33 | signingConfig signingConfigs.release 34 | } 35 | } 36 | } 37 | 38 | if (isNeedPackage.toBoolean()) { 39 | applicationVariants.all { variant -> 40 | variant.outputs.all { 41 | if (variant.buildType.name.equals('release')) { 42 | outputFileName = "xtcpdemo_${defaultConfig.versionName}.apk" 43 | } 44 | } 45 | } 46 | } 47 | 48 | lintOptions { 49 | abortOnError false 50 | } 51 | 52 | compileOptions { 53 | sourceCompatibility JavaVersion.VERSION_1_8 54 | targetCompatibility JavaVersion.VERSION_1_8 55 | } 56 | } 57 | 58 | dependencies { 59 | implementation fileTree(dir: 'libs', include: ['*.jar']) 60 | implementation deps.androidx.appcompat 61 | testImplementation deps.junit 62 | androidTestImplementation deps.runner 63 | androidTestImplementation deps.espresso.core 64 | 65 | implementation project(':xtcp_runtime') 66 | annotationProcessor project(':xtcp_compiler') 67 | 68 | // implementation 'com.github.xuexiangjys.XTCP:xtcp_runtime:1.0.3' 69 | // annotationProcessor 'com.github.xuexiangjys.XTCP:xtcp_compiler:1.0.3' 70 | 71 | //工具类 72 | implementation 'com.github.xuexiangjys.XUtil:xutil-core:2.0.0' 73 | //切片 74 | implementation 'com.github.xuexiangjys.XAOP:xaop-runtime:1.1.0' //添加依赖 75 | 76 | //XPage 77 | implementation deps.xlibrary.xpage_lib 78 | annotationProcessor deps.xlibrary.xpage_compiler 79 | //ButterKnife的sdk 80 | implementation deps.butterknife.runtime 81 | annotationProcessor deps.butterknife.compiler 82 | debugImplementation deps.leakcanary 83 | } 84 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | #=========================================基础不变的混淆配置=========================================## 2 | #指定代码的压缩级别 3 | -optimizationpasses 5 4 | #包名不混合大小写 5 | -dontusemixedcaseclassnames 6 | #不去忽略非公共的库类 7 | -dontskipnonpubliclibraryclasses 8 | # 指定不去忽略非公共的库的类的成员 9 | -dontskipnonpubliclibraryclassmembers 10 | #优化 不优化输入的类文件 11 | -dontoptimize 12 | #预校验 13 | -dontpreverify 14 | #混淆时是否记录日志 15 | -verbose 16 | # 混淆时所采用的算法 17 | -optimizations !code/simplification/arithmetic,!field/*,!class/merging/* 18 | #保护注解 19 | -keepattributes *Annotation* 20 | #忽略警告 21 | -ignorewarnings 22 | 23 | ##记录生成的日志数据,gradle build时在本项目根目录输出## 24 | #apk 包内所有 class 的内部结构 25 | -dump class_files.txt 26 | #未混淆的类和成员 27 | -printseeds seeds.txt 28 | #列出从 apk 中删除的代码 29 | -printusage unused.txt 30 | #混淆前后的映射 31 | -printmapping mapping.txt 32 | # 并保留源文件名为"Proguard"字符串,而非原始的类名 并保留行号 33 | -keepattributes SourceFile,LineNumberTable 34 | ########记录生成的日志数据,gradle build时 在本项目根目录输出-end##### 35 | 36 | #需要保留的东西 37 | # 保持哪些类不被混淆 38 | -keep public class * extends android.app.Fragment 39 | -keep public class * extends android.app.Activity 40 | -keep public class * extends android.app.Application 41 | -keep public class * extends android.app.Service 42 | -keep public class * extends android.content.BroadcastReceiver 43 | -keep public class * extends android.content.ContentProvider 44 | -keep public class * extends android.app.backup.BackupAgentHelper 45 | -keep public class * extends android.preference.Preference 46 | -keep public class * extends android.support.v4.** 47 | -keep public class com.android.vending.licensing.ILicensingService 48 | 49 | #如果有引用v4包可以添加下面这行 50 | -keep public class * extends android.support.v4.app.Fragment 51 | 52 | ##########JS接口类不混淆,否则执行不了 53 | -dontwarn com.android.JsInterface.** 54 | -keep class com.android.JsInterface.** {*; } 55 | 56 | #极光推送和百度lbs android sdk一起使用proguard 混淆的问题#http的类被混淆后,导致apk定位失败,保持apache 的http类不被混淆就好了 57 | -dontwarn org.apache.** 58 | -keep class org.apache.**{ *; } 59 | 60 | -keep public class * extends android.view.View { 61 | public (android.content.Context); 62 | public (android.content.Context, android.util.AttributeSet); 63 | public (android.content.Context, android.util.AttributeSet, int); 64 | public void set*(...); 65 | } 66 | 67 | #保持 native 方法不被混淆 68 | -keepclasseswithmembernames class * { 69 | native ; 70 | } 71 | 72 | #保持自定义控件类不被混淆 73 | -keepclasseswithmembers class * { 74 | public (android.content.Context, android.util.AttributeSet); 75 | } 76 | 77 | #保持自定义控件类不被混淆 78 | -keepclassmembers class * extends android.app.Activity { 79 | public void *(android.view.View); 80 | } 81 | 82 | #保持 Parcelable 不被混淆 83 | -keep class * implements android.os.Parcelable { 84 | public static final android.os.Parcelable$Creator *; 85 | } 86 | 87 | #保持 Serializable 不被混淆 88 | -keepnames class * implements java.io.Serializable 89 | 90 | #保持 Serializable 不被混淆并且enum 类也不被混淆 91 | -keepclassmembers class * implements java.io.Serializable { 92 | static final long serialVersionUID; 93 | private static final java.io.ObjectStreamField[] serialPersistentFields; 94 | !static !transient ; 95 | !private ; 96 | !private ; 97 | private void writeObject(java.io.ObjectOutputStream); 98 | private void readObject(java.io.ObjectInputStream); 99 | java.lang.Object writeReplace(); 100 | java.lang.Object readResolve(); 101 | } 102 | 103 | #保持枚举 enum 类不被混淆 如果混淆报错,建议直接使用上面的 -keepclassmembers class * implements java.io.Serializable即可 104 | -keepclassmembers enum * { 105 | public static **[] values(); 106 | public static ** valueOf(java.lang.String); 107 | } 108 | 109 | -keepclassmembers class * { 110 | public void *ButtonClicked(android.view.View); 111 | } 112 | 113 | #不混淆资源类 114 | -keep class **.R$* {*;} 115 | 116 | #===================================混淆保护自己项目的部分代码以及引用的第三方jar包library=============================####### 117 | #如果引用了v4或者v7包 118 | -dontwarn android.support.** 119 | 120 | # zxing 121 | -dontwarn com.google.zxing.** 122 | -keep class com.google.zxing.**{*;} 123 | 124 | #SignalR推送 125 | -keep class microsoft.aspnet.signalr.** { *; } 126 | 127 | # 极光推送混淆 128 | -dontoptimize 129 | -dontpreverify 130 | -dontwarn cn.jpush.** 131 | -keep class cn.jpush.** { *; } 132 | -dontwarn cn.jiguang.** 133 | -keep class cn.jiguang.** { *; } 134 | 135 | # 数据库框架OrmLite 136 | -keepattributes *DatabaseField* 137 | -keepattributes *DatabaseTable* 138 | -keepattributes *SerializedName* 139 | -keep class com.j256.** 140 | -keepclassmembers class com.j256.** { *; } 141 | -keep enum com.j256.** 142 | -keepclassmembers enum com.j256.** { *; } 143 | -keep interface com.j256.** 144 | -keepclassmembers interface com.j256.** { *; } 145 | 146 | #XHttp2 147 | -keep class com.xuexiang.xhttp2.model.** { *; } 148 | -keep class com.xuexiang.xhttp2.cache.model.** { *; } 149 | -keep class com.xuexiang.xhttp2.cache.stategy.**{*;} 150 | -keep class com.xuexiang.xhttp2.annotation.** { *; } 151 | 152 | #okhttp 153 | -dontwarn com.squareup.okhttp3.** 154 | -keep class com.squareup.okhttp3.** { *;} 155 | -dontwarn okio.** 156 | -dontwarn javax.annotation.Nullable 157 | -dontwarn javax.annotation.ParametersAreNonnullByDefault 158 | -dontwarn javax.annotation.** 159 | 160 | #如果用到Gson解析包的,直接添加下面这几行就能成功混淆,不然会报错 161 | -keepattributes Signature 162 | -keep class com.google.gson.stream.** { *; } 163 | -keepattributes EnclosingMethod 164 | -keep class org.xz_sale.entity.**{*;} 165 | -keep class com.google.gson.** {*;} 166 | -keep class com.google.**{*;} 167 | -keep class sun.misc.Unsafe { *; } 168 | -keep class com.google.gson.stream.** { *; } 169 | -keep class com.google.gson.examples.android.model.** { *; } 170 | 171 | # Retrofit 172 | -dontwarn retrofit2.** 173 | -keep class retrofit2.** { *; } 174 | -keepattributes Exceptions#XHt 175 | 176 | # RxJava RxAndroid 177 | -dontwarn sun.misc.** 178 | -keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* { 179 | long producerIndex; 180 | long consumerIndex; 181 | } 182 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef { 183 | rx.internal.util.atomic.LinkedQueueNode producerNode; 184 | } 185 | -keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef { 186 | rx.internal.util.atomic.LinkedQueueNode consumerNode; 187 | } 188 | 189 | -dontwarn okio.** 190 | -dontwarn javax.annotation.Nullable 191 | -dontwarn javax.annotation.ParametersAreNonnullByDefault 192 | -dontwarn javax.annotation.** 193 | 194 | # fastjson 195 | -dontwarn com.alibaba.fastjson.** 196 | -keep class com.alibaba.fastjson.** { *; } 197 | -keepattributes Signature 198 | 199 | # xpage 200 | -keep class com.xuexiang.xpage.annotation.** { *; } 201 | -keep class com.xuexiang.xpage.config.** { *; } 202 | 203 | # xaop 204 | -keep @com.xuexiang.xaop.annotation.* class * {*;} 205 | -keep @org.aspectj.lang.annotation.* class * {*;} 206 | -keep class * { 207 | @com.xuexiang.xaop.annotation.* ; 208 | @org.aspectj.lang.annotation.* ; 209 | } 210 | -keepclassmembers class * { 211 | @com.xuexiang.xaop.annotation.* ; 212 | @org.aspectj.lang.annotation.* ; 213 | } 214 | 215 | # xrouter 216 | -keep public class com.xuexiang.xrouter.routes.**{*;} 217 | -keep class * implements com.xuexiang.xrouter.facade.template.ISyringe{*;} 218 | # 如果使用了 byType 的方式获取 Service,需添加下面规则,保护接口 219 | -keep interface * implements com.xuexiang.xrouter.facade.template.IProvider 220 | # 如果使用了 单类注入,即不定义接口实现 IProvider,需添加下面规则,保护实现 221 | -keep class * implements com.xuexiang.xrouter.facade.template.IProvider 222 | 223 | # xupdate 224 | -keep class com.xuexiang.xupdate.entity.** { *; } 225 | 226 | # xvideo 227 | -keep class com.xuexiang.xvideo.jniinterface.** { *; } 228 | 229 | # xipc 230 | -keep @com.xuexiang.xipc.annotation.* class * {*;} 231 | -keep class * { 232 | @com.xuexiang.xipc.annotation.* ; 233 | } 234 | -keepclassmembers class * { 235 | @com.xuexiang.xipc.annotation.* ; 236 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/com/xuexiang/xtcpdemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.xuexiang.templateproject", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/MyApp.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import com.xuexiang.xaop.XAOP; 7 | import com.xuexiang.xpage.PageConfig; 8 | import com.xuexiang.xtcp.AppProtocolCenter; 9 | import com.xuexiang.xtcp.AppProtocolFieldCenter; 10 | import com.xuexiang.xtcp.XTCP; 11 | import com.xuexiang.xtcp.XTCPProtocolFieldCenter; 12 | import com.xuexiang.xtcp.core.XProtocolCenter; 13 | import com.xuexiang.xtcp.core.model.IntArray; 14 | import com.xuexiang.xtcp.enums.StorageMode; 15 | import com.xuexiang.xutil.XUtil; 16 | import com.xuexiang.xutil.common.StringUtils; 17 | import com.xuexiang.xutil.net.JsonUtil; 18 | import com.xuexiang.xutil.tip.ToastUtils; 19 | 20 | import java.util.Arrays; 21 | 22 | /** 23 | * @author xuexiang 24 | * @since 2018/11/7 下午1:12 25 | */ 26 | public class MyApp extends Application { 27 | 28 | @Override 29 | public void onCreate() { 30 | super.onCreate(); 31 | 32 | initLibs(); 33 | 34 | initXTCP(); 35 | } 36 | 37 | /** 38 | * 初始化基础库 39 | */ 40 | private void initLibs() { 41 | XUtil.init(this); 42 | XUtil.debug(true); 43 | 44 | PageConfig.getInstance().debug("PageLog").init(this); 45 | 46 | XAOP.init(this); //初始化插件 47 | XAOP.debug(true); //日志打印切片开启 48 | XAOP.setISerializer(JsonUtil::toJson); 49 | //设置动态申请权限切片 申请权限被拒绝的事件响应监听 50 | XAOP.setOnPermissionDeniedListener(permissionsDenied -> ToastUtils.toast("权限申请被拒绝:" + StringUtils.listToString(permissionsDenied, ","))); 51 | } 52 | 53 | private void initXTCP() { 54 | XTCP.getInstance() 55 | .addIProtocolCenter(AppProtocolCenter.getInstance()) //添加协议中心 56 | .addIProtocolFieldCenter(AppProtocolFieldCenter.getInstance(), XTCPProtocolFieldCenter.getInstance()) //添加协议字段中心 57 | .setDefaultStorageMode(StorageMode.BigEndian) //设置默认存储方式 58 | .debug(true); 59 | 60 | Log.e("xuexiang", XProtocolCenter.getInstance().getProtocol((byte) 0x34).toString()); 61 | 62 | Log.e("xuexiang", Arrays.toString(XProtocolCenter.getInstance().getProtocolFields(IntArray.class.getName()))); 63 | 64 | } 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo.activity; 2 | 3 | import android.os.Bundle; 4 | 5 | import com.xuexiang.xtcpdemo.fragment.MainFragment; 6 | import com.xuexiang.xpage.base.XPageActivity; 7 | 8 | public class MainActivity extends XPageActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | openPage(MainFragment.class); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/fragment/MainFragment.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo.fragment; 2 | 3 | import android.view.KeyEvent; 4 | import android.view.View; 5 | 6 | import com.xuexiang.xpage.annotation.Page; 7 | import com.xuexiang.xpage.base.XPageContainerListFragment; 8 | import com.xuexiang.xpage.utils.TitleBar; 9 | import com.xuexiang.xutil.common.ClickUtils; 10 | import com.xuexiang.xutil.common.StringUtils; 11 | 12 | /** 13 | * @author xuexiang 14 | * @since 2018/11/7 下午1:16 15 | */ 16 | @Page(name = "XTCP") 17 | public class MainFragment extends XPageContainerListFragment { 18 | 19 | @Override 20 | protected Class[] getPagesClasses() { 21 | return new Class[] { 22 | //此处填写fragment 23 | TestFragment.class 24 | }; 25 | } 26 | 27 | @Override 28 | protected TitleBar initTitleBar() { 29 | return super.initTitleBar().setLeftClickListener(new View.OnClickListener() { 30 | @Override 31 | public void onClick(View view) { 32 | ClickUtils.exitBy2Click(); 33 | } 34 | }); 35 | } 36 | 37 | 38 | /** 39 | * 菜单、返回键响应 40 | */ 41 | @Override 42 | public boolean onKeyDown(int keyCode, KeyEvent event) { 43 | if (keyCode == KeyEvent.KEYCODE_BACK) { 44 | ClickUtils.exitBy2Click(); 45 | } 46 | return true; 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/model/LoginInfo.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo.model; 2 | 3 | import com.xuexiang.xtcp.annotation.ProtocolField; 4 | import com.xuexiang.xtcp.core.model.StringField; 5 | import com.xuexiang.xtcp.core.model.XProtocolItem; 6 | 7 | /** 8 | * 登陆信息协议项 9 | * 10 | * @author xuexiang 11 | * @since 2018/12/15 下午6:53 12 | */ 13 | public class LoginInfo extends XProtocolItem { 14 | 15 | 16 | @ProtocolField(index = 0) 17 | private StringField loginName; 18 | 19 | @ProtocolField(index = 1) 20 | private StringField password; 21 | 22 | public LoginInfo() { 23 | 24 | } 25 | 26 | public LoginInfo(String loginName, String password) { 27 | setLoginName(loginName); 28 | setPassword(password); 29 | } 30 | 31 | public LoginInfo setLoginName(String loginName) { 32 | this.loginName = StringField.wrap(loginName); 33 | return this; 34 | } 35 | 36 | public LoginInfo setPassword(String password) { 37 | this.password = StringField.wrap(password); 38 | return this; 39 | } 40 | 41 | @Override 42 | public String toString() { 43 | return "LoginInfo{" + 44 | "loginName=" + loginName + 45 | ", password=" + password + 46 | '}'; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/model/LoginInfoArray.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo.model; 2 | 3 | import static com.xuexiang.xtcp.core.XTCPConstants.MAX_ARRAY_LENGTH; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import com.xuexiang.xtcp.annotation.ProtocolField; 8 | import com.xuexiang.xtcp.core.model.AbstractArrayItem; 9 | import com.xuexiang.xtcp.enums.StorageMode; 10 | 11 | import java.util.Arrays; 12 | 13 | /** 14 | * 自定义协议数组【由于自定义的协议,其数组的类型不可知,故无法封装,只能自定义协议包装体】 15 | * 16 | * @author xuexiang 17 | * @since 2018/12/15 下午10:02 18 | */ 19 | public class LoginInfoArray extends AbstractArrayItem { 20 | 21 | /** 22 | * 集合数组的长度 23 | */ 24 | @ProtocolField(index = 0, length = DEFAULT_ARRAY_LENGTH_SIZE) 25 | private int mLength; 26 | 27 | /** 28 | * 集合数据 29 | */ 30 | @ProtocolField(index = 1) 31 | private LoginInfo[] mData; 32 | 33 | /** 34 | * 获取数组包装类 35 | * 36 | * @param data 37 | * @return 38 | */ 39 | public static LoginInfoArray wrap(@NonNull LoginInfo[] data) { 40 | return new LoginInfoArray(data); 41 | } 42 | 43 | 44 | /** 45 | * 空的构造方法不能去除,用于反射构造 46 | */ 47 | public LoginInfoArray() { 48 | 49 | } 50 | 51 | public LoginInfoArray(@NonNull LoginInfo[] data) { 52 | setData(data); 53 | } 54 | 55 | 56 | @Override 57 | public LoginInfoArray setLength(int length) { 58 | mLength = length; 59 | return this; 60 | } 61 | 62 | @Override 63 | public int getLength() { 64 | return mLength; 65 | } 66 | 67 | public LoginInfo[] getData() { 68 | return mData; 69 | } 70 | 71 | public LoginInfoArray setData(@NonNull LoginInfo[] data) { 72 | mData = data; 73 | if (mData.length > MAX_ARRAY_LENGTH) { //长度不能超过255 74 | mData = new LoginInfo[MAX_ARRAY_LENGTH]; 75 | System.arraycopy(data, 0, mData, 0, mData.length); 76 | } 77 | mLength = mData.length; 78 | return this; 79 | } 80 | 81 | @Override 82 | public boolean byte2proto(byte[] bytes, int index, int tailLength, StorageMode storageMode) { 83 | mData = new LoginInfo[getLength()]; 84 | Class type = getArrayFieldType(FIELD_NAME_DATA); 85 | if (type == null) { 86 | return false; 87 | } 88 | 89 | LoginInfo item; 90 | for (int i = 0; i < mData.length; i++) { 91 | if (bytes.length - index - tailLength <= 0) { //剩余长度已不足以读取,直接结束 92 | return false; 93 | } 94 | 95 | item = new LoginInfo(); 96 | item.byte2proto(bytes, index, tailLength, storageMode); 97 | index += item.getProtocolLength(); 98 | mData[i] = item; 99 | } 100 | return true; 101 | } 102 | 103 | @Override 104 | public String toString() { 105 | return "LoginInfoArray{" + 106 | "mLength=" + mLength + 107 | ", mData=" + Arrays.toString(mData) + 108 | '}'; 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/protocol/EmptyRequest.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo.protocol; 2 | 3 | import com.xuexiang.xtcp.annotation.Protocol; 4 | import com.xuexiang.xtcp.core.model.XProtocolItem; 5 | 6 | /** 7 | * @author xuexiang 8 | * @since 2019/5/24 17:02 9 | */ 10 | @Protocol(name = "空请求", opCode = 0x22, resCode = 0x22, desc = "空请求!") 11 | public class EmptyRequest extends XProtocolItem { 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/protocol/SettingRequest.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo.protocol; 2 | 3 | import com.xuexiang.xtcp.annotation.Protocol; 4 | import com.xuexiang.xtcp.annotation.ProtocolField; 5 | import com.xuexiang.xtcp.core.model.BCD; 6 | import com.xuexiang.xtcp.core.model.ByteArray; 7 | import com.xuexiang.xtcp.core.model.FixedString; 8 | import com.xuexiang.xtcp.core.model.IntArray; 9 | import com.xuexiang.xtcp.core.model.LargeString; 10 | import com.xuexiang.xtcp.core.model.LongArray; 11 | import com.xuexiang.xtcp.core.model.ShortArray; 12 | import com.xuexiang.xtcp.core.model.StringField; 13 | import com.xuexiang.xtcp.core.model.XProtocolItem; 14 | import com.xuexiang.xtcpdemo.model.LoginInfo; 15 | import com.xuexiang.xtcpdemo.model.LoginInfoArray; 16 | import com.xuexiang.xtcpdemo.protocol.test.TestProtocolItem; 17 | 18 | import java.util.Date; 19 | 20 | import static com.xuexiang.xtcp.model.ProtocolInfo.byte2HexString; 21 | 22 | /** 23 | * @author xuexiang 24 | * @since 2018/12/11 上午10:15 25 | */ 26 | @Protocol(name = "参数设置请求", opCode = 0x12, resCode = 0x33, desc = "注意重启下位机后生效!") 27 | public class SettingRequest extends XProtocolItem { 28 | 29 | @ProtocolField(index = 0) 30 | private byte func1; 31 | @ProtocolField(index = 1) 32 | private short func2; 33 | @ProtocolField(index = 2) 34 | private int func3; 35 | @ProtocolField(index = 3) 36 | private long func4; 37 | @ProtocolField(index = 4) 38 | private ByteArray list1; 39 | @ProtocolField(index = 5) 40 | private ShortArray list2; 41 | @ProtocolField(index = 6) 42 | private IntArray list3; 43 | @ProtocolField(index = 7) 44 | private LongArray list4; 45 | @ProtocolField(index = 8) 46 | private StringField string1; 47 | @ProtocolField(index = 9) 48 | private LargeString string2; 49 | @ProtocolField(index = 10) 50 | private TestProtocolItem testItem; 51 | @ProtocolField(index = 11) 52 | private LoginInfo loginInfo; 53 | @ProtocolField(index = 12) 54 | private BCD time = new BCD<>(Date.class, "yy-MM-dd HH:mm:ss"); 55 | @ProtocolField(index = 13) 56 | private BCD number = new BCD<>(Integer.class, "XXX"); 57 | @ProtocolField(index = 14) 58 | private BCD total = new BCD<>(Float.class, "XXXXX.XX"); 59 | @ProtocolField(index = 15) 60 | private BCD money = new BCD<>(Double.class, "XXXXX.XX"); 61 | @ProtocolField(index = 16) 62 | private FixedString ID = new FixedString(10); 63 | @ProtocolField(index = 17) 64 | private LoginInfoArray loginInfos; 65 | @ProtocolField(index = 18, unsigned = false) 66 | private short signedShort; 67 | @ProtocolField(index = 19, unsigned = false) 68 | private int signedInt; 69 | @ProtocolField(index = 20, unsigned = false) 70 | private long signedLong; 71 | 72 | public SettingRequest() { 73 | 74 | } 75 | 76 | public SettingRequest setList1(byte... list) { 77 | this.list1 = ByteArray.wrap(list); 78 | return this; 79 | } 80 | 81 | public SettingRequest setList2(short... list) { 82 | this.list2 = ShortArray.wrap(list); 83 | return this; 84 | } 85 | 86 | public SettingRequest setList3(int... list) { 87 | this.list3 = IntArray.wrap(list); 88 | return this; 89 | } 90 | 91 | public SettingRequest setList4(long... list) { 92 | this.list4 = LongArray.wrap(list); 93 | return this; 94 | } 95 | 96 | public SettingRequest setFunc1(byte func1) { 97 | this.func1 = func1; 98 | return this; 99 | } 100 | 101 | public SettingRequest setFunc2(short func2) { 102 | this.func2 = func2; 103 | return this; 104 | } 105 | 106 | public SettingRequest setFunc3(int func3) { 107 | this.func3 = func3; 108 | return this; 109 | } 110 | 111 | public SettingRequest setFunc4(long func4) { 112 | this.func4 = func4; 113 | return this; 114 | } 115 | 116 | public SettingRequest setString1(String s) { 117 | this.string1 = new StringField(s); 118 | return this; 119 | } 120 | 121 | public SettingRequest setString2(String s) { 122 | this.string2 = LargeString.wrap(s); 123 | return this; 124 | } 125 | 126 | public SettingRequest setTestItem(TestProtocolItem item) { 127 | this.testItem = item; 128 | return this; 129 | } 130 | 131 | public SettingRequest setLoginInfo(LoginInfo loginInfo) { 132 | this.loginInfo = loginInfo; 133 | return this; 134 | } 135 | 136 | public SettingRequest setTime(Date date) { 137 | time.setValue(date); 138 | return this; 139 | } 140 | 141 | public SettingRequest setNumber(int number) { 142 | this.number.setValue(number); 143 | return this; 144 | } 145 | 146 | public SettingRequest setTotal(float total) { 147 | this.total.setValue(total); 148 | return this; 149 | } 150 | 151 | public SettingRequest setMoney(double money) { 152 | this.money.setValue(money); 153 | return this; 154 | } 155 | 156 | public SettingRequest setID(String id) { 157 | ID.setFixedString(id); 158 | return this; 159 | } 160 | 161 | public SettingRequest setLoginInfos(LoginInfo... loginInfos) { 162 | this.loginInfos= LoginInfoArray.wrap(loginInfos); 163 | return this; 164 | } 165 | 166 | 167 | public SettingRequest setSignedShort(short signedShort) { 168 | this.signedShort = signedShort; 169 | return this; 170 | } 171 | 172 | public SettingRequest setSignedInt(int signedInt) { 173 | this.signedInt = signedInt; 174 | return this; 175 | } 176 | 177 | public SettingRequest setSignedLong(long signedLong) { 178 | this.signedLong = signedLong; 179 | return this; 180 | } 181 | 182 | @Override 183 | public String toString() { 184 | return "SettingRequest{" + 185 | "func1=" + byte2HexString(func1) + 186 | ", func2=" + func2 + 187 | ", func3=" + func3 + 188 | ", func4=" + func4 + 189 | ", list1=" + list1 + 190 | ", list2=" + list2 + 191 | ", list3=" + list3 + 192 | ", list4=" + list4 + 193 | ", string1=" + string1 + 194 | ", string2=" + string2 + 195 | ", testItem=" + testItem + 196 | ", loginInfo=" + loginInfo + 197 | ", time=" + time.getFormatValue() + 198 | ", number=" + number.getFormatValue() + 199 | ", total=" + total.getFormatValue() + 200 | ", money=" + money.getFormatValue() + 201 | ", ID=" + ID.getFixedString() + 202 | ", loginInfos=" + loginInfos + 203 | ", signedShort=" + signedShort + 204 | ", signedInt=" + signedInt + 205 | ", signedLong=" + signedLong + 206 | '}'; 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/protocol/test/MessageTest.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo.protocol.test; 2 | 3 | import com.xuexiang.xtcp.annotation.Protocol; 4 | import com.xuexiang.xtcp.annotation.ProtocolField; 5 | import com.xuexiang.xtcp.core.model.ShortArray; 6 | import com.xuexiang.xtcp.core.model.XProtocolItem; 7 | import com.xuexiang.xtcpdemo.model.LoginInfo; 8 | 9 | import static com.xuexiang.xtcp.model.ProtocolInfo.byte2HexString; 10 | 11 | /** 12 | * @author xuexiang 13 | * @since 2018/12/17 上午12:36 14 | */ 15 | @Protocol(name = "测试消息包装", opCode = 0x34) 16 | public class MessageTest extends XProtocolItem { 17 | 18 | @ProtocolField(index = 0) 19 | private byte func1; 20 | @ProtocolField(index = 1) 21 | private short func2; 22 | @ProtocolField(index = 2) 23 | private int func3; 24 | @ProtocolField(index = 3) 25 | private long func4; 26 | @ProtocolField(index = 4) 27 | private ShortArray list2; 28 | @ProtocolField(index = 5) 29 | private LoginInfo loginInfo; 30 | 31 | public MessageTest setFunc1(byte func1) { 32 | this.func1 = func1; 33 | return this; 34 | } 35 | 36 | public MessageTest setFunc2(short func2) { 37 | this.func2 = func2; 38 | return this; 39 | } 40 | 41 | public MessageTest setFunc3(int func3) { 42 | this.func3 = func3; 43 | return this; 44 | } 45 | 46 | public MessageTest setFunc4(long func4) { 47 | this.func4 = func4; 48 | return this; 49 | } 50 | 51 | public MessageTest setLoginInfo(LoginInfo loginInfo) { 52 | this.loginInfo = loginInfo; 53 | return this; 54 | } 55 | 56 | public MessageTest setList2(short... list) { 57 | this.list2 = ShortArray.wrap(list); 58 | return this; 59 | } 60 | 61 | @Override 62 | public String toString() { 63 | return "MessageTest{" + 64 | "func1=" + byte2HexString(func1) + 65 | ", func2=" + func2 + 66 | ", func3=" + func3 + 67 | ", func4=" + func4 + 68 | ", list2=" + list2 + 69 | ", loginInfo=" + loginInfo + 70 | '}'; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/protocol/test/TestProtocolItem.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo.protocol.test; 2 | 3 | import com.xuexiang.xtcp.annotation.ProtocolField; 4 | import com.xuexiang.xtcp.core.model.IntArray; 5 | import com.xuexiang.xtcp.core.model.XProtocolItem; 6 | import com.xuexiang.xtcpdemo.model.LoginInfo; 7 | 8 | import static com.xuexiang.xtcp.model.ProtocolInfo.byte2HexString; 9 | 10 | /** 11 | * @author xuexiang 12 | * @since 2018/12/15 下午6:37 13 | */ 14 | public class TestProtocolItem extends XProtocolItem { 15 | 16 | @ProtocolField(index = 0) 17 | private byte func1; 18 | @ProtocolField(index = 1) 19 | private short func2; 20 | @ProtocolField(index = 2) 21 | private int func3; 22 | @ProtocolField(index = 3) 23 | private long func4; 24 | @ProtocolField(index = 4) 25 | private IntArray list; 26 | @ProtocolField(index = 5) 27 | private LoginInfo loginInfo; 28 | 29 | public TestProtocolItem setFunc1(byte func1) { 30 | this.func1 = func1; 31 | return this; 32 | } 33 | 34 | public TestProtocolItem setFunc2(short func2) { 35 | this.func2 = func2; 36 | return this; 37 | } 38 | 39 | public TestProtocolItem setFunc3(int func3) { 40 | this.func3 = func3; 41 | return this; 42 | } 43 | 44 | public TestProtocolItem setFunc4(long func4) { 45 | this.func4 = func4; 46 | return this; 47 | } 48 | 49 | public TestProtocolItem setList1(int... list) { 50 | this.list = IntArray.wrap(list); 51 | return this; 52 | } 53 | 54 | public TestProtocolItem setLoginInfo(LoginInfo loginInfo) { 55 | this.loginInfo = loginInfo; 56 | return this; 57 | } 58 | 59 | @Override 60 | public String toString() { 61 | return "TestProtocolItem{" + 62 | "func1=" + byte2HexString(func1) + 63 | ", func2=" + func2 + 64 | ", func3=" + func3 + 65 | ", func4=" + func4 + 66 | ", list=" + list + 67 | ", LoginInfo=" + loginInfo + 68 | '}'; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/protocol/test/indefiniteArray/TestByteArray.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo.protocol.test.indefiniteArray; 2 | 3 | import com.xuexiang.xtcp.annotation.ProtocolField; 4 | import com.xuexiang.xtcp.core.model.XProtocolItem; 5 | import com.xuexiang.xtcp.utils.ConvertUtils; 6 | 7 | import static com.xuexiang.xtcp.model.ProtocolInfo.byte2HexString; 8 | 9 | /** 10 | * 不定长数组测试【byte】 11 | * 12 | * @author xuexiang 13 | * @since 2018/12/24 上午9:25 14 | */ 15 | public class TestByteArray extends XProtocolItem { 16 | 17 | @ProtocolField(index = 0) 18 | private byte[] bytes; 19 | @ProtocolField(index = 1) 20 | private byte func1; 21 | @ProtocolField(index = 2) 22 | private short func2; 23 | @ProtocolField(index = 3) 24 | private int func3; 25 | @ProtocolField(index = 4) 26 | private long func4; 27 | 28 | public TestByteArray setFunc1(byte func1) { 29 | this.func1 = func1; 30 | return this; 31 | } 32 | 33 | public TestByteArray setFunc2(short func2) { 34 | this.func2 = func2; 35 | return this; 36 | } 37 | 38 | public TestByteArray setFunc3(int func3) { 39 | this.func3 = func3; 40 | return this; 41 | } 42 | 43 | public TestByteArray setFunc4(long func4) { 44 | this.func4 = func4; 45 | return this; 46 | } 47 | 48 | public TestByteArray setBytes(byte[] bytes) { 49 | this.bytes = bytes; 50 | return this; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return "TestByteArray{" + 56 | "bytes=" + ConvertUtils.bytesToHex(bytes) + 57 | ", func1=" + byte2HexString(func1) + 58 | ", func2=" + func2 + 59 | ", func3=" + func3 + 60 | ", func4=" + func4 + 61 | '}'; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/protocol/test/indefiniteArray/TestIntArray.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo.protocol.test.indefiniteArray; 2 | 3 | import com.xuexiang.xtcp.annotation.ProtocolField; 4 | import com.xuexiang.xtcp.core.model.XProtocolItem; 5 | 6 | import java.util.Arrays; 7 | 8 | import static com.xuexiang.xtcp.model.ProtocolInfo.byte2HexString; 9 | 10 | /** 11 | * 不定长数组测试【int】 12 | * 13 | * @author xuexiang 14 | * @since 2018/12/24 上午10:11 15 | */ 16 | public class TestIntArray extends XProtocolItem { 17 | 18 | @ProtocolField(index = 0) 19 | private byte func1; 20 | @ProtocolField(index = 1) 21 | private short func2; 22 | @ProtocolField(index = 2, unsigned = false) 23 | private int[] ints; 24 | @ProtocolField(index = 3) 25 | private int func3; 26 | @ProtocolField(index = 4) 27 | private long func4; 28 | 29 | 30 | public TestIntArray setFunc1(byte func1) { 31 | this.func1 = func1; 32 | return this; 33 | } 34 | 35 | public TestIntArray setFunc2(short func2) { 36 | this.func2 = func2; 37 | return this; 38 | } 39 | 40 | public TestIntArray setFunc3(int func3) { 41 | this.func3 = func3; 42 | return this; 43 | } 44 | 45 | public TestIntArray setFunc4(long func4) { 46 | this.func4 = func4; 47 | return this; 48 | } 49 | 50 | public TestIntArray setInts(int[] ints) { 51 | this.ints = ints; 52 | return this; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "TestIntArray{" + 58 | "func1=" + byte2HexString(func1) + 59 | ", func2=" + func2 + 60 | ", ints=" + Arrays.toString(ints) + 61 | ", func3=" + func3 + 62 | ", func4=" + func4 + 63 | '}'; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/protocol/test/indefiniteArray/TestLongArray.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo.protocol.test.indefiniteArray; 2 | 3 | import com.xuexiang.xtcp.annotation.ProtocolField; 4 | import com.xuexiang.xtcp.core.model.XProtocolItem; 5 | 6 | import java.util.Arrays; 7 | 8 | import static com.xuexiang.xtcp.model.ProtocolInfo.byte2HexString; 9 | 10 | /** 11 | * 不定长数组测试【long】 12 | * 13 | * @author xuexiang 14 | * @since 2018/12/24 上午10:11 15 | */ 16 | public class TestLongArray extends XProtocolItem { 17 | 18 | @ProtocolField(index = 0) 19 | private byte func1; 20 | @ProtocolField(index = 1) 21 | private short func2; 22 | @ProtocolField(index = 2, unsigned = false) 23 | private long[] longs; 24 | @ProtocolField(index = 3) 25 | private int func3; 26 | @ProtocolField(index = 4) 27 | private long func4; 28 | 29 | public TestLongArray setFunc1(byte func1) { 30 | this.func1 = func1; 31 | return this; 32 | } 33 | 34 | public TestLongArray setFunc2(short func2) { 35 | this.func2 = func2; 36 | return this; 37 | } 38 | 39 | public TestLongArray setFunc3(int func3) { 40 | this.func3 = func3; 41 | return this; 42 | } 43 | 44 | public TestLongArray setFunc4(long func4) { 45 | this.func4 = func4; 46 | return this; 47 | } 48 | 49 | public TestLongArray setLongs(long[] longs) { 50 | this.longs = longs; 51 | return this; 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return "TestLongArray{" + 57 | "func1=" + byte2HexString(func1) + 58 | ", func2=" + func2 + 59 | ", longs=" + Arrays.toString(longs) + 60 | ", func3=" + func3 + 61 | ", func4=" + func4 + 62 | '}'; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/protocol/test/indefiniteArray/TestShortArray.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo.protocol.test.indefiniteArray; 2 | 3 | import com.xuexiang.xtcp.annotation.ProtocolField; 4 | import com.xuexiang.xtcp.core.model.XProtocolItem; 5 | 6 | import java.util.Arrays; 7 | 8 | import static com.xuexiang.xtcp.model.ProtocolInfo.byte2HexString; 9 | 10 | /** 11 | * 不定长数组测试【short】 12 | * 13 | * @author xuexiang 14 | * @since 2018/12/24 上午9:25 15 | */ 16 | public class TestShortArray extends XProtocolItem { 17 | 18 | @ProtocolField(index = 0) 19 | private byte func1; 20 | @ProtocolField(index = 1) 21 | private short func2; 22 | @ProtocolField(index = 2) 23 | private int func3; 24 | @ProtocolField(index = 3) 25 | private long func4; 26 | @ProtocolField(index = 4, unsigned = false) 27 | private short[] shorts; 28 | 29 | 30 | public TestShortArray setFunc1(byte func1) { 31 | this.func1 = func1; 32 | return this; 33 | } 34 | 35 | public TestShortArray setFunc2(short func2) { 36 | this.func2 = func2; 37 | return this; 38 | } 39 | 40 | public TestShortArray setFunc3(int func3) { 41 | this.func3 = func3; 42 | return this; 43 | } 44 | 45 | public TestShortArray setFunc4(long func4) { 46 | this.func4 = func4; 47 | return this; 48 | } 49 | 50 | public TestShortArray setShorts(short[] shorts) { 51 | this.shorts = shorts; 52 | return this; 53 | } 54 | 55 | @Override 56 | public String toString() { 57 | return "TestShortArray{" + 58 | "func1=" + byte2HexString(func1) + 59 | ", func2=" + func2 + 60 | ", shorts=" + Arrays.toString(shorts) + 61 | ", func3=" + func3 + 62 | ", func4=" + func4 + 63 | '}'; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /app/src/main/java/com/xuexiang/xtcpdemo/protocol/test/indefiniteArray/TestStringItem.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo.protocol.test.indefiniteArray; 2 | 3 | import com.xuexiang.xtcp.annotation.ProtocolField; 4 | import com.xuexiang.xtcp.core.model.XProtocolItem; 5 | 6 | import static com.xuexiang.xtcp.model.ProtocolInfo.byte2HexString; 7 | 8 | /** 9 | * 不定长数组测试【String】 10 | * 11 | * @author xuexiang 12 | * @since 2018/12/24 上午9:25 13 | */ 14 | public class TestStringItem extends XProtocolItem { 15 | 16 | @ProtocolField(index = 0) 17 | private byte func1; 18 | @ProtocolField(index = 1) 19 | private short func2; 20 | @ProtocolField(index = 2, charset = "GBK") 21 | private String string; 22 | @ProtocolField(index = 3) 23 | private int func3; 24 | @ProtocolField(index = 4) 25 | private long func4; 26 | 27 | 28 | public TestStringItem setFunc1(byte func1) { 29 | this.func1 = func1; 30 | return this; 31 | } 32 | 33 | public TestStringItem setFunc2(short func2) { 34 | this.func2 = func2; 35 | return this; 36 | } 37 | 38 | public TestStringItem setFunc3(int func3) { 39 | this.func3 = func3; 40 | return this; 41 | } 42 | 43 | public TestStringItem setFunc4(long func4) { 44 | this.func4 = func4; 45 | return this; 46 | } 47 | 48 | public TestStringItem setString(String string) { 49 | this.string = string; 50 | return this; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return "TestStringItem{" + 56 | "func1=" + byte2HexString(func1) + 57 | ", func2=" + func2 + 58 | ", string='" + string + '\'' + 59 | ", func3=" + func3 + 60 | ", func4=" + func4 + 61 | '}'; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/XTCP/9016969bb256ea3846ca670454b5f882e9e82219/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/XTCP/9016969bb256ea3846ca670454b5f882e9e82219/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/XTCP/9016969bb256ea3846ca670454b5f882e9e82219/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/XTCP/9016969bb256ea3846ca670454b5f882e9e82219/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/XTCP/9016969bb256ea3846ca670454b5f882e9e82219/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/XTCP/9016969bb256ea3846ca670454b5f882e9e82219/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/XTCP/9016969bb256ea3846ca670454b5f882e9e82219/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/XTCP/9016969bb256ea3846ca670454b5f882e9e82219/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/XTCP/9016969bb256ea3846ca670454b5f882e9e82219/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/XTCP/9016969bb256ea3846ca670454b5f882e9e82219/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #299EE3 4 | #299EE3 5 | #299EE3 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | XTCP 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/com/xuexiang/xtcpdemo/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcpdemo; 2 | 3 | import com.xuexiang.xtcp.core.model.BCD; 4 | 5 | import org.junit.Test; 6 | 7 | import java.util.Date; 8 | 9 | import static org.junit.Assert.assertEquals; 10 | 11 | /** 12 | * Example local unit test, which will execute on the development machine (host). 13 | * 14 | * @see Testing documentation 15 | */ 16 | public class ExampleUnitTest { 17 | @Test 18 | public void addition_isCorrect() { 19 | assertEquals(4, 2 + 2); 20 | 21 | // byte[] bytes = ConvertUtils.hexStringToBytes("00112233441122334411223344"); 22 | // 23 | // int length = 4; 24 | // System.out.println(ConvertUtils.bytesToShort(StorageMode.LittleEndian, bytes, 1, length)); 25 | // 26 | // System.out.println(ConvertUtils.bytesToInt(StorageMode.LittleEndian, bytes, 1, length)); 27 | // 28 | // byte[] b = new byte[20]; 29 | //// System.out.println(ConvertUtils.fillIntToBytes(StorageMode.BigEndian, 2133, b, 1, 5)); 30 | //// 31 | //// System.out.println(ConvertUtils.bytesToHexString(b)); 32 | // 33 | // long value = ConvertUtils.bytesToLong(StorageMode.BigEndian, bytes, 1, 9); 34 | // System.out.println(value); 35 | // 36 | // ConvertUtils.fillLongToBytes(StorageMode.BigEndian, value, b, 1, 9); 37 | // System.out.println(ConvertUtils.bytesToHexString(b)); 38 | // 39 | // String s = "11,22,33,44,55"; 40 | // System.out.println(Arrays.toString(s.split(","))); 41 | 42 | // BCD bcd = new BCD<>(Date.class, "yy-MM-dd HH-mm"); 43 | // System.out.println(bcd.getProtocolLength()); 44 | 45 | 46 | 47 | } 48 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | apply from: './versions.gradle' 5 | addRepos(repositories) //增加代码仓库 6 | dependencies { 7 | classpath deps.android_gradle_plugin 8 | classpath deps.android_maven_gradle_plugin 9 | 10 | classpath 'com.github.xuexiangjys.XAOP:xaop-plugin:1.1.0' 11 | 12 | } 13 | } 14 | 15 | allprojects { 16 | addRepos(repositories) 17 | } 18 | 19 | task clean(type: Delete) { 20 | delete rootProject.buildDir 21 | } 22 | 23 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | # 是否打包APK 16 | isNeedPackage=false 17 | 18 | android.useAndroidX=true 19 | android.enableJetifier=true 20 | 21 | android.enableD8=true 22 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/XTCP/9016969bb256ea3846ca670454b5f882e9e82219/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri May 24 16:08:13 CST 2019 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /img/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuexiangjys/XTCP/9016969bb256ea3846ca670454b5f882e9e82219/img/download.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':xtcp_runtime', ':xtcp_annotation', ':xtcp_compiler' 2 | -------------------------------------------------------------------------------- /versions.gradle: -------------------------------------------------------------------------------- 1 | import java.util.regex.Matcher 2 | import java.util.regex.Pattern 3 | 4 | ext.deps = [:] 5 | def versions = [:] 6 | versions.android_gradle_plugin = "3.6.1" 7 | versions.android_maven_gradle_plugin = "2.0" 8 | versions.gradle_bintray_plugin = "1.8.0" 9 | versions.booster = "3.1.0" 10 | versions.booster_all = "1.1.1" 11 | versions.support = "28.0.0" 12 | versions.annotation = "1.2.0" 13 | versions.androidx = "1.3.1" 14 | versions.recyclerview = "1.2.1" 15 | versions.material = "1.4.0" 16 | versions.junit = "4.12" 17 | versions.espresso = "3.2.0" 18 | versions.constraint_layout = "2.1.0" 19 | versions.glide = "4.11.0" 20 | versions.rxjava2 = "2.2.20" 21 | versions.rxandroid = "2.1.1" 22 | versions.rxbinding = "2.2.0" 23 | versions.butterknife = "10.1.0" 24 | versions.runner = "1.2.0" 25 | versions.gson = "2.8.5" 26 | versions.okhttp3 = "3.12.12" 27 | versions.leakcanary = "2.6" 28 | 29 | //========xlibrary start========// 30 | 31 | versions.xui = "1.1.8" 32 | versions.xupdate = "2.1.1" 33 | versions.xaop = "1.1.0" 34 | versions.xutil = "2.0.0" 35 | versions.xhttp2 = "2.0.4" 36 | versions.xpage = "3.2.0" 37 | versions.xrouter = "1.0.1" 38 | 39 | //========xlibrary end========// 40 | 41 | def deps = [:] 42 | 43 | def support = [:] 44 | support.annotations = "com.android.support:support-annotations:$versions.support" 45 | support.app_compat = "com.android.support:appcompat-v7:$versions.support" 46 | support.recyclerview = "com.android.support:recyclerview-v7:$versions.support" 47 | support.cardview = "com.android.support:cardview-v7:$versions.support" 48 | support.design = "com.android.support:design:$versions.support" 49 | support.v4 = "com.android.support:support-v4:$versions.support" 50 | support.core_utils = "com.android.support:support-core-utils:$versions.support" 51 | deps.support = support 52 | 53 | def androidx = [:] 54 | androidx.annotations = "androidx.annotation:annotation:$versions.annotation" 55 | androidx.appcompat = "androidx.appcompat:appcompat:$versions.androidx" 56 | androidx.recyclerview = "androidx.recyclerview:recyclerview:$versions.recyclerview" 57 | androidx.design = "com.google.android.material:material:$versions.material" 58 | androidx.multidex = 'androidx.multidex:multidex:2.0.1' 59 | deps.androidx = androidx 60 | 61 | def booster = [:] 62 | booster.gradle_plugin = "com.didiglobal.booster:booster-gradle-plugin:$versions.booster" 63 | booster.task_all = "com.didiglobal.booster:booster-task-all:$versions.booster_all" 64 | booster.transform_all = "com.didiglobal.booster:booster-transform-all:$versions.booster_all" 65 | //采用 cwebp 对资源进行压缩 66 | booster.task_compression_cwebp = "com.didiglobal.booster:booster-task-compression-cwebp:$versions.booster" 67 | //采用 pngquant 对资源进行压缩 68 | booster.task_compression_pngquant = "com.didiglobal.booster:booster-task-compression-pngquant:$versions.booster" 69 | //ap_ 文件压缩 70 | booster.task_processed_res = "com.didiglobal.booster:booster-task-compression-processed-res:$versions.booster" 71 | //去冗余资源 72 | booster.task_resource_deredundancy = "com.didiglobal.booster:booster-task-resource-deredundancy:$versions.booster" 73 | //检查 SNAPSHOT 版本 74 | booster.task_check_snapshot = "com.didiglobal.booster:booster-task-check-snapshot:$versions.booster" 75 | //性能瓶颈检测 76 | booster.transform_lint = "com.didiglobal.booster:booster-transform-lint:$versions.booster" 77 | //多线程优化 78 | booster.transform_thread = "com.didiglobal.booster:booster-transform-thread:$versions.booster" 79 | //资源索引内联 80 | booster.transform_r_inline = "com.didiglobal.booster:booster-transform-r-inline:$versions.booster" 81 | //WebView 预加载 82 | booster.transform_webview = "com.didiglobal.booster:booster-transform-webview:$versions.booster" 83 | //SharedPreferences 优化 84 | booster.transform_shared_preferences = "com.didiglobal.booster:booster-transform-shared-preferences:$versions.booster" 85 | //检查覆盖安装导致的 Resources 和 Assets 未加载的 Bug 86 | booster.transform_res_check = "com.didiglobal.booster:booster-transform-res-check:$versions.booster" 87 | //修复 Toast 在 Android 7.1 上的 Bug 88 | booster.transform_toast = "com.didiglobal.booster:booster-transform-toast:$versions.booster" 89 | //处理系统 Crash 90 | booster.transform_activity_thread = "com.didiglobal.booster:booster-transform-activity-thread:$versions.booster" 91 | deps.booster = booster 92 | 93 | def butterknife = [:] 94 | butterknife.runtime = "com.jakewharton:butterknife:$versions.butterknife" 95 | butterknife.compiler = "com.jakewharton:butterknife-compiler:$versions.butterknife" 96 | 97 | deps.butterknife = butterknife 98 | 99 | def espresso = [:] 100 | espresso.core = "androidx.test.espresso:espresso-core:$versions.espresso" 101 | espresso.contrib = "androidx.test.espresso:espresso-contrib:$versions.espresso" 102 | espresso.intents = "androidx.test.espresso:espresso-intents:$versions.espresso" 103 | deps.espresso = espresso 104 | 105 | deps.android_gradle_plugin = "com.android.tools.build:gradle:$versions.android_gradle_plugin" 106 | deps.android_maven_gradle_plugin = "com.github.dcendents:android-maven-gradle-plugin:$versions.android_maven_gradle_plugin" 107 | deps.gradle_bintray_plugin = "com.jfrog.bintray.gradle:gradle-bintray-plugin:$versions.gradle_bintray_plugin" 108 | deps.glide = "com.github.bumptech.glide:glide:$versions.glide" 109 | deps.constraint_layout = "androidx.constraint:constraint-layout:$versions.constraint_layout" 110 | deps.junit = "junit:junit:$versions.junit" 111 | deps.runner = "androidx.test:runner:$versions.runner" 112 | deps.rxjava2 = "io.reactivex.rxjava2:rxjava:$versions.rxjava2" 113 | deps.rxandroid = "io.reactivex.rxjava2:rxandroid:$versions.rxandroid" 114 | deps.rxbinding = "com.jakewharton.rxbinding2:rxbinding:$versions.rxbinding" 115 | deps.gson = "com.google.code.gson:gson:$versions.gson" 116 | deps.okhttp3 = "com.squareup.okhttp3:okhttp:$versions.okhttp3" 117 | deps.leakcanary = "com.squareup.leakcanary:leakcanary-android:$versions.leakcanary" 118 | 119 | //========xlibrary start=================// 120 | 121 | def xlibrary = [:] 122 | 123 | xlibrary.xui = "com.github.xuexiangjys:XUI:$versions.xui" 124 | xlibrary.xupdate = "com.github.xuexiangjys:XUpdate:$versions.xupdate" 125 | xlibrary.xaop_runtime = "com.github.xuexiangjys.XAOP:xaop-runtime:$versions.xaop" 126 | xlibrary.xaop_plugin = "com.github.xuexiangjys.XAOP:xaop-plugin:$versions.xaop" 127 | xlibrary.xutil_core = "com.github.xuexiangjys.XUtil:xutil-core:$versions.xutil" 128 | xlibrary.xhttp2 = "com.github.xuexiangjys:XHttp2:$versions.xhttp2" 129 | xlibrary.xpage_lib = "com.github.xuexiangjys.XPage:xpage-lib:$versions.xpage" 130 | xlibrary.xpage_compiler = "com.github.xuexiangjys.XPage:xpage-compiler:$versions.xpage" 131 | xlibrary.xrouter_runtime = "com.github.xuexiangjys.XRouter:xrouter-runtime:$versions.xrouter" 132 | xlibrary.xrouter_compiler = "com.github.xuexiangjys.XRouter:xrouter-compiler:$versions.xrouter" 133 | xlibrary.xrouter_plugin = "com.github.xuexiangjys.XRouter:xrouter-plugin:$versions.xrouter" 134 | 135 | deps.xlibrary = xlibrary 136 | 137 | //========xlibrary end=================// 138 | 139 | ext.deps = deps 140 | 141 | def build_versions = [:] 142 | build_versions.min_sdk = 19 143 | build_versions.target_sdk = 29 144 | build_versions.build_tools = "29.0.3" 145 | ext.build_versions = build_versions 146 | 147 | def app_release = [:] 148 | app_release.storeFile = "../keystores/android.keystore" 149 | app_release.storePassword = "xuexiang" 150 | app_release.keyAlias = "android.keystore" 151 | app_release.keyPassword = "xuexiang" 152 | 153 | ext.app_release = app_release 154 | 155 | /** 156 | * @return 是否为release 157 | */ 158 | def isRelease() { 159 | Gradle gradle = getGradle() 160 | String tskReqStr = gradle.getStartParameter().getTaskRequests().toString() 161 | 162 | Pattern pattern 163 | if (tskReqStr.contains("assemble")) { 164 | println tskReqStr 165 | pattern = Pattern.compile("assemble(\\w*)(Release|Debug)") 166 | } else { 167 | pattern = Pattern.compile("generate(\\w*)(Release|Debug)") 168 | } 169 | Matcher matcher = pattern.matcher(tskReqStr) 170 | 171 | if (matcher.find()) { 172 | String task = matcher.group(0).toLowerCase() 173 | println("[BuildType] Current task: " + task) 174 | return task.contains("release") 175 | } else { 176 | println "[BuildType] NO MATCH FOUND" 177 | return true 178 | } 179 | } 180 | 181 | ext.isRelease = this.&isRelease 182 | 183 | //默认添加代码仓库路径 184 | static def addRepos(RepositoryHandler handler) { 185 | handler.mavenLocal() 186 | handler.google { url 'https://maven.aliyun.com/repository/google' } 187 | handler.jcenter { url 'https://maven.aliyun.com/repository/jcenter' } 188 | handler.mavenCentral { url 'https://maven.aliyun.com/repository/central' } 189 | handler.maven { url "https://jitpack.io" } 190 | handler.maven { url 'https://maven.aliyun.com/repository/public' } 191 | handler.maven { url "https://repo1.maven.org/maven2/" } 192 | handler.maven { url 'https://oss.sonatype.org/content/repositories/public' } 193 | //Add the Local repository 194 | handler.maven { url 'LocalRepository' } 195 | } 196 | 197 | ext.addRepos = this.&addRepos 198 | -------------------------------------------------------------------------------- /xtcp_annotation/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /xtcp_annotation/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | implementation fileTree(dir: 'libs', include: ['*.jar']) 5 | } 6 | 7 | targetCompatibility = JavaVersion.VERSION_1_8 8 | sourceCompatibility = JavaVersion.VERSION_1_8 9 | 10 | apply from: "../JitPackUpload.gradle" -------------------------------------------------------------------------------- /xtcp_annotation/src/main/java/com/xuexiang/xtcp/annotation/Protocol.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.annotation; 2 | 3 | import com.xuexiang.xtcp.enums.StorageMode; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * 协议实体的注解 12 | * 13 | * @author xuexiang 14 | * @since 2018/12/10 下午4:43 15 | */ 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target(ElementType.TYPE) 18 | public @interface Protocol { 19 | 20 | /** 21 | * @return 协议的名称 22 | */ 23 | String name() default ""; 24 | 25 | /** 26 | * @return 协议命令码,协议命令的唯一号 27 | */ 28 | byte opCode(); 29 | 30 | /** 31 | * @return 协议响应码(结果对应的命令码) 32 | */ 33 | byte resCode() default -1; 34 | 35 | /** 36 | * @return 存储方式 37 | */ 38 | StorageMode mode() default StorageMode.Default; 39 | 40 | 41 | /** 42 | * @return 描述信息 43 | */ 44 | String desc() default ""; 45 | 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /xtcp_annotation/src/main/java/com/xuexiang/xtcp/annotation/ProtocolField.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.annotation; 2 | 3 | import com.xuexiang.xtcp.enums.StorageMode; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * 协议字段的注解 12 | * 13 | * @author xuexiang 14 | * @since 2018/12/10 下午3:22 15 | */ 16 | @Retention(RetentionPolicy.RUNTIME) 17 | @Target(ElementType.FIELD) 18 | public @interface ProtocolField { 19 | 20 | /** 21 | * @return 字段的顺序索引 22 | */ 23 | int index(); 24 | 25 | /** 26 | * @return 是否为协议解析字段 27 | */ 28 | boolean isField() default true; 29 | 30 | /** 31 | * @return 协议字段的长度, 不设置的话,默认自动识别 32 | */ 33 | int length() default -1; 34 | 35 | /** 36 | * @return 是否是无符号数,默认是true 37 | */ 38 | boolean unsigned() default true; 39 | 40 | /** 41 | * @return 存储方式 42 | */ 43 | StorageMode mode() default StorageMode.Default; 44 | 45 | /** 46 | * @return 字符集(只对String有效) 47 | */ 48 | String charset() default "UTF-8"; 49 | 50 | } 51 | -------------------------------------------------------------------------------- /xtcp_annotation/src/main/java/com/xuexiang/xtcp/enums/StorageMode.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.enums; 2 | 3 | /** 4 | * 数据的存储方式 5 | * 6 | * @author xuexiang 7 | * @since 2018/12/10 下午3:35 8 | */ 9 | public enum StorageMode { 10 | 11 | /** 12 | * 大端存储(默认符合我们正常的习惯) 13 | */ 14 | BigEndian, 15 | /** 16 | * 小端存储 17 | */ 18 | LittleEndian, 19 | 20 | /** 21 | * 使用全局设置的默认方式 22 | */ 23 | Default 24 | 25 | } 26 | -------------------------------------------------------------------------------- /xtcp_annotation/src/main/java/com/xuexiang/xtcp/model/IArrayItem.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.model; 2 | 3 | import com.xuexiang.xtcp.enums.StorageMode; 4 | 5 | /** 6 | * 解析byte时为了获取数组的长度。 7 | * 8 | * @author xuexiang 9 | * @since 2018/12/12 下午4:35 10 | */ 11 | public interface IArrayItem extends IProtocolItem { 12 | 13 | /** 14 | * @return 获取数组数据的长度(length) 15 | */ 16 | int getLength(); 17 | 18 | /** 19 | * 填充数组的长度 20 | * 21 | * @param bytes 22 | * @param index 23 | * @param storageMode 24 | * @return 读取长度占的byte位数 25 | */ 26 | int fillArrayLength(byte[] bytes, int index, StorageMode storageMode); 27 | 28 | 29 | /** 30 | * 默认数组长度所占的byte位数 31 | */ 32 | int DEFAULT_ARRAY_LENGTH_SIZE = 1; 33 | 34 | /** 35 | * 大数组长度所占的byte位数 36 | */ 37 | int LARGE_ARRAY_LENGTH_SIZE = 2; 38 | 39 | 40 | } 41 | -------------------------------------------------------------------------------- /xtcp_annotation/src/main/java/com/xuexiang/xtcp/model/IProtocol.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.model; 2 | 3 | import com.xuexiang.xtcp.enums.StorageMode; 4 | 5 | /** 6 | * 协议实现接口 7 | * 8 | * @author xuexiang 9 | * @since 2018/12/11 上午9:11 10 | */ 11 | public interface IProtocol { 12 | 13 | /** 14 | * 将协议实体转化为byte数组 15 | * 16 | * @param storageMode 存储形式 17 | * @return 18 | */ 19 | byte[] proto2byte(StorageMode storageMode); 20 | 21 | /** 22 | * 将byte数组数据转化为协议实体 23 | * 24 | * @param bytes byte数组数据 25 | * @param index 起始字节 26 | * @param tailLength 消息尾的长度[和index一起决定了数据解析的范围] 27 | * @param storageMode 存储形式 28 | * @return 是否解析成功 29 | */ 30 | boolean byte2proto(byte[] bytes, int index, int tailLength, StorageMode storageMode); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /xtcp_annotation/src/main/java/com/xuexiang/xtcp/model/IProtocolCenter.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.model; 2 | 3 | import java.util.Map; 4 | 5 | /** 6 | * 协议中心 7 | * 8 | * @author xuexiang 9 | * @since 2018/12/11 下午1:52 10 | */ 11 | public interface IProtocolCenter { 12 | 13 | /** 14 | * 根据协议的类名获取协议的详细信息 15 | * 16 | * @param className 17 | * @return 18 | */ 19 | ProtocolInfo getProtocol(final String className); 20 | 21 | /** 22 | * 根据opcode获取协议的详细信息 23 | * 24 | * @param opcode 25 | * @return 26 | */ 27 | ProtocolInfo getProtocol(final byte opcode); 28 | 29 | /** 30 | * 根据协议的类名获取对应的OpCode 31 | * 32 | * @param className 33 | * @return 34 | */ 35 | byte getOpCodeByClassName(final String className); 36 | 37 | /** 38 | * @return 获取 协议类名 -> 协议信息 的映射 39 | */ 40 | Map getClass2Info(); 41 | 42 | /** 43 | * @return 获取 opCode -> 协议信息 的映射 44 | */ 45 | Map getOpCode2Info(); 46 | 47 | 48 | 49 | } 50 | -------------------------------------------------------------------------------- /xtcp_annotation/src/main/java/com/xuexiang/xtcp/model/IProtocolFieldCenter.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.model; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.Map; 5 | 6 | /** 7 | * 协议字段管理中心 8 | * 9 | * @author xuexiang 10 | * @since 2018/12/12 下午4:19 11 | */ 12 | public interface IProtocolFieldCenter { 13 | 14 | /** 15 | * 根据类名获取协议字段名集合 16 | * 17 | * @param className 18 | * @return 19 | */ 20 | Field[] getProtocolFields(final String className); 21 | 22 | /** 23 | * @return 获取 实体类 -> 协议字段名集合 的映射 24 | */ 25 | Map getClass2Fields(); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /xtcp_annotation/src/main/java/com/xuexiang/xtcp/model/IProtocolItem.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.model; 2 | 3 | /** 4 | * 协议项 5 | * 6 | * @author xuexiang 7 | * @since 2018/12/11 下午3:13 8 | */ 9 | public interface IProtocolItem extends IProtocol { 10 | 11 | /** 12 | * @return 获取协议项的数据长度 13 | */ 14 | int getProtocolLength(); 15 | 16 | 17 | } 18 | -------------------------------------------------------------------------------- /xtcp_annotation/src/main/java/com/xuexiang/xtcp/model/ProtocolFieldInfo.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.model; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | /** 6 | * 存储协议的字段名信息 7 | * 8 | * @author xuexiang 9 | * @since 2018/12/12 下午3:52 10 | */ 11 | public final class ProtocolFieldInfo { 12 | 13 | /** 14 | * 存储使用@ProtocolField注解的字段信息 15 | */ 16 | private Field[] mFields; 17 | 18 | public ProtocolFieldInfo(String className, String fields) { 19 | try { 20 | Class cls = Class.forName(className); 21 | String[] fieldNames = fields.split(","); 22 | if (fieldNames.length > 0) { 23 | mFields = new Field[fieldNames.length]; 24 | for (int i = 0; i < fieldNames.length; i++) { 25 | mFields[i] = cls.getDeclaredField(fieldNames[i]); 26 | } 27 | } 28 | } catch (ClassNotFoundException | NoSuchFieldException e) { 29 | e.printStackTrace(); 30 | } 31 | } 32 | 33 | public Field[] getFields() { 34 | return mFields; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /xtcp_annotation/src/main/java/com/xuexiang/xtcp/model/ProtocolInfo.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.model; 2 | 3 | import com.xuexiang.xtcp.enums.StorageMode; 4 | 5 | /** 6 | * 协议信息 7 | * 8 | * @author xuexiang 9 | * @since 2018/12/11 上午12:40 10 | */ 11 | public final class ProtocolInfo { 12 | 13 | /** 14 | * 协议的名称 15 | */ 16 | private String mName; 17 | 18 | /** 19 | * 协议的类名 20 | */ 21 | private String mClassName; 22 | 23 | /** 24 | * 协议的命令码 25 | */ 26 | private byte mOpCode; 27 | 28 | /** 29 | * 协议的响应码(结果码) 30 | */ 31 | private byte mResCode; 32 | 33 | /** 34 | * 数据的存储方式 35 | */ 36 | private StorageMode mStorageMode; 37 | 38 | /** 39 | * 协议的描述信息 40 | */ 41 | private String mDescription; 42 | 43 | public ProtocolInfo(String name, String className, byte opCode, byte resCode, StorageMode mode, String description) { 44 | mName = name; 45 | mClassName = className; 46 | mOpCode = opCode; 47 | mResCode = resCode; 48 | mStorageMode = mode; 49 | mDescription = description; 50 | } 51 | 52 | public String getName() { 53 | return mName; 54 | } 55 | 56 | /** 57 | * @return 获取协议的类名 58 | */ 59 | public String getClassName() { 60 | return mClassName; 61 | } 62 | 63 | /** 64 | * @return 获取协议的命令码 65 | */ 66 | public byte getOpCode() { 67 | return mOpCode; 68 | } 69 | 70 | public byte getResCode() { 71 | return mResCode; 72 | } 73 | 74 | /** 75 | * @return 获取协议的数据存储方式 76 | */ 77 | public StorageMode getStorageMode() { 78 | return mStorageMode; 79 | } 80 | 81 | public String getDescription() { 82 | return mDescription; 83 | } 84 | 85 | @Override 86 | public String toString() { 87 | return "ProtocolInfo{" + 88 | "mName='" + mName + '\'' + 89 | ", mClassName='" + mClassName + '\'' + 90 | ", mOpCode=" + byte2HexString(mOpCode) + 91 | ", mResCode=" + byte2HexString(mResCode) + 92 | ", mStorageMode=" + mStorageMode + 93 | ", mDescription='" + mDescription + '\'' + 94 | '}'; 95 | } 96 | 97 | /** 98 | * 将byte转为16进制便于查看 99 | * 100 | * @param value 101 | * @return 102 | */ 103 | public static String byte2HexString(byte value) { 104 | int v = value & 0xFF; 105 | String hv = Integer.toHexString(v); 106 | if (hv.length() < 2) { 107 | hv = "0" + hv; 108 | } 109 | return "0x" + hv; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /xtcp_compiler/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /xtcp_compiler/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6' 5 | implementation 'com.google.auto.service:auto-service:1.0-rc6' 6 | //谷歌的帮助我们快速实现注解处理器 7 | implementation 'com.squareup:javapoet:1.13.0' 8 | //用来生成java文件的,避免字符串拼接的尴尬 9 | implementation 'org.apache.commons:commons-lang3:3.11' 10 | implementation 'org.apache.commons:commons-collections4:4.4' 11 | 12 | implementation project(':xtcp_annotation') 13 | } 14 | 15 | targetCompatibility = JavaVersion.VERSION_1_8 16 | sourceCompatibility = JavaVersion.VERSION_1_8 17 | 18 | apply from: "../JitPackUpload.gradle" 19 | -------------------------------------------------------------------------------- /xtcp_compiler/src/main/java/com/xuexiang/xtcp/util/Consts.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.util; 2 | 3 | /** 4 | * 常量 5 | * @author xuexiang 6 | */ 7 | public class Consts { 8 | // Generate 9 | public static final String SEPARATOR = "$$"; 10 | public static final String PROJECT = "XTCP"; 11 | /** 12 | * Log前缀 13 | */ 14 | static final String PREFIX_OF_LOGGER = PROJECT + "::Compiler "; 15 | 16 | // Options of processor 17 | public static final String KEY_MODULE_NAME = "moduleName"; 18 | } -------------------------------------------------------------------------------- /xtcp_compiler/src/main/java/com/xuexiang/xtcp/util/Logger.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.util; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import javax.annotation.processing.Messager; 6 | import javax.tools.Diagnostic; 7 | 8 | /** 9 | * 日志记录 10 | * 11 | * @author xuexiang 12 | * @since 2018/12/10 下午5:22 13 | */ 14 | public class Logger { 15 | 16 | private Messager msg; 17 | 18 | public Logger(Messager messager) { 19 | msg = messager; 20 | } 21 | 22 | /** 23 | * Print info log. 24 | */ 25 | public void info(CharSequence info) { 26 | if (StringUtils.isNotEmpty(info)) { 27 | msg.printMessage(Diagnostic.Kind.NOTE, Consts.PREFIX_OF_LOGGER + info); 28 | } 29 | } 30 | 31 | public void error(CharSequence error) { 32 | if (StringUtils.isNotEmpty(error)) { 33 | msg.printMessage(Diagnostic.Kind.ERROR, Consts.PREFIX_OF_LOGGER + "An exception is encountered, [" + error + "]"); 34 | } 35 | } 36 | 37 | public void error(Throwable error) { 38 | if (null != error) { 39 | msg.printMessage(Diagnostic.Kind.ERROR, Consts.PREFIX_OF_LOGGER + "An exception is encountered, [" + error.getMessage() + "]" + "\n" + formatStackTrace(error.getStackTrace())); 40 | } 41 | } 42 | 43 | public void warning(CharSequence warning) { 44 | if (StringUtils.isNotEmpty(warning)) { 45 | msg.printMessage(Diagnostic.Kind.WARNING, Consts.PREFIX_OF_LOGGER + warning); 46 | } 47 | } 48 | 49 | private String formatStackTrace(StackTraceElement[] stackTrace) { 50 | StringBuilder sb = new StringBuilder(); 51 | for (StackTraceElement element : stackTrace) { 52 | sb.append(" at ").append(element.toString()); 53 | sb.append("\n"); 54 | } 55 | return sb.toString(); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /xtcp_runtime/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /xtcp_runtime/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion build_versions.target_sdk 5 | buildToolsVersion build_versions.build_tools 6 | 7 | defaultConfig { 8 | minSdkVersion 14 9 | targetSdkVersion build_versions.target_sdk 10 | 11 | javaCompileOptions { 12 | annotationProcessorOptions { 13 | arguments = [ moduleName : "XTCP" ] 14 | } 15 | } 16 | consumerProguardFiles "proguard-rules.pro" 17 | } 18 | 19 | compileOptions { 20 | sourceCompatibility JavaVersion.VERSION_1_8 21 | targetCompatibility JavaVersion.VERSION_1_8 22 | } 23 | 24 | lintOptions { 25 | abortOnError false 26 | } 27 | 28 | } 29 | 30 | dependencies { 31 | implementation fileTree(include: ['*.jar'], dir: 'libs') 32 | compileOnly deps.androidx.appcompat 33 | 34 | api project(':xtcp_annotation') 35 | annotationProcessor project(':xtcp_compiler') 36 | 37 | } 38 | 39 | apply from: "../JitPackUpload.gradle" 40 | 41 | -------------------------------------------------------------------------------- /xtcp_runtime/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | 23 | # xtcp 24 | -keep @com.xuexiang.xtcp.annotation.* class * {*;} 25 | -keep class * { 26 | @com.xuexiang.xtcp.annotation.* ; 27 | } 28 | -keepclassmembers class * { 29 | @com.xuexiang.xtcp.annotation.* ; 30 | } -------------------------------------------------------------------------------- /xtcp_runtime/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/XTCP.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.xuexiang.xtcp.core.XProtocolCenter; 6 | import com.xuexiang.xtcp.core.component.buffer.impl.SimpleBuffer; 7 | import com.xuexiang.xtcp.core.component.monitor.IMonitor; 8 | import com.xuexiang.xtcp.core.component.monitor.impl.TimeoutMonitor; 9 | import com.xuexiang.xtcp.core.parser.IProtocolParser; 10 | import com.xuexiang.xtcp.core.parser.impl.DefaultProtocolParser; 11 | import com.xuexiang.xtcp.enums.StorageMode; 12 | import com.xuexiang.xtcp.logs.ILogger; 13 | import com.xuexiang.xtcp.logs.XTLog; 14 | import com.xuexiang.xtcp.model.IProtocolCenter; 15 | import com.xuexiang.xtcp.model.IProtocolFieldCenter; 16 | 17 | /** 18 | * XTCP API中心 19 | * 20 | * @author xuexiang 21 | * @since 2018/12/11 下午1:27 22 | */ 23 | public class XTCP { 24 | 25 | private static volatile XTCP sInstance = null; 26 | 27 | private XTCP() { 28 | _XTCP.setIProtocolParser(new DefaultProtocolParser()); 29 | //默认使用大端的存储方式 30 | _XTCP.setDefaultStorageMode(StorageMode.BigEndian); 31 | } 32 | 33 | /** 34 | * 获取单例 35 | * 36 | * @return 37 | */ 38 | public static XTCP getInstance() { 39 | if (sInstance == null) { 40 | synchronized (XTCP.class) { 41 | if (sInstance == null) { 42 | sInstance = new XTCP(); 43 | } 44 | } 45 | } 46 | return sInstance; 47 | } 48 | 49 | //=================初始化设置=========================// 50 | 51 | /** 52 | * 设置协议中心 53 | * 54 | * @param iProtocolCenters 55 | * @return 56 | */ 57 | public XTCP addIProtocolCenter(IProtocolCenter... iProtocolCenters) { 58 | XProtocolCenter.getInstance().addIProtocolCenter(iProtocolCenters); 59 | return this; 60 | } 61 | 62 | /** 63 | * 设置协议字段中心 64 | * 65 | * @param iProtocolFieldCenters 66 | * @return 67 | */ 68 | public XTCP addIProtocolFieldCenter(IProtocolFieldCenter... iProtocolFieldCenters) { 69 | XProtocolCenter.getInstance().addIProtocolFieldCenter(iProtocolFieldCenters); 70 | return this; 71 | } 72 | 73 | /** 74 | * 设置协议解析器 75 | * 76 | * @param iProtocolParser 77 | * @return 78 | */ 79 | public XTCP setIProtocolParser(IProtocolParser iProtocolParser) { 80 | _XTCP.setIProtocolParser(iProtocolParser); 81 | return this; 82 | } 83 | 84 | /** 85 | * 设置默认的数据存储方式 86 | * 87 | * @param sStorageMode 88 | * @return 89 | */ 90 | public XTCP setDefaultStorageMode(StorageMode sStorageMode) { 91 | _XTCP.setDefaultStorageMode(sStorageMode); 92 | return this; 93 | } 94 | 95 | //=================日志=========================// 96 | 97 | /** 98 | * 设置是否是debug模式 99 | * 100 | * @param isDebug 101 | * @return 102 | */ 103 | public XTCP debug(boolean isDebug) { 104 | XTLog.debug(isDebug); 105 | return this; 106 | } 107 | 108 | /** 109 | * 设置日志打印接口 110 | * 111 | * @param logger 112 | * @return 113 | */ 114 | public XTCP setILogger(@NonNull ILogger logger) { 115 | XTLog.setLogger(logger); 116 | return this; 117 | } 118 | 119 | //=================API=========================// 120 | /** 121 | * 创建一个监控器 122 | * 123 | * @param interval 监控器检查的间期 124 | * @return 125 | */ 126 | public static IMonitor newMonitor(long interval) { 127 | return TimeoutMonitor.get(interval); 128 | } 129 | 130 | /** 131 | * 创建一个监控器 132 | * 133 | * @param targetName 监听目标的名称 134 | * @param interval 监控器检查的间期 135 | * @return 136 | */ 137 | public static IMonitor newMonitor(String targetName, long interval) { 138 | return TimeoutMonitor.get(targetName, interval); 139 | } 140 | 141 | /** 142 | * 创建一个缓冲区 143 | * 144 | * @return 145 | */ 146 | public static SimpleBuffer newBuffer() { 147 | return SimpleBuffer.get(); 148 | } 149 | 150 | /** 151 | * 创建一个缓冲区 152 | * 153 | * @param bufferSize 缓冲区大小 154 | * @return 155 | */ 156 | public static SimpleBuffer newBuffer(int bufferSize) { 157 | return SimpleBuffer.get(bufferSize); 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/_XTCP.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp; 2 | 3 | import com.xuexiang.xtcp.core.parser.IProtocolParser; 4 | import com.xuexiang.xtcp.enums.StorageMode; 5 | 6 | /** 7 | * @author xuexiang 8 | * @since 2018/12/11 下午2:08 9 | */ 10 | public class _XTCP { 11 | 12 | /** 13 | * 协议解析器 14 | */ 15 | private static IProtocolParser sIProtocolParser; 16 | 17 | /** 18 | * 默认数据存储方式 19 | */ 20 | private static StorageMode sStorageMode; 21 | 22 | /** 23 | * 设置协议解析器 24 | * 25 | * @param iProtocolParser 协议解析器 26 | * @return 27 | */ 28 | public static void setIProtocolParser(IProtocolParser iProtocolParser) { 29 | _XTCP.sIProtocolParser = iProtocolParser; 30 | } 31 | 32 | public static IProtocolParser getIProtocolParser() { 33 | return sIProtocolParser; 34 | } 35 | 36 | /** 37 | * 设置默认存储方式 38 | * 39 | * @param sStorageMode 40 | */ 41 | public static void setDefaultStorageMode(StorageMode sStorageMode) { 42 | _XTCP.sStorageMode = sStorageMode; 43 | } 44 | 45 | public static StorageMode getDefaultStorageMode() { 46 | return sStorageMode; 47 | } 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/XProtocolCenter.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import com.xuexiang.xtcp.model.IProtocolCenter; 7 | import com.xuexiang.xtcp.model.IProtocolFieldCenter; 8 | import com.xuexiang.xtcp.model.ProtocolFieldInfo; 9 | import com.xuexiang.xtcp.model.ProtocolInfo; 10 | 11 | import java.lang.reflect.Field; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | /** 16 | * 协议管理中心 17 | * 18 | * @author xuexiang 19 | * @since 2018/12/12 下午5:06 20 | */ 21 | public class XProtocolCenter implements IProtocolCenter, IProtocolFieldCenter { 22 | 23 | 24 | private static volatile XProtocolCenter sInstance = null; 25 | 26 | /** 27 | * 协议类名 -> 协议信息【message -> bytes】 28 | */ 29 | private Map mClass2Info = new HashMap<>(); 30 | 31 | /** 32 | * opCode -> 协议信息【bytes -> message】 33 | */ 34 | private Map mOpCode2Info = new HashMap<>(); 35 | 36 | /** 37 | * 类名 -> 协议字段名集合【数据解析时使用】 38 | */ 39 | private Map mClass2Fields = new HashMap<>(); 40 | 41 | private XProtocolCenter() { 42 | 43 | } 44 | 45 | /** 46 | * 获取单例 47 | * 48 | * @return 49 | */ 50 | public static XProtocolCenter getInstance() { 51 | if (sInstance == null) { 52 | synchronized (XProtocolCenter.class) { 53 | if (sInstance == null) { 54 | sInstance = new XProtocolCenter(); 55 | } 56 | } 57 | } 58 | return sInstance; 59 | } 60 | 61 | /** 62 | * 设置协议中心 63 | * 64 | * @param iProtocolCenters 协议中心 65 | * @return 66 | */ 67 | public XProtocolCenter addIProtocolCenter(@NonNull IProtocolCenter... iProtocolCenters) { 68 | for (IProtocolCenter iProtocolCenter : iProtocolCenters) { 69 | addIProtocolCenter(iProtocolCenter); 70 | } 71 | return this; 72 | } 73 | 74 | /** 75 | * 设置协议中心 76 | * 77 | * @param iProtocolCenter 协议中心 78 | * @return 79 | */ 80 | public XProtocolCenter addIProtocolCenter(@NonNull IProtocolCenter iProtocolCenter) { 81 | mClass2Info.putAll(iProtocolCenter.getClass2Info()); 82 | mOpCode2Info.putAll(iProtocolCenter.getOpCode2Info()); 83 | return this; 84 | } 85 | 86 | /** 87 | * 设置协议字段中心 88 | * 89 | * @param iIProtocolFieldCenters 协议字段中心 90 | * @return 91 | */ 92 | public XProtocolCenter addIProtocolFieldCenter(@NonNull IProtocolFieldCenter... iIProtocolFieldCenters) { 93 | for (IProtocolFieldCenter iProtocolFieldCenter : iIProtocolFieldCenters) { 94 | addIProtocolFieldCenter(iProtocolFieldCenter); 95 | } 96 | return this; 97 | } 98 | 99 | /** 100 | * 设置协议字段中心 101 | * 102 | * @param iIProtocolFieldCenter 协议字段中心 103 | * @return 104 | */ 105 | public XProtocolCenter addIProtocolFieldCenter(@NonNull IProtocolFieldCenter iIProtocolFieldCenter) { 106 | mClass2Fields.putAll(iIProtocolFieldCenter.getClass2Fields()); 107 | return this; 108 | } 109 | 110 | 111 | /** 112 | * 根据协议的类名获取协议的详细信息 113 | */ 114 | @Override 115 | public ProtocolInfo getProtocol(final String className) { 116 | return mClass2Info.get(className); 117 | } 118 | 119 | /** 120 | * 根据opcode获取协议的详细信息 121 | */ 122 | @Override 123 | public ProtocolInfo getProtocol(final byte opcode) { 124 | return mOpCode2Info.get(opcode); 125 | } 126 | 127 | /** 128 | * 根据协议的类名获取对应的OpCode(在将协议自动转为byte放入消息体时调用)【message -> bytes】 129 | */ 130 | @Override 131 | public byte getOpCodeByClassName(final String className) { 132 | ProtocolInfo protocolInfo = mClass2Info.get(className); 133 | return protocolInfo != null ? protocolInfo.getOpCode() : -1; 134 | } 135 | 136 | /** 137 | * 根据类名获取协议字段名集合 138 | */ 139 | @Override 140 | @Nullable 141 | public Field[] getProtocolFields(final String className) { 142 | ProtocolFieldInfo protocolFieldInfo = mClass2Fields.get(className); 143 | return protocolFieldInfo != null ? protocolFieldInfo.getFields() : null; 144 | } 145 | 146 | @Override 147 | public Map getClass2Info() { 148 | return mClass2Info; 149 | } 150 | 151 | @Override 152 | public Map getOpCode2Info() { 153 | return mOpCode2Info; 154 | } 155 | 156 | @Override 157 | public Map getClass2Fields() { 158 | return mClass2Fields; 159 | } 160 | 161 | /** 162 | * 根据类名获取协议字段 163 | * 164 | * @param cls 165 | * @return 166 | */ 167 | public Field[] getProtocolFields(final @NonNull Class cls) { 168 | return getProtocolFields(cls.getCanonicalName()); 169 | } 170 | 171 | /** 172 | * 根据opCode获取协议类名(在获取到byte数据后,根据opcode获取协议的类名进行反射构建)【bytes -> message】 173 | * 174 | * @param opCode 175 | * @return 176 | */ 177 | @Nullable 178 | public String getClassNameByOpCode(final byte opCode) { 179 | ProtocolInfo protocolInfo = mOpCode2Info.get(opCode); 180 | return protocolInfo != null ? protocolInfo.getClassName() : null; 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/XTCPConstants.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core; 2 | 3 | /** 4 | * 常量 5 | * 6 | * @author xuexiang 7 | * @since 2018/12/14 下午6:23 8 | */ 9 | public final class XTCPConstants { 10 | 11 | /** 12 | * short所占byte的最大位数 13 | */ 14 | public static final int SHORT_MAX_LENGTH = 2; 15 | 16 | /** 17 | * int所占byte的最大位数 18 | */ 19 | public static final int INT_MAX_LENGTH = 4; 20 | 21 | /** 22 | * long所占byte的最大位数 23 | */ 24 | public static final int LONG_MAX_LENGTH = 8; 25 | 26 | 27 | /** 28 | * 一位byte所能表示的最大无符号整数是255 29 | */ 30 | public static final int MAX_ARRAY_LENGTH = 255; 31 | 32 | /** 33 | * 两位byte所能表示的最大无符号整数是65535 34 | */ 35 | public static final int MAX_LARGE_ARRAY_LENGTH = 65535; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/component/buffer/BufferException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.xuexiang.xtcp.core.component.buffer; 18 | 19 | /** 20 | * 数据缓冲区错误 21 | * 22 | * @author xuexiang 23 | * @since 2018/12/17 下午1:09 24 | */ 25 | public class BufferException extends Exception { 26 | 27 | /** 28 | * 错误码(默认为-1) 29 | */ 30 | private int mCode = -1; 31 | 32 | public int getCode() { 33 | return mCode; 34 | } 35 | 36 | public void setCode(int code) { 37 | mCode = code; 38 | } 39 | 40 | public BufferException(String detailMessage) { 41 | super(detailMessage); 42 | } 43 | 44 | public BufferException(int code, String detailMessage) { 45 | super(detailMessage); 46 | mCode = code; 47 | } 48 | 49 | public BufferException(String detailMessage, Throwable throwable) { 50 | super(detailMessage, throwable); 51 | } 52 | 53 | public BufferException(Throwable throwable) { 54 | super(throwable); 55 | } 56 | 57 | public BufferException(int code, Throwable throwable) { 58 | super(throwable); 59 | mCode = code; 60 | } 61 | 62 | /** 63 | * @return 获取详细的错误信息 64 | */ 65 | public String getDetailMessage() { 66 | return "code:" + mCode + ", message:" + getMessage(); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/component/buffer/IBuffer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.xuexiang.xtcp.core.component.buffer; 18 | 19 | /** 20 | * 缓冲区的实现接口 21 | * 22 | * @author xuexiang 23 | * @since 2018/12/17 下午1:09 24 | */ 25 | public interface IBuffer { 26 | 27 | /** 28 | * 向缓冲区中添加数据 29 | * 30 | * @param data 要添加的数据 31 | * @param available 添加数据的长度 32 | * @throws BufferException 33 | */ 34 | void putData(byte[] data, int available) throws BufferException; 35 | 36 | /** 37 | * 从缓冲区中取出所有有效数据 38 | * 39 | * @return 缓冲区中有效区间的数据 40 | */ 41 | byte[] getData() throws BufferException; 42 | 43 | /** 44 | * 从缓冲区中取出指定长度的数据 45 | * 46 | * @param dataLength 数据长度 47 | * @return 指定长度的数据 48 | */ 49 | byte[] getData(int dataLength) throws BufferException; 50 | 51 | /** 52 | * 重置缓冲区,不释放缓冲区内存空间 53 | */ 54 | void clear(); 55 | 56 | /** 57 | * 针对缓冲区中有效数据的空间释放 58 | * 59 | * @param length 60 | * @throws BufferException 61 | */ 62 | void release(int length) throws BufferException; 63 | 64 | /** 65 | * @return 获取当前缓冲区中存储有效数据的长度 66 | */ 67 | int getValidDataLength(); 68 | 69 | /** 70 | * 回收资源 71 | */ 72 | void recycle(); 73 | } 74 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/component/buffer/impl/CircularBuffer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.xuexiang.xtcp.core.component.buffer.impl; 18 | 19 | import com.xuexiang.xtcp.core.component.buffer.BufferException; 20 | import com.xuexiang.xtcp.core.component.buffer.IBuffer; 21 | 22 | /** 23 | * 环形缓冲区,默认大小为2M 24 | * 25 | * @author xuexiang 26 | * @since 2018/12/17 下午1:09 27 | */ 28 | public class CircularBuffer implements IBuffer { 29 | /** 30 | * 默认缓冲区的长度为2M 31 | */ 32 | public static final int DEFAULT_BUFFER_SIZE = 2 * 1024 * 1024; 33 | /** 34 | * 缓冲区 35 | */ 36 | private byte[] mBuffer; 37 | /** 38 | * 缓冲区中有效数据的起始索引 39 | */ 40 | private int mStartIndex; 41 | /** 42 | * 缓冲区中有效数据的长度 43 | */ 44 | private int mValidDataLength; 45 | 46 | /** 47 | * 构造方法 48 | */ 49 | public CircularBuffer() { 50 | this(DEFAULT_BUFFER_SIZE); 51 | } 52 | 53 | /** 54 | * 构造方法 55 | * 56 | * @param bufferSize 缓冲区大小 57 | */ 58 | public CircularBuffer(int bufferSize) { 59 | mBuffer = new byte[bufferSize]; 60 | mStartIndex = 0; 61 | mValidDataLength = 0; 62 | } 63 | 64 | @Override 65 | public void putData(byte[] data, int available) throws BufferException { 66 | // 缓冲区溢出,抛出异常 67 | if (getBufferFreeSpace() < available) { 68 | throw new BufferException("data buffer overflow, must extend buffer size!"); 69 | } else { 70 | int totalDataLength = mValidDataLength + available; //现有数据长度加上需要加入的数据长度 71 | // 当前有效数据没有分段 72 | if (isDataStoreContinuous()) { 73 | // 待存数据不需要分段 74 | if ((getIndexRightSpace()) >= totalDataLength) { 75 | System.arraycopy(data, 0, mBuffer, mStartIndex + mValidDataLength, available); 76 | } else {// 数据需要分段 77 | System.arraycopy(data, 0, mBuffer, mStartIndex + mValidDataLength, getIndexRightSpace() - mValidDataLength); // 填充缓冲区后半部分 78 | System.arraycopy(data, getIndexRightSpace() - mValidDataLength, mBuffer, 0, available - (getIndexRightSpace() - mValidDataLength)); // 填充缓冲区前半部分 79 | } 80 | } else {// 当前有效数据已经分段 81 | System.arraycopy(data, 0, mBuffer, mValidDataLength - getIndexRightSpace(), available); 82 | } 83 | mValidDataLength = totalDataLength; 84 | } 85 | } 86 | 87 | /** 88 | * 获取指针mStartIndex右侧的剩余空间长度 89 | * 90 | * @return 91 | */ 92 | private int getIndexRightSpace() { 93 | return mBuffer.length - mStartIndex; 94 | } 95 | 96 | /** 97 | * 获取缓冲器空余长度 98 | * 99 | * @return 100 | */ 101 | private int getBufferFreeSpace() { 102 | return mBuffer.length - mValidDataLength; 103 | } 104 | 105 | /** 106 | * 当前数据存储是否连续 107 | * 【如果当前指针右侧的剩余空间长度大于目前存储数据的长度,那么就不需要分段存储了】 108 | * 109 | * @return 数据存储是否连续 110 | */ 111 | private boolean isDataStoreContinuous() { 112 | return getIndexRightSpace() >= mValidDataLength; 113 | } 114 | 115 | /** 116 | * 指定长度的数据存储是否连续 117 | * 【如果当前指针右侧的剩余空间长度大于指定数据的长度,那么就不需要分段存储了】 118 | * 119 | * @param dataLength 指定数据的长度 120 | * @return 数据存储是否连续 121 | */ 122 | private boolean isDataStoreContinuous(int dataLength) { 123 | return getIndexRightSpace() >= dataLength; 124 | } 125 | 126 | @Override 127 | public byte[] getData() throws BufferException { 128 | return getData(mValidDataLength); 129 | } 130 | 131 | @Override 132 | public byte[] getData(int dataLength) throws BufferException { 133 | if (dataLength > mValidDataLength) { 134 | throw new BufferException("Buffer does not have enough data for access! The length of data currently stored is " + mValidDataLength + ", but the length of data to be acquired is " + dataLength); 135 | } 136 | 137 | byte[] data = new byte[dataLength]; 138 | // 未分段数据 139 | if (isDataStoreContinuous(dataLength)) { 140 | System.arraycopy(mBuffer, mStartIndex, data, 0, dataLength); 141 | } else {// 分段数据 142 | System.arraycopy(mBuffer, mStartIndex, data, 0, getIndexRightSpace()); 143 | System.arraycopy(mBuffer, 0, data, getIndexRightSpace(), dataLength - getIndexRightSpace()); 144 | } 145 | return data; 146 | } 147 | 148 | @Override 149 | public void clear() { 150 | mStartIndex = 0; 151 | mValidDataLength = 0; 152 | } 153 | 154 | @Override 155 | public void recycle() { 156 | mBuffer = null; 157 | clear(); 158 | } 159 | 160 | /** 161 | * 释放缓存【实质就是指针往后偏移】 162 | * 163 | * @param length 需要释放的长度 164 | */ 165 | @Override 166 | public void release(int length) throws BufferException { 167 | // 请求释放的长度大于有效区间的长度,就是指针往后移动 168 | if (mValidDataLength < length) { 169 | throw new BufferException("no enough valid data to release!"); 170 | } 171 | int offsetIndex = mStartIndex + length; 172 | if (offsetIndex < mBuffer.length) { //偏移后的指针位置小于缓存的长度,不需要分段的释放 173 | mStartIndex = offsetIndex; 174 | } else { //偏移后的指针位置大于缓存的长度,需要分段的释放 175 | mStartIndex = offsetIndex - mBuffer.length; 176 | } 177 | mValidDataLength = mValidDataLength - length; 178 | } 179 | 180 | @Override 181 | public int getValidDataLength() { 182 | return mValidDataLength; 183 | } 184 | 185 | } 186 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/component/buffer/impl/SimpleBuffer.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.component.buffer.impl; 2 | 3 | import com.xuexiang.xtcp.core.component.buffer.BufferException; 4 | 5 | /** 6 | * 简单实用的环形缓冲区 7 | * 8 | * @author xuexiang 9 | * @since 2019-07-23 8:44 10 | */ 11 | public class SimpleBuffer extends CircularBuffer { 12 | 13 | /** 14 | * 获取一个缓冲区 15 | * 16 | * @return 17 | */ 18 | public static SimpleBuffer get() { 19 | return new SimpleBuffer(); 20 | } 21 | 22 | /** 23 | * 获取一个缓冲区 24 | * 25 | * @param bufferSize 缓冲区大小 26 | * @return 27 | */ 28 | public static SimpleBuffer get(int bufferSize) { 29 | return new SimpleBuffer(bufferSize); 30 | } 31 | 32 | /** 33 | * 构造方法 34 | */ 35 | public SimpleBuffer() { 36 | super(); 37 | } 38 | 39 | /** 40 | * 构造方法 41 | * 42 | * @param bufferSize 缓冲区大小 43 | */ 44 | public SimpleBuffer(int bufferSize) { 45 | super(bufferSize); 46 | } 47 | 48 | /** 49 | * 添加数据 50 | * 51 | * @param data 52 | */ 53 | public void putData(byte[] data) { 54 | putData(data, data.length); 55 | } 56 | 57 | @Override 58 | public void putData(byte[] data, int available) { 59 | try { 60 | super.putData(data, available); 61 | } catch (BufferException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | 66 | 67 | @Override 68 | public byte[] getData() { 69 | try { 70 | return super.getData(); 71 | } catch (BufferException e) { 72 | e.printStackTrace(); 73 | } 74 | return new byte[0]; 75 | } 76 | 77 | @Override 78 | public byte[] getData(int dataLength) { 79 | try { 80 | return super.getData(dataLength); 81 | } catch (BufferException e) { 82 | e.printStackTrace(); 83 | } 84 | return new byte[0]; 85 | } 86 | 87 | /** 88 | * 释放缓存【实质就是指针往后偏移】 89 | * 90 | * @param length 需要释放的长度 91 | */ 92 | @Override 93 | public void release(int length) { 94 | try { 95 | super.release(length); 96 | } catch (BufferException e) { 97 | e.printStackTrace(); 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/component/monitor/IMonitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.xuexiang.xtcp.core.component.monitor; 18 | 19 | /** 20 | * 监控器,用于监控设备的状态 21 | * 22 | * @author xuexiang 23 | * @since 2019-07-23 23:54 24 | */ 25 | public interface IMonitor { 26 | 27 | /** 28 | * 开始监听工作 29 | */ 30 | void startWork(); 31 | 32 | /** 33 | * 结束监听工作 34 | */ 35 | void finishWork(); 36 | 37 | /** 38 | * 处理监听工作 39 | */ 40 | void processWork(); 41 | 42 | /** 43 | * 处理监听工作,并更改监听对象 44 | * 45 | * @param targetName 46 | */ 47 | void processWork(String targetName); 48 | 49 | /** 50 | * 处理监听工作,并更改监听对象和监听间期 51 | * 52 | * @param targetName 53 | */ 54 | void processWork(String targetName, long interval); 55 | 56 | /** 57 | * @return 是否在工作 58 | */ 59 | boolean isWorking(); 60 | 61 | /** 62 | * @return 监听对象的名称 63 | */ 64 | String getTargetName(); 65 | 66 | /** 67 | * 设置监控器的监听回调接口 68 | * 69 | * @param listener 70 | */ 71 | IMonitor setOnMonitorListener(OnMonitorListener listener); 72 | 73 | } 74 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/component/monitor/OnMonitorListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.xuexiang.xtcp.core.component.monitor; 18 | 19 | /** 20 | * 监控器的监听回调接口 21 | * 22 | * @author xuexiang 23 | * @since 2018/8/21 上午1:52 24 | */ 25 | public interface OnMonitorListener { 26 | 27 | /** 28 | * 监听到异常 29 | * 30 | * @param monitor 监控器 31 | */ 32 | void onExcepted(IMonitor monitor); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/component/monitor/impl/TimeoutMonitor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.xuexiang.xtcp.core.component.monitor.impl; 18 | 19 | import com.xuexiang.xtcp.core.component.monitor.IMonitor; 20 | import com.xuexiang.xtcp.core.component.monitor.OnMonitorListener; 21 | import com.xuexiang.xtcp.logs.XTLog; 22 | 23 | /** 24 | * 超时监控器 25 | * 26 | * @author xuexiang 27 | * @since 2019-07-23 23:54 28 | */ 29 | public class TimeoutMonitor extends Thread implements IMonitor { 30 | /** 31 | * 默认监听对象 32 | */ 33 | public static final String DEFAULT_TARGET_NAME = "Device_Timeout"; 34 | /** 35 | * 默认监控器监控间期 36 | */ 37 | public static final long DEFAULT_MONITOR_INTERVAL = 2 * 1000; 38 | /** 39 | * 监控器是否在运行 40 | */ 41 | private volatile boolean mIsMonitorRunning = false; 42 | /** 43 | * 记录超时的次数 44 | */ 45 | private int mCount = 0; 46 | 47 | /** 48 | * 检查间期 49 | */ 50 | private long mInterval; 51 | /** 52 | * 监听目标的名称 53 | */ 54 | private String mTargetName; 55 | /** 56 | * 通信通道超时监听 57 | */ 58 | private OnMonitorListener mOnMonitorListener; 59 | 60 | /** 61 | * 获得超时监控器 62 | * 63 | * @param targetName 监听目标的名称 64 | * @param interval 监控器检查的间期 65 | * @return 66 | */ 67 | public static TimeoutMonitor get(String targetName, long interval) { 68 | return new TimeoutMonitor(targetName, interval); 69 | } 70 | 71 | /** 72 | * 获得超时监控器 73 | * 74 | * @param interval 监控器检查的间期 75 | * @return 76 | */ 77 | public static TimeoutMonitor get(long interval) { 78 | return new TimeoutMonitor(interval); 79 | } 80 | 81 | /** 82 | * 构造方法 83 | * 84 | * @param interval 监控器检查的间期 85 | */ 86 | public TimeoutMonitor(long interval) { 87 | this(DEFAULT_TARGET_NAME, interval); 88 | } 89 | 90 | /** 91 | * 构造方法 92 | * 93 | * @param targetName 监听目标的名称 94 | */ 95 | public TimeoutMonitor(String targetName) { 96 | this(targetName, DEFAULT_MONITOR_INTERVAL); 97 | } 98 | 99 | /** 100 | * 构造方法 101 | * 102 | * @param targetName 监听目标的名称 103 | * @param interval 监控器检查的间期 104 | */ 105 | public TimeoutMonitor(String targetName, long interval) { 106 | mTargetName = targetName; 107 | mInterval = interval; 108 | } 109 | 110 | @Override 111 | public void run() { 112 | while (mIsMonitorRunning) { 113 | mCount++; 114 | try { 115 | sleep(mInterval); 116 | } catch (InterruptedException e) { 117 | return; 118 | } 119 | if (mCount > 0 && mIsMonitorRunning) { 120 | if (mOnMonitorListener != null) { 121 | mOnMonitorListener.onExcepted(this); 122 | } 123 | mIsMonitorRunning = false; 124 | } 125 | } 126 | } 127 | 128 | @Override 129 | public synchronized void startWork() { 130 | if (!mIsMonitorRunning) { 131 | XTLog.i("超时监控器已启动..."); 132 | mIsMonitorRunning = true; 133 | super.start(); 134 | } 135 | } 136 | 137 | @Override 138 | public void finishWork() { 139 | XTLog.i("超时监控器已停止..."); 140 | mIsMonitorRunning = false; 141 | interrupt(); 142 | } 143 | 144 | @Override 145 | public void processWork() { 146 | mCount = 0; 147 | } 148 | 149 | @Override 150 | public void processWork(String targetName) { 151 | mCount = 0; 152 | mTargetName = targetName; 153 | } 154 | 155 | @Override 156 | public void processWork(String targetName, long interval) { 157 | mCount = 0; 158 | mTargetName = targetName; 159 | mInterval = interval; 160 | } 161 | 162 | @Override 163 | public boolean isWorking() { 164 | return mIsMonitorRunning; 165 | } 166 | 167 | @Override 168 | public String getTargetName() { 169 | return mTargetName; 170 | } 171 | 172 | @Override 173 | public IMonitor setOnMonitorListener(OnMonitorListener listener) { 174 | mOnMonitorListener = listener; 175 | return this; 176 | } 177 | 178 | } 179 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/message/IMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.xuexiang.xtcp.core.message; 18 | 19 | import com.xuexiang.xtcp.enums.StorageMode; 20 | import com.xuexiang.xtcp.model.IProtocolItem; 21 | 22 | /** 23 | * 消息体都需要实现该接口,消息体是承载协议项的容器 24 | * 25 | * @author xuexiang 26 | * @since 2018/12/16 下午11:19 27 | */ 28 | public interface IMessage { 29 | 30 | /** 31 | * 将消息实体转化为byte数组 32 | * 33 | * @return 34 | */ 35 | byte[] msg2Byte(StorageMode storageMode); 36 | 37 | /** 38 | * 将byte数组数据转化为消息实体 39 | * 40 | * @param bytes 需要解析的byte数组数据 41 | * @return 是否解析成功 42 | */ 43 | boolean byte2Msg(byte[] bytes, StorageMode storageMode); 44 | 45 | /** 46 | * 设置消息的协议项 47 | * 48 | * @param protocolItem 协议项 49 | * @param 50 | * @return 51 | */ 52 | T setIProtocolItem(IProtocolItem protocolItem); 53 | 54 | /** 55 | * @return 消息的协议项 56 | */ 57 | IProtocolItem getProtocolItem(); 58 | 59 | /** 60 | * @return 消息体最小长度 61 | */ 62 | int getMinMessageLength(); 63 | } 64 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/message/MessageConstants.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.message; 2 | 3 | /** 4 | * 消息常量 5 | * 6 | * @author xuexiang 7 | * @since 2018/12/17 上午9:58 8 | */ 9 | public final class MessageConstants { 10 | 11 | /** 12 | * 默认帧头 13 | */ 14 | public static final byte[] DEFAULT_FRAME_HEAD = new byte[]{(byte) 0x55, (byte) 0xAA}; 15 | /** 16 | * 默认帧尾 17 | */ 18 | public static final byte[] DEFAULT_FRAME_END = new byte[]{(byte) 0x00, (byte) 0xFF}; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/message/template/XMessage.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.message.template; 2 | 3 | import com.xuexiang.xtcp._XTCP; 4 | import com.xuexiang.xtcp.core.XProtocolCenter; 5 | import com.xuexiang.xtcp.core.message.IMessage; 6 | import com.xuexiang.xtcp.enums.StorageMode; 7 | import com.xuexiang.xtcp.model.IProtocolItem; 8 | import com.xuexiang.xtcp.utils.ConvertUtils; 9 | import com.xuexiang.xtcp.utils.MessageUtils; 10 | 11 | import static com.xuexiang.xtcp.core.message.MessageConstants.DEFAULT_FRAME_END; 12 | import static com.xuexiang.xtcp.core.message.MessageConstants.DEFAULT_FRAME_HEAD; 13 | 14 | import androidx.annotation.Nullable; 15 | 16 | /** 17 | * 提供的一套TCP传输协议消息模版(无消息序号ID,无序的)
18 | *

19 | * 格式如下:
20 | *

21 | * 55AA | Len | OpCode | CheckSum | Ret | ...data... | 00FF
22 | * 帧头 (固定) | 帧长 | 命令码 | 校验和 | 结果码 | 数据 | 帧尾(固定) 23 | * 24 | * @author xuexiang 25 | * @since 2018/12/16 下午6:22 26 | */ 27 | public class XMessage implements IMessage { 28 | 29 | /** 30 | * 最小数据长度为10[data == null] 31 | */ 32 | private static final int MIN_MESSAGE_LENGTH = 10; 33 | 34 | /** 35 | * 帧头(2个byte) 36 | * index:0 37 | */ 38 | private byte[] mFrameHead; 39 | /** 40 | * 帧长度(2个byte) -> short[0~65535] 41 | * index:2 42 | */ 43 | private short mFrameLength; 44 | /** 45 | * 功能码(一个byte) 46 | * index:4 47 | */ 48 | private byte mOpCode; 49 | /** 50 | * 是否需要校验 51 | */ 52 | private boolean mIsCheck; 53 | /** 54 | * 校验和(二个byte) 55 | * index:5 56 | */ 57 | private short mCheckSum; 58 | /** 59 | * 结果码(一个byte) 60 | * index:7 61 | */ 62 | private byte mRetCode; 63 | /** 64 | * 数据项(不定长) 65 | * index:8 66 | */ 67 | private IProtocolItem mIProtocolItem; 68 | /** 69 | * 帧尾(2个byte) 70 | */ 71 | private byte[] mFrameEnd; 72 | 73 | /** 74 | * 构造方法 75 | */ 76 | public XMessage() { 77 | this(false); 78 | } 79 | 80 | /** 81 | * 构造方法 82 | * 83 | * @param isCheck 是否需要校验和 84 | */ 85 | public XMessage(boolean isCheck) { 86 | mIsCheck = isCheck; 87 | } 88 | 89 | /** 90 | * 包装协议 91 | * 92 | * @param protocolItem 93 | * @return 94 | */ 95 | public static XMessage wrap(IProtocolItem protocolItem) { 96 | return new XMessage().setIProtocolItem(protocolItem); 97 | } 98 | 99 | /** 100 | * 解析协议 101 | * 102 | * @param messageData 103 | * @return 104 | */ 105 | @Nullable 106 | public static XMessage parse(byte[] messageData) { 107 | XMessage message = new XMessage(); 108 | return message.byte2Msg(messageData) ? message : null; 109 | } 110 | 111 | public byte[] msg2Byte() { 112 | return msg2Byte(_XTCP.getDefaultStorageMode()); 113 | } 114 | 115 | @Override 116 | public byte[] msg2Byte(StorageMode storageMode) { 117 | byte[] dataBytes = mIProtocolItem != null ? mIProtocolItem.proto2byte(storageMode) : null; 118 | 119 | //计算长度 120 | mFrameLength = calculateFrameLength(dataBytes); 121 | 122 | //计算校验和 123 | if (mIsCheck) { 124 | mCheckSum = MessageUtils.calculateChecksum(dataBytes); 125 | } else { 126 | mCheckSum = 0; //没有校验和就为0 127 | } 128 | 129 | //获取OpCode 130 | if (mOpCode == 0 && mIProtocolItem != null) { 131 | mOpCode = XProtocolCenter.getInstance().getOpCodeByClassName(mIProtocolItem.getClass().getCanonicalName()); 132 | } 133 | 134 | byte[] messageBytes = new byte[mFrameLength + 4]; //帧头帧尾加起来长度为4 135 | System.arraycopy(mFrameHead, 0, messageBytes, 0, mFrameHead.length); //帧头 136 | ConvertUtils.fillShortToBytes(storageMode, mFrameLength, messageBytes, 2); //帧的长度 137 | messageBytes[4] = mOpCode; //命令码 138 | 139 | ConvertUtils.fillShortToBytes(storageMode, mCheckSum, messageBytes, 5); //校验和 140 | messageBytes[7] = mRetCode; //结果码 141 | 142 | if (dataBytes != null && dataBytes.length > 0) { 143 | System.arraycopy(dataBytes, 0, messageBytes, 8, dataBytes.length); //填充数据 144 | } 145 | System.arraycopy(mFrameEnd, 0, messageBytes, messageBytes.length - 2, mFrameEnd.length);//帧尾 146 | return messageBytes; 147 | } 148 | 149 | public boolean byte2Msg(byte[] messageData) { 150 | return byte2Msg(messageData, _XTCP.getDefaultStorageMode()); 151 | } 152 | 153 | @Override 154 | public boolean byte2Msg(byte[] messageData, StorageMode storageMode) { 155 | if (!MessageUtils.verifyMessage(getMinMessageLength(), 5, 8, messageData, storageMode)) { 156 | return false; 157 | } 158 | 159 | mFrameHead = new byte[2]; 160 | System.arraycopy(messageData, 0, mFrameHead, 0, mFrameHead.length); 161 | mFrameLength = ConvertUtils.bytesToShort(storageMode, messageData, 2); //帧的长度 162 | mOpCode = messageData[4]; 163 | mCheckSum = ConvertUtils.bytesToShort(storageMode, messageData, 5);//校验和 164 | mRetCode = messageData[7]; 165 | 166 | if (messageData.length - getMinMessageLength() > 0) { 167 | String className = XProtocolCenter.getInstance().getClassNameByOpCode(mOpCode); 168 | try { 169 | if (className != null && className.length() > 0) { 170 | Class clazz = Class.forName(className); 171 | mIProtocolItem = (IProtocolItem) clazz.newInstance(); 172 | mIProtocolItem.byte2proto(messageData, 8, 2, storageMode); 173 | 174 | mFrameEnd = new byte[2]; 175 | System.arraycopy(messageData, messageData.length - 2, mFrameEnd, 0, mFrameEnd.length); 176 | return true; 177 | } 178 | } catch (Exception e) { 179 | e.printStackTrace(); 180 | } 181 | return false; 182 | } else { 183 | mFrameEnd = new byte[2]; 184 | System.arraycopy(messageData, messageData.length - 2, mFrameEnd, 0, mFrameEnd.length); 185 | return true; 186 | } 187 | } 188 | 189 | @Override 190 | public int getMinMessageLength() { 191 | return MIN_MESSAGE_LENGTH; 192 | } 193 | 194 | /** 195 | * 计算帧的长度
196 | * LEN = (Len(2) + OpCode(1) + CheckSum(2) + Ret(1)) + DATA 197 | * 198 | * @param dataBytes 数据内容 199 | * @return 获取帧的长度 200 | */ 201 | private short calculateFrameLength(byte[] dataBytes) { 202 | return (short) (dataBytes != null ? (dataBytes.length + 6) : 6); 203 | } 204 | 205 | /** 206 | * 设置协议项 207 | * 208 | * @param protocolItem 协议项 209 | * @return 210 | */ 211 | @Override 212 | public XMessage setIProtocolItem(IProtocolItem protocolItem) { 213 | mIProtocolItem = protocolItem; 214 | mFrameHead = DEFAULT_FRAME_HEAD; 215 | mFrameEnd = DEFAULT_FRAME_END; 216 | return this; 217 | } 218 | 219 | @Override 220 | public IProtocolItem getProtocolItem() { 221 | return mIProtocolItem; 222 | } 223 | 224 | //========set===========// 225 | 226 | public XMessage setFrameHead(byte[] frameHead) { 227 | mFrameHead = frameHead; 228 | return this; 229 | } 230 | 231 | public XMessage setOpCode(byte opCode) { 232 | mOpCode = opCode; 233 | return this; 234 | } 235 | 236 | public XMessage setIsCheck(boolean isCheck) { 237 | mIsCheck = isCheck; 238 | return this; 239 | } 240 | 241 | public XMessage setCheckSum(short checkSum) { 242 | mCheckSum = checkSum; 243 | return this; 244 | } 245 | 246 | public XMessage setRetCode(byte retCode) { 247 | mRetCode = retCode; 248 | return this; 249 | } 250 | 251 | public XMessage setFrameEnd(byte[] frameEnd) { 252 | mFrameEnd = frameEnd; 253 | return this; 254 | } 255 | 256 | //========get===========// 257 | 258 | public byte[] getFrameHead() { 259 | return mFrameHead; 260 | } 261 | 262 | public short getFrameLength() { 263 | return (short) (mIProtocolItem != null ? (mIProtocolItem.getProtocolLength() + 6) : 6); 264 | } 265 | 266 | public byte getOpCode() { 267 | return mOpCode; 268 | } 269 | 270 | public boolean isCheck() { 271 | return mIsCheck; 272 | } 273 | 274 | public short getCheckSum() { 275 | return mCheckSum; 276 | } 277 | 278 | public byte getRetCode() { 279 | return mRetCode; 280 | } 281 | 282 | public byte[] getFrameEnd() { 283 | return mFrameEnd; 284 | } 285 | 286 | @Override 287 | public String toString() { 288 | return "XMessage{" + 289 | "mFrameHead=" + ConvertUtils.bytesToHex(mFrameHead) + 290 | ", mFrameLength=" + mFrameLength + 291 | ", mOpCode=" + mOpCode + 292 | ", mIsCheck=" + mIsCheck + 293 | ", mCheckSum=" + mCheckSum + 294 | ", mRetCode=" + mRetCode + 295 | ", mIProtocolItem=" + mIProtocolItem + 296 | ", mFrameEnd=" + ConvertUtils.bytesToHex(mFrameEnd) + 297 | '}'; 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/message/template/XOrderlyMessage.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.message.template; 2 | 3 | import com.xuexiang.xtcp._XTCP; 4 | import com.xuexiang.xtcp.core.XProtocolCenter; 5 | import com.xuexiang.xtcp.core.message.IMessage; 6 | import com.xuexiang.xtcp.enums.StorageMode; 7 | import com.xuexiang.xtcp.model.IProtocolItem; 8 | import com.xuexiang.xtcp.utils.ConvertUtils; 9 | import com.xuexiang.xtcp.utils.MessageUtils; 10 | 11 | import static com.xuexiang.xtcp.core.message.MessageConstants.DEFAULT_FRAME_END; 12 | import static com.xuexiang.xtcp.core.message.MessageConstants.DEFAULT_FRAME_HEAD; 13 | 14 | import androidx.annotation.Nullable; 15 | 16 | /** 17 | * 提供的一套TCP传输协议消息模版(有消息序号ID,有序的)
18 | *

19 | * 格式如下:
20 | *

21 | * 55AA | Len | OpCode | ID | CheckSum | ...data... | 00FF
22 | * 帧头 (固定) | 帧长 | 命令码 | 消息ID | 校验和 | 数据 | 帧尾(固定) 23 | * 24 | * @author xuexiang 25 | * @since 2018/12/17 上午9:49 26 | */ 27 | public class XOrderlyMessage implements IMessage { 28 | 29 | /** 30 | * 最小数据长度为11(2 + 2 + 1 + 2 + 2 + 2)[data == null] 31 | */ 32 | private static final int MIN_MESSAGE_LENGTH = 11; 33 | 34 | /** 35 | * 帧头(2个byte) 36 | * index:0 37 | */ 38 | private byte[] mFrameHead; 39 | /** 40 | * 帧长度(2个byte) -> short[0~65535] 41 | * index:2 42 | */ 43 | private short mFrameLength; 44 | /** 45 | * 功能码(一个byte) 46 | * index:4 47 | */ 48 | private byte mOpCode; 49 | /** 50 | * 消息序号(2个byte) -> short[0~65535] 51 | * index:5 52 | */ 53 | private short mMsgID; 54 | /** 55 | * 是否需要校验 56 | */ 57 | private boolean mIsCheck; 58 | /** 59 | * 校验和(二个byte) 60 | * index:7 61 | */ 62 | private short mCheckSum; 63 | 64 | /** 65 | * 数据项(不定长) 66 | * index:9 67 | */ 68 | private IProtocolItem mIProtocolItem; 69 | /** 70 | * 帧尾(2个byte) 71 | */ 72 | private byte[] mFrameEnd; 73 | 74 | /** 75 | * 构造方法 76 | */ 77 | public XOrderlyMessage() { 78 | this(false); 79 | } 80 | 81 | /** 82 | * 构造方法 83 | * 84 | * @param isCheck 是否需要校验和 85 | */ 86 | public XOrderlyMessage(boolean isCheck) { 87 | mIsCheck = isCheck; 88 | } 89 | 90 | /** 91 | * 包装协议 92 | * 93 | * @param protocolItem 94 | * @param msgID 95 | * @return 96 | */ 97 | public static XOrderlyMessage wrap(IProtocolItem protocolItem, int msgID) { 98 | return new XOrderlyMessage().setIProtocolItem(protocolItem).setMsgID(msgID); 99 | } 100 | 101 | /** 102 | * 解析协议 103 | * 104 | * @param messageData 105 | * @return 106 | */ 107 | @Nullable 108 | public static XOrderlyMessage parse(byte[] messageData) { 109 | XOrderlyMessage message = new XOrderlyMessage(); 110 | return message.byte2Msg(messageData) ? message : null; 111 | } 112 | 113 | public byte[] msg2Byte() { 114 | return msg2Byte(_XTCP.getDefaultStorageMode()); 115 | } 116 | 117 | @Override 118 | public byte[] msg2Byte(StorageMode storageMode) { 119 | byte[] dataBytes = mIProtocolItem != null ? mIProtocolItem.proto2byte(storageMode) : null; 120 | 121 | //计算长度 122 | mFrameLength = calculateFrameLength(dataBytes); 123 | 124 | //计算校验和 125 | if (mIsCheck) { 126 | mCheckSum = MessageUtils.calculateChecksum(dataBytes); 127 | } else { 128 | mCheckSum = 0; //没有校验和就为0 129 | } 130 | 131 | //获取OpCode 132 | if (mOpCode == 0 && mIProtocolItem != null) { 133 | mOpCode = XProtocolCenter.getInstance().getOpCodeByClassName(mIProtocolItem.getClass().getCanonicalName()); 134 | } 135 | 136 | byte[] messageBytes = new byte[mFrameLength + 4]; //帧头帧尾加起来长度为4 137 | System.arraycopy(mFrameHead, 0, messageBytes, 0, mFrameHead.length); //帧头 138 | ConvertUtils.fillShortToBytes(storageMode, mFrameLength, messageBytes, 2); //帧的长度 139 | messageBytes[4] = mOpCode; //命令码 140 | ConvertUtils.fillShortToBytes(storageMode, mMsgID, messageBytes, 5); //消息序号 141 | ConvertUtils.fillShortToBytes(storageMode, mCheckSum, messageBytes, 7); //校验和 142 | 143 | if (dataBytes != null && dataBytes.length > 0) { 144 | System.arraycopy(dataBytes, 0, messageBytes, 9, dataBytes.length); //填充数据 145 | } 146 | System.arraycopy(mFrameEnd, 0, messageBytes, messageBytes.length - 2, mFrameEnd.length);//帧尾 147 | return messageBytes; 148 | } 149 | 150 | public boolean byte2Msg(byte[] messageData) { 151 | return byte2Msg(messageData, _XTCP.getDefaultStorageMode()); 152 | } 153 | 154 | @Override 155 | public boolean byte2Msg(byte[] messageData, StorageMode storageMode) { 156 | if (!MessageUtils.verifyMessage(getMinMessageLength(), 7, 9, messageData, storageMode)) { 157 | return false; 158 | } 159 | 160 | mFrameHead = new byte[2]; //帧头 161 | System.arraycopy(messageData, 0, mFrameHead, 0, mFrameHead.length); 162 | mFrameLength = ConvertUtils.bytesToShort(storageMode, messageData, 2); //帧的长度 163 | mOpCode = messageData[4]; //命令码 164 | mMsgID = ConvertUtils.bytesToShort(storageMode, messageData, 5); //消息序号 165 | mCheckSum = ConvertUtils.bytesToShort(storageMode, messageData, 7);//校验和 166 | 167 | if (messageData.length - getMinMessageLength() > 0) { 168 | String className = XProtocolCenter.getInstance().getClassNameByOpCode(mOpCode); 169 | try { 170 | if (className != null && className.length() > 0) { 171 | Class clazz = Class.forName(className); 172 | mIProtocolItem = (IProtocolItem) clazz.newInstance(); 173 | mIProtocolItem.byte2proto(messageData, 9, 2, storageMode); 174 | 175 | mFrameEnd = new byte[2]; 176 | System.arraycopy(messageData, messageData.length - 2, mFrameEnd, 0, mFrameEnd.length); 177 | return true; 178 | } 179 | } catch (Exception e) { 180 | e.printStackTrace(); 181 | } 182 | return false; 183 | } else { 184 | mFrameEnd = new byte[2]; 185 | System.arraycopy(messageData, messageData.length - 2, mFrameEnd, 0, mFrameEnd.length); 186 | return true; 187 | } 188 | } 189 | 190 | @Override 191 | public int getMinMessageLength() { 192 | return MIN_MESSAGE_LENGTH; 193 | } 194 | 195 | /** 196 | * 计算帧的长度
197 | * LEN = (Len(2) + OpCode(1) + ID(2) + CheckSum(2)) + DATA 198 | * 199 | * @param dataBytes 数据内容 200 | * @return 获取帧的长度 201 | */ 202 | private short calculateFrameLength(byte[] dataBytes) { 203 | return (short) (dataBytes != null ? (dataBytes.length + 7) : 7); 204 | } 205 | 206 | /** 207 | * 设置协议项 208 | * 209 | * @param protocolItem 协议项 210 | * @return 211 | */ 212 | @Override 213 | public XOrderlyMessage setIProtocolItem(IProtocolItem protocolItem) { 214 | mIProtocolItem = protocolItem; 215 | mFrameHead = DEFAULT_FRAME_HEAD; 216 | mFrameEnd = DEFAULT_FRAME_END; 217 | return this; 218 | } 219 | 220 | @Override 221 | public IProtocolItem getProtocolItem() { 222 | return mIProtocolItem; 223 | } 224 | 225 | //========set===========// 226 | 227 | 228 | public XOrderlyMessage setFrameHead(byte[] frameHead) { 229 | mFrameHead = frameHead; 230 | return this; 231 | } 232 | 233 | public XOrderlyMessage setOpCode(byte opCode) { 234 | mOpCode = opCode; 235 | return this; 236 | } 237 | 238 | public XOrderlyMessage setIsCheck(boolean isCheck) { 239 | mIsCheck = isCheck; 240 | return this; 241 | } 242 | 243 | public XOrderlyMessage setCheckSum(short checkSum) { 244 | mCheckSum = checkSum; 245 | return this; 246 | } 247 | 248 | public XOrderlyMessage setMsgID(int msgID) { 249 | mMsgID = (short) msgID; 250 | return this; 251 | } 252 | 253 | public XOrderlyMessage setMsgID(short msgID) { 254 | mMsgID = msgID; 255 | return this; 256 | } 257 | 258 | public XOrderlyMessage setFrameEnd(byte[] frameEnd) { 259 | mFrameEnd = frameEnd; 260 | return this; 261 | } 262 | 263 | //========get===========// 264 | 265 | public byte[] getFrameHead() { 266 | return mFrameHead; 267 | } 268 | 269 | public short getFrameLength() { 270 | return (short) (mIProtocolItem != null ? (mIProtocolItem.getProtocolLength() + 7) : 7); 271 | } 272 | 273 | public byte getOpCode() { 274 | return mOpCode; 275 | } 276 | 277 | public boolean isCheck() { 278 | return mIsCheck; 279 | } 280 | 281 | public short getCheckSum() { 282 | return mCheckSum; 283 | } 284 | 285 | public short getMsgID() { 286 | return mMsgID; 287 | } 288 | 289 | public byte[] getFrameEnd() { 290 | return mFrameEnd; 291 | } 292 | 293 | @Override 294 | public String toString() { 295 | return "XOrderlyMessage{" + 296 | "mFrameHead=" + ConvertUtils.bytesToHex(mFrameHead) + 297 | ", mFrameLength=" + mFrameLength + 298 | ", mOpCode=" + mOpCode + 299 | ", mMsgID=" + mMsgID + 300 | ", mIsCheck=" + mIsCheck + 301 | ", mCheckSum=" + mCheckSum + 302 | ", mIProtocolItem=" + mIProtocolItem + 303 | ", mFrameEnd=" + ConvertUtils.bytesToHex(mFrameEnd) + 304 | '}'; 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/model/AbstractArrayItem.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.model; 2 | 3 | import androidx.annotation.Nullable; 4 | 5 | import com.xuexiang.xtcp._XTCP; 6 | import com.xuexiang.xtcp.annotation.ProtocolField; 7 | import com.xuexiang.xtcp.enums.StorageMode; 8 | import com.xuexiang.xtcp.model.IArrayItem; 9 | import com.xuexiang.xtcp.utils.ConvertUtils; 10 | 11 | import java.lang.reflect.Field; 12 | 13 | /** 14 | * 抽象的数组类[只是为了解决byte解析时,对应数组数据长度未知的问题]。所有的数组,只要是需要解析的,就必须要实现IArrayItem。 15 | * 16 | * @author xuexiang 17 | * @since 2018/12/13 上午11:28 18 | */ 19 | public abstract class AbstractArrayItem implements IArrayItem { 20 | 21 | /** 22 | * 数组数据的字段名 23 | */ 24 | public static final String FIELD_NAME_DATA = "mData"; 25 | 26 | @Override 27 | public int getProtocolLength() { 28 | return _XTCP.getIProtocolParser().getProtocolLength(this); 29 | } 30 | 31 | @Override 32 | public byte[] proto2byte(StorageMode storageMode) { 33 | return _XTCP.getIProtocolParser().protoBody2Byte(this, storageMode); 34 | } 35 | 36 | /** 37 | * 设置数组的长度 38 | * 39 | * @param length 40 | * @param 41 | * @return 42 | */ 43 | public abstract T setLength(int length); 44 | 45 | @Override 46 | public int fillArrayLength(byte[] bytes, int index, StorageMode storageMode) { 47 | setLength(ConvertUtils.bytesToInt(storageMode, bytes, index, DEFAULT_ARRAY_LENGTH_SIZE)); //拿到长度 48 | return DEFAULT_ARRAY_LENGTH_SIZE; 49 | } 50 | 51 | /** 52 | * 数据是否足够解析 53 | * 54 | * @param bytes 数据集合[整个消息体数据,包含头和尾] 55 | * @param index 起始字节 56 | * @param tailLength 消息尾的长度[和index一起决定了数据解析的范围] 57 | * @param parseLength 需要解析的长度 58 | * @return 59 | */ 60 | protected boolean isDataEnoughToParse(byte[] bytes, int index, int tailLength, int parseLength) { 61 | return bytes.length - index - tailLength < parseLength; 62 | } 63 | 64 | /** 65 | * 获取数组协议字段的长度 66 | * 67 | * @param fieldName 字段名 68 | * @param maxLength 最大的长度 69 | * @return 70 | */ 71 | protected int getArrayFieldLength(String fieldName, int maxLength) { 72 | try { 73 | Field field = getClass().getDeclaredField(fieldName); 74 | field.setAccessible(true); 75 | ProtocolField protocolField = field.getAnnotation(ProtocolField.class); 76 | return protocolField.length() > maxLength || protocolField.length() < 1 ? maxLength : protocolField.length(); 77 | } catch (NoSuchFieldException e) { 78 | e.printStackTrace(); 79 | } 80 | return maxLength; 81 | } 82 | 83 | /** 84 | * 获取数组协议字段的类型 85 | * 86 | * @param fieldName 字段名 87 | * @return 88 | */ 89 | @Nullable 90 | protected Class getArrayFieldType(String fieldName) { 91 | try { 92 | Field field = getClass().getDeclaredField(fieldName); 93 | field.setAccessible(true); 94 | String arrTypeStr = field.getType().toString(); 95 | // 通过对象数组类型获取对象类型 96 | return Class.forName(arrTypeStr.substring(arrTypeStr.indexOf('[') + 2, arrTypeStr.length() - 1)); 97 | } catch (NoSuchFieldException e) { 98 | e.printStackTrace(); 99 | } catch (ClassNotFoundException e) { 100 | e.printStackTrace(); 101 | } 102 | return null; 103 | } 104 | 105 | } 106 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/model/BCD.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.model; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.xuexiang.xtcp.enums.StorageMode; 6 | import com.xuexiang.xtcp.logs.XTLog; 7 | import com.xuexiang.xtcp.model.IProtocolItem; 8 | import com.xuexiang.xtcp.utils.BCDUtils; 9 | 10 | import java.lang.reflect.Type; 11 | import java.util.Date; 12 | 13 | /** 14 | * BCD编码 15 | *

16 | * new BCD(Date.class, "yy-MM-dd HH-mm"); 17 | * new BCD(Date.class, "yyyy-MM-dd HH:mm:ss") 18 | * new BCD(Date.class, "yyMMddHHmmss"); 19 | * new BCD(int.class, "XXXX"); 20 | * new BCD(float.class, "xxxx.xx"); 21 | * new BCD(float.class, "xxxxx.x"); 22 | * 23 | * @author xuexiang 24 | * @since 2019/3/5 下午11:46 25 | */ 26 | public class BCD implements IProtocolItem { 27 | 28 | /** 29 | * byte数据 30 | */ 31 | private byte[] mData; 32 | /** 33 | * 数值 34 | */ 35 | private Object mValue; 36 | /** 37 | * 数值的类型 38 | */ 39 | private Type mType; 40 | /** 41 | * BCD编码的格式 42 | */ 43 | private String mFormat; 44 | 45 | public BCD(Type type, String format) { 46 | mType = type; 47 | mFormat = format; 48 | int len = calculateFormatLength(format); 49 | mData = new byte[len]; 50 | } 51 | 52 | public BCD(Type type, T value, String format) { 53 | mType = type; 54 | mValue = value; 55 | mFormat = format; 56 | int len = calculateFormatLength(format); 57 | mData = new byte[len]; 58 | } 59 | 60 | /** 61 | * 设置值 62 | * @param value 63 | */ 64 | public void setValue(T value) { 65 | mValue = value; 66 | } 67 | 68 | /** 69 | * @return 获取协议项的数据长度 70 | */ 71 | @Override 72 | public int getProtocolLength() { 73 | return mData != null ? mData.length : 0; 74 | } 75 | 76 | /** 77 | * 将协议实体转化为byte数组 78 | * 79 | * @param storageMode 存储形式 80 | * @return 81 | */ 82 | @Override 83 | public byte[] proto2byte(StorageMode storageMode) { 84 | if (mType == null || mFormat == null || mValue == null || mData == null) { 85 | return null; 86 | } 87 | 88 | if (int.class.equals(mType) || Integer.class.equals(mType) || byte.class.equals(mType) 89 | || Byte.class.equals(mType) || short.class.equals(mType) || Short.class.equals(mType) 90 | || long.class.equals(mType) || Long.class.equals(mType)) { 91 | mData = BCDUtils.int2Bcd((Integer) mValue, mFormat); 92 | } else if (double.class.equals(mType) || Double.class.equals(mType)) { 93 | mData = BCDUtils.double2Bcd((Double) mValue, mFormat); 94 | } else if (float.class.equals(mType) || Float.class.equals(mType)) { 95 | mData = BCDUtils.float2Bcd((Float) mValue, mFormat); 96 | } else if (String.class.equals(mType)) { 97 | mData = BCDUtils.string2Bcd((String) mValue); 98 | } else if (Date.class.equals(mType)) { 99 | mData = BCDUtils.date2Bcd((Date) mValue, mFormat); 100 | } 101 | return mData; 102 | } 103 | 104 | /** 105 | * 将byte数组数据转化为协议实体 106 | * 107 | * @param bytes byte数组数据 108 | * @param index 起始字节 109 | * @param tailLength 消息尾的长度[和index一起决定了数据解析的范围] 110 | * @param storageMode 存储形式 111 | * @return 是否解析成功 112 | */ 113 | @Override 114 | public boolean byte2proto(byte[] bytes, int index, int tailLength, StorageMode storageMode) { 115 | if (bytes == null || mData == null) { 116 | return false; 117 | } 118 | 119 | if (bytes.length - index - tailLength < mData.length) { //剩余数据不够解析 120 | XTLog.d("[BCD] 剩余数据不够解析,直接退出!"); 121 | return false; 122 | } 123 | 124 | System.arraycopy(bytes, index, mData, 0, mData.length); 125 | if (mType == null || mFormat == null || mData == null) { 126 | return false; 127 | } 128 | if (int.class.equals(mType) || Integer.class.equals(mType) || byte.class.equals(mType) 129 | || Byte.class.equals(mType) || short.class.equals(mType) || Short.class.equals(mType) 130 | || long.class.equals(mType) || Long.class.equals(mType)) { 131 | mValue = BCDUtils.bcd2Int(mData); 132 | } else if (double.class.equals(mType) || Double.class.equals(mType)) { 133 | mValue = BCDUtils.bcd2Double(mData, mFormat); 134 | } else if (float.class.equals(mType) || Float.class.equals(mType)) { 135 | mValue = BCDUtils.bcd2Float(mData, mFormat); 136 | } else if (String.class.equals(mType)) { 137 | mValue = BCDUtils.bcd2String(mData); 138 | } else if (Date.class.equals(mType)) { 139 | mValue = BCDUtils.bcd2Date(mData, mFormat); 140 | } 141 | return true; 142 | } 143 | 144 | /** 145 | * 计算格式的长度 146 | * 147 | * @param format 148 | * @return 149 | */ 150 | private int calculateFormatLength(String format) { 151 | return (formatString(format).length() + 1) / 2; 152 | } 153 | 154 | /** 155 | * 格式化String, 去除字符串中非字母和数字的字符 156 | * @param format 157 | * @return 158 | */ 159 | @NonNull 160 | private String formatString(String format) { 161 | return format.replaceAll("[^a-zA-Z0-9]*", ""); 162 | } 163 | 164 | //=======get========// 165 | 166 | public byte[] getData() { 167 | return mData; 168 | } 169 | 170 | public T getValue() { 171 | return (T) mValue; 172 | } 173 | 174 | /** 175 | * @return 获取格式化的值 176 | */ 177 | public String getFormatValue() { 178 | if (mType == null || mFormat == null || mValue == null) { 179 | return ""; 180 | } 181 | if (String.class.equals(mType)) { 182 | return (String) mValue; 183 | } else if (Date.class.equals(mType)) { 184 | return BCDUtils.date2String((Date) mValue, mFormat); 185 | } else { 186 | return String.valueOf(mValue); 187 | } 188 | } 189 | 190 | public String getFormat() { 191 | return mFormat; 192 | } 193 | 194 | public Type getType() { 195 | return mType; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/model/ByteArray.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.model; 2 | 3 | import static com.xuexiang.xtcp.core.XTCPConstants.MAX_ARRAY_LENGTH; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import com.xuexiang.xtcp.annotation.ProtocolField; 8 | import com.xuexiang.xtcp.enums.StorageMode; 9 | import com.xuexiang.xtcp.logs.XTLog; 10 | import com.xuexiang.xtcp.utils.ConvertUtils; 11 | 12 | /** 13 | * byte数组协议项
14 | *

15 | * mLength所占的byte位数为1,可表示的长度范围为【0~255】 16 | * 17 | * @author xuexiang 18 | * @since 2018/12/13 下午11:47 19 | */ 20 | public class ByteArray extends AbstractArrayItem { 21 | 22 | /** 23 | * 集合数组的长度 24 | */ 25 | @ProtocolField(index = 0, length = DEFAULT_ARRAY_LENGTH_SIZE) 26 | private int mLength; 27 | 28 | /** 29 | * 集合数据 30 | */ 31 | @ProtocolField(index = 1) 32 | private byte[] mData; 33 | 34 | /** 35 | * 获取Byte数组包装类 36 | * 37 | * @param data 38 | * @return 39 | */ 40 | public static ByteArray wrap(@NonNull byte[] data) { 41 | return new ByteArray(data); 42 | } 43 | 44 | /** 45 | * 空的构造方法不能去除,用于反射构造 46 | */ 47 | public ByteArray() { 48 | 49 | } 50 | 51 | public ByteArray(@NonNull byte[] data) { 52 | setData(data); 53 | } 54 | 55 | 56 | @Override 57 | public ByteArray setLength(int length) { 58 | mLength = length; 59 | return this; 60 | } 61 | 62 | @Override 63 | public int getLength() { 64 | return mLength; 65 | } 66 | 67 | public byte[] getData() { 68 | return mData; 69 | } 70 | 71 | public ByteArray setData(@NonNull byte[] data) { 72 | mData = data; 73 | if (mData.length > MAX_ARRAY_LENGTH) { //长度不能超过255 74 | XTLog.d("[ByteArray] 数组长度溢出,需要进行截取处理..."); 75 | mData = new byte[MAX_ARRAY_LENGTH]; 76 | System.arraycopy(data, 0, mData, 0, mData.length); 77 | } 78 | mLength = mData.length; 79 | return this; 80 | } 81 | 82 | @Override 83 | public boolean byte2proto(byte[] bytes, int index, int tailLength, StorageMode storageMode) { 84 | mData = new byte[getLength()]; 85 | if (isDataEnoughToParse(bytes, index, tailLength, mLength)) { //剩余数据不够解析 86 | XTLog.d("[ByteArray] 剩余数据不够解析,直接退出!"); 87 | return false; 88 | } 89 | System.arraycopy(bytes, index, mData, 0, mData.length); 90 | return true; 91 | } 92 | 93 | @Override 94 | public String toString() { 95 | return "ByteArray{" + 96 | "mLength=" + mLength + 97 | ", mData=" + ConvertUtils.bytesToHexString(mData) + 98 | '}'; 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/model/FixedString.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.model; 2 | 3 | import com.xuexiang.xtcp.enums.StorageMode; 4 | import com.xuexiang.xtcp.logs.XTLog; 5 | import com.xuexiang.xtcp.model.IProtocolItem; 6 | 7 | import java.nio.charset.Charset; 8 | 9 | /** 10 | * 固定长度的字符串 11 | * 例如: 12 | * new FixedString(10) 13 | * @author XUE 14 | * @since 2019/3/6 14:28 15 | */ 16 | public class FixedString implements IProtocolItem { 17 | 18 | /** 19 | * 长度 20 | */ 21 | private int mLength; 22 | /** 23 | * 定长字符串转成的数组,在内部使用,外部无法获取 24 | */ 25 | private byte[] mFixedData; 26 | 27 | /** 28 | * 初始化的时候必须定义长度 29 | * 30 | * @param length 31 | */ 32 | public FixedString(int length) { 33 | if (length < 0) { 34 | throw new IllegalArgumentException("Length should not be less than 0"); 35 | } 36 | mLength = length; 37 | mFixedData = new byte[length]; 38 | } 39 | 40 | /** 41 | * 设置定长字符串 42 | * 43 | * @param fixedString 44 | * @return 45 | */ 46 | public FixedString setFixedString(String fixedString) { 47 | byte[] tmp = fixedString.getBytes(); 48 | if (tmp.length > mLength) { 49 | throw new IndexOutOfBoundsException("The length of fixedString is too long"); 50 | } 51 | System.arraycopy(tmp, 0, mFixedData, 0, tmp.length); 52 | return this; 53 | } 54 | 55 | /** 56 | * 设置定长字符串 57 | * 58 | * @param fixedString 59 | * @return 60 | */ 61 | public FixedString setFixedString(String fixedString, String charset) { 62 | byte[] tmp = fixedString.getBytes(Charset.forName(charset)); 63 | if (tmp.length > mLength) { 64 | throw new IndexOutOfBoundsException("The length of fixedString is too long"); 65 | } 66 | System.arraycopy(tmp, 0, mFixedData, 0, tmp.length); 67 | return this; 68 | } 69 | 70 | @Override 71 | public int getProtocolLength() { 72 | return mLength; 73 | } 74 | 75 | @Override 76 | public byte[] proto2byte(StorageMode storageMode) { 77 | return mFixedData; 78 | } 79 | 80 | @Override 81 | public boolean byte2proto(byte[] bytes, int index, int tailLength, StorageMode storageMode) { 82 | if (bytes == null || mFixedData == null) { 83 | return false; 84 | } 85 | 86 | if (bytes.length - index - tailLength < mLength) { //剩余数据不够解析 87 | XTLog.d("[FixedString] 剩余数据不够解析,直接退出!"); 88 | return false; 89 | } 90 | System.arraycopy(bytes, index, mFixedData, 0, mFixedData.length); 91 | return true; 92 | } 93 | 94 | public String getFixedString() { 95 | return new String(mFixedData); 96 | } 97 | 98 | public String getFixedString(String charset) { 99 | return new String(mFixedData, Charset.forName(charset)); 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/model/IntArray.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.model; 2 | 3 | import static com.xuexiang.xtcp.core.XTCPConstants.INT_MAX_LENGTH; 4 | import static com.xuexiang.xtcp.core.XTCPConstants.MAX_ARRAY_LENGTH; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.xuexiang.xtcp.annotation.ProtocolField; 9 | import com.xuexiang.xtcp.enums.StorageMode; 10 | import com.xuexiang.xtcp.logs.XTLog; 11 | import com.xuexiang.xtcp.utils.ConvertUtils; 12 | 13 | import java.util.Arrays; 14 | 15 | /** 16 | * int数组协议项【无符号】
17 | *

18 | * mLength所占的byte位数为1,可表示的长度范围为【0~255】 19 | * 20 | * @author xuexiang 21 | * @since 2018/12/12 下午1:33 22 | */ 23 | public class IntArray extends AbstractArrayItem { 24 | /** 25 | * 集合数组的长度 26 | */ 27 | @ProtocolField(index = 0, length = DEFAULT_ARRAY_LENGTH_SIZE) 28 | private int mLength; 29 | 30 | /** 31 | * 集合数据 32 | */ 33 | @ProtocolField(index = 1) 34 | private int[] mData; 35 | 36 | /** 37 | * 获取int数组包装类 38 | * 39 | * @param data 40 | * @return 41 | */ 42 | public static IntArray wrap(@NonNull int[] data) { 43 | return new IntArray(data); 44 | } 45 | 46 | /** 47 | * 空的构造方法不能去除,用于反射构造 48 | */ 49 | public IntArray() { 50 | 51 | } 52 | 53 | public IntArray(@NonNull int[] data) { 54 | setData(data); 55 | } 56 | 57 | @Override 58 | public IntArray setLength(int length) { 59 | mLength = length; 60 | return this; 61 | } 62 | 63 | @Override 64 | public int getLength() { 65 | return mLength; 66 | } 67 | 68 | public int[] getData() { 69 | return mData; 70 | } 71 | 72 | public IntArray setData(@NonNull int[] data) { 73 | mData = data; 74 | if (mData.length > MAX_ARRAY_LENGTH) { //长度不能超过255 75 | XTLog.d("[IntArray] 数组长度溢出,需要进行截取处理..."); 76 | mData = new int[MAX_ARRAY_LENGTH]; 77 | System.arraycopy(data, 0, mData, 0, mData.length); 78 | } 79 | mLength = mData.length; 80 | return this; 81 | } 82 | 83 | @Override 84 | public boolean byte2proto(byte[] bytes, int index, int tailLength, StorageMode storageMode) { 85 | mData = new int[getLength()]; 86 | int dataFieldLength = getArrayFieldLength(FIELD_NAME_DATA, INT_MAX_LENGTH); 87 | if (isDataEnoughToParse(bytes, index, tailLength, mLength * dataFieldLength)) { //剩余数据不够解析 88 | XTLog.d("[IntArray] 剩余数据不够解析,直接退出!"); 89 | return false; 90 | } 91 | for (int i = 0; i < mLength; i++) { 92 | mData[i] = ConvertUtils.bytesToInt(storageMode, bytes, index, dataFieldLength); 93 | index += dataFieldLength; 94 | } 95 | return true; 96 | } 97 | 98 | @Override 99 | public String toString() { 100 | return "IntArray{" + 101 | "mLength=" + mLength + 102 | ", mData=" + Arrays.toString(mData) + 103 | '}'; 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/model/LargeByteArray.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.model; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.xuexiang.xtcp.annotation.ProtocolField; 6 | import com.xuexiang.xtcp.enums.StorageMode; 7 | import com.xuexiang.xtcp.logs.XTLog; 8 | import com.xuexiang.xtcp.utils.ConvertUtils; 9 | 10 | import static com.xuexiang.xtcp.core.XTCPConstants.MAX_LARGE_ARRAY_LENGTH; 11 | 12 | /** 13 | * 长byte数组协议项
14 | *

15 | * 和ByteArray相比,LargeByteArray中mLength所占的byte位数为2,可表示的长度范围为【0~65535】 16 | * 17 | * @author xuexiang 18 | * @since 2018/12/14 下午6:44 19 | */ 20 | public class LargeByteArray extends AbstractArrayItem { 21 | 22 | /** 23 | * 集合数组的长度 24 | */ 25 | @ProtocolField(index = 0, length = LARGE_ARRAY_LENGTH_SIZE) 26 | private int mLength; 27 | 28 | /** 29 | * 集合数据 30 | */ 31 | @ProtocolField(index = 1) 32 | private byte[] mData; 33 | 34 | /** 35 | * 获取长Byte数组包装类 36 | * 37 | * @param data 38 | * @return 39 | */ 40 | public static LargeByteArray wrap(@NonNull byte[] data) { 41 | return new LargeByteArray(data); 42 | } 43 | 44 | /** 45 | * 空的构造方法不能去除,用于反射构造 46 | */ 47 | public LargeByteArray() { 48 | 49 | } 50 | 51 | public LargeByteArray(@NonNull byte[] data) { 52 | setData(data); 53 | } 54 | 55 | 56 | @Override 57 | public LargeByteArray setLength(int length) { 58 | mLength = length; 59 | return this; 60 | } 61 | 62 | @Override 63 | public int getLength() { 64 | return mLength; 65 | } 66 | 67 | public byte[] getData() { 68 | return mData; 69 | } 70 | 71 | public LargeByteArray setData(@NonNull byte[] data) { 72 | mData = data; 73 | if (mData.length > MAX_LARGE_ARRAY_LENGTH) { //长度不能超过65535 74 | XTLog.d("[LargeByteArray] 数组长度溢出,需要进行截取处理..."); 75 | 76 | mData = new byte[MAX_LARGE_ARRAY_LENGTH]; 77 | System.arraycopy(data, 0, mData, 0, mData.length); 78 | } 79 | mLength = mData.length; 80 | return this; 81 | } 82 | 83 | @Override 84 | public boolean byte2proto(byte[] bytes, int index, int tailLength, StorageMode storageMode) { 85 | mData = new byte[getLength()]; 86 | if (isDataEnoughToParse(bytes, index, tailLength, mLength)) { //剩余数据不够解析 87 | XTLog.d("[LargeByteArray] 剩余数据不够解析,直接退出!"); 88 | return false; 89 | } 90 | System.arraycopy(bytes, index, mData, 0, mData.length); 91 | return true; 92 | } 93 | 94 | @Override 95 | public int fillArrayLength(byte[] bytes, int index, StorageMode storageMode) { 96 | setLength(ConvertUtils.bytesToInt(storageMode, bytes, index, LARGE_ARRAY_LENGTH_SIZE)); //拿到长度 97 | return LARGE_ARRAY_LENGTH_SIZE; 98 | } 99 | 100 | @Override 101 | public String toString() { 102 | return "LargeByteArray{" + 103 | "mLength=" + mLength + 104 | ", mData=" + ConvertUtils.bytesToHexString(mData) + 105 | '}'; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/model/LargeString.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.model; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.xuexiang.xtcp.annotation.ProtocolField; 6 | import com.xuexiang.xtcp.enums.StorageMode; 7 | import com.xuexiang.xtcp.logs.XTLog; 8 | import com.xuexiang.xtcp.utils.ConvertUtils; 9 | 10 | import java.nio.charset.Charset; 11 | 12 | import static com.xuexiang.xtcp.core.XTCPConstants.MAX_LARGE_ARRAY_LENGTH; 13 | 14 | /** 15 | * 长String协议项
16 | *

17 | * mLength所占的byte位数为2,可表示的长度范围为【0~65535】 18 | * 19 | * @author xuexiang 20 | * @since 2018/12/14 下午11:57 21 | */ 22 | public class LargeString extends AbstractArrayItem { 23 | /** 24 | * 集合数组的长度 25 | */ 26 | @ProtocolField(index = 0, length = LARGE_ARRAY_LENGTH_SIZE) 27 | private int mLength; 28 | 29 | /** 30 | * 集合数据 31 | */ 32 | @ProtocolField(index = 1) 33 | private byte[] mData; 34 | 35 | /** 36 | * 获取长String数组包装类 37 | * 38 | * @param data 39 | * @return 40 | */ 41 | public static LargeString wrap(@NonNull String data) { 42 | return new LargeString(data); 43 | } 44 | 45 | /** 46 | * 获取长String数组包装类 47 | * 48 | * @param data 49 | * @return 50 | */ 51 | public static LargeString wrap(@NonNull String data, String charset) { 52 | return new LargeString(data, charset); 53 | } 54 | 55 | /** 56 | * 空的构造方法不能去除,用于反射构造 57 | */ 58 | public LargeString() { 59 | 60 | } 61 | 62 | public LargeString(@NonNull String data) { 63 | setData(data); 64 | } 65 | 66 | public LargeString(@NonNull String data, String charset) { 67 | setData(data, charset); 68 | } 69 | 70 | public LargeString setData(@NonNull String data) { 71 | mData = data.getBytes(); 72 | if (mData.length > MAX_LARGE_ARRAY_LENGTH) { //长度不能超过65535 73 | XTLog.d("[LargeString] 数组长度溢出,需要进行截取处理..."); 74 | 75 | mData = new byte[MAX_LARGE_ARRAY_LENGTH]; 76 | System.arraycopy(data.getBytes(), 0, mData, 0, mData.length); 77 | } 78 | mLength = mData.length; 79 | return this; 80 | } 81 | 82 | public LargeString setData(@NonNull String data, String charset) { 83 | mData = data.getBytes(Charset.forName(charset)); 84 | if (mData.length > MAX_LARGE_ARRAY_LENGTH) { //长度不能超过65535 85 | XTLog.d("[LargeString] 数组长度溢出,需要进行截取处理..."); 86 | 87 | mData = new byte[MAX_LARGE_ARRAY_LENGTH]; 88 | System.arraycopy(data.getBytes(Charset.forName(charset)), 0, mData, 0, mData.length); 89 | } 90 | mLength = mData.length; 91 | return this; 92 | } 93 | 94 | public String getData() { 95 | return new String(mData); 96 | } 97 | 98 | public String getData(String charset) { 99 | return new String(mData, Charset.forName(charset)); 100 | } 101 | 102 | @Override 103 | public LargeString setLength(int length) { 104 | mLength = length; 105 | return this; 106 | } 107 | 108 | @Override 109 | public int getLength() { 110 | return mLength; 111 | } 112 | 113 | @Override 114 | public boolean byte2proto(byte[] bytes, int index, int tailLength, StorageMode storageMode) { 115 | mData = new byte[getLength()]; 116 | if (isDataEnoughToParse(bytes, index, tailLength, mLength)) { //剩余数据不够解析 117 | XTLog.d("[LargeString] 剩余数据不够解析,直接退出!"); 118 | return false; 119 | } 120 | System.arraycopy(bytes, index, mData, 0, mData.length); 121 | return true; 122 | } 123 | 124 | @Override 125 | public int fillArrayLength(byte[] bytes, int index, StorageMode storageMode) { 126 | setLength(ConvertUtils.bytesToInt(storageMode, bytes, index, LARGE_ARRAY_LENGTH_SIZE)); //拿到长度 127 | return LARGE_ARRAY_LENGTH_SIZE; 128 | } 129 | 130 | @Override 131 | public String toString() { 132 | return "LargeString{" + 133 | "mLength=" + mLength + 134 | ", mData=" + getData() + 135 | '}'; 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/model/LongArray.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.model; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.xuexiang.xtcp.annotation.ProtocolField; 6 | import com.xuexiang.xtcp.enums.StorageMode; 7 | import com.xuexiang.xtcp.logs.XTLog; 8 | import com.xuexiang.xtcp.utils.ConvertUtils; 9 | 10 | import java.util.Arrays; 11 | 12 | import static com.xuexiang.xtcp.core.XTCPConstants.LONG_MAX_LENGTH; 13 | import static com.xuexiang.xtcp.core.XTCPConstants.MAX_ARRAY_LENGTH; 14 | 15 | /** 16 | * long数组协议项【无符号】
17 | *

18 | * mLength所占的byte位数为1,可表示的长度范围为【0~255】 19 | * 20 | * @author xuexiang 21 | * @since 2018/12/14 上午12:05 22 | */ 23 | public class LongArray extends AbstractArrayItem { 24 | 25 | /** 26 | * 集合数组的长度 27 | */ 28 | @ProtocolField(index = 0, length = DEFAULT_ARRAY_LENGTH_SIZE) 29 | private int mLength; 30 | 31 | /** 32 | * 集合数据 33 | */ 34 | @ProtocolField(index = 1) 35 | private long[] mData; 36 | 37 | /** 38 | * 获取long数组包装类 39 | * 40 | * @param data 41 | * @return 42 | */ 43 | public static LongArray wrap(@NonNull long[] data) { 44 | return new LongArray(data); 45 | } 46 | 47 | /** 48 | * 空的构造方法不能去除,用于反射构造 49 | */ 50 | public LongArray() { 51 | 52 | } 53 | 54 | public LongArray(@NonNull long[] data) { 55 | setData(data); 56 | } 57 | 58 | @Override 59 | public LongArray setLength(int length) { 60 | mLength = length; 61 | return this; 62 | } 63 | 64 | @Override 65 | public int getLength() { 66 | return mLength; 67 | } 68 | 69 | public long[] getData() { 70 | return mData; 71 | } 72 | 73 | public LongArray setData(@NonNull long[] data) { 74 | mData = data; 75 | if (mData.length > MAX_ARRAY_LENGTH) { //长度不能超过255 76 | XTLog.d("[LongArray] 数组长度溢出,需要进行截取处理..."); 77 | 78 | mData = new long[MAX_ARRAY_LENGTH]; 79 | System.arraycopy(data, 0, mData, 0, mData.length); 80 | } 81 | mLength = mData.length; 82 | return this; 83 | } 84 | 85 | @Override 86 | public boolean byte2proto(byte[] bytes, int index, int tailLength, StorageMode storageMode) { 87 | mData = new long[getLength()]; 88 | int dataFieldLength = getArrayFieldLength(FIELD_NAME_DATA, LONG_MAX_LENGTH); 89 | if (isDataEnoughToParse(bytes, index, tailLength, mLength * dataFieldLength)) { //剩余数据不够解析 90 | XTLog.d("[LongArray] 剩余数据不够解析,直接退出!"); 91 | return false; 92 | } 93 | for (int i = 0; i < mLength; i++) { 94 | mData[i] = ConvertUtils.bytesToLong(storageMode, bytes, index, dataFieldLength); 95 | index += dataFieldLength; 96 | } 97 | return true; 98 | } 99 | 100 | @Override 101 | public String toString() { 102 | return "LongArray{" + 103 | "mLength=" + mLength + 104 | ", mData=" + Arrays.toString(mData) + 105 | '}'; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/model/ShortArray.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.model; 2 | 3 | import static com.xuexiang.xtcp.core.XTCPConstants.MAX_ARRAY_LENGTH; 4 | import static com.xuexiang.xtcp.core.XTCPConstants.SHORT_MAX_LENGTH; 5 | 6 | import androidx.annotation.NonNull; 7 | 8 | import com.xuexiang.xtcp.annotation.ProtocolField; 9 | import com.xuexiang.xtcp.enums.StorageMode; 10 | import com.xuexiang.xtcp.logs.XTLog; 11 | import com.xuexiang.xtcp.utils.ConvertUtils; 12 | 13 | import java.util.Arrays; 14 | 15 | /** 16 | * short数组协议项【无符号】
17 | *

18 | * mLength所占的byte位数为1,可表示的长度范围为【0~255】 19 | * 20 | * @author xuexiang 21 | * @since 2018/12/13 下午6:25 22 | */ 23 | public class ShortArray extends AbstractArrayItem { 24 | 25 | /** 26 | * 集合数组的长度 27 | */ 28 | @ProtocolField(index = 0, length = DEFAULT_ARRAY_LENGTH_SIZE) 29 | private int mLength; 30 | 31 | /** 32 | * 集合数据 33 | */ 34 | @ProtocolField(index = 1) 35 | private short[] mData; 36 | 37 | /** 38 | * 获取short数组包装类 39 | * 40 | * @param data 41 | * @return 42 | */ 43 | public static ShortArray wrap(@NonNull short[] data) { 44 | return new ShortArray(data); 45 | } 46 | 47 | /** 48 | * 空的构造方法不能去除,用于反射构造 49 | */ 50 | public ShortArray() { 51 | 52 | } 53 | 54 | public ShortArray(@NonNull short[] data) { 55 | setData(data); 56 | } 57 | 58 | @Override 59 | public ShortArray setLength(int length) { 60 | mLength = length; 61 | return this; 62 | } 63 | 64 | @Override 65 | public int getLength() { 66 | return mLength; 67 | } 68 | 69 | public ShortArray setData(@NonNull short[] data) { 70 | mData = data; 71 | if (mData.length > MAX_ARRAY_LENGTH) { //长度不能超过255 72 | XTLog.d("[ShortArray] 数组长度溢出,需要进行截取处理..."); 73 | 74 | mData = new short[MAX_ARRAY_LENGTH]; 75 | System.arraycopy(data, 0, mData, 0, mData.length); 76 | } 77 | mLength = mData.length; 78 | return this; 79 | } 80 | 81 | public short[] getData() { 82 | return mData; 83 | } 84 | 85 | @Override 86 | public boolean byte2proto(byte[] bytes, int index, int tailLength, StorageMode storageMode) { 87 | mData = new short[getLength()]; 88 | int dataFieldLength = getArrayFieldLength(FIELD_NAME_DATA, SHORT_MAX_LENGTH); 89 | if (isDataEnoughToParse(bytes, index, tailLength, mLength * dataFieldLength)) { //剩余数据不够解析 90 | XTLog.d("[ShortArray] 剩余数据不够解析,直接退出!"); 91 | return false; 92 | } 93 | for (int i = 0; i < mLength; i++) { 94 | mData[i] = ConvertUtils.bytesToShort(storageMode, bytes, index, dataFieldLength); 95 | index += dataFieldLength; 96 | } 97 | return true; 98 | } 99 | 100 | @Override 101 | public String toString() { 102 | return "ShortArray{" + 103 | "mLength=" + mLength + 104 | ", mData=" + Arrays.toString(mData) + 105 | '}'; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/model/StringField.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.model; 2 | 3 | import static com.xuexiang.xtcp.core.XTCPConstants.MAX_ARRAY_LENGTH; 4 | 5 | import androidx.annotation.NonNull; 6 | 7 | import com.xuexiang.xtcp.annotation.ProtocolField; 8 | import com.xuexiang.xtcp.enums.StorageMode; 9 | import com.xuexiang.xtcp.logs.XTLog; 10 | 11 | import java.nio.charset.Charset; 12 | 13 | /** 14 | * String协议项
15 | *

16 | * mLength所占的byte位数为1,可表示的长度范围为【0~255】 17 | * 18 | * @author xuexiang 19 | * @since 2018/12/14 下午6:37 20 | */ 21 | public class StringField extends AbstractArrayItem { 22 | 23 | /** 24 | * 集合数组的长度 25 | */ 26 | @ProtocolField(index = 0, length = DEFAULT_ARRAY_LENGTH_SIZE) 27 | private int mLength; 28 | 29 | /** 30 | * 集合数据 31 | */ 32 | @ProtocolField(index = 1) 33 | private byte[] mData; 34 | 35 | /** 36 | * 获取String数组包装类 37 | * 38 | * @param data 39 | * @return 40 | */ 41 | public static StringField wrap(@NonNull String data) { 42 | return new StringField(data); 43 | } 44 | 45 | /** 46 | * 获取String数组包装类 47 | * 48 | * @param data 49 | * @return 50 | */ 51 | public static StringField wrap(@NonNull String data, String charset) { 52 | return new StringField(data, charset); 53 | } 54 | 55 | /** 56 | * 空的构造方法不能去除,用于反射构造 57 | */ 58 | public StringField() { 59 | 60 | } 61 | 62 | public StringField(@NonNull String data) { 63 | setData(data); 64 | } 65 | 66 | public StringField(@NonNull String data, String charset) { 67 | setData(data, charset); 68 | } 69 | 70 | public StringField setData(@NonNull String data) { 71 | mData = data.getBytes(); 72 | if (mData.length > MAX_ARRAY_LENGTH) { //长度不能超过255 73 | XTLog.d("[StringField] 数组长度溢出,需要进行截取处理..."); 74 | 75 | mData = new byte[MAX_ARRAY_LENGTH]; 76 | System.arraycopy(data.getBytes(), 0, mData, 0, mData.length); 77 | } 78 | mLength = mData.length; 79 | return this; 80 | } 81 | 82 | public StringField setData(@NonNull String data, String charset) { 83 | mData = data.getBytes(Charset.forName(charset)); 84 | if (mData.length > MAX_ARRAY_LENGTH) { //长度不能超过255 85 | XTLog.d("[StringField] 数组长度溢出,需要进行截取处理..."); 86 | 87 | mData = new byte[MAX_ARRAY_LENGTH]; 88 | System.arraycopy(data.getBytes(Charset.forName(charset)), 0, mData, 0, mData.length); 89 | } 90 | mLength = mData.length; 91 | return this; 92 | } 93 | 94 | public String getData() { 95 | return new String(mData); 96 | } 97 | 98 | public String getData(String charset) { 99 | return new String(mData, Charset.forName(charset)); 100 | } 101 | 102 | @Override 103 | public StringField setLength(int length) { 104 | mLength = length; 105 | return this; 106 | } 107 | 108 | @Override 109 | public int getLength() { 110 | return mLength; 111 | } 112 | 113 | @Override 114 | public boolean byte2proto(byte[] bytes, int index, int tailLength, StorageMode storageMode) { 115 | mData = new byte[getLength()]; 116 | if (isDataEnoughToParse(bytes, index, tailLength, mLength)) { //剩余数据不够解析 117 | XTLog.d("[StringField] 剩余数据不够解析,直接退出!"); 118 | return false; 119 | } 120 | System.arraycopy(bytes, index, mData, 0, mData.length); 121 | return true; 122 | } 123 | 124 | @Override 125 | public String toString() { 126 | return "StringField{" + 127 | "mLength=" + mLength + 128 | ", mData=" + getData() + 129 | '}'; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/model/XProtocolItem.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.model; 2 | 3 | import com.xuexiang.xtcp._XTCP; 4 | import com.xuexiang.xtcp.enums.StorageMode; 5 | import com.xuexiang.xtcp.model.IProtocolItem; 6 | 7 | /** 8 | * 默认提供的基础协议项,自定义协议可直接继承XProtocolItem 9 | * 10 | * @author xuexiang 11 | * @since 2018/12/12 下午1:39 12 | */ 13 | public class XProtocolItem implements IProtocolItem { 14 | 15 | @Override 16 | public int getProtocolLength() { 17 | return _XTCP.getIProtocolParser().getProtocolLength(this); 18 | } 19 | 20 | @Override 21 | public byte[] proto2byte(StorageMode storageMode) { 22 | return _XTCP.getIProtocolParser().protoBody2Byte(this, storageMode); 23 | } 24 | 25 | @Override 26 | public boolean byte2proto(byte[] bytes, int index, int tailLength, StorageMode storageMode) { 27 | return _XTCP.getIProtocolParser().byte2ProtoBody(this, bytes, index, tailLength, storageMode); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/parser/IProtocolParser.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.parser; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.xuexiang.xtcp.enums.StorageMode; 6 | 7 | /** 8 | * 协议解析器 9 | * 10 | * @author xuexiang 11 | * @since 2018/12/11 下午1:30 12 | */ 13 | public interface IProtocolParser { 14 | 15 | /** 16 | * 解析协议体获取协议的数据长度 17 | * 18 | * @param obj 19 | * @return 20 | */ 21 | int getProtocolLength(@NonNull Object obj); 22 | 23 | /** 24 | * 将协议体转为传输的Byte数组 25 | * 26 | * @param obj 消息对象, 类需要被@Protocol修饰 27 | * @param storageMode 数据的存储形式 28 | * @return 29 | */ 30 | byte[] protoBody2Byte(@NonNull Object obj, StorageMode storageMode); 31 | 32 | /** 33 | * 将传输的Byte数组解析为协议体 34 | * 35 | * @param obj 消息对象, 类需要被@Protocol修饰 36 | * @param bytes 数据集合[整个消息体数据,包含头和尾] 37 | * @param index 需要开始解析的索引 38 | * @param tailLength 消息尾的长度[和index一起决定了数据解析的范围] 39 | * @param storageMode 数据的存储形式 40 | */ 41 | boolean byte2ProtoBody(@NonNull Object obj, byte[] bytes, int index, int tailLength, StorageMode storageMode); 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/core/parser/impl/DefaultProtocolParser.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.core.parser.impl; 2 | 3 | import androidx.annotation.NonNull; 4 | 5 | import com.xuexiang.xtcp.core.parser.IProtocolParser; 6 | import com.xuexiang.xtcp.enums.StorageMode; 7 | import com.xuexiang.xtcp.logs.XTLog; 8 | import com.xuexiang.xtcp.utils.ParserUtils; 9 | 10 | /** 11 | * 默认的协议解析器 12 | * 13 | * @author xuexiang 14 | * @since 2018/12/11 下午2:20 15 | */ 16 | public class DefaultProtocolParser implements IProtocolParser { 17 | 18 | private static final String TAG = "DefaultProtocolParser"; 19 | 20 | @Override 21 | public int getProtocolLength(@NonNull Object obj) { 22 | try { 23 | return ParserUtils.calculateProtocolLength(obj); 24 | } catch (IllegalAccessException e) { 25 | e.printStackTrace(); 26 | XTLog.eTag(TAG, e); 27 | } 28 | return 0; 29 | } 30 | 31 | @Override 32 | public byte[] protoBody2Byte(@NonNull Object obj, StorageMode storageMode) { 33 | try { 34 | return ParserUtils.protoBody2Byte(obj, storageMode); 35 | } catch (IllegalAccessException e) { 36 | e.printStackTrace(); 37 | XTLog.eTag(TAG, e); 38 | } 39 | return new byte[0]; 40 | } 41 | 42 | @Override 43 | public boolean byte2ProtoBody(@NonNull Object obj, byte[] bytes, int index, int tailLength, StorageMode storageMode) { 44 | try { 45 | return ParserUtils.byte2ProtoBody(obj, bytes, index, tailLength, storageMode); 46 | } catch (IllegalAccessException e) { 47 | e.printStackTrace(); 48 | XTLog.eTag(TAG, e); 49 | } catch (InstantiationException e) { 50 | e.printStackTrace(); 51 | XTLog.eTag(TAG, e); 52 | } 53 | return false; 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/logs/ILogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.xuexiang.xtcp.logs; 18 | 19 | /** 20 | * 简易的日志接口 21 | * 22 | * @author xuexiang 23 | * @since 2018/6/29 下午7:57 24 | */ 25 | public interface ILogger { 26 | 27 | /** 28 | * 打印信息 29 | * 30 | * @param priority 优先级 31 | * @param tag 标签 32 | * @param message 信息 33 | * @param t 出错信息 34 | */ 35 | void log(int priority, String tag, String message, Throwable t); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/logs/LogcatLogger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.xuexiang.xtcp.logs; 18 | 19 | import androidx.annotation.NonNull; 20 | import android.util.Log; 21 | 22 | import java.io.PrintWriter; 23 | import java.io.StringWriter; 24 | 25 | /** 26 | * 默认Logcat日志记录 27 | * 28 | * @author xuexiang 29 | * @since 2018/6/29 下午7:57 30 | */ 31 | public class LogcatLogger implements ILogger { 32 | 33 | /** 34 | * logcat里日志的最大长度. 35 | */ 36 | private static final int MAX_LOG_LENGTH = 4000; 37 | 38 | /** 39 | * 打印信息 40 | * 41 | * @param priority 优先级 42 | * @param tag 标签 43 | * @param message 信息 44 | * @param t 出错信息 45 | */ 46 | @Override 47 | public void log(int priority, String tag, String message, Throwable t) { 48 | if (message != null && message.length() == 0) { 49 | message = null; 50 | } 51 | if (message == null) { 52 | if (t == null) { 53 | return; // Swallow message if it's null and there's no throwable. 54 | } 55 | message = getStackTraceString(t); 56 | } else { 57 | if (t != null) { 58 | message += "\n" + getStackTraceString(t); 59 | } 60 | } 61 | 62 | log(priority, tag, message); 63 | } 64 | 65 | private String getStackTraceString(Throwable t) { 66 | // Don't replace this with Log.getStackTraceString() - it hides 67 | // UnknownHostException, which is not what we want. 68 | StringWriter sw = new StringWriter(256); 69 | PrintWriter pw = new PrintWriter(sw, false); 70 | t.printStackTrace(pw); 71 | pw.flush(); 72 | return sw.toString(); 73 | } 74 | 75 | 76 | /** 77 | * 使用LogCat输出日志,字符长度超过4000则自动换行. 78 | * 79 | * @param priority 优先级 80 | * @param tag 标签 81 | * @param message 信息 82 | */ 83 | public void log(int priority, String tag, String message) { 84 | int subNum = message.length() / MAX_LOG_LENGTH; 85 | if (subNum > 0) { 86 | int index = 0; 87 | for (int i = 0; i < subNum; i++) { 88 | int lastIndex = index + MAX_LOG_LENGTH; 89 | String sub = message.substring(index, lastIndex); 90 | logSub(priority, tag, sub); 91 | index = lastIndex; 92 | } 93 | logSub(priority, tag, message.substring(index, message.length())); 94 | } else { 95 | logSub(priority, tag, message); 96 | } 97 | } 98 | 99 | 100 | /** 101 | * 使用LogCat输出日志. 102 | * 103 | * @param priority 优先级 104 | * @param tag 标签 105 | * @param sub 信息 106 | */ 107 | private void logSub(int priority, @NonNull String tag, @NonNull String sub) { 108 | switch (priority) { 109 | case Log.VERBOSE: 110 | Log.v(tag, sub); 111 | break; 112 | case Log.DEBUG: 113 | Log.d(tag, sub); 114 | break; 115 | case Log.INFO: 116 | Log.i(tag, sub); 117 | break; 118 | case Log.WARN: 119 | Log.w(tag, sub); 120 | break; 121 | case Log.ERROR: 122 | Log.e(tag, sub); 123 | break; 124 | case Log.ASSERT: 125 | Log.wtf(tag, sub); 126 | break; 127 | default: 128 | Log.v(tag, sub); 129 | break; 130 | } 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/logs/XTLog.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 xuexiangjys(xuexiangjys@163.com) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.xuexiang.xtcp.logs; 18 | 19 | import androidx.annotation.NonNull; 20 | import android.text.TextUtils; 21 | import android.util.Log; 22 | 23 | /** 24 | * XTCP日志打印 25 | * 26 | * @author xuexiang 27 | * @since 2018/6/29 下午7:58 28 | */ 29 | public final class XTLog { 30 | 31 | /** 32 | * Don't let anyone instantiate this class. 33 | */ 34 | private XTLog() { 35 | throw new UnsupportedOperationException("Do not need instantiate!"); 36 | } 37 | 38 | //==============常量================// 39 | /** 40 | * 默认tag 41 | */ 42 | public final static String DEFAULT_LOG_TAG = "[XTCP]"; 43 | /** 44 | * 最大日志优先级【日志优先级为最大等级,所有日志都不打印】 45 | */ 46 | private final static int MAX_LOG_PRIORITY = 10; 47 | /** 48 | * 最小日志优先级【日志优先级为最小等级,所有日志都打印】 49 | */ 50 | private final static int MIN_LOG_PRIORITY = 0; 51 | 52 | //==============属性================// 53 | /** 54 | * 默认的日志记录为Logcat 55 | */ 56 | private static ILogger sILogger = new LogcatLogger(); 57 | 58 | private static String sTag = DEFAULT_LOG_TAG; 59 | /** 60 | * 是否是调试模式 61 | */ 62 | private static boolean sIsDebug = false; 63 | /** 64 | * 日志打印优先级 65 | */ 66 | private static int sLogPriority = MAX_LOG_PRIORITY; 67 | 68 | //==============属性设置================// 69 | 70 | /** 71 | * 设置日志记录者的接口 72 | * 73 | * @param logger 74 | */ 75 | public static void setLogger(@NonNull ILogger logger) { 76 | sILogger = logger; 77 | } 78 | 79 | /** 80 | * 设置日志的tag 81 | * 82 | * @param tag 83 | */ 84 | public static void setTag(String tag) { 85 | sTag = tag; 86 | } 87 | 88 | /** 89 | * 设置是否是调试模式 90 | * 91 | * @param isDebug 92 | */ 93 | public static void setDebug(boolean isDebug) { 94 | sIsDebug = isDebug; 95 | } 96 | 97 | /** 98 | * 设置打印日志的等级(只打印改等级以上的日志) 99 | * 100 | * @param priority 101 | */ 102 | public static void setPriority(int priority) { 103 | sLogPriority = priority; 104 | } 105 | 106 | //===================对外接口=======================// 107 | 108 | /** 109 | * 设置是否打开调试 110 | * 111 | * @param isDebug 112 | */ 113 | public static void debug(boolean isDebug) { 114 | if (isDebug) { 115 | debug(DEFAULT_LOG_TAG); 116 | } else { 117 | debug(""); 118 | } 119 | } 120 | 121 | /** 122 | * 设置调试模式 123 | * 124 | * @param tag 125 | */ 126 | public static void debug(String tag) { 127 | if (!TextUtils.isEmpty(tag)) { 128 | setDebug(true); 129 | setPriority(MIN_LOG_PRIORITY); 130 | setTag(tag); 131 | } else { 132 | setDebug(false); 133 | setPriority(MAX_LOG_PRIORITY); 134 | setTag(""); 135 | } 136 | } 137 | 138 | //=============打印方法===============// 139 | 140 | /** 141 | * 打印任何(所有)信息 142 | * 143 | * @param msg 144 | */ 145 | public static void v(String msg) { 146 | if (enableLog(Log.VERBOSE)) { 147 | sILogger.log(Log.VERBOSE, sTag, msg, null); 148 | } 149 | } 150 | 151 | /** 152 | * 打印任何(所有)信息 153 | * 154 | * @param tag 155 | * @param msg 156 | */ 157 | public static void vTag(String tag, String msg) { 158 | if (enableLog(Log.VERBOSE)) { 159 | sILogger.log(Log.VERBOSE, tag, msg, null); 160 | } 161 | } 162 | 163 | /** 164 | * 打印调试信息 165 | * 166 | * @param msg 167 | */ 168 | public static void d(String msg) { 169 | if (enableLog(Log.DEBUG)) { 170 | sILogger.log(Log.DEBUG, sTag, msg, null); 171 | } 172 | } 173 | 174 | /** 175 | * 打印调试信息 176 | * 177 | * @param tag 178 | * @param msg 179 | */ 180 | public static void dTag(String tag, String msg) { 181 | if (enableLog(Log.DEBUG)) { 182 | sILogger.log(Log.DEBUG, tag, msg, null); 183 | } 184 | } 185 | 186 | /** 187 | * 打印提示性的信息 188 | * 189 | * @param msg 190 | */ 191 | public static void i(String msg) { 192 | if (enableLog(Log.INFO)) { 193 | sILogger.log(Log.INFO, sTag, msg, null); 194 | } 195 | } 196 | 197 | /** 198 | * 打印提示性的信息 199 | * 200 | * @param tag 201 | * @param msg 202 | */ 203 | public static void iTag(String tag, String msg) { 204 | if (enableLog(Log.INFO)) { 205 | sILogger.log(Log.INFO, tag, msg, null); 206 | } 207 | } 208 | 209 | /** 210 | * 打印warning警告信息 211 | * 212 | * @param msg 213 | */ 214 | public static void w(String msg) { 215 | if (enableLog(Log.WARN)) { 216 | sILogger.log(Log.WARN, sTag, msg, null); 217 | } 218 | } 219 | 220 | /** 221 | * 打印warning警告信息 222 | * 223 | * @param tag 224 | * @param msg 225 | */ 226 | public static void wTag(String tag, String msg) { 227 | if (enableLog(Log.WARN)) { 228 | sILogger.log(Log.WARN, tag, msg, null); 229 | } 230 | } 231 | 232 | /** 233 | * 打印出错信息 234 | * 235 | * @param msg 236 | */ 237 | public static void e(String msg) { 238 | if (enableLog(Log.ERROR)) { 239 | sILogger.log(Log.ERROR, sTag, msg, null); 240 | } 241 | } 242 | 243 | /** 244 | * 打印出错信息 245 | * 246 | * @param tag 247 | * @param msg 248 | */ 249 | public static void eTag(String tag, String msg) { 250 | if (enableLog(Log.ERROR)) { 251 | sILogger.log(Log.ERROR, tag, msg, null); 252 | } 253 | } 254 | 255 | /** 256 | * 打印出错堆栈信息 257 | * 258 | * @param t 259 | */ 260 | public static void e(Throwable t) { 261 | if (enableLog(Log.ERROR)) { 262 | sILogger.log(Log.ERROR, sTag, null, t); 263 | } 264 | } 265 | 266 | /** 267 | * 打印出错堆栈信息 268 | * 269 | * @param tag 270 | * @param t 271 | */ 272 | public static void eTag(String tag, Throwable t) { 273 | if (enableLog(Log.ERROR)) { 274 | sILogger.log(Log.ERROR, tag, null, t); 275 | } 276 | } 277 | 278 | 279 | /** 280 | * 打印出错堆栈信息 281 | * 282 | * @param msg 283 | * @param t 284 | */ 285 | public static void e(String msg, Throwable t) { 286 | if (enableLog(Log.ERROR)) { 287 | sILogger.log(Log.ERROR, sTag, msg, t); 288 | } 289 | } 290 | 291 | /** 292 | * 打印出错堆栈信息 293 | * 294 | * @param tag 295 | * @param msg 296 | * @param t 297 | */ 298 | public static void eTag(String tag, String msg, Throwable t) { 299 | if (enableLog(Log.ERROR)) { 300 | sILogger.log(Log.ERROR, tag, msg, t); 301 | } 302 | } 303 | 304 | /** 305 | * 打印严重的错误信息 306 | * 307 | * @param msg 308 | */ 309 | public static void wtf(String msg) { 310 | if (enableLog(Log.ASSERT)) { 311 | sILogger.log(Log.ASSERT, sTag, msg, null); 312 | } 313 | } 314 | 315 | /** 316 | * 打印严重的错误信息 317 | * 318 | * @param tag 319 | * @param msg 320 | */ 321 | public static void wtfTag(String tag, String msg) { 322 | if (enableLog(Log.ASSERT)) { 323 | sILogger.log(Log.ASSERT, tag, msg, null); 324 | } 325 | } 326 | 327 | /** 328 | * 能否打印 329 | * 330 | * @param logPriority 331 | * @return 332 | */ 333 | private static boolean enableLog(int logPriority) { 334 | return sILogger != null && sIsDebug && logPriority >= sLogPriority; 335 | } 336 | } 337 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/utils/BCDUtils.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.utils; 2 | 3 | import java.text.DateFormat; 4 | import java.text.ParseException; 5 | import java.text.SimpleDateFormat; 6 | import java.util.Date; 7 | 8 | /** 9 | * BCD编码工具类 10 | * 11 | * @author XUE 12 | * @since 2019/3/6 8:50 13 | */ 14 | public final class BCDUtils { 15 | 16 | private BCDUtils() { 17 | throw new UnsupportedOperationException("u can't instantiate me..."); 18 | } 19 | 20 | /** 21 | * BCD转String 22 | * 23 | * @param bcd 24 | * @return 25 | */ 26 | public static String bcd2String(byte[] bcd) { 27 | StringBuilder res = new StringBuilder(); 28 | if (null == bcd) { 29 | return res.toString(); 30 | } 31 | for (int b : bcd) { 32 | if (b < 0) { 33 | b = 256 + b; 34 | } 35 | b = b / 16 * 10 + b % 16; 36 | if (b < 10) { 37 | res.append("0").append(b); 38 | } else { 39 | res.append(b); 40 | } 41 | } 42 | return res.toString(); 43 | } 44 | 45 | /** 46 | * BCD转int 47 | * 48 | * @param bcd 49 | * @return 50 | */ 51 | public static int bcd2Int(byte[] bcd) { 52 | return toInt(bcd2String(bcd), 0); 53 | } 54 | 55 | /** 56 | * BCD转double 57 | * 58 | * @param bcd BCD编码 59 | * @param format double的格式【例如:XXX.XX】 60 | * @return 61 | */ 62 | public static double bcd2Double(byte[] bcd, String format) { 63 | int point = getPointLength(format); 64 | return (bcd2Int(bcd) / (Math.pow(10, point))); 65 | } 66 | 67 | /** 68 | * BCD转float 69 | * 70 | * @param bcd BCD编码 71 | * @param format float的格式【例如:XXX.XX】 72 | * @return 73 | */ 74 | public static float bcd2Float(byte[] bcd, String format) { 75 | int point = getPointLength(format); 76 | return (float) (bcd2Int(bcd) / (Math.pow(10, point))); 77 | } 78 | 79 | /** 80 | * BCD转日期 81 | * 82 | * @param bcd 83 | * @return 84 | */ 85 | public static Date bcd2Date(byte[] bcd, String sFormat) { 86 | if (bcd == null) { 87 | return new Date(); 88 | } 89 | return string2Date(bcd2String(bcd), sFormat); 90 | } 91 | 92 | //==================================// 93 | /** 94 | * String转BCD 95 | * 96 | * @param sIn 字符串 97 | * @param formatIn BCD格式 98 | * @return 99 | */ 100 | public static byte[] string2Bcd(String sIn, String formatIn) { 101 | if (sIn == null) { 102 | return null; 103 | } 104 | StringBuilder s = formatString(sIn); 105 | StringBuilder format = formatString(formatIn); 106 | 107 | byte[] res = null; 108 | try { 109 | int len = s.length(); 110 | if (len % 2 != 0) { 111 | s.insert(0, "0"); 112 | } 113 | if (format.length() % 2 != 0) { 114 | format.insert(0, "0"); 115 | } 116 | int allLength = format.length() / 2; 117 | len = s.length(); 118 | byte[] resValue = new byte[len / 2]; 119 | for (int i = 0; i <= len - 2; i += 2) { 120 | resValue[i / 2] = (byte) ((byte) (s.charAt(i) - '0') * 16 + (byte) (s.charAt(i + 1) - '0')); 121 | } 122 | res = new byte[allLength]; 123 | if (allLength >= resValue.length) { 124 | System.arraycopy(resValue, 0, res, res.length - resValue.length, resValue.length); 125 | } 126 | } catch (Exception e) { 127 | e.printStackTrace(); 128 | } 129 | return res; 130 | } 131 | 132 | public static byte[] string2Bcd(String sIn) { 133 | return string2Bcd(sIn, sIn); 134 | } 135 | 136 | /** 137 | * int转bcd 138 | * 139 | * @param value 值 140 | * @param format bcd格式 141 | * @return 142 | */ 143 | public static byte[] int2Bcd(int value, String format) { 144 | return string2Bcd(String.valueOf(value), format); 145 | } 146 | 147 | /** 148 | * double转bcd 149 | * 150 | * @param value 值 151 | * @param format bcd格式 152 | * @return 153 | */ 154 | public static byte[] double2Bcd(double value, String format) { 155 | int point = getPointLength(format); 156 | return int2Bcd((int) (value * Math.pow(10, point)), format); 157 | } 158 | 159 | /** 160 | * float转bcd 161 | * 162 | * @param value 值 163 | * @param format bcd格式 164 | * @return 165 | */ 166 | public static byte[] float2Bcd(float value, String format) { 167 | int point = getPointLength(format); 168 | return int2Bcd((int) (value * (int)Math.pow(10, point)), format); 169 | } 170 | 171 | /** 172 | * 日期转BCD 173 | * 174 | * @param date 175 | * @param format BCD格式 176 | * @return 177 | */ 178 | public static byte[] date2Bcd(Date date, String format) { 179 | String sDate = date2String(date, format); 180 | format = format.replaceAll("[^a-zA-Z0-9]*", ""); 181 | sDate = sDate.replaceAll("[^a-zA-Z0-9]*", ""); 182 | return string2Bcd(sDate, format); 183 | } 184 | 185 | //=========================================// 186 | /** 187 | * 获取小数点的位数 188 | * 189 | * @param format 190 | * @return 191 | */ 192 | private static int getPointLength(String format) { 193 | int index = format.indexOf("."); 194 | if (index >= 0) { 195 | index++; 196 | } else { 197 | index = format.length(); 198 | } 199 | //得到小数点的位数 200 | return format.length() - index; 201 | } 202 | 203 | /** 204 | * String转Int(防止崩溃) 205 | * 206 | * @param value 207 | * @param defValue 默认值 208 | * @return 209 | */ 210 | private static int toInt(final String value, final int defValue) { 211 | try { 212 | return Integer.parseInt(value); 213 | } catch (NumberFormatException e) { 214 | e.printStackTrace(); 215 | } 216 | return defValue; 217 | } 218 | 219 | /** 220 | * 日期转为字符串 221 | * 222 | * @param date 223 | * @return 224 | */ 225 | public static String date2String(Date date, String type) { 226 | return date2String(date, new SimpleDateFormat(type)); 227 | } 228 | 229 | /** 230 | * 将 Date 类型转为时间字符串 231 | *

格式为 format

232 | * 233 | * @param date Date 类型时间 234 | * @param format 时间格式 235 | * @return 时间字符串 236 | */ 237 | private static String date2String(final Date date, final DateFormat format) { 238 | if (date != null && format != null) { 239 | return format.format(date); 240 | } else { 241 | return ""; 242 | } 243 | } 244 | 245 | /** 246 | * 字符串转日期 247 | * 248 | * @param str 249 | * @param sFormat 250 | * @return 251 | */ 252 | private static Date string2Date(String str, String sFormat) { 253 | if (str == null) { 254 | return null; 255 | } 256 | StringBuilder sFormat2Parse = new StringBuilder(); 257 | StringBuilder s2Parse = new StringBuilder(); 258 | // 去掉格式化的字符 259 | for (int i = 0; i < sFormat.length(); i++) { 260 | char c = sFormat.charAt(i); 261 | if (((c >= 'A') && (c <= 'Z')) || ((c >= 'a') && (c <= 'z'))) { 262 | sFormat2Parse.append(c); 263 | } 264 | } 265 | for (int i = 0; i < str.length(); i++) { 266 | char c = str.charAt(i); 267 | if ((c >= '0') && (c <= '9')) { 268 | s2Parse.append(c); 269 | } 270 | } 271 | // 去掉内容的字符 272 | SimpleDateFormat format = new SimpleDateFormat(sFormat2Parse.toString()); 273 | Date date = null; 274 | try { 275 | date = format.parse(s2Parse.toString()); 276 | } catch (ParseException e) { 277 | e.printStackTrace(); 278 | } catch (Exception e) { 279 | e.printStackTrace(); 280 | } 281 | return date; 282 | } 283 | 284 | /** 285 | * 去掉格式化的字符 286 | * @param sIn 287 | * @return 288 | */ 289 | private static StringBuilder formatString(String sIn) { 290 | StringBuilder s = new StringBuilder(); 291 | for (int i = 0; i < sIn.length(); i++) { 292 | char c = sIn.charAt(i); 293 | if (((c >= '0') && (c <= '9')) || ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'))) { 294 | s.append(c); 295 | } 296 | } 297 | return s; 298 | } 299 | 300 | public static void main(String[] args) { 301 | 302 | // int i = 12345; 303 | // byte[] bcd = int2Bcd(i, "XXXXX"); 304 | // System.out.println(Arrays.toString(bcd)); 305 | // System.out.println(bcd2int(bcd)); 306 | 307 | // double i = 123.45; 308 | // byte[] bcd = double2bcd(i, "AAA.AA"); 309 | // System.out.println(Arrays.toString(bcd)); 310 | // System.out.println(bcd2double(bcd, "AAA.AA")); 311 | 312 | // String format = "yy-MM-dd-HH-mm-ss"; 313 | // Date dOld = new Date(); 314 | // dOld.setMinutes(9); 315 | // String s = date2String(dOld, format); 316 | // System.out.println("date2String : " + s); 317 | // Date d = string2Date(s, format); 318 | // System.out.println("string2Date : " + d); 319 | // byte[] bcd = date2Bcd(d, format);// string2Bcd(s, format); 320 | // System.out.println(Arrays.toString(bcd)); 321 | // System.out.println(bcd2String(bcd)); 322 | // System.out.println(bcd2Date(bcd, format)); 323 | 324 | 325 | float value = 12345.67F; 326 | System.out.println(value * Math.pow(10, 2)); 327 | 328 | double value1 = 12345.67; 329 | System.out.println(value1 * Math.pow(10, 2)); 330 | 331 | } 332 | 333 | } 334 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/java/com/xuexiang/xtcp/utils/MessageUtils.java: -------------------------------------------------------------------------------- 1 | package com.xuexiang.xtcp.utils; 2 | 3 | import androidx.annotation.NonNull; 4 | import androidx.annotation.Nullable; 5 | 6 | import com.xuexiang.xtcp._XTCP; 7 | import com.xuexiang.xtcp.core.message.IMessage; 8 | import com.xuexiang.xtcp.enums.StorageMode; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | import static com.xuexiang.xtcp.core.message.MessageConstants.DEFAULT_FRAME_END; 14 | import static com.xuexiang.xtcp.core.message.MessageConstants.DEFAULT_FRAME_HEAD; 15 | 16 | /** 17 | * 消息工具类 18 | * 19 | * @author xuexiang 20 | * @since 2018/12/17 上午10:11 21 | */ 22 | public final class MessageUtils { 23 | 24 | private MessageUtils() { 25 | throw new UnsupportedOperationException("u can't instantiate me..."); 26 | } 27 | 28 | //==============分包==================// 29 | /** 30 | * 解析消息包数据 31 | * 32 | * @param cls 消息体类 33 | * @param data 数据 34 | * @return 已解析的消息集合 35 | */ 36 | public static List parseMessageBytes(Class cls, @NonNull byte[] data) { 37 | try { 38 | return parseMessageBytes(cls, data, _XTCP.getDefaultStorageMode()); 39 | } catch (IllegalAccessException e) { 40 | e.printStackTrace(); 41 | } catch (InstantiationException e) { 42 | e.printStackTrace(); 43 | } 44 | return null; 45 | } 46 | 47 | /** 48 | * 解析消息包数据 49 | * 50 | * @param cls 消息体类 51 | * @param data 数据 52 | * @param mode 存储方式 53 | * @return 已解析的消息集合 54 | */ 55 | public static List parseMessageBytes(Class cls, @NonNull byte[] data, StorageMode mode) throws IllegalAccessException, InstantiationException { 56 | IMessage message = cls.newInstance(); 57 | List packages = getSubPackage(data, message.getMinMessageLength()); 58 | if (packages == null || packages.size() == 0) { 59 | return null; 60 | } 61 | 62 | List result = new ArrayList<>(); 63 | for (int i = 0; i < packages.size(); i++) { 64 | message = cls.newInstance(); 65 | if (message.byte2Msg(packages.get(i), mode)) { 66 | result.add(message); 67 | } 68 | } 69 | return result; 70 | } 71 | 72 | 73 | /** 74 | * 获取默认消息模版的分包 75 | * 76 | * @param data 77 | * @param minMessageLength 最小消息长度 78 | * @return 79 | */ 80 | @Nullable 81 | public static List getSubPackage(@NonNull byte[] data, int minMessageLength) { 82 | if (data.length < minMessageLength) { 83 | return null; 84 | } 85 | 86 | //头索引 87 | List headIndex = new ArrayList<>(); 88 | //尾索引 89 | List endIndex = new ArrayList<>(); 90 | 91 | for (int i = 0; i < data.length - 1; i++) { 92 | if (data[i] == DEFAULT_FRAME_HEAD[0] && data[i + 1] == DEFAULT_FRAME_HEAD[1]) { 93 | headIndex.add(i); 94 | } else if (data[i] == DEFAULT_FRAME_END[0] && data[i + 1] == DEFAULT_FRAME_END[1]) { 95 | endIndex.add(i); 96 | } 97 | } 98 | 99 | int size = Math.min(headIndex.size(), endIndex.size()); 100 | if (size == 0) { 101 | return null; 102 | } 103 | 104 | List packages = new ArrayList<>(); 105 | byte[] tmp; 106 | for (int i = 0; i < size; i++) { 107 | tmp = new byte[endIndex.get(i) - headIndex.get(i) + 2]; 108 | System.arraycopy(data, headIndex.get(i), tmp, 0, tmp.length); 109 | packages.add(tmp); 110 | } 111 | return packages; 112 | } 113 | 114 | //==============校验==================// 115 | 116 | /** 117 | * 计算校验和【简单地对数据内容进行相加】 118 | * 119 | * @param dataBytes 数据内容 120 | * @return 121 | */ 122 | public static short calculateChecksum(byte[] dataBytes) { 123 | if (dataBytes == null || dataBytes.length == 0) return 0; 124 | 125 | short checkSum = 0; 126 | for (byte dataItem : dataBytes) { 127 | checkSum += ConvertUtils.byteToUnSigned(dataItem); 128 | } 129 | return checkSum; 130 | } 131 | 132 | /** 133 | * 校验消息是否正确 134 | * 135 | * @param minMessageLength 数据包的最小长度 136 | * @param checksumIndex 校验和所在的索引 137 | * @param dataIndex 数据所在的索引 138 | * @param messageData 整个消息体数据 139 | * @param storageMode 存储方式 140 | * @return 141 | */ 142 | public static boolean verifyMessage(int minMessageLength, int checksumIndex, int dataIndex, byte[] messageData, StorageMode storageMode) { 143 | return verifyMessageLength(minMessageLength, messageData, storageMode) 144 | && verifyChecksum(minMessageLength, checksumIndex, dataIndex, messageData, storageMode); 145 | } 146 | 147 | /** 148 | * 验证消息长度 149 | * 150 | * @param minMessageLength 数据包的最小长度 151 | * @param messageData 整个消息体 152 | * @param storageMode 存储方式 153 | * @return 消息长度是否正确 154 | */ 155 | public static boolean verifyMessageLength(int minMessageLength, byte[] messageData, StorageMode storageMode) { 156 | //头尾占4位byte,第三位是帧长度 157 | return messageData.length >= minMessageLength 158 | && (messageData.length - 4) == ConvertUtils.bytesToShort(storageMode, messageData, 2); 159 | } 160 | 161 | /** 162 | * 检查校验和 163 | * 164 | * @param minMessageLength 数据包的最小长度 165 | * @param checksumIndex 校验和所在的索引 166 | * @param dataIndex 数据所在的索引 167 | * @param messageData 整个消息体 168 | * @param storageMode 存储方式 169 | * @return 校验和是否通过 170 | */ 171 | public static boolean verifyChecksum(int minMessageLength, int checksumIndex, int dataIndex, byte[] messageData, StorageMode storageMode) { 172 | if (messageData.length < minMessageLength) return false; 173 | 174 | short checkSum = ConvertUtils.bytesToShort(storageMode, messageData, checksumIndex); //读取校验和 175 | //默认约定,读取到的校验和为0的话,视为不对校验和进行校验 176 | return checkSum == 0 || checkSum == calculatePackageMessageChecksum(minMessageLength, dataIndex, messageData); 177 | } 178 | 179 | 180 | /** 181 | * 计算消息包的校验和 182 | * 183 | * @param minMessageLength 数据包的最小长度 184 | * @param dataIndex 数据所在的索引 185 | * @param messageData 整个消息体 186 | * @return 校验和 187 | */ 188 | public static short calculatePackageMessageChecksum(int minMessageLength, int dataIndex, @NonNull byte[] messageData) { 189 | if (messageData.length <= minMessageLength) return 0; 190 | 191 | short checkSum = 0; 192 | for (int index = dataIndex; index < messageData.length - 2; index++) { 193 | checkSum += ConvertUtils.byteToUnSigned(messageData[index]); 194 | } 195 | return checkSum; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /xtcp_runtime/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | xtcp_runtime 3 | 4 | --------------------------------------------------------------------------------