在android系统应用开发中,常常需要获取不同应用的状态来进行逻辑处理,在应用内部可以通过SharedPreferences进行数据保存,但SharedPreferences不能进行应用间数据共享,当然只能通过进程间的通讯来共享数据。其他进程通讯的方式如AIDL来获取的方式这里不做描述,下面只描述三种相似的数据共享方式。

1.ContentProvider内容提供器

ContentProvider的方式如下,这里只重写call和update方法,也可以重写query、insert、delete方法来实现增删改查。

package com.example.myapplication;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

public class ModelProvider extends ContentProvider {

    public static final String CONTENT_AUTHORITY = "com.example.myapplication";
    public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);
    public static final String KEY_MODEL = "model";
    public static final String KEY_FEATURE = "feature";
    public static final Uri CONTENT_URI_MODEL = BASE_CONTENT_URI.buildUpon().appendPath(KEY_MODEL).build();
    public static final Uri CONTENT_URI_FEATURE = BASE_CONTENT_URI.buildUpon().appendPath(KEY_FEATURE).build();

    public static final String METHOD_GET_MODEL = "get_model";
    public static final String METHOD_GET_FEATURE = "get_feature";

    public static final int CUR_MODEL = 1;
    public static final int CUR_FEATURE = 2;

    public final static String MODEL_CONFIG_FILE = "model_feature";

    public final static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        mUriMatcher.addURI(CONTENT_AUTHORITY, KEY_MODEL, CUR_MODEL);
        mUriMatcher.addURI(CONTENT_AUTHORITY, KEY_FEATURE, CUR_FEATURE);
    }

    @Override
    public boolean onCreate() {
        return true;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection,
                        @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return null;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection,
                      @Nullable String[] selectionArgs) {
        if (values == null || getContext() == null) {
            return 0;
        }
        //自定义存储方式,可通过数据库、SharedPreferences保存等数据,下面通过SharedPreferences保存
        switch (mUriMatcher.match(uri)) {
            case CUR_MODEL: {
                String model = values.getAsString(KEY_MODEL);
                SharedPreferences preferences = getContext().getSharedPreferences(MODEL_CONFIG_FILE,
                        Context.MODE_PRIVATE);
                SharedPreferences.Editor editor = preferences.edit();
                editor.putString(KEY_MODEL, model);
                if (editor.commit()) {
                    return CUR_MODEL;
                } else {
                    return 0;
                }
            }
            case CUR_FEATURE: {
                String model = values.getAsString(KEY_FEATURE);
                SharedPreferences preferences = getContext().getSharedPreferences(MODEL_CONFIG_FILE,
                        Context.MODE_PRIVATE);
                SharedPreferences.Editor editor = preferences.edit();
                editor.putString(KEY_FEATURE, model);
                if (editor.commit()) {
                    return CUR_FEATURE;
                } else {
                    return 0;
                }
            }
            default:
                break;

        }
        getContext().getContentResolver().notifyChange(uri, null);
        return 0;
    }

    @Nullable
    @Override
    public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
        if (getContext() == null) {
            return null;
        }
        Bundle bundle = new Bundle();
        SharedPreferences pref = getContext().getSharedPreferences(MODEL_CONFIG_FILE, Context.MODE_PRIVATE);
        switch (method) {
            case METHOD_GET_MODEL: {
                String model = pref.getString(KEY_MODEL, "");
                bundle.putString(KEY_MODEL, model);
                break;
            }
            case METHOD_GET_FEATURE: {
                String model = pref.getString(KEY_FEATURE, "");
                bundle.putString(KEY_FEATURE, model);
                break;
            }
            default:
                break;
        }
        return bundle;
    }
}

在AndroidManifest.xml中配置内容提供器

<provider
            android:authorities="com.example.myapplication"
            android:name="com.example.myapplication.ModelProvider"
            android:exported="true"
            android:enabled="true" />

记得反注册

Bundle bundle = getContentResolver().call(ModelProvider.CONTENT_URI_MODEL, ModelProvider.METHOD_GET_MODEL, null, null);
        String curModel = bundle.getString(ModelProvider.KEY_MODEL,"");
        getContentResolver().registerContentObserver(ModelProvider.CONTENT_URI_MODEL, false,
                mContentObserver);
private ContentObserver mContentObserver = new ContentObserver(new Handler()) {
        @Override
        public void onChange(boolean selfChange, Uri uri) {
            super.onChange(selfChange, uri);
            Bundle bundle = getContentResolver().call(ModelProvider.CONTENT_URI_MODEL, ModelProvider.METHOD_GET_MODEL, null, null);
            String curModel = bundle.getString(ModelProvider.KEY_MODEL,"");
        }
    };

