ContentProvider仿Settings数据存储架构实现

在Android应用中,我们可以使用以下代码轻易的进行设置值的写入与读取:

boolean result = Settings.Global.putString(this.getContentResolver(), Settings.Global.DEVICE_NAME, "Z97A");
Settings.Global.getString(this.getContentResolver(), Settings.Global.DEVICE_NAME);

该设计在应用中进行调用显得非常优雅,因此在想着仿制framework中android.provider.Settings以最小量的代码进行相应的实现。

内容提供者实现

package io.cstack.xviewlander;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.util.Log;

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

import java.util.HashMap;
import java.util.Map;

/**
 * 实现内容提供者
 */
public class FoxProvider extends ContentProvider {
    public static final String TAG = "FoxProvider";
    private Map<String, String> globalDatas;//某一个个表格的数据存放
    static final int GLOBAL_ID = 1;//对应的表格ID
    static final UriMatcher uriMatcher;

    static {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);//定义一个uri匹配器
        uriMatcher.addURI(FoxSettings.AUTHORITY, "global", GLOBAL_ID);//定义与之匹配的uri
    }

    @Override
    public boolean onCreate() {
        globalDatas = new HashMap<>();
        globalDatas.put(FoxSettings.Global.DEVICE_NAME, "F0X996");//模拟数据加载
        return true;
    }

    /**
     * 构造键值返回对象的Cursor
     *
     * @param name
     * @param value
     * @return
     */
    private MatrixCursor buildParameterCursor(String name, String value) {
        String[] columns = new String[]{name};//定义列名
        MatrixCursor cursor = new MatrixCursor(columns);
        cursor.addRow(new Object[]{value});//定义值
        return cursor;
    }

    /**
     * 获取数据对应的查询方法
     *
     * @param uri
     * @param projection
     * @param selection
     * @param selectionArgs
     * @param sortOrder
     * @return
     */
    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        String key = projection[0];//获取查询的键
        String value;
        int code = uriMatcher.match(uri);//获取匹配到的表格id
        switch (code) {
            case GLOBAL_ID:
                value = globalDatas.get(key);//从对应表格中获取值
                break;
            default:
                value = null;
                break;
        }
        return buildParameterCursor(key, value);//返回结果
    }

    /**
     * 构建Uri返回MIME类型
     *
     * @param uri
     * @return
     */
    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        switch (uriMatcher.match(uri)) {
            /**
             * global
             */
            case GLOBAL_ID:
                return "vnd.android.cursor.dir/vnd.foxsettings.global";
            default:
                throw new IllegalArgumentException("Unsupported URI: " + uri);
        }
    }

    /**
     * 设置数据的对应方法
     *
     * @param uri
     * @param values
     * @return
     */
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        Log.e(TAG, "insert:" + uri.toString());
        int code = uriMatcher.match(uri);
        Uri _uri = null;
        switch (code) {
            case GLOBAL_ID:
                String name = null;
                for (String key : values.keySet()) {
                    String value = (String) values.get(key);
                    globalDatas.put(key, value);
                    name = key;
                    _uri = Uri.withAppendedPath(FoxSettings.Global.CONTENT_URI, name);
                    getContext().getContentResolver().notifyChange(_uri, null);//通知注册数据改变监听
                    break;
                }
                break;
            default: {
                throw new IllegalArgumentException("Bad Uri path:" + uri);
            }
        }
        return _uri;
    }

    @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) {
        return 0;
    }
}

配置清单中注册内容提供者

        <provider
            android:name="io.cstack.xviewlander.FoxProvider"
            android:authorities="foxsettings" />

调用接口实现

package io.cstack.xviewlander;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;

/**
 * 用于调用的工具接口
 */
public class FoxSettings {
    /**
     * 定义authorities该值需要需要与AndroidManifest.xml中配置保持一致
     */
    public static final String AUTHORITY = "foxsettings";

    /**
     * 基础的设置查询实现
     */
    public static class NameValueTable {
        /**
         * 设置一个值
         *
         * @param resolver
         * @param uri
         * @param name
         * @param value
         * @return
         */
        protected static boolean putString(ContentResolver resolver, Uri uri, String name, String value) {
            try {
                ContentValues values = new ContentValues();
                values.put(name, value);
                resolver.insert(uri, values);
                return true;
            } catch (SQLException e) {
                return false;
            }
        }

        /**
         * 获取一个值
         *
         * @param resolver
         * @param uri
         * @param name
         * @param value
         * @return
         */
        protected static String getString(ContentResolver resolver, Uri uri, String name, String value) {
            try {
                Cursor query = resolver.query(uri, new String[]{name}, null, new String[]{value}, null);
                if (query.moveToFirst()) {
                    return query.getString(0);
                }
                return value;
            } catch (SQLException e) {
                return value;
            }
        }

        public static Uri getUriFor(Uri uri, String name) {
            return Uri.withAppendedPath(uri, name);
        }
    }

    /**
     * 某一个设置表格实现
     */
    public static final class Global extends NameValueTable {
        //定义对应表格的Url
        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/global");
        //定义可支持设置的KEY常量
        public static final String DEVICE_NAME = "device_name";

        public static boolean putString(ContentResolver resolver, String name, String value) {
            return NameValueTable.putString(resolver, CONTENT_URI, name, value);
        }

        public static String getString(ContentResolver resolver, String name) {
            return NameValueTable.getString(resolver, CONTENT_URI, name, null);
        }

        public static Uri getUriFor(String name) {
            return getUriFor(CONTENT_URI, name);
        }
    }
}

调用activity实现

public class MainActivity extends Activity {
    public static final String TAG = "MainActivity";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getContentResolver().registerContentObserver(FoxSettings.Global.getUriFor(FoxSettings.Global.DEVICE_NAME), true, new ContentObserver(new Handler()) {
            @Override
            public void onChange(boolean selfChange) {
                super.onChange(selfChange);
                Log.e(TAG, "selfChange:" + selfChange);
            }
        });
        boolean result = FoxSettings.Global.putString(this.getContentResolver(), FoxSettings.Global.DEVICE_NAME, "F0X007");
        String value = FoxSettings.Global.getString(this.getContentResolver(), FoxSettings.Global.DEVICE_NAME);
        Log.e("fox", "getString:" + value);
    }
}

发表回复

CAPTCHAis initialing...