模仿Android系统Settings跨应用数据共享

模仿Android系统Settings跨应用数据共享

做Android终端我们总会遇到跨应用通信的,比较采用Broadcast、AIDL等方式进行实时数据更新,或者采用文件共享的方式进行配置保存。在做配置保存的时候Android提供了一个Settings配置来让我们进行跨进程数据互通,但是该种方式只适合Android6.0以下的做法,Android6.0及以上会报一个异常,不得不让我们采用其他的形式去进行配置共享。

ContentProvider

Android系统提供一个数据共享的系统组件ContentProvider,我们可以采用该种方式进行模仿系统的Settings,从而达到不管Android版本多少都能使用,使用该类,首先我们需要在AndroidManifest.xml中配置我们的Provider:

        
        

我们需要去手造一个SettingsProvider类,该类具体实现如下:

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;

/**
 * @Description: 数据共享
 * @Encode: UTF-8
 * Created by zzj on 2018/6/5.
 */

public class SettingsProvider extends ContentProvider {
    private final String DATABASE_NAME = "_zzj_settings.db";
    private final String TABLE_NAME = "my_settings";
    private SettingsHelper helper;
    private SQLiteDatabase db;

    @Override
    public boolean onCreate() {
        helper = new SettingsHelper(getContext(), DATABASE_NAME, null, 1);
        db = helper.getReadableDatabase();
        return true;
    }

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

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

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        synchronized (SettingsProvider.class) {
            db.insert(TABLE_NAME, null, values);
        }
        return null;
    }

    private boolean hasElement(String table, String key) {
        synchronized (SettingsProvider.this) {
            boolean isRepeat = false;
            Cursor cursor = null;
            try {
                cursor = db.rawQuery("select * from " + table + " where key = ?", new String[]{key});
                if (cursor != null && cursor.getCount() > 0) {
                    isRepeat = true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (cursor != null) {
                    cursor.close();
                }
            }
            return isRepeat;
        }
    }

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

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values,
                     @Nullable String selection, @Nullable String[] selectionArgs) {
        long id;
        synchronized (SettingsProvider.class) {
            if (hasElement(TABLE_NAME, selectionArgs[0])) {
                Log.d("zzj", "SettingsProvider update() hasElement is true ");
                id = db.update(TABLE_NAME, values, selection, selectionArgs);
            } else {
                Log.d("zzj", "SettingsProvider update() hasElement is false ");
                id = db.insert(TABLE_NAME, null, values);
            }
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return (int) id;
    }

    class SettingsHelper extends SQLiteOpenHelper {
        private final byte[] LOCK = new byte[0];
        private final String TABLE_NAME = "my_settings";
        private static final String COLUMN_KEY = "key";
        private static final String COLUMN_INT_VALUE = "intValue";
        private static final String COLUMN_STRING_VALUE = "stringValue";
        private final String SQL_CREATE_TABLE = "create table if not exists " +
                "my_settings(id integer primary key autoincrement," +
                "key text not null unique,intValue integer,stringValue text)";

        public SettingsHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
            super(context, name, factory, version);
        }

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(SQL_CREATE_TABLE);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

        }
    }


}

我们需要去继承ContentProvider然后自己手动去实现一个SQLiteOpenHelper,创建属于我们的数据库表,这里我只做了两个简单的字段,一个是整形int的intValue,一个是文本型text的stringValue,能满足我们大部分的需求,如果自己需要更多的功能,自己可在这基础上继续适量往上加字段。

写完ContentProvider后我们需要提供一个Util工具类供其他应用使用,这样就免去了其他应用手动去读写这个数据库了。

SettingsUtil.java

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

/**
 * @Description: settings ContentProvider
 * @Encode: UTF-8
 * Created by zzj on 2018/6/6.
 */

public class SettingsUtil {
    public static final String URI_CONTENT = "content://com.zzj.provider";
    public static final String COLUMN_KEY = "key";
    public static final String COLUMN_INT_VALUE = "intValue";
    public static final String COLUMN_String_VALUE = "stringValue";

    /**
     * update int value
     *
     * @param context
     * @param key     KEY
     */
    public static void putInt(Context context, String key, int value) {
        ContentResolver resolver = context.getContentResolver();
        ContentValues values = new ContentValues();
        values.put(COLUMN_KEY, key);
        values.put(COLUMN_INT_VALUE, value);
        resolver.update(Uri.parse(URI_CONTENT), values, COLUMN_KEY + " = ?", new String[]{key});
    }

    /**
     * update String value
     * @param context
     * @param key
     * @param value
     */
    public static void putString(Context context, String key, String value) {
        ContentResolver resolver = context.getContentResolver();
        ContentValues values = new ContentValues();
        values.put(COLUMN_KEY, key);
        values.put(COLUMN_String_VALUE, value);
        resolver.update(Uri.parse(URI_CONTENT), values, COLUMN_KEY + " = ?", new String[]{key});
    }


    /**
     * get int value by key
     *
     * @param context
     * @param key
     * @param defaultValue
     * @return
     */
    public static int getInt(Context context, String key, int defaultValue) {
        ContentResolver resolver = context.getContentResolver();
        Cursor cursor = resolver.query(Uri.parse(URI_CONTENT), null, COLUMN_KEY + " = ?", new String[]{key}, null);
        if (cursor == null) {
            return defaultValue;
        }
        if (cursor.getCount() <= 0){
            cursor.close();
            return defaultValue;
        }
        cursor.moveToFirst();
        int value = cursor.getInt(2);
        cursor.close();
        return value;
    }

    /**
     * get String value by key
     * @param context
     * @param key
     * @param defaultValue
     * @return
     */
    public static String  getString(Context context,String key,String defaultValue){
        ContentResolver resolver = context.getContentResolver();
        Cursor cursor = resolver.query(Uri.parse(URI_CONTENT), null, COLUMN_KEY + " = ?", new String[]{key}, null);
        if (cursor == null) {
            return defaultValue;
        }
        if (cursor.getCount() <= 0){
            cursor.close();
            return defaultValue;
        }
        cursor.moveToFirst();
        String value = cursor.getString(3);
        cursor.close();
        return value;
    }

    /**
    * 自行定义的Key值,根据应用自行商量
    */
    public final class KEY {
        /**
         * 测试Key
         */
        public final static String TEST = "test";

       
    }
}

我们可以看到SettingsUtil里面提供了获取整形和String类型的值,也提供一个写入int和String的方法,如果需要更加丰富的功能,可自己往里面添加,写完Util后我们就可以将这个类共享给其他应用使用了,需要特别注意的一点就是提供数据共享的那个应用要注册Provider,不然数据共享不了。

结语

真正的核心在于ContentProvider的使用,使用该类能轻松的让我们进行IPC通信,有人说使用SharePreference也能达到效果,但SharePreference是非线程安全的,就是说数据的正确性并不能保证,所以还是推荐采用ContentProvider的方式进行。欢迎大家拍砖点赞分享转发~

你可能感兴趣的:(Android技术)