Commit aa13fe39 authored by 王雷's avatar 王雷

first init

parents
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
# Default ignored files
/shelf/
/workspace.xml
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$USER_HOME$/gradle/gradle-7.3" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
<option value="$PROJECT_DIR$/websocket" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true">
<option name="TOP_LEVEL_CLASS_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="INNER_CLASS_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="METHOD_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
</value>
</option>
<option name="FIELD_OPTIONS">
<value>
<option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
<option name="REQUIRED_TAGS" value="" />
</value>
</option>
<option name="IGNORE_DEPRECATED" value="false" />
<option name="IGNORE_JAVADOC_PERIOD" value="true" />
<option name="IGNORE_DUPLICATED_THROWS" value="false" />
<option name="IGNORE_POINT_TO_ITSELF" value="false" />
<option name="myAdditionalJavadocTags" value="date" />
</inspection_tool>
</profile>
</component>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="Android Studio default JDK" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
\ No newline at end of file
/build
\ No newline at end of file
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
compileSdk 31
defaultConfig {
applicationId "com.example.websocket2"
minSdk 21
targetSdk 31
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation project(path: ':websocket')
//implementation project(path: ':websocket2')
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.6.10"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
implementation 'com.blankj:utilcodex:1.31.0'
}
\ No newline at end of file
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
package com.example.websocket2
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.example.websocket2", appContext.packageName)
}
}
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.websocket2">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.WebSocket2">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
\ No newline at end of file
package com.example.websocket2
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
lifecycle.addObserver(WebSocketClientService(this))
}
}
\ No newline at end of file
package com.example.websocket2
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import com.dhh.websocket.Config
import com.dhh.websocket.RxWebSocket
import com.dhh.websocket.WebSocketSubscriber
import java.util.concurrent.TimeUnit
/**
* @author: leiwang
* @time: 2022/3/24 00:15
* @desc:
*/
class SocketHelper : LifecycleObserver {
private val mSocketAddress = ""
private var mObserver: WebSocketSubscriber? = null
fun startSocket() {
mObserver = object : WebSocketSubscriber() {
override fun onError(e: Throwable) {
super.onError(e)
}
override fun onOpen(webSocket: okhttp4.WebSocket) {
super.onOpen(webSocket)
}
override fun onMessage(text: String) {
super.onMessage(text)
}
override fun onMessage(byteString: okio.ByteString) {
super.onMessage(byteString)
}
override fun onClose() {
super.onClose()
}
}
val config = Config.Builder()
.setShowLog(true) //show log
//.setClient(yourClient) //if you want to set your okhttpClient
.setReconnectInterval(2, TimeUnit.SECONDS) //set reconnect interval
// .setSSLSocketFactory(yourSSlSocketFactory, yourX509TrustManager) // wss support
.build()
RxWebSocket.setConfig(config)
RxWebSocket.get("")
.subscribe(mObserver!!)
}
fun cancelSocket() {
mObserver?.dispose()
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
fun onDestroy() {
mObserver?.dispose()
}
//发送socket消息
fun sendSocketMessage(message: String) {
RxWebSocket.send(mSocketAddress, message)
}
}
\ No newline at end of file
package com.example.websocket2
/**
* @author: 高英祥
* @github:
* @time: 2021/7/8 14:24
* @desc:
* @doc:
*/
class SocketReq {
// 消息版本号,必传,默认 1.0
public var version:String? = null
// 设备编号
public var deviceId:String? = null
// 消息tag, 非必传
public var tags:MutableList<String>? = null
// 客户端 -> 目标系统消息传递,目标系统 tag
public var toSystem:String? = null
// 消息类型:type = 1000,登录消息,ack消息:type = 1001
public var type:String? = null
// 消费消息的标识
public var routingKey:String? = null
public var commitSeqId:String? = null
// 消息操作类型,0-消费所有, 1-只消费当前登陆后的消息
public var opType:Int? = null
}
\ No newline at end of file
package com.example.websocket2
import android.content.Context
import android.util.Log
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import com.blankj.utilcode.util.GsonUtils
import com.dhh.websocket.Config
import com.dhh.websocket.RxWebSocket
import com.dhh.websocket.RxWebSocketUtil
import com.dhh.websocket.WebSocketSubscriber
import kotlinx.coroutines.*
import okhttp3.OkHttpClient
import okhttp3.WebSocket
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
/**
* @author: 高英祥
* @github:
* @time: 2022/3/11 17:36
* @desc:
* @doc:
*/
class WebSocketClientService(
private val ctx: Context,
) : LifecycleObserver {
//最后一次收到消息的时间
private var mLastReceiveMessageTime: Long = 0
private var mSocketAddress1 = "wss://sockets.qmai.cn/transport"
private var mObserver: WebSocketSubscriber? = null
private var mSocketStatus: Boolean = false
private val job = Job()
private val mSendMsgScope =
CoroutineScope(job + Executors.newSingleThreadExecutor().asCoroutineDispatcher())
//socket正在重连
private var mSocketIsReconnect = false
companion object {
//ping 的内容
private const val TAG = "WebSocketClientService"
}
init {
//ACK 消息帮助类
//删除历史记录
mObserver = object : WebSocketSubscriber() {
override fun onError(e: Throwable) {
super.onError(e)
mSocketStatus = false
Log.d(TAG, "onError: e = $e")
}
override fun onReconnect(preSocketErrorMsg: String) {
super.onReconnect(preSocketErrorMsg)
mSocketIsReconnect = true
Log.d(TAG, "onReconnect: preSocketErrorMsg = $preSocketErrorMsg")
}
override fun onOpen(webSocket: WebSocket) {
super.onOpen(webSocket)
mSocketStatus = true
mSocketIsReconnect = false
val currentWebSocketHashCode =
RxWebSocketUtil.getInstance().webSocketMap.get(mSocketAddress1).hashCode()
Log.d(
"webSocketCode",
"onOpen map hashcode = $currentWebSocketHashCode new webScoket hashCode = ${webSocket.hashCode()}"
)
var status = false
//连接成功发送登录通知
status = RxWebSocket.send(mSocketAddress1,getLoginSocketReq())
// status = ackMessageHelper.sendLogin()
Log.d(
TAG,
"onOpen: send login info status = $status thread = ${Thread.currentThread().id} ${Thread.currentThread().name}"
)
if (status) {
RxWebSocket.send(mSocketAddress1,"ping")
} else {
}
}
override fun onMessage(text: String) {
super.onMessage(text)
startCutDown()
mLastReceiveMessageTime = System.currentTimeMillis()
when (text) {
"ping" -> {
mSendMsgScope.launch {
//延时2s发送消息
delay(2000)
val status = RxWebSocket.send(mSocketAddress1,"pong")
Log.d(TAG, "onMessage: send ping status = $status")
}
}
"pong" -> {
//刷新排队
mSendMsgScope.launch {
//延时2s发送消息
delay(2000)
val status = RxWebSocket.send(mSocketAddress1,"ping")
Log.d(TAG, "onMessage: send ping status = $status")
}
}
else -> {
}
}
Log.d(TAG, "onMessage: $text")
}
override fun onClose() {
//this@WebSocketClientService.logHelper.upLoadWebSocketState("onClose")
super.onClose()
Log.d(TAG, "onMessage: onClose")
}
}
val config = Config.Builder()
.setShowLog(true) //show log
.setClient(
//设置超时时间 3s
OkHttpClient.Builder()
.readTimeout(3, TimeUnit.SECONDS)
.connectTimeout(3, TimeUnit.SECONDS)
.writeTimeout(3, TimeUnit.SECONDS)
.build()
)
.setReconnectInterval(2, TimeUnit.SECONDS) //set reconnect interval
// .setSSLSocketFactory(yourSSlSocketFactory, yourX509TrustManager) // wss support
.build()
RxWebSocket.setConfig(config)
RxWebSocket.get(mSocketAddress1)
.subscribe(mObserver!!)
}
@OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
private fun onCreate() {
//注册
}
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
private fun destroy() = destroyWebSocket()
/**
* 销毁 WebSocket
*/
private fun destroyWebSocket() {
mObserver?.dispose()
//ping 记录清空
//销毁
}
private var mPongTimerJob: Job? = null
// region 处理socket 事物方法
/**
* 开启倒计时
*/
@OptIn(InternalCoroutinesApi::class)
private fun startCutDown() {
mPongTimerJob?.cancel()
mPongTimerJob = GlobalScope.launch(Dispatchers.IO) {
delay(30000)
Log.d(TAG, "startCutDown: 30 秒未收到服务器 pong 已离线")
}
}
fun getLoginSocketReq(): String = GsonUtils.toJson(SocketReq().apply {
version = "1.0"
deviceId = "b93af0aa527a82eb"
tags = getDeviceTags()
toSystem = "socket-center-message"
type = "1000"
})
/**
* 获取Tags[] 为了标识设备用的
*/
private fun getDeviceTags(): MutableList<String> =
mutableListOf("pos-205387_47025")
//endregion
}
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
</vector>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>
\ No newline at end of file
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.WebSocket2" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_200</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/black</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_200</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>
\ No newline at end of file
<resources>
<string name="app_name">WebSocket2</string>
</resources>
\ No newline at end of file
<resources xmlns:tools="http://schemas.android.com/tools">
<!-- Base application theme. -->
<style name="Theme.WebSocket2" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
<!-- Primary brand color. -->
<item name="colorPrimary">@color/purple_500</item>
<item name="colorPrimaryVariant">@color/purple_700</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/teal_200</item>
<item name="colorSecondaryVariant">@color/teal_700</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
</style>
</resources>
\ No newline at end of file
package com.example.websocket2
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}
\ No newline at end of file
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '7.1.2' apply false
id 'com.android.library' version '7.1.2' apply false
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
}
task clean(type: Delete) {
delete rootProject.buildDir
}
\ No newline at end of file
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true
\ No newline at end of file
#Wed Mar 23 23:34:20 CST 2022
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
#!/usr/bin/env sh
#
# Copyright 2015 the original author or authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
##############################################################################
##
## Gradle start up script for UN*X
##
##############################################################################
# Attempt to set APP_HOME
# Resolve links: $0 may be a link
PRG="$0"
# Need this for relative symlinks.
while [ -h "$PRG" ] ; do
ls=`ls -ld "$PRG"`
link=`expr "$ls" : '.*-> \(.*\)$'`
if expr "$link" : '/.*' > /dev/null; then
PRG="$link"
else
PRG=`dirname "$PRG"`"/$link"
fi
done
SAVED="`pwd`"
cd "`dirname \"$PRG\"`/" >/dev/null
APP_HOME="`pwd -P`"
cd "$SAVED" >/dev/null
APP_NAME="Gradle"
APP_BASE_NAME=`basename "$0"`
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD="maximum"
warn () {
echo "$*"
}
die () {
echo
echo "$*"
echo
exit 1
}
# OS specific support (must be 'true' or 'false').
cygwin=false
msys=false
darwin=false
nonstop=false
case "`uname`" in
CYGWIN* )
cygwin=true
;;
Darwin* )
darwin=true
;;
MINGW* )
msys=true
;;
NONSTOP* )
nonstop=true
;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
# IBM's JDK on AIX uses strange locations for the executables
JAVACMD="$JAVA_HOME/jre/sh/java"
else
JAVACMD="$JAVA_HOME/bin/java"
fi
if [ ! -x "$JAVACMD" ] ; then
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
else
JAVACMD="java"
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
Please set the JAVA_HOME variable in your environment to match the
location of your Java installation."
fi
# Increase the maximum file descriptors if we can.
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
MAX_FD_LIMIT=`ulimit -H -n`
if [ $? -eq 0 ] ; then
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
MAX_FD="$MAX_FD_LIMIT"
fi
ulimit -n $MAX_FD
if [ $? -ne 0 ] ; then
warn "Could not set maximum file descriptor limit: $MAX_FD"
fi
else
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
fi
fi
# For Darwin, add options to specify how the application appears in the dock
if $darwin; then
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
JAVACMD=`cygpath --unix "$JAVACMD"`
# We build the pattern for arguments to be converted via cygpath
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
SEP=""
for dir in $ROOTDIRSRAW ; do
ROOTDIRS="$ROOTDIRS$SEP$dir"
SEP="|"
done
OURCYGPATTERN="(^($ROOTDIRS))"
# Add a user-defined pattern to the cygpath arguments
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
fi
# Now convert the arguments - kludge to limit ourselves to /bin/sh
i=0
for arg in "$@" ; do
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
else
eval `echo args$i`="\"$arg\""
fi
i=`expr $i + 1`
done
case $i in
0) set -- ;;
1) set -- "$args0" ;;
2) set -- "$args0" "$args1" ;;
3) set -- "$args0" "$args1" "$args2" ;;
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
esac
fi
# Escape application args
save () {
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
echo " "
}
APP_ARGS=`save "$@"`
# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
exec "$JAVACMD" "$@"
@rem
@rem Copyright 2015 the original author or authors.
@rem
@rem Licensed under the Apache License, Version 2.0 (the "License");
@rem you may not use this file except in compliance with the License.
@rem You may obtain a copy of the License at
@rem
@rem https://www.apache.org/licenses/LICENSE-2.0
@rem
@rem Unless required by applicable law or agreed to in writing, software
@rem distributed under the License is distributed on an "AS IS" BASIS,
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
@rem See the License for the specific language governing permissions and
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@rem
@rem ##########################################################################
@rem Set local scope for the variables with windows NT shell
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
@rem Find java.exe
if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:findJavaFromJavaHome
set JAVA_HOME=%JAVA_HOME:"=%
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
if exist "%JAVA_EXE%" goto execute
echo.
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
echo.
echo Please set the JAVA_HOME variable in your environment to match the
echo location of your Java installation.
goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
:fail
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
rem the _cmd.exe /c_ return code!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
:mainEnd
if "%OS%"=="Windows_NT" endlocal
:omega
pluginManagement {
repositories {
maven { url 'https://repo1.maven.org/maven2/' }
maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' }
maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }//阿里云镜像服务
maven { url 'https://developer.huawei.com/repo/' }
maven { url 'https://jitpack.io' }
maven {
url 'https://maven.aliyun.com/nexus/content/repositories/releases/'
}
maven {
url "https://oss.sonatype.org/content/groups/public"
}
maven {
url 'https://hub.zmcms.cn/repository/maven-public/'
}
maven { url 'https://developer.huawei.com/repo/' }
gradlePluginPortal()
google()
mavenCentral()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
maven { url 'https://repo1.maven.org/maven2/' }
maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' }
maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }//阿里云镜像服务
maven { url 'https://developer.huawei.com/repo/' }
maven { url 'https://jitpack.io' }
maven {
url 'https://maven.aliyun.com/nexus/content/repositories/releases/'
}
maven {
url "https://oss.sonatype.org/content/groups/public"
}
maven {
url 'https://hub.zmcms.cn/repository/maven-public/'
}
maven { url 'https://developer.huawei.com/repo/' }
google()
mavenCentral()
}
}
rootProject.name = "WebSocket2"
include ':app'
//include ':websocket2'
include ':websocket'
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'maven-publish'
android {
compileSdkVersion 31
defaultConfig {
minSdkVersion 15
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release_debug {
debuggable true
manifestPlaceholders = [
BUILD_TYPE: 'prod',
isdebug : false
]
minifyEnabled false
jniDebuggable true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
release {
debuggable false
manifestPlaceholders = [
BUILD_TYPE: 'prod',
isdebug : false
]
minifyEnabled false
jniDebuggable true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
shop {
debuggable true
manifestPlaceholders = [
BUILD_TYPE: 'shop',
isdebug : true
]
minifyEnabled false
jniDebuggable true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
beta {
debuggable true
manifestPlaceholders = [
BUILD_TYPE: 'beta',
isdebug : true
]
minifyEnabled false
jniDebuggable true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
debug {
debuggable true
manifestPlaceholders = [
BUILD_TYPE: 'dev',
isdebug : true
]
minifyEnabled false
jniDebuggable true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
// implementation 'com.squareup.okhttp3:okhttp:3.9.0'
implementation "com.squareup.okhttp3:okhttp:3.13.1"
implementation 'io.reactivex.rxjava2:rxjava:2.1.5'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation 'com.google.code.gson:gson:2.8.2'
api 'org.litepal.guolindev:core:3.2.3'
api 'com.squareup.okhttp3:okhttp:3.9.0'
api 'io.reactivex.rxjava2:rxjava:2.1.5'
api 'io.reactivex.rxjava2:rxandroid:2.0.1'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
}
task generateSourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier 'sources'
}
def versionName = '1.0.0'
def SNAPSHOT_REPOSITORY_URL = "https://hub.zmcms.cn/repository/maven-snapshots/"
def RELEASE_REPOSITORY_URL = "https://hub.zmcms.cn/repository/maven-releases/"
afterEvaluate {
publishing {
publications {
release(MavenPublication) {
from components.release
groupId "com.qmai.android.socket"
artifactId "rxwebSocket"
version versionName
artifact generateSourcesJar
}
}
repositories {
// 定义一个 maven 仓库
maven {
// 可以有且仅有一个仓库不指定 name 属性,会隐式设置为 Maven
// 根据 versionName 来判断仓库地址
url = versionName.endsWith('SNAPSHOT') ? SNAPSHOT_REPOSITORY_URL : RELEASE_REPOSITORY_URL
// 仓库用户名密码
credentials {
username = "wanglei1"
password = "woshiwanglei123"
}
}
}
}
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
package com.zhimai.websocket;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.zhimai.websocket.test", appContext.getPackageName());
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.zhimai.websocket">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application>
<activity android:name=".activity.WebsocketMainActivity"></activity>
</application>
</manifest>
\ No newline at end of file
<?xml version="1.0" encoding="utf-8"?>
<litepal>
<!--
Define the database name of your application.
By default each database name should be end with .db.
If you didn't name your database end with .db,
LitePal would plus the suffix automatically for you.
For example:
<dbname value="demo" />
-->
<dbname value="sllibrary" />
<!--
Define the version of your database. Each time you want
to upgrade your database, the version tag would helps.
Modify the models you defined in the mapping tag, and just
make the version value plus one, the upgrade of database
will be processed automatically without concern.
For example:
<version value="1" />
-->
<version value="6" />
<!--
Define your models in the list with mapping tag, LitePal will
create tables for each mapping class. The supported fields
defined in models will be mapped into columns.
For example:
<list>
<mapping class="com.test.model.Reader" />
<mapping class="com.test.model.Magazine" />
</list>
-->
<list>
<mapping class="com.zhimai.websocket.bean.MessageSqlBean" />
<mapping class="com.zhimai.websocket.bean.SocketOrderInfoBean"/>
<mapping class="com.zhimai.websocket.bean.SocketCheckMessage"/>
<mapping class="com.zhimai.websocket.bean.SocketRecordBean"/>
</list>
<!--
Define where the .db file should be. "internal" means the .db file
will be stored in the database folder of internal storage which no
one can access. "external" means the .db file will be stored in the
path to the directory on the primary external storage device where
the application can place persistent files it owns which everyone
can access. "internal" will act as default.
For example:
<storage value="external" />
-->
</litepal>
package com.dhh.websocket;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.OkHttpClient;
public final class Config {
protected long reconnectInterval = 1;
protected TimeUnit reconnectIntervalTimeUnit = TimeUnit.SECONDS;
protected boolean showLog = false;
protected String logTag = "RxWebSocket";
protected OkHttpClient client = new OkHttpClient();
protected SSLSocketFactory sslSocketFactory;
protected X509TrustManager trustManager;
private Config() {
}
public static final class Builder {
private Config config;
public Builder() {
config = new Config();
}
/**
* set your client
*
* @param client
*/
public Builder setClient(OkHttpClient client) {
config.client = client;
return this;
}
/**
* wss support
*
* @param sslSocketFactory
* @param trustManager
*/
public Builder setSSLSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) {
config.sslSocketFactory = sslSocketFactory;
config.trustManager = trustManager;
return this;
}
/**
* set reconnect interval
*
* @param Interval reconncet interval
* @param timeUnit unit
* @return
*/
public Builder setReconnectInterval(long Interval, TimeUnit timeUnit) {
config.reconnectInterval = Interval;
config.reconnectIntervalTimeUnit = timeUnit;
return this;
}
public Builder setShowLog(boolean showLog) {
config.showLog = showLog;
return this;
}
public Builder setShowLog(boolean showLog, String logTag) {
config.showLog = showLog;
config.logTag = logTag;
return this;
}
public Config build() {
return config;
}
}
}
package com.dhh.websocket;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import okio.ByteString;
/**
* Created by dhh on 2018/3/29.
*
* @author dhh
*/
public final class RxWebSocket {
public static void setConfig(Config config) {
RxWebSocketUtil instance = RxWebSocketUtil.getInstance();
instance.setShowLog(config.showLog, config.logTag);
instance.setClient(config.client);
instance.setReconnectInterval(config.reconnectInterval, config.reconnectIntervalTimeUnit);
if (config.sslSocketFactory != null && config.trustManager != null) {
instance.setSSLSocketFactory(config.sslSocketFactory, config.trustManager);
}
}
/**
* default timeout: 30 days
* <p>
* 若忽略小米平板,请调用这个方法
* </p>
*/
public static Observable<WebSocketInfo> get(String url) {
return RxWebSocketUtil.getInstance().getWebSocketInfo(url);
}
/**
* @param url ws://127.0.0.1:8080/websocket
* @param timeout The WebSocket will be reconnected after the specified time interval is not "onMessage",
* <p>
* 在指定时间间隔后没有收到消息就会重连WebSocket,为了适配小米平板,因为小米平板断网后,不会发送错误通知
* @param timeUnit unit
* @return
*/
public static Observable<WebSocketInfo> get(String url, long timeout, TimeUnit timeUnit) {
return RxWebSocketUtil.getInstance().getWebSocketInfo(url, timeout, timeUnit);
}
/**
* 如果url的WebSocket已经打开,可以直接调用这个发送消息.
*
* @param url
* @param msg
*/
public static Boolean send(String url, String msg) {
return RxWebSocketUtil.getInstance().send(url, msg);
}
/**
* 如果url的WebSocket已经打开,可以直接调用这个发送消息.
*
* @param url
* @param byteString
*/
public static Boolean send(String url, ByteString byteString) {
return RxWebSocketUtil.getInstance().send(url, byteString);
}
/**
* 不用关心url 的WebSocket是否打开,可以直接发送
*
* @param url
* @param msg
*/
public static void asyncSend(String url, String msg) {
RxWebSocketUtil.getInstance().asyncSend(url, msg);
}
/**
* 不用关心url 的WebSocket是否打开,可以直接发送
*
* @param url
* @param byteString
*/
public static void asyncSend(String url, ByteString byteString) {
RxWebSocketUtil.getInstance().asyncSend(url, byteString);
}
}
This diff is collapsed.
package com.dhh.websocket;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
/**
* Created by zhy on 15/12/14.
*/
public class SSLHelper {
public static class SSLParams {
public SSLSocketFactory sSLSocketFactory;
public X509TrustManager trustManager;
}
/**
* @param certificates
* @param bksFile
* @param password
* @return
*/
public static SSLParams getSslSocketFactory(InputStream[] certificates, InputStream bksFile, String password) {
SSLParams sslParams = new SSLParams();
try {
TrustManager[] trustManagers = prepareTrustManager(certificates);
KeyManager[] keyManagers = prepareKeyManager(bksFile, password);
SSLContext sslContext = SSLContext.getInstance("TLS");
X509TrustManager trustManager = null;
if (trustManagers != null) {
trustManager = new MyTrustManager(chooseTrustManager(trustManagers));
} else {
trustManager = new UnSafeTrustManager();
}
sslContext.init(keyManagers, new TrustManager[]{trustManager}, null);
sslParams.sSLSocketFactory = sslContext.getSocketFactory();
sslParams.trustManager = trustManager;
return sslParams;
} catch (Exception e) {
throw new AssertionError(e);
}
}
private class UnSafeHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
private static class UnSafeTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
private static TrustManager[] prepareTrustManager(InputStream... certificates) {
if (certificates == null || certificates.length <= 0) return null;
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try {
if (certificate != null)
certificate.close();
} catch (IOException e)
{
}
}
TrustManagerFactory trustManagerFactory = null;
trustManagerFactory = TrustManagerFactory.
getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
return trustManagers;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static KeyManager[] prepareKeyManager(InputStream bksFile, String password) {
try {
if (bksFile == null || password == null) return null;
KeyStore clientKeyStore = KeyStore.getInstance("BKS");
clientKeyStore.load(bksFile, password.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, password.toCharArray());
return keyManagerFactory.getKeyManagers();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers) {
for (TrustManager trustManager : trustManagers) {
if (trustManager instanceof X509TrustManager) {
return (X509TrustManager) trustManager;
}
}
return null;
}
private static class MyTrustManager implements X509TrustManager {
private X509TrustManager defaultTrustManager;
private X509TrustManager localTrustManager;
public MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException {
TrustManagerFactory var4 = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
var4.init((KeyStore) null);
defaultTrustManager = chooseTrustManager(var4.getTrustManagers());
this.localTrustManager = localTrustManager;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ce) {
localTrustManager.checkServerTrusted(chain, authType);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
}
package com.dhh.websocket;
/**
* @author: leiwang
* @time: 2022/3/28 15:59
* @desc:
*/
public class SendSocketStatus {
private Boolean status;
private String errorMsg;
public SendSocketStatus(Boolean status, String errorMsg) {
this.status = status;
this.errorMsg = errorMsg;
}
public Boolean getStatus() {
return status;
}
public void setStatus(Boolean status) {
this.status = status;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
@Override
public String toString() {
return "SendSocketStatus{" +
"status=" + status +
", errorMsg='" + errorMsg + '\'' +
'}';
}
}
package com.dhh.websocket;
import okhttp3.WebSocket;
import okio.ByteString;
/**
* Created by dhh on 2017/9/21.
*/
public class WebSocketInfo {
private WebSocket mWebSocket;
private String mString;
private ByteString mByteString;
private boolean onOpen;
private boolean onReconnect;
private String mPreSocketErrorMessage;
private WebSocketInfo() {
}
WebSocketInfo(WebSocket webSocket, boolean onOpen) {
mWebSocket = webSocket;
this.onOpen = onOpen;
}
WebSocketInfo(WebSocket webSocket, String mString) {
mWebSocket = webSocket;
this.mString = mString;
}
WebSocketInfo(WebSocket webSocket, ByteString byteString) {
mWebSocket = webSocket;
mByteString = byteString;
}
static WebSocketInfo createReconnect() {
WebSocketInfo socketInfo = new WebSocketInfo();
socketInfo.onReconnect = true;
return socketInfo;
}
public WebSocket getWebSocket() {
return mWebSocket;
}
public void setWebSocket(WebSocket webSocket) {
mWebSocket = webSocket;
}
public String getString() {
return mString;
}
public void setString(String string) {
this.mString = string;
}
public ByteString getByteString() {
return mByteString;
}
public void setByteString(ByteString byteString) {
mByteString = byteString;
}
public boolean isOnOpen() {
return onOpen;
}
public boolean isOnReconnect() {
return onReconnect;
}
public String getPreSocketErrorMessage() {
return mPreSocketErrorMessage;
}
public void setPreSocketErrorMessage(String mPreSocketErrorMessage) {
this.mPreSocketErrorMessage = mPreSocketErrorMessage;
}
}
package com.dhh.websocket;
import io.reactivex.Observer;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import okhttp3.WebSocket;
import okio.ByteString;
/**
*
* @author dhh
* @date 2017/10/24
* <p>
* override the method of you want to use
* <p>
* 根据业务需求重写你想使用的方法
*/
public abstract class WebSocketSubscriber implements Observer<WebSocketInfo> {
private boolean hasOpened;
protected Disposable disposable;
@Override
public final void onNext(@NonNull WebSocketInfo webSocketInfo) {
if (webSocketInfo.isOnOpen()) {
hasOpened = true;
onOpen(webSocketInfo.getWebSocket());
} else if (webSocketInfo.getString() != null) {
onMessage(webSocketInfo.getString());
} else if (webSocketInfo.getByteString() != null) {
onMessage(webSocketInfo.getByteString());
} else if (webSocketInfo.isOnReconnect()) {
onReconnect(webSocketInfo.getPreSocketErrorMessage());
}
}
/**
* Callback when the WebSocket is opened
*
* @param webSocket
*/
protected void onOpen(@NonNull WebSocket webSocket) {
}
protected void onMessage(@NonNull String text) {
}
protected void onMessage(@NonNull ByteString byteString) {
}
/**
* Callback when the WebSocket is reconnecting
*/
protected void onReconnect(String preSocketErrorMessage) {
}
protected void onClose() {
}
@Override
public final void onSubscribe(Disposable disposable) {
this.disposable = disposable;
}
public final void dispose() {
if (disposable != null) {
disposable.dispose();
}
}
@Override
public final void onComplete() {
if (hasOpened) {
onClose();
}
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
}
package com.dhh.websocket;
import android.annotation.SuppressLint;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import io.reactivex.Observable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
/**
* Created by dhh on 2018/4/9.
* <p>
* 功能:可根据传入的泛型,直接将String类型的textshi使用Gson转化成实体类
* </P>
*
* @author dhh
*/
public abstract class WebSocketSubscriber2<T> extends WebSocketSubscriber {
private static final Gson GSON = new Gson();
protected Type type;
public WebSocketSubscriber2() {
analysisType();
}
private void analysisType() {
Type superclass = getClass().getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("No generics found!");
}
ParameterizedType type = (ParameterizedType) superclass;
this.type = type.getActualTypeArguments()[0];
}
@SuppressLint("CheckResult")
@Override
protected void onMessage( String text) {
Observable.just(text)
.map(new Function<String, T>() {
@Override
public T apply(String s) throws Exception {
try {
return GSON.fromJson(s, type);
} catch (JsonSyntaxException e) {
return GSON.fromJson(GSON.fromJson(s, String.class), type);
}
}
})
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(new Consumer<T>() {
@Override
public void accept(T t) throws Exception {
onMessage(t);
}
});
}
protected abstract void onMessage(T t);
}
package com.zhimai.websocket;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import okhttp4.OkHttpClient;
public final class Config {
protected long reconnectInterval = 1;
protected TimeUnit reconnectIntervalTimeUnit = TimeUnit.SECONDS;
protected boolean showLog = false;
protected String logTag = "RxWebSocket";
protected OkHttpClient client = new OkHttpClient();
protected SSLSocketFactory sslSocketFactory;
protected X509TrustManager trustManager;
private Config() {
}
public static final class Builder {
private Config config;
public Builder() {
config = new Config();
}
/**
* set your client
*
* @param client
*/
public Builder setClient(OkHttpClient client) {
config.client = client;
return this;
}
/**
* wss support
*
* @param sslSocketFactory
* @param trustManager
*/
public Builder setSSLSocketFactory(SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) {
config.sslSocketFactory = sslSocketFactory;
config.trustManager = trustManager;
return this;
}
/**
* set reconnect interval
*
* @param Interval reconncet interval
* @param timeUnit unit
* @return
*/
public Builder setReconnectInterval(long Interval, TimeUnit timeUnit) {
config.reconnectInterval = Interval;
config.reconnectIntervalTimeUnit = timeUnit;
return this;
}
public Builder setShowLog(boolean showLog) {
config.showLog = showLog;
return this;
}
public Builder setShowLog(boolean showLog, String logTag) {
config.showLog = showLog;
config.logTag = logTag;
return this;
}
public Config build() {
return config;
}
}
}
package com.zhimai.websocket;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import okio.ByteString;
public final class RxWebSocket {
public static void setConfig(Config config) {
RxWebSocketUtil instance = RxWebSocketUtil.getInstance();
instance.setShowLog(config.showLog, config.logTag);
instance.setClient(config.client);
instance.setReconnectInterval(config.reconnectInterval, config.reconnectIntervalTimeUnit);
if (config.sslSocketFactory != null && config.trustManager != null) {
instance.setSSLSocketFactory(config.sslSocketFactory, config.trustManager);
}
}
/**
* default timeout: 30 days
* <p>
* 若忽略小米平板,请调用这个方法
* </p>
*/
public static Observable<WebSocketInfo> get(String url) {
return RxWebSocketUtil.getInstance().getWebSocketInfo(url);
}
/**
* @param url ws://127.0.0.1:8080/websocket
* @param timeout The WebSocket will be reconnected after the specified time interval is not "onMessage",
* <p>
* 在指定时间间隔后没有收到消息就会重连WebSocket,为了适配小米平板,因为小米平板断网后,不会发送错误通知
* @param timeUnit unit
* @return
*/
public static Observable<WebSocketInfo> get(String url, long timeout, TimeUnit timeUnit) {
return RxWebSocketUtil.getInstance().getWebSocketInfo(url, timeout, timeUnit);
}
/**
* 如果url的WebSocket已经打开,可以直接调用这个发送消息.
*
* @param url
* @param msg
*/
public static void send(String url, String msg) {
RxWebSocketUtil.getInstance().send(url, msg);
}
/**
* 如果url的WebSocket已经打开,可以直接调用这个发送消息.
*
* @param url
* @param byteString
*/
public static void send(String url, ByteString byteString) {
RxWebSocketUtil.getInstance().send(url, byteString);
}
/**
* 不用关心url 的WebSocket是否打开,可以直接发送
*
* @param url
* @param msg
*/
public static void asyncSend(String url, String msg) {
RxWebSocketUtil.getInstance().asyncSend(url, msg);
}
/**
* 不用关心url 的WebSocket是否打开,可以直接发送
*
* @param url
* @param byteString
*/
public static void asyncSend(String url, ByteString byteString) {
RxWebSocketUtil.getInstance().asyncSend(url, byteString);
}
}
package com.zhimai.websocket;
import java.io.IOException;
import java.io.InputStream;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
public class SSLHelper {
public static class SSLParams {
public SSLSocketFactory sSLSocketFactory;
public X509TrustManager trustManager;
}
/**
* @param certificates
* @param bksFile
* @param password
* @return
*/
public static SSLParams getSslSocketFactory(InputStream[] certificates, InputStream bksFile, String password) {
SSLParams sslParams = new SSLParams();
try {
TrustManager[] trustManagers = prepareTrustManager(certificates);
KeyManager[] keyManagers = prepareKeyManager(bksFile, password);
SSLContext sslContext = SSLContext.getInstance("TLS");
X509TrustManager trustManager = null;
if (trustManagers != null) {
trustManager = new MyTrustManager(chooseTrustManager(trustManagers));
} else {
trustManager = new UnSafeTrustManager();
}
sslContext.init(keyManagers, new TrustManager[]{trustManager}, null);
sslParams.sSLSocketFactory = sslContext.getSocketFactory();
sslParams.trustManager = trustManager;
return sslParams;
} catch (Exception e) {
throw new AssertionError(e);
}
}
private class UnSafeHostnameVerifier implements HostnameVerifier {
@Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
}
private static class UnSafeTrustManager implements X509TrustManager {
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType)
throws CertificateException {
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[]{};
}
}
private static TrustManager[] prepareTrustManager(InputStream... certificates) {
if (certificates == null || certificates.length <= 0) return null;
try {
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null);
int index = 0;
for (InputStream certificate : certificates) {
String certificateAlias = Integer.toString(index++);
keyStore.setCertificateEntry(certificateAlias, certificateFactory.generateCertificate(certificate));
try {
if (certificate != null)
certificate.close();
} catch (IOException e)
{
}
}
TrustManagerFactory trustManagerFactory = null;
trustManagerFactory = TrustManagerFactory.
getInstance(TrustManagerFactory.getDefaultAlgorithm());
trustManagerFactory.init(keyStore);
TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
return trustManagers;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static KeyManager[] prepareKeyManager(InputStream bksFile, String password) {
try {
if (bksFile == null || password == null) return null;
KeyStore clientKeyStore = KeyStore.getInstance("BKS");
clientKeyStore.load(bksFile, password.toCharArray());
KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
keyManagerFactory.init(clientKeyStore, password.toCharArray());
return keyManagerFactory.getKeyManagers();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers) {
for (TrustManager trustManager : trustManagers) {
if (trustManager instanceof X509TrustManager) {
return (X509TrustManager) trustManager;
}
}
return null;
}
private static class MyTrustManager implements X509TrustManager {
private X509TrustManager defaultTrustManager;
private X509TrustManager localTrustManager;
public MyTrustManager(X509TrustManager localTrustManager) throws NoSuchAlgorithmException, KeyStoreException {
TrustManagerFactory var4 = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
var4.init((KeyStore) null);
defaultTrustManager = chooseTrustManager(var4.getTrustManagers());
this.localTrustManager = localTrustManager;
}
@Override
public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
}
@Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
defaultTrustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ce) {
localTrustManager.checkServerTrusted(chain, authType);
}
}
@Override
public X509Certificate[] getAcceptedIssuers() {
return new X509Certificate[0];
}
}
}
package com.zhimai.websocket;
import androidx.annotation.CallSuper;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Consumer;
import okhttp4.WebSocket;
import okio.ByteString;
@Deprecated
public abstract class WebSocketConsumer implements Consumer<WebSocketInfo> {
@CallSuper
@Override
public void accept(WebSocketInfo webSocketInfo) throws Exception {
if (webSocketInfo.isOnOpen()) {
onOpen(webSocketInfo.getWebSocket());
} else if (webSocketInfo.getString() != null) {
onMessage(webSocketInfo.getString());
} else if (webSocketInfo.getByteString() != null) {
onMessage(webSocketInfo.getByteString());
}
}
public abstract void onOpen(@NonNull WebSocket webSocket);
public abstract void onMessage(@NonNull String text);
public abstract void onMessage(@NonNull ByteString bytes);
}
package com.zhimai.websocket;
import androidx.annotation.Nullable;
import okhttp4.WebSocket;
import okio.ByteString;
public class WebSocketInfo {
private WebSocket mWebSocket;
private String mString;
private ByteString mByteString;
private boolean onOpen;
private boolean onReconnect;
private WebSocketInfo() {
}
WebSocketInfo(WebSocket webSocket, boolean onOpen) {
mWebSocket = webSocket;
this.onOpen = onOpen;
}
WebSocketInfo(WebSocket webSocket, String mString) {
mWebSocket = webSocket;
this.mString = mString;
}
WebSocketInfo(WebSocket webSocket, ByteString byteString) {
mWebSocket = webSocket;
mByteString = byteString;
}
static WebSocketInfo createReconnect() {
WebSocketInfo socketInfo = new WebSocketInfo();
socketInfo.onReconnect = true;
return socketInfo;
}
public WebSocket getWebSocket() {
return mWebSocket;
}
public void setWebSocket(WebSocket webSocket) {
mWebSocket = webSocket;
}
@Nullable
public String getString() {
return mString;
}
public void setString(String string) {
this.mString = string;
}
@Nullable
public ByteString getByteString() {
return mByteString;
}
public void setByteString(ByteString byteString) {
mByteString = byteString;
}
public boolean isOnOpen() {
return onOpen;
}
public boolean isOnReconnect() {
return onReconnect;
}
}
package com.zhimai.websocket;
import io.reactivex.Observer;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import okhttp4.WebSocket;
import okio.ByteString;
public abstract class WebSocketSubscriber implements Observer<WebSocketInfo> {
private boolean hasOpened;
protected Disposable disposable;
@Override
public final void onNext(@NonNull WebSocketInfo webSocketInfo) {
if (webSocketInfo.isOnOpen()) {
hasOpened = true;
onOpen(webSocketInfo.getWebSocket());
} else if (webSocketInfo.getString() != null) {
onMessage(webSocketInfo.getString());
} else if (webSocketInfo.getByteString() != null) {
onMessage(webSocketInfo.getByteString());
} else if (webSocketInfo.isOnReconnect()) {
onReconnect();
}
}
/**
* Callback when the WebSocket is opened
*
* @param webSocket
*/
protected void onOpen(@NonNull WebSocket webSocket) {
}
protected void onMessage(@NonNull String text) {
}
protected void onMessage(@NonNull ByteString byteString) {
}
protected void onCatchMsg(String errorMsg){
}
/**
* Callback when the WebSocket is reconnecting
*/
protected void onReconnect() {
}
protected void onClose() {
}
@Override
public final void onSubscribe(Disposable disposable) {
this.disposable = disposable;
}
public final void dispose() {
if (disposable != null) {
disposable.dispose();
}
}
@Override
public final void onComplete() {
if (hasOpened) {
onClose();
}
}
@Override
public void onError(Throwable e) {
e.printStackTrace();
}
}
package com.zhimai.websocket;
import android.annotation.SuppressLint;
import androidx.annotation.CallSuper;
import androidx.annotation.NonNull;
import com.google.gson.Gson;
import com.google.gson.JsonSyntaxException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
public abstract class WebSocketSubscriber2<T> extends WebSocketSubscriber {
private static final Gson GSON = new Gson();
protected Type type;
public WebSocketSubscriber2() {
analysisType();
}
private void analysisType() {
Type superclass = getClass().getGenericSuperclass();
if (superclass instanceof Class) {
throw new RuntimeException("No generics found!");
}
ParameterizedType type = (ParameterizedType) superclass;
this.type = type.getActualTypeArguments()[0];
}
@SuppressLint("CheckResult")
@Override
@CallSuper
protected void onMessage(@NonNull String text) {
Observable.just(text)
.map(new Function<String, T>() {
@Override
public T apply(String s) throws Exception {
try {
return GSON.fromJson(s, type);
} catch (JsonSyntaxException e) {
return GSON.fromJson(GSON.fromJson(s, String.class), type);
}
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<T>() {
@Override
public void accept(T t) throws Exception {
onMessage(t);
}
});
}
protected abstract void onMessage(T t);
}
package com.zhimai.websocket.activity;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.zhimai.websocket.R;
import com.zhimai.websocket.listener.QueueMessageBackListener;
import com.zhimai.websocket.listener.SocketMessageBackListener;
import com.zhimai.websocket.util.MsgWebSocketUtil;
/**
* 这里只是启动socket的示例,也可以放全局或者其它地方启动
*/
public class WebsocketMainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_websocket);
//开启消息长连接 websocket
// MsgWebSocketUtil
// .getInstance()
// .showLog(true)//是否打印日志
// .setUrl("")//socket链接地址
// .setDeviceName("")//设备号
//// .setPingRate(5000)//socket ping频率 默认5000
//// .setPingData("")//socket ping内容 默认 "{\"type\":\"ping\",\"data\":\"\"}"
// .useQueue(true)//是否开启 队列
// //不开启队列时,消息回调
// .setSocketMessageListener(new SocketMessageBackListener() {
// @Override
// public void recieveMessage(String message) {
//
// }
// })
// //开启队列 消息回调
// //消费完消息 注意:调用 QueueMessageService.getInstance().consumMessage(); 否则 队列服务不会主动下放消息
// .setQueueMessageListener(new QueueMessageBackListener() {
// @Override
// public void reciever(String message) {
//
// }
// })
// .initSocket();
}
}
package com.zhimai.websocket.bean;
/**
* Explain:socket 2.0 外侧数据 结构
* Created on 2021/6/4.
*
* @author zhangshenglong
*/
public class CommonMsgData {
int storeId;
String clientUid;
String tag;
String id;
public int getStoreId() {
return storeId;
}
public void setStoreId(int storeId) {
this.storeId = storeId;
}
public String getClientUid() {
return clientUid;
}
public void setClientUid(String clientUid) {
this.clientUid = clientUid;
}
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}
package com.zhimai.websocket.bean;
import org.litepal.annotation.Column;
import org.litepal.crud.LitePalSupport;
/**
* Explain:
* Created on 2020/10/30.
*
* @author zhangshenglong
*/
public class MessageSqlBean extends LitePalSupport {
@Column(unique = true)
private String tag;
@Column(defaultValue = "false")
private boolean confirm;
@Column(defaultValue = "false")
private boolean print;
private String content;
public String getTag() {
return tag;
}
public void setTag(String tag) {
this.tag = tag;
}
public boolean isConfirm() {
return confirm;
}
public void setConfirm(boolean confirm) {
this.confirm = confirm;
}
public boolean isPrint() {
return print;
}
public void setPrint(boolean print) {
this.print = print;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
package com.zhimai.websocket.bean
import org.litepal.annotation.Column
import org.litepal.crud.LitePalSupport
/**
* @author: 高英祥
* @github:
* @time: 2021/9/13 15:02
* @desc:
* @doc:
*/
class SocketCheckMessage : LitePalSupport() {
//消息唯一id
@Column(unique = true)
var messageId:String? = null
//订单号
var orderNo:String? = null
//消息的routingKey
var routingKey:String?= null
//设备id
var deviceId:String? = null
//设备的tag
var tag:String? = null
//门店id
var mutableId:String? = null
//门店名称
var mutableName:String? = null
//店铺名称:
var shopName:String? = null
//店铺id
var shopId:String? = null
//当前版本
var version:String? = null
//订单类容
var orderDetails:String? = null
//订单时间
var orderInTime:String? = null
//打印机状态
var printerState:String? = null
//是否打印
var isPrint:String? = null
//是否播报
var isPlay:String? = null
//扩展字段 预留
var other:String? = null
}
\ No newline at end of file
package com.zhimai.websocket.bean;
import org.litepal.crud.LitePalSupport;
/**
* <pre>
* author leiwang
* e-mail xxx@xx
* time 2021/07/08
* desc
* version 1.0
* </pre>
*/
public class SocketOrderInfoBean extends LitePalSupport {
public String tag_id;//标记id
public String order_no; //订单no
public String order_info; //订单信息
public int is_print; //是否打印
public int is_voice; //是否播报
public long save_currentTime; //保存的时间戳
public String save_time; //保存时间
public int is_confirm; //是否确认
public String confirm_time; // 确认时间
public int is_upload; //是否上报
public String upload_time; //上报上次
public String socket_receive_time; //socket收到时间
public String order_create_time; //订单创建时间
public String order_print_time; //订单打印时间
public String order_get_time; //订单列表获取时间
}
package com.zhimai.websocket.bean
import org.litepal.crud.LitePalSupport
/**
* @author: 高英祥
* @github:
* @time: 2021/9/29 16:27
* @desc:
* @doc:
*/
class SocketRecordBean : LitePalSupport() {
var time: String? = null
//当时的动作
var action: String? = null
//错误信息
var error: String? = null
//说明
var desc: String? = null
//预留扩展字段1
var other:String? = null
}
\ No newline at end of file
package com.zhimai.websocket.db;
import android.content.Context;
import com.zhimai.websocket.bean.MessageSqlBean;
import com.zhimai.websocket.util.SpUtils;
import com.zhimai.websocket.util.TimeUtils;
import org.litepal.LitePal;
import org.litepal.crud.callback.FindMultiCallback;
import java.util.List;
/**
* Explain:
* Created on 2020/10/30.
*
* @author zhangshenglong
*/
public class MessageDBManager {
/**
* 查询是否存在消息记录
*/
public static boolean isRecieverMessage(String tag) {
List<MessageSqlBean> lsPrintHistory = LitePal.where("tag=?", tag).find(MessageSqlBean.class);
if (lsPrintHistory == null || lsPrintHistory.size() == 0) {
return false;
} else {
return true;
}
}
/**
* 写入记录
*/
public static void addMessage(String tag) {
MessageSqlBean messageSqlBean = new MessageSqlBean();
messageSqlBean.setTag(tag);
messageSqlBean.save();
}
/**
* 直接插入
*/
public static boolean addSocketMessage(String tag) {
MessageSqlBean messageSqlBean = new MessageSqlBean();
messageSqlBean.setTag(tag);
return messageSqlBean.save();
}
public static void addMessage(String tag, String content) {
MessageSqlBean messageSqlBean = new MessageSqlBean();
messageSqlBean.setTag(tag);
messageSqlBean.setContent(content);
messageSqlBean.save();
}
/**
* 如果数据量超过一定数额,删除历史数据
* 可在每次启动的时候调用
*/
public static void checkHistoryCount(final int maxNum) {
List<MessageSqlBean> lsPrintHistory = LitePal.findAll(MessageSqlBean.class);
if (lsPrintHistory.size() > 2000) {
LitePal.deleteAll(MessageSqlBean.class);
}
}
/**
* 建议初始化litepal时调用此方法
* 隔天删除 历史数据
* 使用本地存储 当天年月日
*/
public static void deleteYesterdayAllHistory(Context context) {
//获取当天时间 年月日
String todyTime = TimeUtils.getTodayTimeStr();
//获取存储时间 年月日
String myTime = SpUtils.getInstance(context).getMyTime();
//比对日期,如果不一致,初始化时 删除所有记录
if (!todyTime.equals(myTime)) {
LitePal.deleteAll(MessageSqlBean.class);
SpUtils.getInstance(context).setMyTime(todyTime);
}
}
}
package com.zhimai.websocket.http;
public interface CallBackListener {
void error(String error);
void fail(String message);
void success(String content);
}
package com.zhimai.websocket.http;
import android.os.Looper;
import java.io.IOException;
import okhttp4.Call;
import okhttp4.Callback;
import okhttp4.Response;
public class CallbackToMainThread implements Callback {
private MCallback mCallback = null;
private Call mCall = null;
private IOException mIOException = null;
private Response mStringResponse = null;
/**
* @param callback 这里是请求结果的回调,其回调方法将会在UI线程内执行
*/
public CallbackToMainThread(MCallback callback) {
mCallback = callback;
}
/**
* 此处new android.os.Handler(Looper.getMainLooper()).post(runnableFailureUI)是将失败通过UI线程返回
*/
@Override
public void onFailure(Call call, IOException e) {
mCall = call;
mIOException = e;
new android.os.Handler(Looper.getMainLooper()).post(runnableFailureUI);
}
/**
* 这里会根据主线程内Callback的getTClassName实现来获取想要得到的数据类型,然后去做类型转化
*/
@Override
public void onResponse(Call call, Response response) {
mCall = call;
mStringResponse = response;
new android.os.Handler(Looper.getMainLooper()).post(runnableSuccessUI);
}
Runnable runnableFailureUI = new Runnable() {
@Override
public void run() {
mCallback.onFailure(mCall, mIOException);
}
};
Runnable runnableSuccessUI = new Runnable() {
@Override
public void run() {
try {
if (mStringResponse != null) {
mCallback.onResponse(mCall, mStringResponse);
}
} catch (IOException e) {
mCallback.onFailure(mCall, e);
}
}
};
/**
* 此处接口将会在UI线程内实现
* 成功失败都将会通过这里面的方法返回给UI线程
*/
public interface MCallback {
void onFailure(Call call, IOException e);
void onResponse(Call call, Response response) throws IOException;
}
}
package com.zhimai.websocket.http;//package com.slzhang.update.http;
//
//
//import com.slzhang.update.util.Logger;
//
//import okhttp3.logging.HttpLoggingInterceptor;
//
//public class HttpLogger implements HttpLoggingInterceptor.Logger {
// @Override
// public void log(String message) {
// Logger.e("-----------------",message);
// }
//
//}
package com.zhimai.websocket.http;//package com.slzhang.update.http;
//
//import com.tsy.sdk.myokhttp.MyOkHttp;
//
//import java.util.ArrayList;
//import java.util.HashMap;
//import java.util.List;
//import java.util.concurrent.TimeUnit;
//
//import okhttp3.Cookie;
//import okhttp3.CookieJar;
//import okhttp3.HttpUrl;
//import okhttp3.OkHttpClient;
//import okhttp3.logging.HttpLoggingInterceptor;
//
//public class HttpUtil {
// public static MyOkHttp mMyOkhttp;
// public static String headkey="Authorization";
// public static String headvalue="";
//
// public static MyOkHttp getInstance() {
// if (mMyOkhttp == null) {
// HttpLoggingInterceptor logInterceptor = new HttpLoggingInterceptor(new HttpLogger());//创建拦截对象
// logInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);//这一句一定要记得写,否则没有数据输出
// OkHttpClient okHttpClient = new OkHttpClient.Builder()
//
//
// .cookieJar(new CookieJar() {
//
// private final HashMap<HttpUrl, List<Cookie>> cookieStore = new HashMap<>();
//
// @Override
// public void saveFromResponse(HttpUrl url, List<Cookie> cookies) {
//
// cookieStore.put(url, cookies);
//
// }
//
// @Override
// public List<Cookie> loadForRequest(HttpUrl url) {
//
// List<Cookie> cookies = cookieStore.get(url);
//
// return cookies != null ? cookies : new ArrayList<Cookie>();
// }
// })
// .connectTimeout(10000L, TimeUnit.MILLISECONDS)
// .readTimeout(10000L, TimeUnit.MILLISECONDS)
// .addInterceptor(logInterceptor) //设置打印拦截日志
//// .addInterceptor(new LogInterceptor()) //自定义的拦截日志,拦截简单东西用,后面会有介绍/////下载打印日志的话,打印内容会超
// //其他配置
// .build();
// mMyOkhttp = new MyOkHttp(okHttpClient);
// }
// return mMyOkhttp;
// }
//
// public static void setHead(String token_type,String access_token){
// headvalue=token_type+" "+access_token;
// }
//}
package com.zhimai.websocket.http;//package com.slzhang.update.http;
//
//import com.tsy.sdk.myokhttp.MyOkHttp;
//import com.tsy.sdk.myokhttp.builder.GetBuilder;
//import com.tsy.sdk.myokhttp.builder.PostBuilder;
//
///**
// * 这个供收银接口使用,因为请求头不一样
// */
//public class HttpUtil2 {
// private static HttpUtil2 httpUtil2;
// public static MyOkHttp mMyOkhttp;
//
// private HttpUtil2() {
// }
//
// public static HttpUtil2 getInstance() {
// if (httpUtil2 == null) {
// httpUtil2 = new HttpUtil2();
// mMyOkhttp = HttpUtil.getInstance();
// }
// return httpUtil2;
// }
//
// public GetBuilder get() {
// return mMyOkhttp.get()
// .addHeader("Accept", "application/json, text/plain, */*")
// .addHeader("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2")
// .addHeader("QM-STORE-AUTH", "undefined")
// .addHeader("X-CSRF-TOKEN", "{{csrf_token()}}")
// .addHeader("Connection", "keep-alive")
// .addHeader("Accept", "*/*; v=1.0")
//// .addHeader("Referer", MyApplication.INAPI_BASE_RUL_HEAD)
//// .addHeader("Qm-From", "android")
// .addHeader("Qm-From-Type", "cy")
//// .addHeader("Qm-Account-Token", Constant.Token)
// .addHeader("Qm-From", "android" );
//
// }
//
// public PostBuilder post() {
// return mMyOkhttp.post()
// .addHeader("Accept", "application/json, text/plain, */*")
// .addHeader("Accept-Language", "zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2")
// .addHeader("QM-STORE-AUTH", "undefined")
// .addHeader("X-CSRF-TOKEN", "{{csrf_token()}}")
// .addHeader("Connection", "keep-alive")
// .addHeader("Accept", "*/*; v=1.0")
//// .addHeader("Referer", MyApplication.INAPI_BASE_RUL_HEAD)
//// .addHeader("Qm-From", "android")
// .addHeader("Qm-From-Type", "cy")
//// .addHeader("Qm-Account-Token", Constant.Token)
// .addHeader("Qm-From", "android" );
//
// }
//
//}
package com.zhimai.websocket.http;
import com.zhimai.websocket.util.Logger;
import java.io.IOException;
import java.nio.charset.Charset;
import okhttp4.Interceptor;
import okhttp4.MediaType;
import okhttp4.Request;
import okhttp4.RequestBody;
import okhttp4.Response;
import okhttp4.ResponseBody;
import okio.Buffer;
public class NetworkIntercepter implements Interceptor {
private static final String TAG = "*---------------*";
@Override
public Response intercept(Chain chain) {
long start = System.currentTimeMillis();
Response response=null;
String responseBody = null;
String responseCode = null;
String url = null;
String requestBody = null;
try {
Request request = chain.request();
url = request.method()+" "+request.url().toString();
requestBody = getRequestBody(request);
response = chain.proceed(request);
responseBody = response.body().string();
responseCode = String.valueOf(response.code());
MediaType mediaType = response.body().contentType();
response = response.newBuilder().body(ResponseBody.create(mediaType,responseBody)).build();
}
catch (Exception e){
Logger.e(TAG,e.getMessage());
}
finally {
long end = System.currentTimeMillis();
String duration = String.valueOf(end - start);
// Logger.e(TAG,("responseTime= {}, requestUrl= {}, params={}, responseCode= {}, result= {}",
// duration, url,requestBody,responseCode,responseBody);
Logger.e(TAG,"responseTime="+duration);
Logger.e(TAG,"requestUrl="+url);
Logger.e(TAG,"params="+requestBody);
Logger.e(TAG,"responseCode="+responseCode);
Logger.e(TAG,"result="+responseBody);
}
return response;
}
private String getRequestBody(Request request) {
String requestContent = "";
if (request == null) {
return requestContent;
}
RequestBody requestBody = request.body();
if (requestBody == null) {
return requestContent;
}
try {
Buffer buffer = new Buffer();
requestBody.writeTo(buffer);
Charset charset = Charset.forName("utf-8");
requestContent = buffer.readString(charset);
} catch (IOException e) {
e.printStackTrace();
}
return requestContent;
}
}
package com.zhimai.websocket.http;
import java.io.IOException;
import java.net.UnknownHostException;
import okhttp4.Interceptor;
import okhttp4.Request;
import okhttp4.Response;
public class RetryIntercepter implements Interceptor {
public int maxRetryCount;
private int count = 0;
public RetryIntercepter(int maxRetryCount) {
this.maxRetryCount = maxRetryCount;
}
@Override
public Response intercept(Chain chain) throws IOException {
return retry(chain);
}
public Response retry(Chain chain) throws IOException {
Response response = null;
Request request = chain.request();
try {
response = chain.proceed(request);
while (!response.isSuccessful() && count < maxRetryCount) {
count++;
response = retry(chain);
}
}
catch( UnknownHostException ignored){
throw ignored;
}
catch (Exception e){
while (count < maxRetryCount){
count++;
response = retry(chain);
}
}
if (response==null){
throw new IOException("");
}
return response;
}
}
package com.zhimai.websocket.listener;
/**
* 消费消息 监听
* 配合队列使用
*/
public interface ConsumeMessageListener {
//消息处理后回调此接口,销毁队列第一个消息,然后队列消息回调 会自动发下一个消息
void consumeMessage();
}
package com.zhimai.websocket.listener;
/**
* 队列取消息监听
*/
public interface MessageQueueServiceListner {
void getMessage(String message);
}
package com.zhimai.websocket.listener;
/**
* 队列消息回调 监听
*/
public interface QueueMessageBackListener {
void reciever(String message);
}
package com.zhimai.websocket.listener;
public interface SocketConnectListener {
//已连接
void connected();
//断连
void disconnect();
//关闭,暂时没写关闭方法,所以这个方法一般不会被调用
void closed();
}
package com.zhimai.websocket.listener;
public interface SocketConnectListener2 extends SocketConnectListener {
//
void ping(boolean status);
}
package com.zhimai.websocket.listener;
/**
* socket消息接收 监听
*/
public interface SocketMessageBackListener {
void recieveMessage(String message);
}
package com.zhimai.websocket.listener;
/**
* @author: 高英祥
* @github:
* @time: 2021/7/9 15:12
* @desc:
* @doc:
*/
public interface SocketTimerMessageListener {
void timerMessage();
}
package com.zhimai.websocket.listener;
import okhttp4.WebSocket;
import okio.ByteString;
public interface WebSocketSubscriberListener {
void onClose() ;
void onMessage( String text);
void onMessage( ByteString byteString) ;
void onOpen( WebSocket webSocket);
void onReconnect();
void onError(Throwable e);
}
package com.zhimai.websocket.queue;
import com.zhimai.websocket.util.Logger;
import java.util.LinkedList;
public class MessageQueue {
private static final String TAG = "---QueueMessageService---";
private static MessageQueue printMessageQueue;
private static LinkedList<String> list = new LinkedList();
private MessageQueue(){}
public static MessageQueue getInstance(){
if(printMessageQueue==null){
printMessageQueue=new MessageQueue();
}
return printMessageQueue;
}
//进队
public void enQueue(String o) {
Logger.e(TAG,"-*-*-*-队列尾部插入消息:"+o);
list.addLast(o);
}
//出队
public Object deQueue() {
if (!list.isEmpty()) {
String deletestr=list.removeFirst();
Logger.e(TAG,"-*-*-*-队列移出消息:"+deletestr);
return deletestr;
}
return "队列为空";
}
//添加消息到头部(比较优先的消息可以调用此方法)
public void enQueueHead(String o){
Logger.e(TAG,"-*-*-*-队列头部插入消息:"+o);
list.add(0,o);
}
//判断队列是否为空
public boolean QueueEmpty() {
return list.isEmpty();
}
//获取队列长度
public int QueueLength() {
return list.size();
}
//查看队首元素
public String QueuePeek() {
return list.getFirst();
}
//销毁队列
public void clear() {
list.clear();
}
}
package com.zhimai.websocket.queue;
import com.zhimai.websocket.listener.MessageQueueServiceListner;
import com.zhimai.websocket.util.Logger;
import com.zhimai.websocket.util.RxTimerUtil;
import com.zhimai.websocket.util.SysCode;
/**
* 队列取消息服务
*/
public class QueueMessageService {
private static final String TAG = "---QueueMessageService---";
private static QueueMessageService queueMessageService;
private boolean isPlay = false;
private int waiteTime=0;//锁住的等待时间,加一个保护,等待时间过长,直接释放锁
private int interval = 1000;
public void setInterval(int interval) {
this.interval = interval;
}
private QueueMessageService() {
}
public static QueueMessageService getInstance() {
if (queueMessageService == null) {
queueMessageService = new QueueMessageService();
}
return queueMessageService;
}
public void startPrintService(final MessageQueueServiceListner messageQueueServiceListner) {
RxTimerUtil.cancel(SysCode.RX_TIMER_TYPE.PRINT);
RxTimerUtil.interval(SysCode.RX_TIMER_TYPE.PRINT, interval, new RxTimerUtil.IRxNext() {
@Override
public void doNext(long number) {
if (isPlay) {
Logger.e(TAG,"-------正在消费消息-------");
if(waiteTime>10){
Logger.e(TAG,"-------锁定超过10S,自动解锁-------");
consumMessage();
}else{
waiteTime++;
}
return;
}
waiteTime=0;
Logger.e(TAG,"Queue.length="+MessageQueue.getInstance().QueueLength());
//如果播报队列不为空,则开始播报
if (!MessageQueue.getInstance().QueueEmpty()) {
//通过接口下放消息
if (messageQueueServiceListner != null) {
isPlay = true;
String message = MessageQueue.getInstance().QueuePeek();
Logger.e(TAG,"||||||||||-队列下放消息:"+message);
messageQueueServiceListner.getMessage(message);
//成功下放消息后,队列第一条数据出栈
MessageQueue.getInstance().deQueue();
} else {
Logger.e(TAG, "messageQueueServiceListner==null");
}
}
}
});
}
/**
* 消费完一条消息后,调用此方法
* 注:调用后 服务继续下放消息
*/
public void consumMessage() {
//第一条数据出栈
// MessageQueue.getInstance().deQueue();//根据需要,下放消息时 该消息出栈
//重置状态,允许再次下放消息
isPlay = false;
}
}
package com.zhimai.websocket.util;
import android.util.Log;
/**
* Created by efan on 2017/4/13.
*/
public class Logger {
public static boolean LOG_ENABLE = true;
public static void i(String tag, String msg){
if (LOG_ENABLE){
Log.i(tag, msg);
}
}
public static void v(String tag, String msg){
if (LOG_ENABLE){
Log.v(tag, msg);
}
}
public static void d(String tag, String msg){
if (LOG_ENABLE){
Log.d(tag, msg);
}
}
public static void w(String tag, String msg){
if (LOG_ENABLE){
Log.w(tag, msg);
}
}
public static void e(String tag, String msg){
if (LOG_ENABLE){
Log.e(tag, msg);
}
}
}
package com.zhimai.websocket.util;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
/**
* Explain:
* Created on 2020/9/25.
*
* @author zhangshenglong
*/
public class NetWorkUtils {
public static boolean isOnline(Context context) {
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if (cm == null) {
return false;
}
try {
return cm.getActiveNetworkInfo().isAvailable();
}catch (Exception e){
return false;
}
}
public static boolean isOffline(Context context) {
return false == isOnline(context);
}
}
package com.zhimai.websocket.util;
import android.util.Log;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* 隔时请求刷新
*/
public class RxTimerUtil {
private static final String TAG = "RxTimerUtil";
private static Disposable mDisposableTime;
private static Disposable mDisposableSocket;
private static Disposable mDisposableIOTSocket;
private static Disposable mDisposablepPrint;
private static Disposable mDisposableOther;
/**
* milliseconds毫秒后执行next操作
*
* @param milliseconds
* @param next
*/
// public static void timer(long milliseconds, final IRxNext next) {
// Observable.timer(milliseconds, TimeUnit.MILLISECONDS)
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(new Observer<Long>() {
// @Override
// public void onSubscribe(@NonNull Disposable disposable) {
// mDisposableTime = disposable;
// }
//
// @Override
// public void onNext(@NonNull Long number) {
// if (next != null) {
// next.doNext(number);
// }
// }
//
// @Override
// public void onError(@NonNull Throwable e) {
// //取消订阅
// cancel();
// }
//
// @Override
// public void onComplete() {
// //取消订阅
// cancel();
// }
// });
// }
/**
* 每隔milliseconds毫秒后执行next操作
*
* @param milliseconds
* @param next
*/
public static void interval(final int which, long milliseconds, final IRxNext next) {
Observable.interval(milliseconds, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.io())
// .subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Long>() {
@Override
public void onSubscribe(@NonNull Disposable disposable) {
switch (which) {
case SysCode.RX_TIMER_TYPE.TIME:
mDisposableTime = disposable;
break;
case SysCode.RX_TIMER_TYPE.SOCKET:
mDisposableSocket = disposable;
break;
case SysCode.RX_TIMER_TYPE.PRINT:
mDisposablepPrint = disposable;
break;
case SysCode.RX_TIMER_TYPE.OTHER:
mDisposableOther = disposable;
break;
case SysCode.RX_TIMER_TYPE.IOT_SOCKET_PING:
mDisposableIOTSocket = disposable;
break;
}
}
@Override
public void onNext(@NonNull Long number) {
if (next != null) {
// Logger.e("************",Thread.currentThread().getName());
next.doNext(number);
}
}
@Override
public void onError(@NonNull Throwable e) {
}
@Override
public void onComplete() {
}
});
}
/**
* 取消订阅
*/
public static void cancel(int which) {
switch (which) {
case SysCode.RX_TIMER_TYPE.TIME:
if (mDisposableTime != null && !mDisposableTime.isDisposed()) {
mDisposableTime.dispose();
Log.e(TAG, "====定时器取消mDisposableTime======");
}
break;
case SysCode.RX_TIMER_TYPE.SOCKET:
if (mDisposableSocket != null && !mDisposableSocket.isDisposed()) {
mDisposableSocket.dispose();
Log.e(TAG, "====定时器取消mDisposableSocket======");
}
break;
case SysCode.RX_TIMER_TYPE.PRINT:
if (mDisposablepPrint != null && !mDisposablepPrint.isDisposed()) {
mDisposablepPrint.dispose();
Log.e(TAG, "====定时器取消mDisposablepPrint======");
}
break;
case SysCode.RX_TIMER_TYPE.OTHER:
if (mDisposableOther != null && !mDisposableOther.isDisposed()) {
mDisposableOther.dispose();
Log.e(TAG, "====定时器取消mDisposableOther======");
}
break;
case SysCode.RX_TIMER_TYPE.IOT_SOCKET_PING:
if (mDisposableIOTSocket != null && !mDisposableIOTSocket.isDisposed()) {
mDisposableIOTSocket.dispose();
Log.e(TAG, "====定时器取消mDisposableIOTSocket======");
}
break;
}
}
public interface IRxNext {
void doNext(long number);
}
}
This diff is collapsed.
package com.zhimai.websocket.util;
/**
* 常量
*/
public class SysCode {
/**
* 定时器类型
*/
public interface RX_TIMER_TYPE {
int TIME = 0;
int SOCKET = 1;
int PRINT = 2;
int OTHER = 3;
int IOT_SOCKET_PING=4;
}
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment