android 共进程 安全性,Android多进程线程安全SharedPreferences

原理

使用ContentProvider 来保证进程访问数据安全,使用读写锁来保证线程访问安全

测试

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

MPSharedPreferences mpSharedPreferences = new MPSharedPreferences(getApplicationContext(),

getPackageName());

mpSharedPreferences.registerOnSharedPreferenceChangeListener(new MPSharedPreferences.OnSharedPreferenceChangeListener() {

@Override

public void onSharedPreferenceChanged(MPSharedPreferences sharedPreferences,

String key) {

System.out.println(key);

}

});

mpSharedPreferences.putBoolean("boolean",true);

mpSharedPreferences.putFloat("float",9.9f);

mpSharedPreferences.putInt("int",10);

mpSharedPreferences.putLong("long",System.currentTimeMillis());

mpSharedPreferences.putString("string","hello world");

Set sets = new HashSet<>();

sets.add("8888");

sets.add("9999");

sets.add("0000");

mpSharedPreferences.putStringSet("stringSet",sets);

System.out.println(mpSharedPreferences.getString("string",""));

System.out.println(mpSharedPreferences.getBoolean("boolean",false));

System.out.println(mpSharedPreferences.getFloat("float",0));

System.out.println(mpSharedPreferences.getInt("int",0));

System.out.println(mpSharedPreferences.getLong("long",0));

System.out.println(mpSharedPreferences.getStringSet("stringSet",null));

for (int i = 0;i < 10000;i++) {

System.out.println("i == " + i);

System.out.println(mpSharedPreferences.getAll());

}

}

实现

package com.jove.mpsp;

import android.content.ContentValues;

import android.content.Context;

import android.database.ContentObserver;

import android.database.Cursor;

import android.net.Uri;

import android.os.Handler;

import android.os.Looper;

import java.util.HashMap;

import java.util.HashSet;

import java.util.Map;

import java.util.Set;

import androidx.annotation.NonNull;

import androidx.annotation.Nullable;

public class MPSharedPreferences {

private HashSet listeners = new HashSet<>();

public interface OnSharedPreferenceChangeListener {

void onSharedPreferenceChanged(final MPSharedPreferences sharedPreferences,

final String key);

}

private String name;

private Context context;

private ContentObserver mContentObserver =

new ContentObserver(new Handler(Looper.getMainLooper())) {

@Override

public void onChange(boolean selfChange, Uri uri) {

synchronized (MPSharedPreferences.this) {

for (final OnSharedPreferenceChangeListener listener : listeners) {

listener.onSharedPreferenceChanged(MPSharedPreferences.this

, uri.getPathSegments().get(1));

}

}

}

};

public MPSharedPreferences(@NonNull Context context, @NonNull final String name) {

this.name = name;

this.context = context;

this.context.getContentResolver()

.registerContentObserver(Uri.parse(String.format("content://com.jove.mpsp" +

".provider/%s"

, name)), true, mContentObserver);

}

public Map getAll() {

Cursor cursor = null;

try {

cursor = context.getContentResolver()

.query(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/all/0"

, name)), null, null, null, null);

if (null != cursor && cursor.moveToNext()) {

final Map map = new HashMap<>(cursor.getColumnCount());

for (int i = 0; i < cursor.getColumnCount(); i++) {

switch (cursor.getType(i)) {

case Cursor.FIELD_TYPE_BLOB:

map.put(cursor.getColumnName(i), cursor.getBlob(i));

break;

case Cursor.FIELD_TYPE_STRING:

case Cursor.FIELD_TYPE_NULL:

map.put(cursor.getColumnName(i), cursor.getString(i));

break;

case Cursor.FIELD_TYPE_INTEGER:

map.put(cursor.getColumnName(i), cursor.getInt(i));

if (Long.parseLong(cursor.getString(i)) != cursor.getInt(i)) {

map.put(cursor.getColumnName(i),

Long.parseLong(cursor.getString(i)));

}

break;

case Cursor.FIELD_TYPE_FLOAT:

map.put(cursor.getColumnName(i), cursor.getFloat(i));

break;

}

}

return map;

}

} finally {

if (null != cursor) {

cursor.close();

}

}

