From edb49731e49e56132c43af0cf2f97ad893d530cf Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Fri, 24 Jun 2022 17:28:21 +0800 Subject: [PATCH 1/5] 666 --- .../main/java/com/mx/keyvalue/MXKeyValue.kt | 26 ++++++++- .../com/mx/keyvalue/utils/MXKVObservable.kt | 55 +++++++++++++++++++ .../com/mx/keyvalue/utils/MXKVObserver.kt | 5 ++ .../main/java/com/mx/example/MainActivity.kt | 4 +- 4 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 LibKeyValue/src/main/java/com/mx/keyvalue/utils/MXKVObservable.kt create mode 100644 LibKeyValue/src/main/java/com/mx/keyvalue/utils/MXKVObserver.kt diff --git a/LibKeyValue/src/main/java/com/mx/keyvalue/MXKeyValue.kt b/LibKeyValue/src/main/java/com/mx/keyvalue/MXKeyValue.kt index 35de783..d396de7 100644 --- a/LibKeyValue/src/main/java/com/mx/keyvalue/MXKeyValue.kt +++ b/LibKeyValue/src/main/java/com/mx/keyvalue/MXKeyValue.kt @@ -5,12 +5,16 @@ import com.mx.keyvalue.base.IMXKeyValue import com.mx.keyvalue.db.MXDBKeyValue import com.mx.keyvalue.secret.IMXSecret import com.mx.keyvalue.secret.MXNoSecret +import com.mx.keyvalue.utils.MXKVObservable +import com.mx.keyvalue.utils.MXKVObserver +import kotlin.collections.HashMap class MXKeyValue( private val context: Context, name: String, secret: IMXSecret = MXNoSecret() ) { + private val observerSet = HashMap() private val dbKeyValue: IMXKeyValue = MXDBKeyValue(context.applicationContext, name.trim(), secret) @@ -40,11 +44,13 @@ class MXKeyValue( fun set(key: String, value: String?, expire_time: Long? = null): Boolean { val key_trim = key.trim() if (key_trim.isBlank()) return false - return if (value != null && value.isNotEmpty()) { + val result = if (value != null && value.isNotEmpty()) { dbKeyValue.set(key_trim, value, expire_time) } else { dbKeyValue.delete(key_trim) } + observerSet[key]?.set(value) + return result } /** @@ -62,7 +68,9 @@ class MXKeyValue( fun delete(key: String): Boolean { val key_trim = key.trim() if (key_trim.isBlank()) return false - return dbKeyValue.delete(key_trim) + val result = dbKeyValue.delete(key_trim) + observerSet[key]?.set(null) + return result } fun getAll(): Map { @@ -82,4 +90,18 @@ class MXKeyValue( fun cleanAll(): Boolean { return dbKeyValue.cleanAll() } + + fun addKeyObserver(key: String, observer: MXKVObserver) { + var observable = observerSet[key] + if (observable == null) { + observable = MXKVObservable(key, get(key)) + observerSet[key] = observable + } + observable.addObserver(observer) + } + + fun removeKeyObserver(key: String, observer: MXKVObserver) { + val observable = observerSet[key] ?: return + observable.deleteObserver(observer) + } } \ No newline at end of file diff --git a/LibKeyValue/src/main/java/com/mx/keyvalue/utils/MXKVObservable.kt b/LibKeyValue/src/main/java/com/mx/keyvalue/utils/MXKVObservable.kt new file mode 100644 index 0000000..8f339a5 --- /dev/null +++ b/LibKeyValue/src/main/java/com/mx/keyvalue/utils/MXKVObservable.kt @@ -0,0 +1,55 @@ +package com.mx.keyvalue.utils + +import android.os.Handler +import android.os.Looper + +internal class MXKVObservable(val key: String, defaultValue: String?) { + private val mHandler = Handler(Looper.getMainLooper()) + private val lock = Object() + private val observerList = HashSet() + + private var _value: String? = defaultValue + + fun set(value: String?) { + if (value == _value) { + return + } + _value = value + val list = synchronized(lock) { + observerList.toList() + } + if (list.isEmpty()) return + if (Looper.myLooper() == Looper.getMainLooper()) { + list.forEach { it.onChange(key, value) } + } else { + mHandler.post { + list.forEach { it.onChange(key, value) } + } + } + } + + fun get() = _value + + fun addObserver(o: MXKVObserver?) { + o ?: return + synchronized(lock) { + observerList.add(o) + } + mHandler.post { o.onChange(key, _value) } + } + + fun deleteObserver(o: MXKVObserver?) { + o ?: return + synchronized(lock) { + observerList.remove(o) + } + } + + fun deleteObservers() { + synchronized(lock) { + mHandler.removeCallbacksAndMessages(null) + observerList.clear() + } + } +} + diff --git a/LibKeyValue/src/main/java/com/mx/keyvalue/utils/MXKVObserver.kt b/LibKeyValue/src/main/java/com/mx/keyvalue/utils/MXKVObserver.kt new file mode 100644 index 0000000..55936e8 --- /dev/null +++ b/LibKeyValue/src/main/java/com/mx/keyvalue/utils/MXKVObserver.kt @@ -0,0 +1,5 @@ +package com.mx.keyvalue.utils + +interface MXKVObserver { + fun onChange(key: String, value: String?) +} \ No newline at end of file diff --git a/app/src/main/java/com/mx/example/MainActivity.kt b/app/src/main/java/com/mx/example/MainActivity.kt index a43237d..1659e3b 100644 --- a/app/src/main/java/com/mx/example/MainActivity.kt +++ b/app/src/main/java/com/mx/example/MainActivity.kt @@ -43,7 +43,9 @@ class MainActivity : AppCompatActivity() { repeat(100000) { val key = generalString(12) val value = generalString(1280) - KV.set(key, value) + if (!KV.set(key, value)) { + println("错误:$key -> $value") + } Thread.sleep(Random.nextLong(10, 100)) } } -- Gitee From 2484329c6a841f40defbaa8d0c0e5cb0215ce8b1 Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Sat, 25 Jun 2022 14:10:53 +0800 Subject: [PATCH 2/5] 666 --- .../java/com/mx/keyvalue/db/MXDBKeyValue.kt | 34 +--- app/src/main/AndroidManifest.xml | 7 +- .../main/java/com/mx/example/MainActivity.kt | 171 ------------------ app/src/main/java/com/mx/example/MyApp.kt | 17 ++ .../mx/example/app/DelegateTestActivity.kt | 99 ++++++++++ .../com/mx/example/app/ExpireTestActivity.kt | 33 ++++ .../java/com/mx/example/app/MainActivity.kt | 35 ++++ .../mx/example/app/MultThreadTestActivity.kt | 42 +++++ .../com/mx/example/{ => utils}/MySecret.kt | 2 +- .../main/java/com/mx/example/utils/SPUtils.kt | 58 ++++++ .../java/com/mx/example/utils/StringUtils.kt | 41 +++++ .../res/layout/activity_delegate_test.xml | 30 +++ .../main/res/layout/activity_expire_test.xml | 30 +++ app/src/main/res/layout/activity_main.xml | 21 +-- .../res/layout/activity_mult_thread_test.xml | 16 ++ gradle/wrapper/gradle-wrapper.properties | 4 +- 16 files changed, 424 insertions(+), 216 deletions(-) delete mode 100644 app/src/main/java/com/mx/example/MainActivity.kt create mode 100644 app/src/main/java/com/mx/example/MyApp.kt create mode 100644 app/src/main/java/com/mx/example/app/DelegateTestActivity.kt create mode 100644 app/src/main/java/com/mx/example/app/ExpireTestActivity.kt create mode 100644 app/src/main/java/com/mx/example/app/MainActivity.kt create mode 100644 app/src/main/java/com/mx/example/app/MultThreadTestActivity.kt rename app/src/main/java/com/mx/example/{ => utils}/MySecret.kt (94%) create mode 100644 app/src/main/java/com/mx/example/utils/SPUtils.kt create mode 100644 app/src/main/java/com/mx/example/utils/StringUtils.kt create mode 100644 app/src/main/res/layout/activity_delegate_test.xml create mode 100644 app/src/main/res/layout/activity_expire_test.xml create mode 100644 app/src/main/res/layout/activity_mult_thread_test.xml diff --git a/LibKeyValue/src/main/java/com/mx/keyvalue/db/MXDBKeyValue.kt b/LibKeyValue/src/main/java/com/mx/keyvalue/db/MXDBKeyValue.kt index 69c5d24..88c4a5a 100644 --- a/LibKeyValue/src/main/java/com/mx/keyvalue/db/MXDBKeyValue.kt +++ b/LibKeyValue/src/main/java/com/mx/keyvalue/db/MXDBKeyValue.kt @@ -6,6 +6,8 @@ import android.database.Cursor import com.mx.keyvalue.base.IMXKeyValue import com.mx.keyvalue.secret.IMXSecret import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.read +import kotlin.concurrent.write internal class MXDBKeyValue( context: Context, @@ -13,14 +15,11 @@ internal class MXDBKeyValue( private val mxSecret: IMXSecret ) : IMXKeyValue { private val lock = ReentrantReadWriteLock(true) - private val read_lock = lock.readLock() - private val write_lock = lock.writeLock() private val openHelper = MXKVSQLiteOpenHelper(context, dbName) override fun get(key: String): String? { - read_lock.lock() - try { + lock.read { val database = openHelper.readableDatabase var cursor: Cursor? = null try { @@ -56,15 +55,12 @@ internal class MXDBKeyValue( } catch (e: Exception) { } } - } finally { - read_lock.unlock() } return null } override fun set(key: String, value: String, dead_time: Long?): Boolean { - write_lock.lock() - try { + lock.write { val database = openHelper.writableDatabase try { val salt = mxSecret.generalSalt() @@ -82,15 +78,12 @@ internal class MXDBKeyValue( } finally { // database.close() } - } finally { - write_lock.unlock() } return false } override fun delete(key: String): Boolean { - write_lock.lock() - try { + lock.write { val database = openHelper.writableDatabase try { return database.delete( @@ -106,15 +99,12 @@ internal class MXDBKeyValue( } catch (e: Exception) { } } - } finally { - write_lock.unlock() } return false } override fun getAll(): Map { - read_lock.lock() - try { + lock.read { val database = openHelper.readableDatabase val result = HashMap() var cursor: Cursor? = null @@ -147,14 +137,11 @@ internal class MXDBKeyValue( } } return result - } finally { - read_lock.unlock() } } override fun cleanExpire(): Boolean { - write_lock.lock() - try { + lock.write { val database = openHelper.writableDatabase try { return database.delete( @@ -170,15 +157,12 @@ internal class MXDBKeyValue( } catch (e: Exception) { } } - } finally { - write_lock.unlock() } return false } override fun cleanAll(): Boolean { - write_lock.lock() - try { + lock.write { val database = openHelper.writableDatabase try { return database.delete(dbName, null, null) > 0 @@ -190,8 +174,6 @@ internal class MXDBKeyValue( } catch (e: Exception) { } } - } finally { - write_lock.unlock() } return false } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4431f16..3fdb134 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ package="com.mx.example"> @@ -18,7 +19,9 @@ - + + + \ No newline at end of file diff --git a/app/src/main/java/com/mx/example/MainActivity.kt b/app/src/main/java/com/mx/example/MainActivity.kt deleted file mode 100644 index 1659e3b..0000000 --- a/app/src/main/java/com/mx/example/MainActivity.kt +++ /dev/null @@ -1,171 +0,0 @@ -package com.mx.example - -import android.os.Bundle -import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity -import com.fasterxml.jackson.annotation.JsonIgnoreProperties -import com.fasterxml.jackson.databind.ObjectMapper -import com.fasterxml.jackson.module.kotlin.registerKotlinModule -import com.mx.keyvalue.MXKeyValue -import com.mx.keyvalue.delegate.* -import com.mx.keyvalue.secret.MXAESSecret -import com.mx.keyvalue.secret.MXNoSecret -import kotlinx.android.synthetic.main.activity_main.* -import java.lang.StringBuilder -import java.util.concurrent.TimeUnit -import kotlin.concurrent.thread -import kotlin.random.Random - -class MainActivity : AppCompatActivity() { - private val KV by lazy { - MXKeyValue( - application, "mx_kv_test", - MXAESSecret("89qew0lkcjz;lkui1=2=--093475kjhzcklj") - ) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - setContentView(R.layout.activity_main) - KV.cleanAll() - syncTestTxv.setOnClickListener { - repeat(10) { - thread { - repeat(100000) { - val key = generalString(12) - KV.get(key) - Thread.sleep(Random.nextLong(10, 100)) - } - } - } - repeat(10) { - thread { - repeat(100000) { - val key = generalString(12) - val value = generalString(1280) - if (!KV.set(key, value)) { - println("错误:$key -> $value") - } - Thread.sleep(Random.nextLong(10, 100)) - } - } - } - } - - - setExpireTxv.setOnClickListener { - KV.set( - "test_expire_key", - "1分钟失效:" + System.currentTimeMillis(), - System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(1) - ) - Toast.makeText(this, KV.get("test_expire_key", "失效"), Toast.LENGTH_SHORT).show() - } - readExpireTxv.setOnClickListener { - Toast.makeText(this, KV.get("test_expire_key", "失效"), Toast.LENGTH_SHORT).show() - } - deleteTxv.setOnClickListener { - KV.delete("test_expire_key") - Toast.makeText(this, KV.get("test_expire_key", "失效"), Toast.LENGTH_SHORT).show() - } - - var spend = 0L - val time = 200 - repeat(time) { - val key = generalString(12) - val value = generalString(1280) - val start = System.currentTimeMillis() - KV.set(key, value) - val read_value = KV.get(key) - if (read_value != value) { - println("错误:$key -> $value -> $read_value") - } else { -// println("正确:$key -> $value") - } - spend += (System.currentTimeMillis() - start) - } - println("平均耗时:${spend / time.toFloat()} ms") - - var boolDelegate by MXBoolDelegate(KV, "bool_test", true) - println("测试 MXBoolDelegate -> $boolDelegate") - boolDelegate = false - println("测试 MXBoolDelegate -> $boolDelegate") - - - var doubleDelegate by MXDoubleDelegate(KV, "double_test", 1.0) - println("测试 MXDoubleDelegate -> $doubleDelegate") - doubleDelegate = 2.0 - println("测试 MXDoubleDelegate -> $doubleDelegate") - - - var floatDelegate by MXFloatDelegate(KV, "float_test", 1f) - println("测试 MXFloatDelegate -> $floatDelegate") - floatDelegate = 2f - println("测试 MXFloatDelegate -> $floatDelegate") - - - var intDelegate by MXIntDelegate(KV, "int_test", 1) - println("测试 MXIntDelegate -> $intDelegate") - intDelegate = 2 - println("测试 MXIntDelegate -> $intDelegate") - - - var longDelegate by MXLongDelegate(KV, "long_test", 1) - println("测试 MXLongDelegate -> $longDelegate") - longDelegate = 2 - println("测试 MXLongDelegate -> $longDelegate") - - - var stringDelegate by MXStringDelegate(KV, "string_test", "testdef") - println("测试 MXStringDelegate -> $stringDelegate") - stringDelegate = "2" - println("测试 MXStringDelegate -> $stringDelegate") - - - var beanDelegate by MXBeanDelegate(KV, TestBean::class.java, "bean_test", null) - println("测试 BeanDelegate -> ${beanDelegate?.id} -> ${beanDelegate?.name}") - beanDelegate = TestBean("2sdf", "name") - println("测试 BeanDelegate -> ${beanDelegate?.id} -> ${beanDelegate?.name}") - - } - - private fun generalString(size: Int): String { - val KEYS = ('a'..'z') + ('A'..'Z') + ('0'..'9') - val stringBuilder = StringBuilder() - repeat(size) { - stringBuilder.append(KEYS.random()) - } - return stringBuilder.toString() - } - - - @JsonIgnoreProperties(ignoreUnknown = true) - data class TestBean constructor(val id: String, val name: String) - - class MXBeanDelegate( - kv: MXKeyValue, - private val clazz: Class, - name: String, - default: T - ) : MXBaseDelegate(kv, name, default) { - override fun stringToObject(value: String): T { - try { - val mapper = ObjectMapper() - mapper.registerKotlinModule() - return mapper.readValue(value, clazz) - } catch (e: Exception) { - } - return default - } - - override fun objectToString(obj: T): String? { - obj ?: return null - try { - val mapper = ObjectMapper() - return mapper.writeValueAsString(obj) - } catch (e: Exception) { - } - return null - } - } -} diff --git a/app/src/main/java/com/mx/example/MyApp.kt b/app/src/main/java/com/mx/example/MyApp.kt new file mode 100644 index 0000000..4b525d8 --- /dev/null +++ b/app/src/main/java/com/mx/example/MyApp.kt @@ -0,0 +1,17 @@ +package com.mx.example + +import android.app.Application + +class MyApp : Application() { + companion object { + var _appContext: Application? = null + val appContext: Application + get() = _appContext!! + } + + override fun onCreate() { + super.onCreate() + _appContext = this + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/mx/example/app/DelegateTestActivity.kt b/app/src/main/java/com/mx/example/app/DelegateTestActivity.kt new file mode 100644 index 0000000..006c537 --- /dev/null +++ b/app/src/main/java/com/mx/example/app/DelegateTestActivity.kt @@ -0,0 +1,99 @@ +package com.mx.example.app + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import com.mx.example.R +import com.mx.example.utils.SPUtils +import com.mx.keyvalue.MXKeyValue +import com.mx.keyvalue.delegate.* +import kotlinx.android.synthetic.main.activity_delegate_test.* + +class DelegateTestActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_delegate_test) + + testBtn.setOnClickListener { + var boolDelegate by MXBoolDelegate(SPUtils.KV, "bool_test", true) + println("测试 MXBoolDelegate -> $boolDelegate") + boolDelegate = false + println("测试 MXBoolDelegate -> $boolDelegate") + + + var doubleDelegate by MXDoubleDelegate(SPUtils.KV, "double_test", 1.0) + println("测试 MXDoubleDelegate -> $doubleDelegate") + doubleDelegate = 2.0 + println("测试 MXDoubleDelegate -> $doubleDelegate") + + + var floatDelegate by MXFloatDelegate(SPUtils.KV, "float_test", 1f) + println("测试 MXFloatDelegate -> $floatDelegate") + floatDelegate = 2f + println("测试 MXFloatDelegate -> $floatDelegate") + + + var intDelegate by MXIntDelegate(SPUtils.KV, "int_test", 1) + println("测试 MXIntDelegate -> $intDelegate") + intDelegate = 2 + println("测试 MXIntDelegate -> $intDelegate") + + + var longDelegate by MXLongDelegate(SPUtils.KV, "long_test", 1) + println("测试 MXLongDelegate -> $longDelegate") + longDelegate = 2 + println("测试 MXLongDelegate -> $longDelegate") + + + var stringDelegate by MXStringDelegate(SPUtils.KV, "string_test", "testdef") + println("测试 MXStringDelegate -> $stringDelegate") + stringDelegate = "2" + println("测试 MXStringDelegate -> $stringDelegate") + + + var beanDelegate by MXBeanDelegate( + SPUtils.KV, + TestBean::class.java, + "bean_test", + null + ) + println("测试 BeanDelegate -> ${beanDelegate?.id} -> ${beanDelegate?.name}") + beanDelegate = TestBean("2sdf", "name") + println("测试 BeanDelegate -> ${beanDelegate?.id} -> ${beanDelegate?.name}") + + } + } + + @JsonIgnoreProperties(ignoreUnknown = true) + data class TestBean constructor(val id: String, val name: String) + + class MXBeanDelegate( + kv: MXKeyValue, + private val clazz: Class, + name: String, + default: T + ) : MXBaseDelegate(kv, name, default) { + override fun stringToObject(value: String): T { + try { + val mapper = ObjectMapper() + mapper.registerKotlinModule() + return mapper.readValue(value, clazz) + } catch (e: Exception) { + } + return default + } + + override fun objectToString(obj: T): String? { + obj ?: return null + try { + val mapper = ObjectMapper() + return mapper.writeValueAsString(obj) + } catch (e: Exception) { + } + return null + } + } +} diff --git a/app/src/main/java/com/mx/example/app/ExpireTestActivity.kt b/app/src/main/java/com/mx/example/app/ExpireTestActivity.kt new file mode 100644 index 0000000..503c47e --- /dev/null +++ b/app/src/main/java/com/mx/example/app/ExpireTestActivity.kt @@ -0,0 +1,33 @@ +package com.mx.example.app + +import android.os.Bundle +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import com.mx.example.R +import com.mx.example.utils.SPUtils +import kotlinx.android.synthetic.main.activity_expire_test.* +import java.util.concurrent.TimeUnit + +class ExpireTestActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_expire_test) + + setExpireTxv.setOnClickListener { + SPUtils.set( + "test_expire_key", + "1分钟失效:" + System.currentTimeMillis(), + System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(1) + ) + Toast.makeText(this, SPUtils.get("test_expire_key", "失效"), Toast.LENGTH_SHORT).show() + } + readExpireTxv.setOnClickListener { + Toast.makeText(this, SPUtils.get("test_expire_key", "失效"), Toast.LENGTH_SHORT).show() + } + deleteTxv.setOnClickListener { + SPUtils.delete("test_expire_key") + Toast.makeText(this, SPUtils.get("test_expire_key", "失效"), Toast.LENGTH_SHORT).show() + } + } +} diff --git a/app/src/main/java/com/mx/example/app/MainActivity.kt b/app/src/main/java/com/mx/example/app/MainActivity.kt new file mode 100644 index 0000000..4c29fec --- /dev/null +++ b/app/src/main/java/com/mx/example/app/MainActivity.kt @@ -0,0 +1,35 @@ +package com.mx.example.app + +import android.content.Intent +import android.os.Bundle +import android.widget.Toast +import androidx.appcompat.app.AppCompatActivity +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import com.mx.example.R +import com.mx.example.utils.SPUtils +import com.mx.keyvalue.MXKeyValue +import com.mx.keyvalue.delegate.* +import kotlinx.android.synthetic.main.activity_main.* +import java.lang.StringBuilder +import java.util.concurrent.TimeUnit +import kotlin.concurrent.thread +import kotlin.random.Random + +class MainActivity : AppCompatActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_main) + expireTestTxv.setOnClickListener { + startActivity(Intent(this, ExpireTestActivity::class.java)) + } + threadTestTxv.setOnClickListener { + startActivity(Intent(this, MultThreadTestActivity::class.java)) + } + delegateTestTxv.setOnClickListener { + startActivity(Intent(this, DelegateTestActivity::class.java)) + } + } +} diff --git a/app/src/main/java/com/mx/example/app/MultThreadTestActivity.kt b/app/src/main/java/com/mx/example/app/MultThreadTestActivity.kt new file mode 100644 index 0000000..bb6ce0b --- /dev/null +++ b/app/src/main/java/com/mx/example/app/MultThreadTestActivity.kt @@ -0,0 +1,42 @@ +package com.mx.example.app + +import android.os.Bundle +import androidx.appcompat.app.AppCompatActivity +import com.mx.example.R +import com.mx.example.utils.SPUtils +import com.mx.example.utils.StringUtils +import kotlinx.android.synthetic.main.activity_mult_thread_test.* +import kotlin.concurrent.thread +import kotlin.random.Random + +class MultThreadTestActivity : AppCompatActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.activity_mult_thread_test) + syncTestTxv.setOnClickListener { + repeat(30) { + thread { + repeat(100000) { + getSetUnit() + } + } + } + } + } + + private fun getSetUnit() { + val key = StringUtils.generalKey() + val value = StringUtils.generalString(128) + if (!SPUtils.set(key, value)) { + println("写入错误:$key -> $value") + } + val read = SPUtils.get(key) + if (read != value) { + println("读取错误1:$key -> $value") + println("读取错误2:$key -> $read") + } + println("测试通过一次") + Thread.sleep(Random.nextLong(10, 100)) + } + +} diff --git a/app/src/main/java/com/mx/example/MySecret.kt b/app/src/main/java/com/mx/example/utils/MySecret.kt similarity index 94% rename from app/src/main/java/com/mx/example/MySecret.kt rename to app/src/main/java/com/mx/example/utils/MySecret.kt index 2ee2c78..55b11fc 100644 --- a/app/src/main/java/com/mx/example/MySecret.kt +++ b/app/src/main/java/com/mx/example/utils/MySecret.kt @@ -1,4 +1,4 @@ -package com.mx.example +package com.mx.example.utils import com.mx.keyvalue.secret.IMXSecret import java.util.* diff --git a/app/src/main/java/com/mx/example/utils/SPUtils.kt b/app/src/main/java/com/mx/example/utils/SPUtils.kt new file mode 100644 index 0000000..f5ad59b --- /dev/null +++ b/app/src/main/java/com/mx/example/utils/SPUtils.kt @@ -0,0 +1,58 @@ +package com.mx.example.utils + +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.module.kotlin.registerKotlinModule +import com.mx.example.MyApp +import com.mx.keyvalue.MXKeyValue +import com.mx.keyvalue.delegate.MXBaseDelegate +import com.mx.keyvalue.secret.MXAESSecret + +// 缓存类 +object SPUtils { + val KV by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { + MXKeyValue(MyApp.appContext, "kvdb_kv_v2") + } + + fun get(key: String, def: String? = null): String? { + return KV.get(key, def) + } + + fun set(key: String, value: String?, expire_time: Long? = null): Boolean { + return KV.set(key, value, expire_time) + } + + fun cleanAll() { + KV.cleanAll() + } + + fun delete(key: String): Boolean { + return KV.delete(key) + } + + class MXBeanDelegate( + kv: MXKeyValue, + private val clazz: Class, + name: String, + default: T? + ) : MXBaseDelegate(kv, name, default) { + override fun stringToObject(value: String): T? { + try { + val mapper = ObjectMapper() + mapper.registerKotlinModule() + return mapper.readValue(value, clazz) + } catch (e: Exception) { + } + return default + } + + override fun objectToString(obj: T?): String? { + obj ?: return null + try { + val mapper = ObjectMapper() + return mapper.writeValueAsString(obj) + } catch (e: Exception) { + } + return null + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/mx/example/utils/StringUtils.kt b/app/src/main/java/com/mx/example/utils/StringUtils.kt new file mode 100644 index 0000000..76bc36b --- /dev/null +++ b/app/src/main/java/com/mx/example/utils/StringUtils.kt @@ -0,0 +1,41 @@ +package com.mx.example.utils + +import java.lang.StringBuilder +import java.security.MessageDigest +import java.util.* + +object StringUtils { + fun generalKey(): String { + return UUID.randomUUID().toString().md5() + } + + fun generalString(size: Int): String { + val KEYS = ('a'..'z') + ('A'..'Z') + ('0'..'9') + val stringBuilder = StringBuilder() + repeat(size) { + stringBuilder.append(KEYS.random()) + } + return stringBuilder.toString() + } + + private fun String.md5(): String { + try { + val instance = MessageDigest.getInstance("MD5")//获取md5加密对象 + val digest = instance.digest(this.toByteArray())//对字符串加密,返回字节数组 + val sb = StringBuffer() + for (b in digest) { + val i: Int = b.toInt() and 0xff//获取低八位有效值 + var hexString = Integer.toHexString(i)//将整数转化为16进制 + if (hexString.length < 2) { + hexString = "0$hexString"//如果是一位的话,补0 + } + sb.append(hexString) + } + return sb.toString() + + } catch (e: Exception) { + e.printStackTrace() + } + return "" + } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_delegate_test.xml b/app/src/main/res/layout/activity_delegate_test.xml new file mode 100644 index 0000000..e7d14cc --- /dev/null +++ b/app/src/main/res/layout/activity_delegate_test.xml @@ -0,0 +1,30 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_expire_test.xml b/app/src/main/res/layout/activity_expire_test.xml new file mode 100644 index 0000000..414e5b0 --- /dev/null +++ b/app/src/main/res/layout/activity_expire_test.xml @@ -0,0 +1,30 @@ + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index a1e24b2..bcaad91 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -5,33 +5,26 @@ android:layout_height="match_parent" android:gravity="center" android:orientation="vertical" - tools:context=".MainActivity"> + tools:context=".app.MainActivity"> + android:text="超时测试" /> - - + android:text="多线程测试" /> + android:text="对象代理测试" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_mult_thread_test.xml b/app/src/main/res/layout/activity_mult_thread_test.xml new file mode 100644 index 0000000..386b1d7 --- /dev/null +++ b/app/src/main/res/layout/activity_mult_thread_test.xml @@ -0,0 +1,16 @@ + + + + + \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index b891b8a..db178d5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Mon Aug 09 13:56:32 CST 2021 +#Sat Jun 25 11:44:57 CST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME -- Gitee From cf88b2dbb28d32166606207b86ec969f24651738 Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Sat, 25 Jun 2022 14:42:22 +0800 Subject: [PATCH 3/5] 666 --- .../com/mx/keyvalue/secret/MXAESSecret.kt | 19 ++++++++----------- .../main/java/com/mx/example/utils/SPUtils.kt | 2 +- .../java/com/mx/example/utils/StringUtils.kt | 2 +- .../res/layout/activity_delegate_test.xml | 13 ------------- 4 files changed, 10 insertions(+), 26 deletions(-) diff --git a/LibKeyValue/src/main/java/com/mx/keyvalue/secret/MXAESSecret.kt b/LibKeyValue/src/main/java/com/mx/keyvalue/secret/MXAESSecret.kt index dc001c7..cde7dc7 100644 --- a/LibKeyValue/src/main/java/com/mx/keyvalue/secret/MXAESSecret.kt +++ b/LibKeyValue/src/main/java/com/mx/keyvalue/secret/MXAESSecret.kt @@ -45,9 +45,6 @@ open class MXAESSecret(private val key: String) : IMXSecret { private val transformation = "AES/CBC/PKCS5Padding" - private var encryptCipher: Cipher? = null - private var decryptCipher: Cipher? = null - private fun generalMixKey(): ByteArray { val list = key.toMutableList() val keys = StringBuilder() @@ -74,16 +71,16 @@ open class MXAESSecret(private val key: String) : IMXSecret { return MXUtils.md5(keys.toString().toByteArray(), 16).toByteArray() } - init { - val keySpec = SecretKeySpec(generalMixKey(), "AES") - val ivSpec = IvParameterSpec(generalMixIv()) - encryptCipher = Cipher.getInstance(transformation).apply { + private val keySpec = SecretKeySpec(generalMixKey(), "AES") + private val ivSpec = IvParameterSpec(generalMixIv()) + private val encryptCipher: Cipher + get() = Cipher.getInstance(transformation).apply { init(Cipher.ENCRYPT_MODE, keySpec, ivSpec) } - decryptCipher = Cipher.getInstance(transformation).apply { + private val decryptCipher: Cipher + get() = Cipher.getInstance(transformation).apply { init(Cipher.DECRYPT_MODE, keySpec, ivSpec) } - } override fun generalSalt(): String { val length = Random.nextInt(15, 30) @@ -92,7 +89,7 @@ open class MXAESSecret(private val key: String) : IMXSecret { override fun encrypt(key: String, value: String, salt: String): String? { val relBytes = try { - encryptCipher?.doFinal((salt + value + key).toByteArray()) ?: return null + encryptCipher.doFinal((salt + value + key).toByteArray()) ?: return null } catch (e: Exception) { return null } @@ -102,7 +99,7 @@ open class MXAESSecret(private val key: String) : IMXSecret { override fun decrypt(key: String, secretValue: String, salt: String): String? { val bytes = try { - decryptCipher?.doFinal(Base64.decode(secretValue, Base64.DEFAULT)) ?: return null + decryptCipher.doFinal(Base64.decode(secretValue, Base64.DEFAULT)) ?: return null } catch (e: Exception) { return null } diff --git a/app/src/main/java/com/mx/example/utils/SPUtils.kt b/app/src/main/java/com/mx/example/utils/SPUtils.kt index f5ad59b..cb3e25b 100644 --- a/app/src/main/java/com/mx/example/utils/SPUtils.kt +++ b/app/src/main/java/com/mx/example/utils/SPUtils.kt @@ -10,7 +10,7 @@ import com.mx.keyvalue.secret.MXAESSecret // 缓存类 object SPUtils { val KV by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { - MXKeyValue(MyApp.appContext, "kvdb_kv_v2") + MXKeyValue(MyApp.appContext, "kvdb_kv_v1", MXAESSecret("27e2125d0a11a9aa65b9c9773673bc2a")) } fun get(key: String, def: String? = null): String? { diff --git a/app/src/main/java/com/mx/example/utils/StringUtils.kt b/app/src/main/java/com/mx/example/utils/StringUtils.kt index 76bc36b..871b6c7 100644 --- a/app/src/main/java/com/mx/example/utils/StringUtils.kt +++ b/app/src/main/java/com/mx/example/utils/StringUtils.kt @@ -6,7 +6,7 @@ import java.util.* object StringUtils { fun generalKey(): String { - return UUID.randomUUID().toString().md5() + return (UUID.randomUUID().toString() + generalString(32)).md5() } fun generalString(size: Int): String { diff --git a/app/src/main/res/layout/activity_delegate_test.xml b/app/src/main/res/layout/activity_delegate_test.xml index e7d14cc..bec6fed 100644 --- a/app/src/main/res/layout/activity_delegate_test.xml +++ b/app/src/main/res/layout/activity_delegate_test.xml @@ -14,17 +14,4 @@ android:padding="10dp" android:text="开始测试" /> - - - \ No newline at end of file -- Gitee From 9987bda3a3bbadcab5e95b38c500b04224bd9918 Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Sat, 25 Jun 2022 18:06:03 +0800 Subject: [PATCH 4/5] 666 --- .../main/java/com/mx/keyvalue/MXKeyValue.kt | 19 +++- .../java/com/mx/keyvalue/base/IMXKeyValue.kt | 2 + .../java/com/mx/keyvalue/db/MXDBKeyValue.kt | 89 ++++++++++++------- app/src/main/java/com/mx/example/MyApp.kt | 3 +- .../mx/example/app/MultThreadTestActivity.kt | 6 +- .../main/java/com/mx/example/utils/SPUtils.kt | 1 - 6 files changed, 79 insertions(+), 41 deletions(-) diff --git a/LibKeyValue/src/main/java/com/mx/keyvalue/MXKeyValue.kt b/LibKeyValue/src/main/java/com/mx/keyvalue/MXKeyValue.kt index d396de7..b046ec9 100644 --- a/LibKeyValue/src/main/java/com/mx/keyvalue/MXKeyValue.kt +++ b/LibKeyValue/src/main/java/com/mx/keyvalue/MXKeyValue.kt @@ -7,16 +7,24 @@ import com.mx.keyvalue.secret.IMXSecret import com.mx.keyvalue.secret.MXNoSecret import com.mx.keyvalue.utils.MXKVObservable import com.mx.keyvalue.utils.MXKVObserver +import com.mx.keyvalue.utils.MXUtils import kotlin.collections.HashMap class MXKeyValue( private val context: Context, - name: String, - secret: IMXSecret = MXNoSecret() + private val name: String, + private val secret: IMXSecret = MXNoSecret() ) { + companion object { + fun setDebug(debug: Boolean) { + MXUtils.setDebug(debug) + } + } + private val observerSet = HashMap() - private val dbKeyValue: IMXKeyValue = + private val dbKeyValue: IMXKeyValue by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { MXDBKeyValue(context.applicationContext, name.trim(), secret) + } init { if (!secret.validate()) { @@ -104,4 +112,9 @@ class MXKeyValue( val observable = observerSet[key] ?: return observable.deleteObserver(observer) } + + fun release() { + observerSet.clear() + dbKeyValue.release() + } } \ No newline at end of file diff --git a/LibKeyValue/src/main/java/com/mx/keyvalue/base/IMXKeyValue.kt b/LibKeyValue/src/main/java/com/mx/keyvalue/base/IMXKeyValue.kt index 6de043b..7bfed27 100644 --- a/LibKeyValue/src/main/java/com/mx/keyvalue/base/IMXKeyValue.kt +++ b/LibKeyValue/src/main/java/com/mx/keyvalue/base/IMXKeyValue.kt @@ -9,4 +9,6 @@ internal interface IMXKeyValue { fun cleanExpire(): Boolean fun cleanAll(): Boolean + + fun release() } \ No newline at end of file diff --git a/LibKeyValue/src/main/java/com/mx/keyvalue/db/MXDBKeyValue.kt b/LibKeyValue/src/main/java/com/mx/keyvalue/db/MXDBKeyValue.kt index 88c4a5a..8b5947a 100644 --- a/LibKeyValue/src/main/java/com/mx/keyvalue/db/MXDBKeyValue.kt +++ b/LibKeyValue/src/main/java/com/mx/keyvalue/db/MXDBKeyValue.kt @@ -3,26 +3,41 @@ package com.mx.keyvalue.db import android.content.ContentValues import android.content.Context import android.database.Cursor +import android.database.sqlite.SQLiteDatabase +import android.database.sqlite.SQLiteOpenHelper import com.mx.keyvalue.base.IMXKeyValue import com.mx.keyvalue.secret.IMXSecret +import com.mx.keyvalue.utils.MXUtils import java.util.concurrent.locks.ReentrantReadWriteLock import kotlin.concurrent.read import kotlin.concurrent.write internal class MXDBKeyValue( - context: Context, + private val context: Context, private val dbName: String, private val mxSecret: IMXSecret ) : IMXKeyValue { private val lock = ReentrantReadWriteLock(true) + private var openHelper: SQLiteOpenHelper? = null - private val openHelper = MXKVSQLiteOpenHelper(context, dbName) + private fun getDatabase(): SQLiteDatabase { + var sqLiteOpenHelper = openHelper + if (sqLiteOpenHelper == null) { + synchronized(this) { + if (sqLiteOpenHelper == null) { + sqLiteOpenHelper = MXKVSQLiteOpenHelper(context, dbName) + this.openHelper = sqLiteOpenHelper + } + } + } + return sqLiteOpenHelper!!.writableDatabase + } override fun get(key: String): String? { lock.read { - val database = openHelper.readableDatabase var cursor: Cursor? = null try { + val database = getDatabase() cursor = database.query( dbName, arrayOf( @@ -45,15 +60,12 @@ internal class MXDBKeyValue( } } catch (e: Exception) { e.printStackTrace() + MXUtils.log("get错误 -> $key -- ${e.message}") } finally { try { cursor?.close() } catch (e: Exception) { } - try { - // database.close() - } catch (e: Exception) { - } } } return null @@ -61,8 +73,9 @@ internal class MXDBKeyValue( override fun set(key: String, value: String, dead_time: Long?): Boolean { lock.write { - val database = openHelper.writableDatabase + val database = getDatabase() try { + database.beginTransaction() val salt = mxSecret.generalSalt() val value_encrypt = mxSecret.encrypt(key, value, salt) @@ -72,11 +85,14 @@ internal class MXDBKeyValue( values.put(MXKVSQLiteOpenHelper.DB_KEY_SALT, salt) values.put(MXKVSQLiteOpenHelper.DB_KEY_UPDATE_TIME, System.currentTimeMillis()) values.put(MXKVSQLiteOpenHelper.DB_KEY_DEAD_TIME, dead_time ?: 0L) - return database.replace(dbName, null, values) >= 0 + val result = database.replace(dbName, null, values) >= 0 + database.setTransactionSuccessful() + return result } catch (e: Exception) { e.printStackTrace() + MXUtils.log("set错误 -> $key = $value ; $dead_time -- ${e.message}") } finally { - // database.close() + database.endTransaction() } } return false @@ -84,20 +100,21 @@ internal class MXDBKeyValue( override fun delete(key: String): Boolean { lock.write { - val database = openHelper.writableDatabase + val database = getDatabase() try { - return database.delete( + database.beginTransaction() + val result = database.delete( dbName, "${MXKVSQLiteOpenHelper.DB_KEY_NAME}=?", arrayOf(key) ) > 0 + database.setTransactionSuccessful() + return result } catch (e: Exception) { e.printStackTrace() + MXUtils.log("delete错误 -> $key -- ${e.message}") } finally { - try { - // database.close() - } catch (e: Exception) { - } + database.endTransaction() } } return false @@ -105,7 +122,7 @@ internal class MXDBKeyValue( override fun getAll(): Map { lock.read { - val database = openHelper.readableDatabase + val database = getDatabase() val result = HashMap() var cursor: Cursor? = null try { @@ -126,15 +143,12 @@ internal class MXDBKeyValue( } } catch (e: Exception) { e.printStackTrace() + MXUtils.log("getAll错误 -> ${e.message}") } finally { try { cursor?.close() } catch (e: Exception) { } - try { - // database.close() - } catch (e: Exception) { - } } return result } @@ -142,20 +156,21 @@ internal class MXDBKeyValue( override fun cleanExpire(): Boolean { lock.write { - val database = openHelper.writableDatabase + val database = getDatabase() try { - return database.delete( + database.beginTransaction() + val result = database.delete( dbName, "${MXKVSQLiteOpenHelper.DB_KEY_DEAD_TIME}>0 and ${MXKVSQLiteOpenHelper.DB_KEY_DEAD_TIME} 0 + database.setTransactionSuccessful() + return result } catch (e: Exception) { e.printStackTrace() + MXUtils.log("cleanExpire错误 -> ${e.message}") } finally { - try { - // database.close() - } catch (e: Exception) { - } + database.endTransaction() } } return false @@ -163,16 +178,17 @@ internal class MXDBKeyValue( override fun cleanAll(): Boolean { lock.write { - val database = openHelper.writableDatabase + val database = getDatabase() try { - return database.delete(dbName, null, null) > 0 + database.beginTransaction() + val result = database.delete(dbName, null, null) > 0 + database.setTransactionSuccessful() + return result } catch (e: Exception) { e.printStackTrace() + MXUtils.log("cleanAll错误 -> ${e.message}") } finally { - try { - // database.close() - } catch (e: Exception) { - } + database.endTransaction() } } return false @@ -198,9 +214,14 @@ internal class MXDBKeyValue( value = mxSecret.decrypt(key, value, salt) return Pair(key, value) - } catch (e: java.lang.Exception) { + } catch (e: Exception) { e.printStackTrace() } return null } + + override fun release() { + openHelper?.close() + openHelper = null + } } \ No newline at end of file diff --git a/app/src/main/java/com/mx/example/MyApp.kt b/app/src/main/java/com/mx/example/MyApp.kt index 4b525d8..e7d70de 100644 --- a/app/src/main/java/com/mx/example/MyApp.kt +++ b/app/src/main/java/com/mx/example/MyApp.kt @@ -1,6 +1,7 @@ package com.mx.example import android.app.Application +import com.mx.keyvalue.MXKeyValue class MyApp : Application() { companion object { @@ -12,6 +13,6 @@ class MyApp : Application() { override fun onCreate() { super.onCreate() _appContext = this + MXKeyValue.setDebug(true) } - } \ No newline at end of file diff --git a/app/src/main/java/com/mx/example/app/MultThreadTestActivity.kt b/app/src/main/java/com/mx/example/app/MultThreadTestActivity.kt index bb6ce0b..e1b50fa 100644 --- a/app/src/main/java/com/mx/example/app/MultThreadTestActivity.kt +++ b/app/src/main/java/com/mx/example/app/MultThreadTestActivity.kt @@ -16,8 +16,11 @@ class MultThreadTestActivity : AppCompatActivity() { syncTestTxv.setOnClickListener { repeat(30) { thread { - repeat(100000) { + repeat(100000) { count -> getSetUnit() + if (count > 0 && count % 100 == 0) { + println("次数:$count") + } } } } @@ -35,7 +38,6 @@ class MultThreadTestActivity : AppCompatActivity() { println("读取错误1:$key -> $value") println("读取错误2:$key -> $read") } - println("测试通过一次") Thread.sleep(Random.nextLong(10, 100)) } diff --git a/app/src/main/java/com/mx/example/utils/SPUtils.kt b/app/src/main/java/com/mx/example/utils/SPUtils.kt index cb3e25b..5f98556 100644 --- a/app/src/main/java/com/mx/example/utils/SPUtils.kt +++ b/app/src/main/java/com/mx/example/utils/SPUtils.kt @@ -12,7 +12,6 @@ object SPUtils { val KV by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { MXKeyValue(MyApp.appContext, "kvdb_kv_v1", MXAESSecret("27e2125d0a11a9aa65b9c9773673bc2a")) } - fun get(key: String, def: String? = null): String? { return KV.get(key, def) } -- Gitee From da98e635261a92397825fbdd629de9dd532a9f5c Mon Sep 17 00:00:00 2001 From: zhangmengxiong Date: Sat, 25 Jun 2022 21:28:53 +0800 Subject: [PATCH 5/5] Update MultThreadTestActivity.kt --- .../com/mx/example/app/MultThreadTestActivity.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/com/mx/example/app/MultThreadTestActivity.kt b/app/src/main/java/com/mx/example/app/MultThreadTestActivity.kt index e1b50fa..74ce3ab 100644 --- a/app/src/main/java/com/mx/example/app/MultThreadTestActivity.kt +++ b/app/src/main/java/com/mx/example/app/MultThreadTestActivity.kt @@ -14,11 +14,11 @@ class MultThreadTestActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.activity_mult_thread_test) syncTestTxv.setOnClickListener { - repeat(30) { + repeat(5) { index -> thread { repeat(100000) { count -> - getSetUnit() - if (count > 0 && count % 100 == 0) { + getSetUnit(index, count) + if (count > 0 && count % 10 == 0) { println("次数:$count") } } @@ -27,9 +27,9 @@ class MultThreadTestActivity : AppCompatActivity() { } } - private fun getSetUnit() { - val key = StringUtils.generalKey() - val value = StringUtils.generalString(128) + private fun getSetUnit(index: Int, count: Int) { + val key = "key_index_" + index + "_" + count + val value = "value_index_" + index + "_" + count if (!SPUtils.set(key, value)) { println("写入错误:$key -> $value") } @@ -38,7 +38,6 @@ class MultThreadTestActivity : AppCompatActivity() { println("读取错误1:$key -> $value") println("读取错误2:$key -> $read") } - Thread.sleep(Random.nextLong(10, 100)) } } -- Gitee