美文网首页
Android多进程线程安全SharedPreferences

Android多进程线程安全SharedPreferences

作者: 贼噶人 | 来源:发表于2020-10-19 17:42 被阅读0次

    https://github.com/jovezhougang/mpsp

    原理

    使用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<String> 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<OnSharedPreferenceChangeListener> 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<String, ?> 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<String, Object> 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<String> getStringSet(String key, @Nullable Set<String> 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<String> 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<String> 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<String> 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<String, ?> 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<String> 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<String> 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<String> 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<String> 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();
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:Android多进程线程安全SharedPreferences

          本文链接:https://www.haomeiwen.com/subject/zaclmktx.html