return null;

}

@Nullable

public String getString(String key, @Nullable String defValue) {

Cursor cursor = null;

try {

cursor = context.getContentResolver()

.query(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s/1"

, name, key)), null, null, null, null);

if (null != cursor && cursor.moveToNext()) {

return cursor.getString(0);

}

} finally {

if (null != cursor) {

cursor.close();

}

}

return defValue;

}

@Nullable

public Set getStringSet(String key, @Nullable Set defValues) {

Cursor cursor = null;

try {

cursor = context.getContentResolver()

.query(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s/6"

, name, key)), null, null, null, null);

if (null != cursor && cursor.moveToNext()) {

final Set sets = new HashSet<>();

for (int i = 0; i < cursor.getColumnCount(); i++) {

sets.add(cursor.getString(i));

}

return sets;

}

} finally {

if (null != cursor) {

cursor.close();

}

}

return defValues;

}

public int getInt(String key, int defValue) {

Cursor cursor = null;

try {

cursor = context.getContentResolver()

.query(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s/4"

, name, key)), null, null, null, null);

if (null != cursor && cursor.moveToNext()) {

return cursor.getInt(0);

}

} finally {

if (null != cursor) {

cursor.close();

}

}

return defValue;

}

public long getLong(String key, long defValue) {

Cursor cursor = null;

try {

cursor = context.getContentResolver()

.query(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s/5"

, name, key)), null, null, null, null);

if (null != cursor && cursor.moveToNext()) {

return Long.parseLong(cursor.getString(0));

}

} finally {

if (null != cursor) {

cursor.close();

}

}

return defValue;

}

public float getFloat(final String key, final float defValue) {

Cursor cursor = null;

try {

cursor = context.getContentResolver()

.query(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s/3"

, name, key)), null, null, null, null);

if (null != cursor && cursor.moveToNext()) {

return cursor.getFloat(0);

}

} finally {

if (null != cursor) {

cursor.close();

}

}

return defValue;

}

public boolean getBoolean(final String key, final boolean defValue) {

Cursor cursor = null;

try {

cursor = context.getContentResolver()

.query(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s/2"

, name, key)), null, null, null, null);

if (null != cursor && cursor.moveToNext()) {

return 0 == cursor.getInt(0) ? false : true;

}

} finally {

if (null != cursor) {

cursor.close();

}

}

return defValue;

}

public boolean contains(final String key) {

Cursor cursor = null;

try {

cursor = context.getContentResolver()

.query(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s/7"

, name, key)), null, null, null, null);

if (null != cursor && cursor.moveToNext()) {

return cursor.getInt(0) > 0;

}

} finally {

if (null != cursor) {

cursor.close();

}

}

return false;

}

public synchronized void registerOnSharedPreferenceChangeListener(final @NonNull OnSharedPreferenceChangeListener listener) {

listeners.add(listener);

}

public synchronized void unregisterOnSharedPreferenceChangeListener(final @NonNull OnSharedPreferenceChangeListener listener) {

listeners.remove(listener);

}

public boolean putString(String key, @Nullable String value) {

final ContentValues values = new ContentValues();

values.put(key, value);

final Uri uri = context.getContentResolver()

.insert(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s/1"

, name, key)), values);

return null != uri;

}

public boolean putStringSet(final String key, final @Nullable Set values) {

final ContentValues contentValues = new ContentValues();

int index = 0;

for (final String value : values) {

contentValues.put("k" + index, value);

index++;

}

final Uri uri = context.getContentResolver()

.insert(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s/6"

, name, key)), contentValues);

return null != uri;

}

public boolean putInt(String key, int value) {

final ContentValues values = new ContentValues();

values.put(key, value);

final Uri uri = context.getContentResolver()

.insert(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s/4"

, name, key)), values);

return null != uri;

}

public boolean putLong(String key, long value) {

final ContentValues values = new ContentValues();

values.put(key, value);

final Uri uri = context.getContentResolver()

.insert(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s/5"

, name, key)), values);

return null != uri;

}

public boolean putFloat(String key, float value) {

final ContentValues values = new ContentValues();

values.put(key, value);

final Uri uri = context.getContentResolver()

.insert(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s/3"

, name, key)), values);

return null != uri;

}

public boolean putBoolean(String key, boolean value) {

final ContentValues values = new ContentValues();

values.put(key, value);

final Uri uri = context.getContentResolver()

.insert(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s/2"

, name, key)), values);

return null != uri;

}

public boolean remove(final String key) {

final int nums = context.getContentResolver()

.delete(Uri.parse(String.format("content://com.jove.mpsp.provider/remove/%s/%s"

, name, key)), null, null);

return nums > 0;

}

public boolean clear() {

final int nums = context.getContentResolver()

.delete(Uri.parse(String.format("content://com.jove.mpsp.provider/clear/%s"

, name)), null, null);

return nums > 0;

}

}

package com.jove.mpsp;

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.database.MatrixCursor;

import android.net.Uri;

import java.io.File;

import java.lang.reflect.Field;

import java.util.HashSet;

import java.util.List;

import java.util.Map;

import java.util.Set;

import java.util.concurrent.locks.ReentrantReadWriteLock;

import androidx.annotation.NonNull;

import androidx.annotation.Nullable;

public class MPContentProvider extends ContentProvider implements SharedPreferences.OnSharedPreferenceChangeListener {

private static UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();

static {

uriMatcher.addURI("com.jove.mpsp.provider", "remove/*/*", 0x666);

uriMatcher.addURI("com.jove.mpsp.provider", "clear/*", 0x999);

uriMatcher.addURI("com.jove.mpsp.provider", "*/*/#", 0x888);

}

@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) {

try {

lock.readLock().lock();

switch (uriMatcher.match(uri)) {

case 0x888:

final List paths = uri.getPathSegments();

final SharedPreferences sp = getContext()

.getSharedPreferences(paths.get(0), Context.MODE_PRIVATE);

final int action = Integer.parseInt(paths.get(2));

if (action == 0) {

final Map map = sp.getAll();

if (null != map && 0 != map.size()) {

final Object[] columnValues = new Object[map.size()];

final String[] columnNames = new String[map.size()];

int index = 0;

for (final String key : map.keySet()) {

columnNames[index] = key;

columnValues[index] = map.get(key);

index++;

}

final MatrixCursor cursor = new MatrixCursor(columnNames

, 1);

cursor.addRow(columnValues);

return cursor;

}

return null;

} else if (action == 1) {

final String key = paths.get(1);

if (sp.contains(key)) {

final MatrixCursor cursor = new MatrixCursor(new String[]{"v"}

, 1);

cursor.addRow(new Object[]{sp.getString(paths.get(1), "")});

return cursor;

}

return null;

} else if (action == 2) {

final String key = paths.get(1);

if (sp.contains(key)) {

final MatrixCursor cursor = new MatrixCursor(new String[]{"v"}

, 1);

cursor.addRow(new Object[]{sp.getBoolean(paths.get(1), false) ? 1 : 0});

return cursor;

}

return null;

} else if (action == 3) {

final String key = paths.get(1);

if (sp.contains(key)) {

final MatrixCursor cursor = new MatrixCursor(new String[]{"v"}

, 1);

cursor.addRow(new Object[]{sp.getFloat(paths.get(1), 0)});

return cursor;

}

return null;

} else if (action == 4) {

final String key = paths.get(1);

if (sp.contains(key)) {

final MatrixCursor cursor = new MatrixCursor(new String[]{"v"}

, 1);

cursor.addRow(new Object[]{sp.getInt(paths.get(1), 0)});

return cursor;

}

return null;

} else if (action == 5) {

final String key = paths.get(1);

if (sp.contains(key)) {

final MatrixCursor cursor = new MatrixCursor(new String[]{"v"}

, 1);

cursor.addRow(new Object[]{sp.getLong(paths.get(1), 0)});

return cursor;

}

return null;

} else if (action == 6) {

final String key = paths.get(1);

if (sp.contains(key)) {

final Set sets = sp.getStringSet(key, null);

if (null != sets && 0 != sets.size()) {

final String[] columnNames = new String[sets.size()];

final Object[] columnValues = new Object[sets.size()];

int index = 0;

for (final String value : sets) {

columnNames[index] = "v" + index;

columnValues[index] = value;

index++;

}

final MatrixCursor cursor = new MatrixCursor(columnNames

, 1);

cursor.addRow(columnValues);

return cursor;

}

}

return null;

} else if (action == 7) {

final String key = paths.get(1);

if (sp.contains(key)) {

final MatrixCursor cursor = new MatrixCursor(new String[]{"v"}

, 1);

cursor.addRow(new Object[]{1});

return cursor;

}

return null;

}

break;

}

return null;

} finally {

lock.readLock().unlock();

}

}

