Xposed 利用ContentProvider实现跨进程数据读取
由于Android N以后,Sharepreference的第三个参数MODE_WORLD_READABLE的被禁止,Shareperference的跨进程通信变得不可用,谷歌推荐使用ContentProvider进行通信。
但是由于ContentProvider在平时简单的使用中过于重量,需要进行数据库操作特别的麻烦,所以我找到了一个库,基于ContentProvider封装,使用和平时SharePreference基本一致。
开源库地址:MultiprocessSharedPreferences
关键库代码:MultiprocessSharedPreferences.java
/*
* 创建日期:2014年9月12日 下午0:0:02
*/
package com.android.zgj.utils;
import com.android.zgj.BuildConfig;
import android.content.BroadcastReceiver;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.UriMatcher;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.DeadObjectException;
import android.support.annotation.NonNull;
import android.util.Log;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
/**
* 使用ContentProvider实现多进程SharedPreferences读写;<br>
* 1、ContentProvider天生支持多进程访问;<br>
* 2、使用内部私有BroadcastReceiver实现多进程OnSharedPreferenceChangeListener监听;<br>
*
* 使用方法:AndroidManifest.xml中添加provider申明:<br>
* <pre>
* <provider android:name="com.android.zgj.utils.MultiprocessSharedPreferences"
* android:authorities="com.android.zgj.MultiprocessSharedPreferences"
* android:process="com.android.zgj.MultiprocessSharedPreferences"
* android:exported="false" />
* <!-- authorities属性里面最好使用包名做前缀,apk在安装时authorities同名的provider需要校验签名,否则无法安装;--!/><br>
* </pre>
*
* ContentProvider方式实现要注意:<br>
* 1、当ContentProvider所在进程android.os.Process.killProcess(pid)时,会导致整个应用程序完全意外退出或者ContentProvider所在进程重启;<br>
* 重启报错信息:Acquiring provider <processName> for user 0: existing object's process dead;<br>
* 2、如果设备处在“安全模式”下,只有系统自带的ContentProvider才能被正常解析使用,因此put值时默认返回false,get值时默认返回null;<br>
*
* 其他方式实现SharedPreferences的问题:<br>
* 使用FileLock和FileObserver也可以实现多进程SharedPreferences读写,但是维护成本高,需要定期对照系统实现更新新的特性;
*
* @author zhangguojun
* @version 1.0
* @since JDK1.6
*/
public class MultiprocessSharedPreferences extends ContentProvider implements SharedPreferences {
private static final String TAG = "MultiprocessSharedPreferences";
public static final boolean DEBUG = BuildConfig.DEBUG;
private Context mContext;
private String mName;
private int mMode;
private boolean mIsSafeMode;
private static final Object CONTENT = new Object();
private WeakHashMap<OnSharedPreferenceChangeListener, Object> mListeners;
private BroadcastReceiver mReceiver;
private static String AUTHORITY;
private static volatile Uri AUTHORITY_URI;
private UriMatcher mUriMatcher;
private static final String KEY = "value";
private static final String KEY_NAME = "name";
private static final String PATH_WILDCARD = "*/";
private static final String PATH_GET_ALL = "getAll";
private static final String PATH_GET_STRING = "getString";
private static final String PATH_GET_INT = "getInt";
private static final String PATH_GET_LONG = "getLong";
private static final String PATH_GET_FLOAT = "getFloat";
private static final String PATH_GET_BOOLEAN = "getBoolean";
private static final String PATH_CONTAINS = "contains";
private static final String PATH_APPLY = "apply";
private static final String PATH_COMMIT = "commit";
private static final String PATH_REGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER = "registerOnSharedPreferenceChangeListener";
private static final String PATH_UNREGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER = "unregisterOnSharedPreferenceChangeListener";
private static final String PATH_GET_STRING_SET = "getStringSet";
private static final int GET_ALL = 1;
private static final int GET_STRING = 2;
private static final int GET_INT = 3;
private static final int GET_LONG = 4;
private static final int GET_FLOAT = 5;
private static final int GET_BOOLEAN = 6;
private static final int CONTAINS = 7;
private static final int APPLY = 8;
private static final int COMMIT = 9;
private static final int REGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER = 10;
private static final int UNREGISTER_ON_SHARED_PREFERENCE_CHANGE_LISTENER = 11;
private static final int GET_STRING_SET = 12;
private HashMap<String, Integer> mListenersCount;
private static class ReflectionUtil {
public static ContentValues contentValuesNewInstance(HashMap<String, Object> values) {
try {
Constructor<ContentValues> c = ContentValues.class.getDeclaredConstructor(new Class[] { HashMap.class }); // hide
c.setAccessible(true);
return c.newInstance(values);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
}
}
public static Editor editorPutStringSet(Editor editor, String key, Set<String> values) {
try {
Method method = editor.getClass().getDeclaredMethod("putStringSet", new Class[] { String.class, Set.class }); // Android 3.0
return (Editor) method.invoke(editor, key, values);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("unchecked")
public static Set<String> sharedPreferencesGetStringSet(SharedPreferences sharedPreferences, String key, Set<String> values) {
try {
Method method = sharedPreferences.getClass().getDeclaredMethod("getStringSet", new Class[] { String.class, Set.class }); // Android 3.0
return (Set<String>) method.invoke(sharedPreferences, key, values);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
public static void editorApply(Editor editor) {
try {
Method method = editor.getClass().getDeclaredMethod("apply"); // Android 2.3
method.invoke(editor);
} catch (IllegalArgumentException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} c