网络状态自定义组件---自定义控件

特点:独立性 + 可移植性
所有的逻辑都在这个控件中,直接在布局中添加就可以
技术点:协程CoroutineScope + 生命感知LifecycleOwner

直接贴代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import android.util.AttributeSet
import android.view.LayoutInflater
import android.widget.FrameLayout
import androidx.appcompat.widget.AppCompatTextView
import androidx.core.content.ContextCompat
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.OnLifecycleEvent
import com.yunda.android.framework.ui.YDLibApplication
import com.yunda.transportapp.base.BuildConfig
import com.yunda.transportapp.base.R
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.BufferedReader
import java.io.InputStreamReader

class NetworkStatusView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr), LifecycleObserver {

private val statusTextView: AppCompatTextView
private val coroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
private var monitoringJob: Job? = null

enum class NetworkStatus {
NO_NETWORK, BAD_NETWORK, WEAK_NETWORK, AVERAGE_NETWORK, GOOD_NETWORK
}

init {
val view = LayoutInflater.from(context).inflate(R.layout.network_status_view, this, true)
statusTextView = view.findViewById(R.id.statusTextView)

if (context is LifecycleOwner) {
(context as LifecycleOwner).lifecycle.addObserver(this)
}
}

private suspend fun updateStatus(newStatus: NetworkStatus) {
withContext(Dispatchers.Main){
when (newStatus) {
NetworkStatus.NO_NETWORK -> {
updateNetworkUI(
"当前无法链接网络,请检查网络设置是否正常。", "#EC4444",
R.drawable.icon_net_fail
)
}

NetworkStatus.BAD_NETWORK -> {
updateNetworkUI(
"当前网络状态极差,请检查网络设置是否正常。", "#EC4444",
R.drawable.icon_net_fail
)
}

NetworkStatus.WEAK_NETWORK -> {
updateNetworkUI(
"当前网络状态较差,请小心驾驶。", "#F68B28",
R.drawable.icon_net_moderate
)
}

NetworkStatus.AVERAGE_NETWORK -> {
updateNetworkUI(
"当前网络状态良好,请小心驾驶。", "#14C97D",
R.drawable.icon_net_good
)
}

NetworkStatus.GOOD_NETWORK -> {
updateNetworkUI(
"当前网络状态极好,请小心驾驶。", "#3976F3",
R.drawable.icon_net_ok
)
}
}
}
}

private fun updateNetworkUI(descStr: String, colorBg: String, iconResId: Int) {
try {
with(statusTextView) {
text = descStr
setCompoundDrawablesWithIntrinsicBounds(
ContextCompat.getDrawable(YDLibApplication.INSTANCE, iconResId),
null,
null,
null
)
backgroundTintList = ColorStateList.valueOf(Color.parseColor(colorBg))
}
} catch (e: Exception) {
e.printStackTrace()
}

}

private suspend fun checkNetworkSpeed(): NetworkStatus {
return withContext(Dispatchers.IO) {
try {
val process = Runtime.getRuntime().exec("ping -c 1 www.baidu.com")
val reader = BufferedReader(InputStreamReader(process.inputStream))
val output = reader.readLines()
reader.close()
val timeLine = output.find { it.contains("time=") }
val timeValue =
timeLine?.substringAfter("time=")?.substringBefore(" ms")?.toDoubleOrNull()

if (BuildConfig.DEBUG) {
println("Ping time: $timeValue ms")
}
when {
timeValue == null -> NetworkStatus.NO_NETWORK
timeValue >= 0 && timeValue < 100 -> NetworkStatus.GOOD_NETWORK
timeValue >= 100 && timeValue < 200 -> NetworkStatus.AVERAGE_NETWORK
timeValue >= 200 && timeValue < 600 -> NetworkStatus.WEAK_NETWORK
else -> NetworkStatus.BAD_NETWORK
}
} catch (e: Exception) {
e.printStackTrace()
NetworkStatus.NO_NETWORK
}
}

}

@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun startMonitoring() {
monitoringJob = coroutineScope.launch {
while (isActive) {
val status = checkNetworkSpeed()
updateStatus(status)
delay(5000) // 每5秒检查一次网络状态
}
}
}

@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun stopMonitoring() {
monitoringJob?.cancel()
}
}