Compare commits
2 Commits
d1fa83d15c
...
5239088041
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5239088041 | ||
|
|
a43fcad558 |
@ -7,7 +7,7 @@ apiVersion = "82"
|
|||||||
datastore = "1.1.7"
|
datastore = "1.1.7"
|
||||||
datastorePreferences = "1.1.7"
|
datastorePreferences = "1.1.7"
|
||||||
googleSymbolProcessingApi = "2.0.0-1.0.24"
|
googleSymbolProcessingApi = "2.0.0-1.0.24"
|
||||||
gson = "2.13.1"
|
gson = "2.13.2"
|
||||||
jsonVersion = "20250517"
|
jsonVersion = "20250517"
|
||||||
kotlin = "2.0.0"
|
kotlin = "2.0.0"
|
||||||
coreKtx = "1.16.0"
|
coreKtx = "1.16.0"
|
||||||
|
|||||||
@ -50,6 +50,8 @@ dependencies {
|
|||||||
implementation(libs.androidx.datastore.preferences)
|
implementation(libs.androidx.datastore.preferences)
|
||||||
// implementation("androidx.datastore:datastore-preferences-rxjava3:1.1.7")
|
// implementation("androidx.datastore:datastore-preferences-rxjava3:1.1.7")
|
||||||
implementation(libs.androidx.datastore)
|
implementation(libs.androidx.datastore)
|
||||||
|
implementation(libs.squareup.okhttp)
|
||||||
|
implementation(libs.gson)
|
||||||
// implementation("androidx.datastore:datastore-rxjava3:1.1.7")
|
// implementation("androidx.datastore:datastore-rxjava3:1.1.7")
|
||||||
implementation("androidx.security:security-state:1.0.0-alpha04")
|
implementation("androidx.security:security-state:1.0.0-alpha04")
|
||||||
testImplementation(libs.junit)
|
testImplementation(libs.junit)
|
||||||
|
|||||||
@ -35,11 +35,6 @@ class BluetoothInfo(private val context: Context) {
|
|||||||
|
|
||||||
val requiredPermissions: Array<String>
|
val requiredPermissions: Array<String>
|
||||||
get() = buildList {
|
get() = buildList {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
add(Manifest.permission.BLUETOOTH_CONNECT)
|
||||||
add(Manifest.permission.BLUETOOTH_CONNECT)
|
|
||||||
} else {
|
|
||||||
add(Manifest.permission.BLUETOOTH)
|
|
||||||
add(Manifest.permission.BLUETOOTH_ADMIN)
|
|
||||||
}
|
|
||||||
}.toTypedArray()
|
}.toTypedArray()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,141 @@
|
|||||||
package com.xyzshell.andinfo.libs
|
package com.xyzshell.andinfo.libs
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.io.RandomAccessFile
|
||||||
|
|
||||||
|
data class MemoryInfo(
|
||||||
|
val totalRam: Long, // 总内存 (字节)
|
||||||
|
val availableRam: Long, // 可用内存 (字节)
|
||||||
|
val usedRam: Long, // 已使用内存 (字节)
|
||||||
|
val memType: String, // 内存类型 (LPDDR4/LPDDR5等)
|
||||||
|
val channels: Int, // 内存通道数
|
||||||
|
val zramTotal: Long, // zram 总大小 (字节)
|
||||||
|
val zramUsed: Long // zram 已使用 (字节)
|
||||||
|
)
|
||||||
|
|
||||||
class MemInfo {
|
class MemInfo {
|
||||||
|
|
||||||
|
fun getMemoryInfo(): MemoryInfo {
|
||||||
|
val totalRam = getTotalMemory()
|
||||||
|
val availableRam = getAvailableMemory()
|
||||||
|
val usedRam = totalRam - availableRam
|
||||||
|
val memType = getMemoryType()
|
||||||
|
val channels = getMemoryChannels()
|
||||||
|
val (zramTotal, zramUsed) = getZramInfo()
|
||||||
|
|
||||||
|
return MemoryInfo(
|
||||||
|
totalRam = totalRam,
|
||||||
|
availableRam = availableRam,
|
||||||
|
usedRam = usedRam,
|
||||||
|
memType = memType,
|
||||||
|
channels = channels,
|
||||||
|
zramTotal = zramTotal,
|
||||||
|
zramUsed = zramUsed
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTotalMemory(): Long {
|
||||||
|
return try {
|
||||||
|
val memInfo = File("/proc/meminfo")
|
||||||
|
memInfo.readLines().find { it.startsWith("MemTotal:") }?.let {
|
||||||
|
it.split("\\s+".toRegex())[1].toLong() * 1024
|
||||||
|
} ?: 0L
|
||||||
|
} catch (e: Exception) {
|
||||||
|
0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAvailableMemory(): Long {
|
||||||
|
return try {
|
||||||
|
val memInfo = File("/proc/meminfo")
|
||||||
|
memInfo.readLines().find { it.startsWith("MemAvailable:") }?.let {
|
||||||
|
it.split("\\s+".toRegex())[1].toLong() * 1024
|
||||||
|
} ?: 0L
|
||||||
|
} catch (e: Exception) {
|
||||||
|
0L
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMemoryType(): String {
|
||||||
|
return try {
|
||||||
|
// 尝试从多个可能的位置读取
|
||||||
|
val paths = listOf(
|
||||||
|
"/sys/class/devfreq/ddrfreq/device/type",
|
||||||
|
"/sys/class/devfreq/soc:qcom,cpu-cpu-llcc-bw/device/type",
|
||||||
|
"/proc/device-tree/memory/device_type"
|
||||||
|
)
|
||||||
|
|
||||||
|
for (path in paths) {
|
||||||
|
val file = File(path)
|
||||||
|
if (file.exists()) {
|
||||||
|
val content = file.readText().trim()
|
||||||
|
if (content.isNotEmpty()) {
|
||||||
|
return content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 尝试从 getprop 获取
|
||||||
|
Runtime.getRuntime().exec("getprop ro.boot.ddr_type").inputStream.bufferedReader().readText().trim()
|
||||||
|
.ifEmpty { "Unknown" }
|
||||||
|
} catch (e: Exception) {
|
||||||
|
"Unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getMemoryChannels(): Int {
|
||||||
|
return try {
|
||||||
|
// 尝试从设备树或内核日志获取
|
||||||
|
val dmesgChannels = Runtime.getRuntime()
|
||||||
|
.exec("dmesg | grep -i 'memory.*channel'")
|
||||||
|
.inputStream.bufferedReader().readText()
|
||||||
|
|
||||||
|
// 解析通道数,默认返回 2
|
||||||
|
when {
|
||||||
|
dmesgChannels.contains("dual", ignoreCase = true) -> 2
|
||||||
|
dmesgChannels.contains("quad", ignoreCase = true) -> 4
|
||||||
|
dmesgChannels.contains("single", ignoreCase = true) -> 1
|
||||||
|
else -> 2 // 默认双通道
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getZramInfo(): Pair<Long, Long> {
|
||||||
|
return try {
|
||||||
|
var total = 0L
|
||||||
|
var used = 0L
|
||||||
|
|
||||||
|
// 检查所有 zram 设备
|
||||||
|
for (i in 0..7) {
|
||||||
|
val devicePath = "/sys/block/zram$i"
|
||||||
|
if (!File(devicePath).exists()) continue
|
||||||
|
|
||||||
|
// 读取总大小
|
||||||
|
File("$devicePath/disksize").readText().trim().toLongOrNull()?.let {
|
||||||
|
total += it
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取已使用大小
|
||||||
|
File("$devicePath/mem_used_total").readText().trim().toLongOrNull()?.let {
|
||||||
|
used += it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair(total, used)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Pair(0L, 0L)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化内存大小
|
||||||
|
fun formatBytes(bytes: Long): String {
|
||||||
|
return when {
|
||||||
|
bytes < 1024 -> "$bytes B"
|
||||||
|
bytes < 1024 * 1024 -> String.format("%.2f KB", bytes / 1024.0)
|
||||||
|
bytes < 1024 * 1024 * 1024 -> String.format("%.2f MB", bytes / (1024.0 * 1024))
|
||||||
|
else -> String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
package com.xyzshell.andinfo.libs
|
||||||
|
|
||||||
|
class NetworkInfo {
|
||||||
|
}
|
||||||
@ -5,22 +5,27 @@ import android.os.Environment
|
|||||||
import android.os.StatFs
|
import android.os.StatFs
|
||||||
import com.xyzshell.andinfo.libs.storage.models.EncryptionType
|
import com.xyzshell.andinfo.libs.storage.models.EncryptionType
|
||||||
import com.xyzshell.andinfo.utils.SystemProperties
|
import com.xyzshell.andinfo.utils.SystemProperties
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
class StorageInfo(private val context: Context) {
|
class StorageInfo(private val context: Context) {
|
||||||
|
|
||||||
|
// 内部存储总空间
|
||||||
val internalStorageTotalSpace: Long
|
val internalStorageTotalSpace: Long
|
||||||
get() = StatFs(Environment.getDataDirectory().absolutePath).let { statFs ->
|
get() = StatFs(Environment.getDataDirectory().absolutePath).let { statFs ->
|
||||||
statFs.blockCountLong * statFs.blockSizeLong
|
statFs.blockCountLong * statFs.blockSizeLong
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 内部存储可用空间
|
||||||
val internalStorageAvailableSpace: Long
|
val internalStorageAvailableSpace: Long
|
||||||
get() = StatFs(Environment.getDataDirectory().absolutePath).let { statFs ->
|
get() = StatFs(Environment.getDataDirectory().absolutePath).let { statFs ->
|
||||||
statFs.availableBlocksLong * statFs.blockSizeLong
|
statFs.availableBlocksLong * statFs.blockSizeLong
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 内部存储已使用空间
|
||||||
val internalStorageUsedSpace: Long
|
val internalStorageUsedSpace: Long
|
||||||
get() = internalStorageTotalSpace - internalStorageAvailableSpace
|
get() = internalStorageTotalSpace - internalStorageAvailableSpace
|
||||||
|
|
||||||
|
// 内部存储是否加密
|
||||||
val isInternalStorageEncrypted: Boolean?
|
val isInternalStorageEncrypted: Boolean?
|
||||||
get() = when (SystemProperties.getString("ro.crypto.state", "unknown")) {
|
get() = when (SystemProperties.getString("ro.crypto.state", "unknown")) {
|
||||||
"encrypted" -> true
|
"encrypted" -> true
|
||||||
@ -28,6 +33,7 @@ class StorageInfo(private val context: Context) {
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 内部存储加密类型
|
||||||
val internalStorageEncryptionType: EncryptionType?
|
val internalStorageEncryptionType: EncryptionType?
|
||||||
get() = when (SystemProperties.getString("ro.crypto.type", "unknown")) {
|
get() = when (SystemProperties.getString("ro.crypto.type", "unknown")) {
|
||||||
"none" -> EncryptionType.NONE
|
"none" -> EncryptionType.NONE
|
||||||
@ -36,6 +42,58 @@ class StorageInfo(private val context: Context) {
|
|||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 内部存储文件系统类型
|
||||||
|
val internalStorageFileSystemType: String?
|
||||||
|
get() = try {
|
||||||
|
// 方法1: 读取 /proc/mounts 文件
|
||||||
|
File("/proc/mounts").readLines()
|
||||||
|
.find { it.contains(" /data ") }
|
||||||
|
?.split("\\s+".toRegex())
|
||||||
|
?.getOrNull(2)
|
||||||
|
?: run {
|
||||||
|
// 方法2: 使用 StatFs (Android 8.0+)
|
||||||
|
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||||
|
val statFs = StatFs("/data")
|
||||||
|
statFs.javaClass.getMethod("getFileSystemName").invoke(statFs) as? String
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
// /data 目录总大小
|
||||||
|
val dataDirectoryTotalSpace: Long
|
||||||
|
get() = StatFs("/data").let { statFs ->
|
||||||
|
statFs.blockCountLong * statFs.blockSizeLong
|
||||||
|
}
|
||||||
|
|
||||||
|
// /data 目录已使用空间
|
||||||
|
val dataDirectoryUsedSpace: Long
|
||||||
|
get() = StatFs("/data").let { statFs ->
|
||||||
|
val total = statFs.blockCountLong * statFs.blockSizeLong
|
||||||
|
val available = statFs.availableBlocksLong * statFs.blockSizeLong
|
||||||
|
total - available
|
||||||
|
}
|
||||||
|
|
||||||
|
// 应用程序和数据占用大小
|
||||||
|
val applicationsAndDataSize: Long
|
||||||
|
get() = try {
|
||||||
|
val dataDir = File("/data/data")
|
||||||
|
val appDir = File("/data/app")
|
||||||
|
calculateDirectorySize(dataDir) + calculateDirectorySize(appDir)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
0L
|
||||||
|
}
|
||||||
|
|
||||||
|
// 系统占用大小
|
||||||
|
val systemSize: Long
|
||||||
|
get() = StatFs(Environment.getRootDirectory().absolutePath).let { statFs ->
|
||||||
|
statFs.blockCountLong * statFs.blockSizeLong
|
||||||
|
}
|
||||||
|
|
||||||
|
// 外部存储总空间
|
||||||
val externalStorageTotalSpace: Long?
|
val externalStorageTotalSpace: Long?
|
||||||
get() = runCatching {
|
get() = runCatching {
|
||||||
StatFs(Environment.getExternalStorageDirectory().absolutePath).let { statFs ->
|
StatFs(Environment.getExternalStorageDirectory().absolutePath).let { statFs ->
|
||||||
@ -43,6 +101,7 @@ class StorageInfo(private val context: Context) {
|
|||||||
}
|
}
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
|
|
||||||
|
// 外部存储可用空间
|
||||||
val externalStorageAvailableSpace: Long?
|
val externalStorageAvailableSpace: Long?
|
||||||
get() = runCatching {
|
get() = runCatching {
|
||||||
StatFs(Environment.getExternalStorageDirectory().absolutePath).let { statFs ->
|
StatFs(Environment.getExternalStorageDirectory().absolutePath).let { statFs ->
|
||||||
@ -50,6 +109,7 @@ class StorageInfo(private val context: Context) {
|
|||||||
}
|
}
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
|
|
||||||
|
// 外部存储已使用空间
|
||||||
val externalStorageUsedSpace: Long?
|
val externalStorageUsedSpace: Long?
|
||||||
get() = externalStorageTotalSpace?.let { total ->
|
get() = externalStorageTotalSpace?.let { total ->
|
||||||
externalStorageAvailableSpace?.let { available ->
|
externalStorageAvailableSpace?.let { available ->
|
||||||
@ -57,36 +117,76 @@ class StorageInfo(private val context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 外部存储是否为模拟存储
|
||||||
val isExternalStorageEmulated: Boolean
|
val isExternalStorageEmulated: Boolean
|
||||||
get() = Environment.isExternalStorageEmulated()
|
get() = Environment.isExternalStorageEmulated()
|
||||||
|
|
||||||
|
// 外部存储是否可移除
|
||||||
val isExternalStorageRemovable: Boolean
|
val isExternalStorageRemovable: Boolean
|
||||||
get() = Environment.isExternalStorageRemovable()
|
get() = Environment.isExternalStorageRemovable()
|
||||||
|
|
||||||
|
// 是否支持可更新的 APEX
|
||||||
val hasUpdatableApex: Boolean?
|
val hasUpdatableApex: Boolean?
|
||||||
get() = SystemProperties.getBoolean("ro.apex.updatable")
|
get() = SystemProperties.getBoolean("ro.apex.updatable")
|
||||||
|
|
||||||
|
// 是否使用 system-as-root 分区方案
|
||||||
val usesSystemAsRoot: Boolean?
|
val usesSystemAsRoot: Boolean?
|
||||||
get() = SystemProperties.getBoolean("ro.build.system_root_image")
|
get() = SystemProperties.getBoolean("ro.build.system_root_image")
|
||||||
|
|
||||||
|
// 是否使用 A/B 分区更新方案
|
||||||
val usesAb: Boolean?
|
val usesAb: Boolean?
|
||||||
get() = SystemProperties.getBoolean("ro.build.ab_update")
|
get() = SystemProperties.getBoolean("ro.build.ab_update")
|
||||||
|
|
||||||
|
// A/B OTA 分区列表
|
||||||
val abOtaPartitions: Array<String>?
|
val abOtaPartitions: Array<String>?
|
||||||
get() = SystemProperties.getString("ro.product.ab_ota_partitions")?.split(",")?.toTypedArray()
|
get() = SystemProperties.getString("ro.product.ab_ota_partitions")?.split(",")?.toTypedArray()
|
||||||
|
|
||||||
|
// 是否使用动态分区
|
||||||
val usesDynamicPartitions: Boolean?
|
val usesDynamicPartitions: Boolean?
|
||||||
get() = SystemProperties.getBoolean("ro.boot.dynamic_partitions")
|
get() = SystemProperties.getBoolean("ro.boot.dynamic_partitions")
|
||||||
|
|
||||||
|
// 是否使用改造的动态分区
|
||||||
val usesRetrofittedDynamicPartitions: Boolean?
|
val usesRetrofittedDynamicPartitions: Boolean?
|
||||||
get() = SystemProperties.getBoolean("ro.boot.dynamic_partitions_retrofit")
|
get() = SystemProperties.getBoolean("ro.boot.dynamic_partitions_retrofit")
|
||||||
|
|
||||||
|
// 是否使用虚拟 A/B 分区
|
||||||
val usesVirtualAb: Boolean?
|
val usesVirtualAb: Boolean?
|
||||||
get() = SystemProperties.getBoolean("ro.virtual_ab.enabled")
|
get() = SystemProperties.getBoolean("ro.virtual_ab.enabled")
|
||||||
|
|
||||||
|
// 是否使用改造的虚拟 A/B 分区
|
||||||
val usesRetrofittedVirtualAb: Boolean?
|
val usesRetrofittedVirtualAb: Boolean?
|
||||||
get() = SystemProperties.getBoolean("ro.virtual_ab.retrofit")
|
get() = SystemProperties.getBoolean("ro.virtual_ab.retrofit")
|
||||||
|
|
||||||
|
// 虚拟 A/B 分区是否启用压缩
|
||||||
val usesCompressedVirtualAb: Boolean?
|
val usesCompressedVirtualAb: Boolean?
|
||||||
get() = SystemProperties.getBoolean("ro.virtual_ab.compression.enabled")
|
get() = SystemProperties.getBoolean("ro.virtual_ab.compression.enabled")
|
||||||
|
|
||||||
|
// 计算目录大小(递归)
|
||||||
|
private fun calculateDirectorySize(directory: File): Long {
|
||||||
|
var size = 0L
|
||||||
|
try {
|
||||||
|
if (directory.exists() && directory.isDirectory) {
|
||||||
|
directory.listFiles()?.forEach { file ->
|
||||||
|
size += if (file.isDirectory) {
|
||||||
|
calculateDirectorySize(file)
|
||||||
|
} else {
|
||||||
|
file.length()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
// 可能因权限问题无法访问某些目录
|
||||||
|
}
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化存储大小
|
||||||
|
fun formatBytes(bytes: Long): String {
|
||||||
|
return when {
|
||||||
|
bytes < 1024 -> "$bytes B"
|
||||||
|
bytes < 1024 * 1024 -> String.format("%.2f KB", bytes / 1024.0)
|
||||||
|
bytes < 1024 * 1024 * 1024 -> String.format("%.2f MB", bytes / (1024.0 * 1024))
|
||||||
|
else -> String.format("%.2f GB", bytes / (1024.0 * 1024 * 1024))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,94 @@
|
|||||||
|
package com.xyzshell.andinfo.utils
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import okhttp3.*
|
||||||
|
import okhttp3.MediaType.Companion.toMediaType
|
||||||
|
import okhttp3.RequestBody.Companion.toRequestBody
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class WebService {
|
||||||
|
private val client = OkHttpClient()
|
||||||
|
val gson = Gson()
|
||||||
|
fun postData(url: String, jsonData: String, callback: (String?, Exception?) -> Unit) {
|
||||||
|
val mediaType = "application/json; charset=utf-8".toMediaType()
|
||||||
|
val body = jsonData.toRequestBody(mediaType)
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.post(body)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
client.newCall(request).enqueue(object : Callback {
|
||||||
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
callback(null, e)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
response.use {
|
||||||
|
if (it.isSuccessful) {
|
||||||
|
callback(it.body?.string(), null)
|
||||||
|
} else {
|
||||||
|
callback(null, Exception("HTTP ${it.code}"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 同步版本
|
||||||
|
fun postDataSync(url: String, jsonData: String): String? {
|
||||||
|
val mediaType = "application/json; charset=utf-8".toMediaType()
|
||||||
|
val body = jsonData.toRequestBody(mediaType)
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.post(body)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
client.newCall(request).execute().use { response ->
|
||||||
|
return if (response.isSuccessful) {
|
||||||
|
response.body?.string()
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 序列化对象为 JSON
|
||||||
|
fun <T> toJson(obj: T): String {
|
||||||
|
return gson.toJson(obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 反序列化 JSON 为对象
|
||||||
|
inline fun <reified T> fromJson(json: String): T? {
|
||||||
|
return try {
|
||||||
|
gson.fromJson(json, T::class.java)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 带序列化的 POST 请求
|
||||||
|
fun <T> postObject(url: String, obj: T, callback: (String?, Exception?) -> Unit) {
|
||||||
|
val jsonData = toJson(obj)
|
||||||
|
postData(url, jsonData, callback)
|
||||||
|
}
|
||||||
|
// 带序列化和反序列化的 POST 请求
|
||||||
|
inline fun <reified T, reified R> postAndParse(
|
||||||
|
url: String,
|
||||||
|
obj: T,
|
||||||
|
crossinline callback: (R?, Exception?) -> Unit
|
||||||
|
) {
|
||||||
|
val jsonData = toJson(obj)
|
||||||
|
postData(url, jsonData) { response, error ->
|
||||||
|
if (error != null) {
|
||||||
|
callback(null, error)
|
||||||
|
} else if (response != null) {
|
||||||
|
val result = fromJson<R>(response)
|
||||||
|
callback(result, null)
|
||||||
|
} else {
|
||||||
|
callback(null, Exception("Empty response"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user