多应用共享数据的两种方式 - Settings.Global.putXxx 和 ContentProvider

有需求:

A 应用联网获取一时间戳,而B应用负责展示。为什么这么神奇,不直接在B应用中向服务端获取?别问,问就是业务导致。

既然存在这么个需求,解决数据存储以及数据获取问题就可以了。如题所说,数据存储方案暂且介绍两种:Settings.Global.putXxx 和 ContentProvider

方式一:Settings.Global.putXxx

存储:

Settings.Global.putLong(getContentResolver(),activationTimeKey,Long.parseLong(edit_text.getText().toString()));

读取:

long activationTimeLong = Settings.Global.getLong(getContentResolver(), activationTimeKey);

实时读取:

        //add listener to receive info
        getContentResolver().registerContentObserver(
                Settings.Global.getUriFor(activationTimeKey),
                false, new ContentObserver(new Handler(Looper.getMainLooper())) {

                    @Override
                    public void onChange(boolean selfChange) {
                        super.onChange(selfChange);
                        // data has changed
                        //time_to_show.setText("设备激活时间:" + Settings.Global.getString(getContentResolver(),activationTimeKey));

                        initDataByTimestampSavedInSettings();
                        getActivationTime();
                    }
                });

注意事项:

但凡是想要写入数据,则有写入需求的app第一必须是系统应用(即是使用的系统签名),第二需要在功能清单列表中声明权限:

<uses-permission android:name="android.permission.WRITE_SETTINGS"/>

adb快速调试相关:

①可以使用下面命令,快速查看数据是否保存成功:

adb shell "settings list global | grep activation_time"

adb shell 命令快速调试:

adb shell settings get global activation_time

adb shell settings put global activation_time 2024-09-12

adb shell settings put global activation_time 1726713724423

③通过上面api写入的数据,其实都写在了下面文件中:

/data/system/users/0/settings_global.xml

方式二:ContentProvider

这种方式使用也比较普遍

存储:

获取:

    public static final long ACTIVATION_TIME_INVALID = -1;
    public static final String ACTIVATION_TIME_PATH = "ActivationTime";
    public static final String KEY_FOR_ACTIVATION_TIME = "ActivationTime";
    public static final String ACTIVATION_TIME_PROVIDER_AUTH = "com.yuanfudao.android.megrez.ActivationTimeProvider";


    /**
     * 获取本地存储的时间戳并尝试将其转换为格式如 xx年xx月xx日xx时xx分(北京时间) 的数据
     * @return   xx年xx月xx日xx时xx分(北京时间)
     *
     * 举例:
     * 转换前 : 1849342080000
     * 转换后 : 2028年8月8日18时8分(北京时间)
     *
     * 设备激活时间源数据是服务端生成的一时间戳,app侧获取时间戳后,存储在定好的指定位置,当用户打开设置界面时,settings
     * 需将该时间戳转译为指定格式的数据,展示给用户。
     *
     * 参考信息:
     * 企微群:feature-设备实时激活+展示激活时间
     * 设备激活时间实时记录&展示:
     * https://confluence.zhenguanyu.com/pages/viewpage.action?pageId=720588110
     * 设备激活 Swagger
     * https://device--megrez.test-venv.yuanfudao.biz/megrez-user-data/api/swagger/doc.html#/defaul
     * t/%E8%AE%BE%E5%A4%87%E6%BF%80%E6%B4%BB/activateUsingPOST
     *
     * 需求负责:@李业来
     * 排期:9月20号提测
     * 在线工具:https://tool.lu/timestamp/
     *         https://blog.youkuaiyun.com/learnframework/article/details/121870431
     *
     *  调试命令:
     *  adb shell content insert --uri content://com.yuanfudao.android.megrez.ActivationTimeProvider/ActivationTime --bind name:s:ActivationTime --bind value:s:1849342080000
     *
     */
    public String getActivationTime(Context context){
        try {
            // ①首先获取app侧保存在 ContentProvider 中的 long 类型数据
            long activationTimeLong = getActivationTimeFromContentProvider(context);

            // ②判断数据的合法性
            if (activationTimeLong <= 0){
                //代码执行到这里,意味着本地没有激活时间,或者app侧存储的数据存在问题
                Log.w("activation_time","ContentProvider ActivationTime:" + activationTimeLong );
                return null;
            }
            // ③将毫秒级时间戳转换为Instant对象
            Instant instant = Instant.ofEpochMilli(activationTimeLong);
            // ④东八区时区ID,对于中国大陆,通常是"Asia/Shanghai"
            ZoneId cstZoneId = ZoneId.of("Asia/Shanghai");
            // ⑤转换为东八区的ZonedDateTime
            ZonedDateTime cstTime = instant.atZone(cstZoneId);
            // ⑥格式化输出日期和时间
            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy年M月d日H时m分(北京时间)");
            String formattedDateTime = cstTime.format(formatter);
            Log.d("activation_time","本地数据:" + activationTimeLong + ",转换后:" + formattedDateTime);
            return formattedDateTime;
        } catch (NumberFormatException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取app侧保存在 ContentProvider 中的激活时间数据
     * @param context Context
     * @return Activation Time
     */
    public static Long getActivationTimeFromContentProvider(Context context) {
        try {
            Uri uri = Uri.parse("content://" + ACTIVATION_TIME_PROVIDER_AUTH + "/" + ACTIVATION_TIME_PATH);
            Cursor cursor = context.getApplicationContext().getContentResolver()
                    .query(uri,
                    null,
                    null,
                    null,
                    null);

            if (cursor != null) {
                try {
                    int index = cursor.getColumnIndex(KEY_FOR_ACTIVATION_TIME);
                    if (cursor.moveToFirst()) {
                        try {
                            return Long.parseLong(cursor.getString(index));
                        } catch (NumberFormatException e) {
                            return ACTIVATION_TIME_INVALID;
                        }
                    }
                } finally {
                    cursor.close();
                }
            }
        } catch (Throwable t) {
            Log.e("ActivationProviderHelper", "找不到Provider, Auth:" + ACTIVATION_TIME_PROVIDER_AUTH, t);
        }
        return ACTIVATION_TIME_INVALID;
    }

注意事项:

①因为我这里是B应用,所以关于数据如何存入的逻辑,我这里没有列举出来;

②另外,因为A应用的开发者讲话说,考虑到数据的安全性来说,他为这个数据做了权限管理,其他想要访问该字段数据的应用,要么和A应用是同一个应用签名,要么是使用了系统签名,否则其他签名的应用,是不能成功获取数据的。

③最后采取了第二个方案,至于为啥,也是因为app端不想使用系统签名对他们的A应用进行签名,再问就是业务导致如此。所以现在是方案二的情况下,我在B应用中使用系统签名,也是能成功获取数据。

④这里其实还有一个调试命令,不过不确定是不是app设置了权限还是怎么着,只能查询获取,无法直接通过adb主动设置该指定数据:

adb shell content query --uri content://com.yuanfudao.android.megrez.ActivationTimeProvider/ActivationTime --projection name:value --where "name='ActivationTime'"

参考:

Android Settings系统属性读写_android settings.global-优快云博客

时间戳(Unix timestamp)转换工具 - 在线工具 (tool.lu)


adb shell调试contentprovider相关命令-千里马android framework_adb 模拟往contentprivider写数据-优快云博客

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值