2.Settings Provider系统设置数据表

Settings Provider原理详见博客:Settings Provider原理 Settings.Global:全局系统设置,包含始终以相同方式应用于所有已定义用户的偏好设置。 应用程序可以读取这些内容,但不允许写入;
与“安全”设置一样,这些设置适用于用户必须通过系统 UI 或这些值的专用 API 显式修改的首选项。常用设置项有蓝牙开关状态、adb开关状态等。使用方式如下:

Settings.Global.getString(getContentResolver(), Settings.Global.DEVICE_NAME);

Settings.Secure:安全系统设置,包含应用程序可以读取但不允许写入的系统首选项。 这些首选项适用于用户必须通过系统 UI 或这些值的专用 API 显式修改的首选项,而不是由应用程序直接修改。常用设置项有无障碍、VPN状态等。使用方式如下:

Settings.Secure.getInt(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, 0);

Settings.System:系统设置,包含混杂的系统偏好设置。 此表包含简单的名称/值对。 有一些方便的方法可用于访问各个设置条目。常用设置项有铃声、亮度、时间制式等。使用方式如下:

Settings.System.putInt(getContentResolver(), "test_key", 1);
 Settings.System.getInt(getContentResolver(), "test_key", 0);

关于这三种Global、Secure和System在什么情况下使用呢?
谷歌没有明确表明什么情况下使用,但根据其注释含义大概描述它们的使用情况。Settings.Global是全局系统设置,只供第三方应用读取不允许写入,可以写入一些跟用户设置有关的设置项供其他三方应用获取状态,只有系统一些必要的应用可以写入修改状态。Settings.Secure是安全系统设置,可以写入一些安全如网络相关的状态供三方应用读取,只有系统一些必要的应用可以写入修改状态。
Settings.System是混杂系统设置,可以通过它自定义偏好设置保存和读取,应用可通过它读取状态也可以通过它写入状态来达到不同应用的数据共享。
Settings Provider也可通过adb命令进行读写。
获取设置项列表:

adb shell settings list system
adb shell settings list global
adb shell settings list secure

读取设置项,例如 adb shell settings get system “test_key”

adb shell settings get system “XXX”
adb shell settings get global “XXX”
adb shell settings get secure “XXX”

写入设置项,例如 adb shell settings put system “test_key” 1

adb shell settings put system “XXX” X
adb shell settings put global “XXX” X
adb shell settings put secure “XXX” X

3.SystemProperties系统属性

原理详见此博客:SystemProperties原理 SystemProperties的使用,系统应用可通过如下方式

import android.os.SystemProperties
  
  {
     SystemProperties.set("test.key", "1")
     SystemProperties.get("test.key", "")
  }

SystemProperties源码如下:

public static final int PROP_NAME_MAX = 31;
    public static final int PROP_VALUE_MAX = 91;

    private static native String native_get(String var0, String var1);

    private static native void native_set(String var0, String var1);

    public static String get(String key, String def) {
        if (key.length() > PROP_NAME_MAX) {
            throw new IllegalArgumentException("key.length() > 31");
        } else {
            return native_get(key, def);
        }
    }

    public static void set(String key, String val) {
        if (key.length() > PROP_NAME_MAX) {
            throw new IllegalArgumentException("key.length() > 31");
        } else if (val != null && val.length() > PROP_VALUE_MAX) {
            throw new IllegalArgumentException("val.length() > 91");
        } else {
            native_set(key, val);
        }
    }

可通过getInt、getLong、getBoolean方法获取系统属性。
第三方应用则需要通过反射获取和设置系统属性。

public static String getProperty(String key, String defaultValue) {
        String value = defaultValue;
        try {
            Class<?> c = Class.forName("android.os.SystemProperties");
            Method get = c.getMethod("get", String.class, String.class);
            value = (String)(get.invoke(c, key, defaultValue));
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            return value;
        }
    }

    public static void setProperty(String key, String value) {
        try {
            Class<?> c = Class.forName("android.os.SystemProperties");
            Method set = c.getMethod("set", String.class, String.class);
            set.invoke(c, key, value);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

“ro.”前缀的属性代表只读,一旦设置,属性值不能改变。有权限的系统应用才可以读取。
“persist.”前缀的属性代表持久性,其值会写入/data/property,重启系统时值不会改变。系统应用可直接读取。
不带特殊前缀的属性,重启后值会清零,系统应用可以读取写入,三方应用需要通过反射获取。

以上是关于android系统的数据共享的描述,如有错误请指正。