Android运用Kotlin封装MMKVUtils
1.简介:
MMKV 是根据 mmap 内存映射的 key-value 组件,底层序列化/反序列化运用 protobuf 完成,功能高,稳定性强。从 2015 年中至今在微信上运用,其功能和稳定性经过了时间的验证。近期也已移植到 Android / macOS / Win32 / POSIX 渠道,一并开源。
2.MMKV 源起
在微信客户端的日常运营中,时不时就会迸发特殊文字引起体系的 crash, 参阅文章,文章里面规划的技术计划是在要害代码前后进行计数器的加减,经过检查计数器的反常,来发现引起闪退的反常文字。在会话列表、会话界面等有大量 cell 的地方,期望新加的计时器不会影响滑动功能;别的这些计数器还要永久存储下来——由于闪退随时可能发生。这就需求一个功能十分高的通用 key-value 存储组件,咱们考察了 SharedPreferences、NSUserDefaults、SQLite 等常见组件,发现都没能满足如此严苛的功能要求。考虑到这个防 crash 计划最主要的诉求仍是实时写入,而 mmap 内存映射文件刚好满足这种需求,咱们测验经过它来完成一套 key-value 组件。
3.MMKV 原理
- 内存预备 经过 mmap 内存映射文件,提供一段可供随时写入的内存块,App 只管往里面写数据,由操作体系负责将内存回写到文件,不用担心 crash 导致数据丢失。
- 数据组织 数据序列化方面咱们选用 protobuf 协议,pb 在功能和空间占用上都有不错的表现。
- 写入优化 考虑到主要运用场景是频频地进行写入更新,咱们需求有增量更新的才能。咱们考虑将增量 kv 目标序列化后,append 到内存末尾。
- 空间增加 运用 append 完成增量更新带来了一个新的问题,便是不断 append 的话,文件大小会增加得不可控。咱们需求在功能和空间上做个折中。
更详细的规划原理参阅 MMKV 原理。
4.MMKV优势:
MMKV的呈现其实是为了处理SharedPreferences的一些问题,微信团队期望以此来代替SharedPreferences,现在在Android中,对于经常运用的快速本地化存储,大部分人往往会挑选SharedPreferences来作为存储方式, 作为Android库中自带的存储方式,SharePreferences在运用方式上仍是很快捷的,可是也往往存在以下的一些问题。
1、经过 getSharedPreferences 可以获取 SP 实例,从初次初始化到读到数据会存在延迟,由于读文件的操作阻塞调用的线程直到文件读取结束,假如在主线程调用,可能会对 UI 流畅度形成影响。(线程阻塞)
2、虽然支撑设置 MODE_MULTI_PROCESS 标志位,可是跨进程同享 SP 存在许多问题,所以不建议运用该模式。(文件跨进程同享)
3、将数据写入文件需求将数据复制两次,再写入到文件中,假如数据量过大,也会有很大的功能损耗。(二次写入)
5.MMKV支撑的数据类型:
支撑以下 Java 言语基础类型:
boolean、int、long、float、double、byte[],String、Set,任何完成了Parcelable的类型,目标存储方式是,转化成json串,经过字符串存储,运用的时分在取出来反序列化.
6.依靠导入:
implementation(libs.mmkv)
7.AGP8.1统一依靠装备:
[versions]
agp = "8.1.0"
org-jetbrains-kotlin-android = "1.8.0"
core-ktx = "1.10.1"
junit = "4.13.2"
androidx-test-ext-junit = "1.1.5"
espresso-core = "3.5.1"
appcompat = "1.6.1"
material = "1.9.0"
constraintlayout = "2.1.4"
mmkv = "1.3.0"
[libraries]
core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "core-ktx" }
junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-ext-junit" }
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espresso-core" }
appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }
material = { group = "com.google.android.material", name = "material", version.ref = "material" }
constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "constraintlayout" }
mmkv = { group = "com.tencent", name = "mmkv", version.ref = "mmkv" }
[plugins]
com-android-application = { id = "com.android.application", version.ref = "agp" }
org-jetbrains-kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "org-jetbrains-kotlin-android" }
[bundles]
8.App.gradle装备:
@Suppress("DSL_SCOPE_VIOLATION")
plugins {
alias(libs.plugins.com.android.application)
alias(libs.plugins.org.jetbrains.kotlin.android)
}
android {
namespace = "com.example.mmkvdemo"
compileSdk = 33
defaultConfig {
applicationId = "com.example.mmkvdemo"
minSdk = 23
targetSdk = 33
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = 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(libs.core.ktx)
implementation(libs.appcompat)
implementation(libs.material)
implementation(libs.constraintlayout)
implementation(libs.mmkv)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.test.ext.junit)
androidTestImplementation(libs.espresso.core)
}
9.运用java封装的东西类:
package com.example.lib_common.utils;
import android.content.Context;
import android.os.Environment;
import com.tencent.mmkv.MMKV;
/**
* @author: njb
* @date: 2023/8/9 14:53
* @desc:
*/
public class MMKVUtils {
private MMKV mmkv;
private static volatile MMKVUtils mInstance;
private MMKVUtils() {
}
public void init(Context context) {
String dir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/mmkv";
//mmkv初始化
MMKV.initialize(context, dir);
mmkv = MMKV.mmkvWithID("MyMMID");
//开启跨进程通讯
mmkv = MMKV.mmkvWithID("MyMMID", MMKV.MULTI_PROCESS_MODE);
}
public static MMKVUtils getInstance() {
if (mInstance == null) {
synchronized (MMKVUtils.class) {
if (mInstance == null) {
mInstance = new MMKVUtils();
}
}
}
return mInstance;
}
public void encode(String key, Object value) {
if (value instanceof String) {
mmkv.encode(key, (String) value);
} else if (value instanceof Integer) {
mmkv.encode(key, (Integer) value);
} else if (value instanceof Boolean) {
mmkv.encode(key, (Boolean) value);
} else if (value instanceof Long) {
mmkv.encode(key, (Long) value);
} else if (value instanceof Float) {
mmkv.encode(key, (Float) value);
} else if (value instanceof Double) {
mmkv.encode(key, (Double) value);
}
}
public Integer decodeInt(String key) {
return mmkv.decodeInt(key);
}
public String decodeString(String key) {
return mmkv.decodeString(key, "");
}
public Boolean decodeBoolean(String key) {
return mmkv.decodeBool(key);
}
public Long decodeLong(String key) {
return mmkv.decodeLong(key);
}
public Float decodeFloat(String key) {
return mmkv.decodeFloat(key);
}
public Double decodeDouble(String key) {
return mmkv.decodeDouble(key);
}
public void clearAllData(){
mmkv.clearAll();
}
}
10.运用kotlin封装的东西类:
package com.example.lib_common.utils
import android.content.Context
import com.tencent.mmkv.MMKV
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
/**
* @author: njb
* @date: 2023/8/9 22:40
* @desc:
*/
class MMKVUtil private constructor(){
lateinit var mmKv:MMKV
companion object {
const val DATE_FORMAT = "yyyy-MM-dd HH.mm.ss"
val instance by lazy(LazyThreadSafetyMode.SYNCHRONIZED) { MMKVUtil() }
}
fun init(context: Context) {
//第一种运用mmkv默许目录
//MMKV.initialize(context)
//第二种运用自定义包名目录
//MMKV.initialize(context, FileManager.getStorageRootDir() + FileManager.MMKV_DIR)
val mFileDateFormat = SimpleDateFormat(DATE_FORMAT, Locale.US)
//视频保存途径
val file =
File(FileManager.getMMKVPath(), mFileDateFormat.format(Date()) + "/mmkv")
//第三种运用自定义的体系目录 dcim、download、music其中一个即可
MMKV.initialize(context,file.absolutePath)
mmKv = MMKV.mmkvWithID("MyMMKVTestID", MMKV.MULTI_PROCESS_MODE)
mmKv.encode("bool", true)
}
fun initTest(context: Context) {
//第一种运用mmkv默许目录
//MMKV.initialize(context)
//第二种运用自定义包名目录
MMKV.initialize(context, FileManager.getStorageRootDir() + FileManager.MMKV_DIR)
//第三种运用自定义的体系目录 dcim、download、music其中一个即可
//MMKV.initialize(context,FileManager.getMMKVPath())
mmKv = MMKV.mmkvWithID("MyTestID", MMKV.MULTI_PROCESS_MODE)
mmKv.encode("bool", true)
}
fun encode(key: String, value: Any) {
when (value) {
is String -> mmKv.encode(key, value)
is Int -> mmKv.encode(key, value)
is Boolean -> mmKv.encode(key, value)
is Long -> mmKv.encode(key, value)
is Float -> mmKv.encode(key, value)
is Double -> mmKv.encode(key, value)
}
}
inline fun <reified T> decode(key: String, defaultValue: T): T = when (T::class) {
String::class -> mmKv.decodeString(key, defaultValue as String?) as T
Int::class -> mmKv.decodeInt(key, defaultValue as Int) as T
Boolean::class -> mmKv.decodeBool(key, defaultValue as Boolean) as T
Long::class -> mmKv.decodeLong(key, defaultValue as Long) as T
Float::class -> mmKv.decodeFloat(key, defaultValue as Float) as T
Double::class -> mmKv.decodeDouble(key, defaultValue as Double) as T
else -> throw IllegalArgumentException("Unsupported type")
}
}
11.初始化:
private fun performFileOperations() {
MyApp.mInstance.initMMKV()
// 履行文件读写操作
initMMKVData()
}
package com.example.mmkvdemo
import android.app.Application
import com.example.mmkvdemo.utils.MMKVUtils
/**
* @author: njb
* @date: 2023/8/9 23:19
* @desc:
*/
class MyApp :Application(){
override fun onCreate() {
super.onCreate()
// initMMKV()
mInstance = this
}
fun initMMKV() {
MMKVUtils.instance.init(this)
}
companion object{
lateinit var mInstance: MyApp
private set
}
}
12.简单运用:
private fun performFileOperations() {
MyApp.mInstance.initMMKV()
// 履行文件读写操作
initMMKVData()
}
private fun initMMKVData() {
// 存储数据
MMKVUtils.instance.encode("key1", "value1")
MMKVUtils.instance.encode("key2", "456")
}
@SuppressLint("SetTextI18n")
private fun initView() {
textView.setOnClickListener {
// 读取数据
val value1: String = MMKVUtils.instance.decode("key1","")
val value2: String = MMKVUtils.instance.decode("key2","")
Log.d(TAG, "====数据为===$value1$value2")
textView.text = value1 + value2
}
}
13.适配Android13:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 检查并恳求权限
if (checkPermissions()) {
// 已经有权限,履行文件读写操作
performFileOperations()
} else {
// 恳求权限
requestPermission()
}
initView()
}
private fun checkPermissions(): Boolean {
if (VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
PERMISSIONS = arrayOf<String>(Manifest.permission.READ_EXTERNAL_STORAGE)
for (permission in PERMISSIONS) {
if (ContextCompat.checkSelfPermission(
this,
permission
) != PackageManager.PERMISSION_GRANTED
) {
return false
}
}
} else {
for (permission in PERMISSIONS) {
if (ContextCompat.checkSelfPermission(
this,
permission
) != PackageManager.PERMISSION_GRANTED
) {
return false
}
}
}
return true
}
private fun requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
PERMISSIONS = arrayOf<String>(Manifest.permission.READ_EXTERNAL_STORAGE)
ActivityCompat.requestPermissions(
this,
PERMISSIONS,
REQUEST_PERMISSION_CODE
)
} else {
ActivityCompat.requestPermissions(
this,
PERMISSIONS,
REQUEST_PERMISSION_CODE
)
}
}
private fun performFileOperations() {
MyApp.mInstance.initMMKV()
// 履行文件读写操作
initMMKVData()
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String?>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_PERMISSION_CODE) {
var allPermissionsGranted = true
for (result in grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allPermissionsGranted = false
break
}
}
if (allPermissionsGranted) {
// 权限已颁发,履行文件读写操作
performFileOperations()
} else {
// 权限被回绝,处理权限恳求失利的状况
}
}
}
14.完整运用代码:
package com.example.mmkvdemo
import android.Manifest
import android.annotation.SuppressLint
import android.content.pm.PackageManager
import android.os.Build
import android.os.Build.VERSION
import android.os.Bundle
import android.util.Log
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.example.mmkvdemo.utils.MMKVUtils
class MainActivity : AppCompatActivity() {
private val TAG = MainActivity::class.java.name
private val REQUEST_PERMISSION_CODE = 100
private var PERMISSIONS: Array<String> = arrayOf(
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.READ_EXTERNAL_STORAGE
)
private val textView:TextView by lazy { findViewById(R.id.tv_test) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 检查并恳求权限
if (checkPermissions()) {
// 已经有权限,履行文件读写操作
performFileOperations()
} else {
// 恳求权限
requestPermission()
}
initView()
}
@SuppressLint("SetTextI18n")
private fun initView() {
textView.setOnClickListener {
// 读取数据
val value1: String = MMKVUtils.instance.decode("key1","")
val value2: String = MMKVUtils.instance.decode("key2","")
Log.d(TAG, "====数据为===$value1$value2")
textView.text = value1 + value2
}
}
private fun checkPermissions(): Boolean {
if (VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
PERMISSIONS = arrayOf<String>(Manifest.permission.READ_EXTERNAL_STORAGE)
for (permission in PERMISSIONS) {
if (ContextCompat.checkSelfPermission(
this,
permission
) != PackageManager.PERMISSION_GRANTED
) {
return false
}
}
} else {
for (permission in PERMISSIONS) {
if (ContextCompat.checkSelfPermission(
this,
permission
) != PackageManager.PERMISSION_GRANTED
) {
return false
}
}
}
return true
}
private fun requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
PERMISSIONS = arrayOf<String>(Manifest.permission.READ_EXTERNAL_STORAGE)
ActivityCompat.requestPermissions(
this,
PERMISSIONS,
REQUEST_PERMISSION_CODE
)
} else {
ActivityCompat.requestPermissions(
this,
PERMISSIONS,
REQUEST_PERMISSION_CODE
)
}
}
private fun performFileOperations() {
MyApp.mInstance.initMMKV()
// 履行文件读写操作
initMMKVData()
}
private fun initMMKVData() {
// 存储数据
MMKVUtils.instance.encode("key1", "value1")
MMKVUtils.instance.encode("key2", "456")
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String?>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_PERMISSION_CODE) {
var allPermissionsGranted = true
for (result in grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allPermissionsGranted = false
break
}
}
if (allPermissionsGranted) {
// 权限已颁发,履行文件读写操作
performFileOperations()
} else {
// 权限被回绝,处理权限恳求失利的状况
}
}
}
}
15.布局代码:
<?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:id="@+id/tv_test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
16.跨进程运用:
16.1 主App存储数据
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 检查并恳求权限
if (checkPermissions()) {
// 已经有权限,履行文件读写操作
performFileOperations()
} else {
// 恳求权限
requestPermission()
}
initView()
}
private fun performFileOperations() {
//BaseApplication.Instance.initMMKV()
// 履行文件读写操作
initMMKVData()
}
private fun initMMKVData() {
// 存储数据
MMKVUtils.getInstance().encode("key1", "用户小明")
MMKVUtils.getInstance().encode("age", 23)
MMKVUtils.getInstance().encode("sex", "男")
MMKVUtils.getInstance().encode("address", "北京市朝阳区")
MMKVUtils.getInstance().encode("birthday", "2020-01-18")
MMKVUtils.getInstance().encode("account", "188888")
MMKVUtils.getInstance().encode("identity", false)
MMKVUtils.getInstance().encode("amount", 888888.88)
}
@SuppressLint("SetTextI18n")
private fun initView() {
textView.setOnClickListener {
// 读取数据
val value1: String = MMKVUtils.getInstance().decodeString("key1")
val age: Int = MMKVUtils.getInstance().decodeInt("age")
val sex: String = MMKVUtils.getInstance().decodeString("sex")
val address: String = MMKVUtils.getInstance().decodeString("address")
val birthday: String = MMKVUtils.getInstance().decodeString("birthday")
val account: String = MMKVUtils.getInstance().decodeString("account")
val identity: Boolean = MMKVUtils.getInstance().decodeBoolean("identity")
val amount: Double = MMKVUtils.getInstance().decodeDouble("amount")
Log.d(TAG, "====数据为===$value1$age$sex$address$birthday$account$identity$account$amount")
textView.text = value1
try {
ToolUtils.openApp("com.example.testmmkv", this@MainActivity)
// ToolUtils.openThirdApp("om.example.testmmkv", this@MainActivity)
}catch (e:Exception){
e.printStackTrace()
}
}
}
16.2 跨进程TestApp接收数据:
private fun performFileOperations() {
// BaseApplication.Instance.initMMKV()
initData()
}
private fun initData() {
val userName = MMKVUtils.getInstance().decodeString("key1")
val age: Int = MMKVUtils.getInstance().decodeInt("age")
val sex: String = MMKVUtils.getInstance().decodeString("sex")
val address: String = MMKVUtils.getInstance().decodeString("address")
val birthday: String = MMKVUtils.getInstance().decodeString("birthday")
val account: String = MMKVUtils.getInstance().decodeString("account")
val identity: Boolean = MMKVUtils.getInstance().decodeBoolean("identity")
val amount: Double = MMKVUtils.getInstance().decodeDouble("amount")
textView.text = "用户姓名:$userName"
tvAddress.text = "用户地址:$address"
tvAge.text = "用户年龄:$age"
tvAccount.text = "用户账号:$account"
tvAmount.text = "用户金额:$amount"
tvBirthday.text = "用户生日:$birthday"
tvIdentity.text = "是否党员:$identity"
tvSex.text = "用户性别:$sex"
Log.d(TAG, "====跨进程通讯测试数据===$userName$age$sex$address$birthday$account$identity$account$amount")
tvBack.setOnClickListener {
try {
MMKVUtils.getInstance().encode("backKey","用户小明回到本来的运用")
finish()
// ToolUtils.openApp("com.example.mmkvdemo", this@MainActivity)
// ToolUtils.openThirdApp("om.example.testmmkv", this@MainActivity)
}catch (e:Exception){
e.printStackTrace()
}
}
}
17.日志打印:
18.翻开第三方app东西类:
package com.example.mmkvdemo.utils;
import static android.content.Context.ACTIVITY_SERVICE;
import android.app.Activity;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.List;
/**
* @author: njb
* @date: 2023/8/8 0:03
* @desc:
*/
public class ToolUtils {
private static final String TAG = ToolUtils.class.getName();
/**
* 翻开软件
*
* @param packageName 包名
* @param context 上下文目标
*/
public static void openApp(String packageName, Context context) {
if (packageName != null) {
PackageManager packageManager = context.getPackageManager();
Intent intent = packageManager.getLaunchIntentForPackage(packageName);
context.startActivity(intent);
}
}
public static void openApp(String packageName, Context context, Bundle bundle) {
Intent intent = new Intent();
ComponentName comp = new ComponentName("com.tencent.mobileqq", "com.tencent.mobileqq.activity.SplashActivity");
intent.setComponent(comp);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
/**
* 获取前台程序包名
*/
public static String getForegroundAppPackageName(Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Activity.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> taskInfo = am.getRunningTasks(1);
ComponentName componentInfo = taskInfo.get(0).topActivity;
return componentInfo.getPackageName();
}
/**
* 根据报名杀死运用
*/
public static void killApp(Context context, String packageName) {
try {
ActivityManager m = (ActivityManager) context.getSystemService(ACTIVITY_SERVICE);
Method method = m.getClass().getMethod("forceStopPackage", String.class);
method.setAccessible(true);
method.invoke(m, packageName);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 杀死第三方运用
*
* @param context
* @param packageName
*/
public static void killThirdApp(Context context, String packageName) {
if (packageName != null) {
killApp(context, packageName);
}
}
/**
* 获取前台activity名称
*
* @param context
* @return
*/
public static String getForegroundActivityName(Context context) {
if (context == null) {
return "";
}
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> list = am.getRunningTasks(1);
if (list != null && list.size() > 0) {
ComponentName cpn = list.get(0).topActivity;
return cpn.getClassName();
}
return "";
}
/**
* 判断APP是否安装了
*
* @param packageName 包名
* @return
*/
public static boolean isAppInstalled(Context context, String packageName) {
PackageManager packageManager = context.getPackageManager();
try {
packageManager.getPackageInfo(packageName, PackageManager.GET_ACTIVITIES);
return true;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return false;
}
}
public static void unInstall(Context context, String packageName) {
if (packageName == null) {
return;
}
Uri uri = Uri.parse("package:" + packageName);
Intent uninstall = new Intent(Intent.ACTION_DELETE, uri);
uninstall.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(uninstall);
}
/**
* 静默卸载App
*
* @param packageName 包名
* @return 是否卸载成功
*/
public static boolean uninstall(String packageName) {
Process process = null;
BufferedReader successResult = null;
BufferedReader errorResult = null;
StringBuilder successMsg = new StringBuilder();
StringBuilder errorMsg = new StringBuilder();
try {
process = new ProcessBuilder("pm", "uninstall", packageName).start();
successResult = new BufferedReader(new InputStreamReader(process.getInputStream()));
errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream()));
String s;
while ((s = successResult.readLine()) != null) {
successMsg.append(s);
}
while ((s = errorResult.readLine()) != null) {
errorMsg.append(s);
}
} catch (Exception e) {
Log.d("e = " , e.toString());
} finally {
try {
if (successResult != null) {
successResult.close();
}
if (errorResult != null) {
errorResult.close();
}
} catch (Exception e) {
Log.d("Exception : " , e.toString());
}
if (process != null) {
process.destroy();
}
}
//假如含有"success"单词则认为卸载成功
return successMsg.toString().equalsIgnoreCase("success");
}
/**
* 判断运用是否存在
*
* @param context 上下文
* @param packageName 包名
* @return 是否存在
*/
private boolean appExist(Context context, String packageName) {
try {
List<PackageInfo> packageInfoList = context.getPackageManager().getInstalledPackages(0);
for (PackageInfo packageInfo : packageInfoList) {
if (packageInfo.packageName.equalsIgnoreCase(packageName)) {
return true;
}
}
} catch (Exception e) {
Log.d(TAG,e.toString());
}
return false;
}
}
19.完成作用:
20.总结:
以上便是今日的内容运用Kotlin封装根据MMKV的东西类,现在基本的数据存储没啥问题,可是跨进程在Android13中有点问题,两个app的存储目录不一样导致数据储存成功,可是跨进程时只能读取到本app目录下的数据,还没想好处理方法,Android9中及以下是可以正常运用的,由于11及以上内外部存储发生了改变,官网在1.3.1版别中更新了关于Android13的适配,他们给出的计划把两个app的mmkv文件存储写入到同一个公共的目录,后边有时间在测验一下,如有处理的小伙伴们说一下.
21.项目demo源码:
gitee.com/jackning_ad…