@Nullable

@Override

public String getType(@NonNull Uri uri) {

return null;

}

@Nullable

@Override

public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {

try {

lock.writeLock().lock();

switch (uriMatcher.match(uri)) {

case 0x888:

final List paths = uri.getPathSegments();

final SharedPreferences sp = getContext()

.getSharedPreferences(paths.get(0), Context.MODE_PRIVATE);

sp.unregisterOnSharedPreferenceChangeListener(this);

sp.registerOnSharedPreferenceChangeListener(this);

final int action = Integer.parseInt(paths.get(2));

if (action == 1) {

final String key = paths.get(1);

if (sp.edit().putString(key, values.getAsString(key)).commit()) {

return uri;

}

return null;

} else if (action == 2) {

final String key = paths.get(1);

if (sp.edit().putBoolean(key, values.getAsBoolean(key)).commit()) {

return uri;

}

return null;

} else if (action == 3) {

final String key = paths.get(1);

if (sp.edit().putFloat(key, values.getAsFloat(key)).commit()) {

return uri;

}

return null;

} else if (action == 4) {

final String key = paths.get(1);

if (sp.edit().putInt(key, values.getAsInteger(key)).commit()) {

return uri;

}

return null;

} else if (action == 5) {

final String key = paths.get(1);

if (sp.edit().putLong(key, values.getAsLong(key)).commit()) {

return uri;

}

return null;

} else if (action == 6) {

final Set sets = new HashSet<>();

for (final String key : values.keySet()) {

sets.add(values.getAsString(key));

}

final String key = paths.get(1);

if (sp.edit().putStringSet(key, sets).commit()) {

return uri;

}

return null;

}

break;

}

return null;

} finally {

lock.writeLock().unlock();

}

}

@Override

public int delete(@NonNull Uri uri, @Nullable String selection,

@Nullable String[] selectionArgs) {

try {

lock.writeLock().lock();

final List paths = uri.getPathSegments();

final SharedPreferences sp = getContext()

.getSharedPreferences(paths.get(1), Context.MODE_PRIVATE);

sp.unregisterOnSharedPreferenceChangeListener(this);

sp.registerOnSharedPreferenceChangeListener(this);

switch (uriMatcher.match(uri)) {

case 0x666:

return sp.edit().remove(paths.get(2)).commit() ? 1 : 0;

case 0x999:

return sp.edit().clear().commit() ? 1 : 0;

}

} finally {

lock.writeLock().unlock();

}

return 0;

}

@Override

public int update(@NonNull Uri uri, @Nullable ContentValues values

, @Nullable String selection, @Nullable String[] selectionArgs) {

return 0;

}

@Override

public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences,

final String key) {

try {

final Field field = sharedPreferences.getClass().getDeclaredField("mFile");

field.setAccessible(true);

final File file = (File) field.get(sharedPreferences);

getContext().getContentResolver()

.notifyChange(Uri.parse(String.format("content://com.jove.mpsp.provider/%s/%s"

, file.getName().replaceAll(".xml", ""), key)), null);

} catch (Exception e) {

e.printStackTrace();

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值