不同App共享SharedPreferences(跨activity, application访问SharedPreferences)

本文介绍了如何在Android中利用SharedPreferences以Context.MODE_WORLD_READABLE模式进行数据存储,并演示了跨应用访问这些数据的方法。

写SharedPreferences的时候设置属性为Context.MODE_WORLD_READABLE:

SharedPreferences sharedPreferences = getSharedPreferences(shareName, MODE_WORLD_READABLE);

访问的时候:

Context otherContext = null;
try { 
    otherContext = createPackageContext("com.android.settings", Context.CONTEXT_IGNORE_SECURITY);
} catch (NameNotFoundException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}
SharedPreferences sharedPreferences = otherContext.getSharedPreferences(shareName, MODE_WORLD_READABLE);


<think>我们讨论的是如何修改SharedPreferences的存储路径。默认情况下,SharedPreferences将数据存储在应用的私有目录下的shared_prefs子目录中,文件格式为XML。但有时我们可能需要修改这个存储路径,例如将文件存储到外部存储或自定义目录。解决方案:1.自定义SharedPreferences实现:通过继承ContextWrapper,重写getSharedPreferences方法,返回一个自定义路径的SharedPreferences实例。2.使用FileProvider或存储访问框架(SAF)将文件保存到外部存储(需要处理权限和兼容性)。注意:在Android7.0及以上,使用FileProvider来共享文件。在Android10及以上,由于分区存储(ScopedStorage)的限制,访问外部存储需要特殊处理。以下是一个示例,展示如何通过自定义Context来改变SharedPreferences的存储路径:步骤:1.创建一个CustomContext类,继承自ContextWrapper。2.重写getSharedPreferences方法,指定自定义的目录。3.在应用中使用这个CustomContext。但是,需要注意的是,SharedPreferences在Android系统中是单例的,我们无法直接改变系统创建的实例。因此,我们可以通过代理模式,将自定义路径的SharedPreferences实例返回。另一种方法是:直接使用SharedPreferences的底层实现(通过File和模式创建SharedPreferencesImpl),但这是非公开API,存在兼容性问题。因此,我们推荐使用公开API:使用Context的createDeviceProtectedStorageContext()方法(如果设备支持)或者使用MODE_PRIVATE模式在自定义目录创建文件,然后通过ContextImpl的私有方法创建SharedPreferences实例?但这样也不稳定。更安全的方法:自己实现SharedPreferences接口,读写自定义路径的XML文件。以下是一个自定义SharedPreferences的简单实现(仅关键部分,非完整):</think>### 修改 SharedPreferences 存储路径的完整解决方案 在 Android 中修改 SharedPreferences 的存储路径需要深入理解其内部机制。以下是几种可靠的方法: #### 方法 1: 使用自定义 ContextWrapper 重写路径 ```java public class CustomPathContext extends ContextWrapper { private final File mCustomDir; public CustomPathContext(Context base, File customDir) { super(base); mCustomDir = customDir; } @Override public File getSharedPreferencesPath(String name) { // Android 7.0+ 方法 return new File(mCustomDir, name + ".xml"); } @Override public SharedPreferences getSharedPreferences(String name, int mode) { // 创建自定义路径的 SharedPreferences File file = getSharedPreferencesPath(name); return new CustomPathSharedPreferences(file, mode); } } // 自定义 SharedPreferences 实现 public class CustomPathSharedPreferences implements SharedPreferences { private final SharedPreferences mDelegate; public CustomPathSharedPreferences(File file, int mode) { Context context = getBaseContext(); // 使用反射创建实际实例 mDelegate = createSharedPreferences(context, file, mode); } private SharedPreferences createSharedPreferences(Context context, File file, int mode) { try { // 使用系统内部方法创建实例 Class<?> contextImplClass = Class.forName("android.app.ContextImpl"); Method getPreferencesDir = contextImplClass.getDeclaredMethod("getPreferencesDir"); getPreferencesDir.setAccessible(true); File prefsDir = (File) getPreferencesDir.invoke(context); Class<?> sharedPreferencesImplClass = Class.forName("android.app.SharedPreferencesImpl"); Constructor<?> constructor = sharedPreferencesImplClass.getDeclaredConstructor(File.class, int.class); constructor.setAccessible(true); return (SharedPreferences) constructor.newInstance(file, mode); } catch (Exception e) { // 回退到默认实现 return context.getSharedPreferences(file.getName().replace(".xml", ""), mode); } } // 实现所有 SharedPreferences 接口方法... @Override public Map<String, ?> getAll() { return mDelegate.getAll(); } // 其他方法委托给 mDelegate... } ``` #### 方法 2: 直接操作 XML 文件路径 ```java public class SharedPreferencesManager { private static final String PREFS_SUFFIX = ".xml"; public static SharedPreferences getCustomPreferences(Context context, String name, File customDir) { File prefsFile = new File(customDir, name + PREFS_SUFFIX); return new CustomSharedPreferences(context, prefsFile); } private static class CustomSharedPreferences implements SharedPreferences { private final Context mContext; private final File mPrefsFile; private SharedPreferences mDelegate; public CustomSharedPreferences(Context context, File prefsFile) { mContext = context; mPrefsFile = prefsFile; initDelegate(); } private void initDelegate() { try { // 使用系统内部类加载 Class<?> sharedPreferencesImplClass = Class.forName("android.app.SharedPreferencesImpl"); Constructor<?> constructor = sharedPreferencesImplClass.getDeclaredConstructor(File.class, int.class); constructor.setAccessible(true); mDelegate = (SharedPreferences) constructor.newInstance(mPrefsFile, Context.MODE_PRIVATE); } catch (Exception e) { // 回退到自定义 XML 解析 mDelegate = new XmlBackedPreferences(mPrefsFile); } } // 委托所有方法给 mDelegate... } // 基于 XML 的自定义实现 private static class XmlBackedPreferences implements SharedPreferences { private final File mPrefsFile; private final Map<String, Object> mMap = new ConcurrentHashMap<>(); public XmlBackedPreferences(File prefsFile) { mPrefsFile = prefsFile; loadFromFile(); } private synchronized void loadFromFile() { if (!mPrefsFile.exists()) return; try (FileInputStream fis = new FileInputStream(mPrefsFile)) { XmlPullParser parser = Xml.newPullParser(); parser.setInput(fis, "UTF-8"); int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { if (eventType == XmlPullParser.START_TAG && "string".equals(parser.getName())) { String key = parser.getAttributeValue(null, "name"); String value = parser.nextText(); mMap.put(key, value); } // 处理其他类型: int, boolean, float 等 eventType = parser.next(); } } catch (Exception e) { Log.e("Prefs", "Error loading preferences", e); } } private synchronized void saveToFile() { try (FileOutputStream fos = new FileOutputStream(mPrefsFile)) { XmlSerializer serializer = Xml.newSerializer(); serializer.setOutput(fos, "UTF-8"); serializer.startDocument("UTF-8", true); serializer.startTag(null, "map"); for (Map.Entry<String, Object> entry : mMap.entrySet()) { Object value = entry.getValue(); String tagName = getValueTagName(value); serializer.startTag(null, tagName); serializer.attribute(null, "name", entry.getKey()); serializer.text(String.valueOf(value)); serializer.endTag(null, tagName); } serializer.endTag(null, "map"); serializer.endDocument(); } catch (Exception e) { Log.e("Prefs", "Error saving preferences", e); } } private String getValueTagName(Object value) { if (value instanceof String) return "string"; if (value instanceof Integer) return "int"; if (value instanceof Boolean) return "boolean"; if (value instanceof Float) return "float"; if (value instanceof Long) return "long"; return "string"; // 默认 } @Override public Editor edit() { return new XmlEditor(); } private class XmlEditor implements Editor { private final Map<String, Object> mEditorMap = new HashMap<>(); private boolean mClear = false; @Override public Editor putString(String key, String value) { mEditorMap.put(key, value); return this; } // 实现其他 put 方法... @Override public Editor remove(String key) { mEditorMap.put(key, null); // 标记为删除 return this; } @Override public Editor clear() { mClear = true; return this; } @Override public boolean commit() { synchronized (XmlBackedPreferences.this) { if (mClear) { mMap.clear(); mClear = false; } for (Map.Entry<String, Object> entry : mEditorMap.entrySet()) { if (entry.getValue() == null) { mMap.remove(entry.getKey()); } else { mMap.put(entry.getKey(), entry.getValue()); } } saveToFile(); return true; } } @Override public void apply() { new Thread(this::commit).start(); } } } } ``` #### 方法 3: 使用 ContentProvider 代理访问 ```java public class CustomPrefsProvider extends ContentProvider { private static final Uri CONTENT_URI = Uri.parse("content://com.example.prefs.provider"); private static final String METHOD_GET_PREFS = "getSharedPreferences"; @Override public Bundle call(String method, String arg, Bundle extras) { if (METHOD_GET_PREFS.equals(method)) { String name = extras.getString("name"); int mode = extras.getInt("mode"); // 自定义路径 File customDir = new File(getContext().getExternalFilesDir(null), "custom_prefs"); File prefsFile = new File(customDir, name + ".xml"); Bundle result = new Bundle(); result.putString("path", prefsFile.getAbsolutePath()); return result; } return super.call(method, arg, extras); } // 在应用中使用 public static SharedPreferences getCustomPreferences(Context context, String name, int mode) { Bundle extras = new Bundle(); extras.putString("name", name); extras.putInt("mode", mode); Bundle result = context.getContentResolver().call( CONTENT_URI, METHOD_GET_PREFS, null, extras ); String path = result.getString("path"); return new CustomPathSharedPreferences(context, new File(path), mode); } } ``` #### 完整集成示例 ```java // 在 Application 类中初始化 public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); File customPrefsDir = new File(getExternalFilesDir(null), "custom_prefs"); if (!customPrefsDir.exists()) { customPrefsDir.mkdirs(); } // 替换基础上下文 Context customContext = new CustomPathContext(this, customPrefsDir); getBaseContext().getResources().updateConfiguration( getResources().getConfiguration(), getResources().getDisplayMetrics() ); } @Override public Context getBaseContext() { // 返回自定义上下文 return super.getBaseContext(); } } // 在 Activity 中使用 public class MainActivity extends Activity { private SharedPreferences mCustomPrefs; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 获取自定义路径的 SharedPreferences mCustomPrefs = SharedPreferencesManager.getCustomPreferences( this, "my_prefs", new File(getExternalFilesDir(null), "custom_prefs") ); // 使用示例 mCustomPrefs.edit().putString("key", "value").apply(); } } ``` #### 路径验证和迁移工具 ```java public class PrefsMigration { public static void migratePreferences(Context context, String prefsName, File newDir) { File oldFile = new File(context.getApplicationInfo().dataDir + "/shared_prefs", prefsName + ".xml"); File newFile = new File(newDir, prefsName + ".xml"); if (oldFile.exists() && !newFile.exists()) { try { Files.copy(oldFile, newFile); Log.i("Migration", "Preferences migrated to: " + newFile); // 可选:删除旧文件 // oldFile.delete(); } catch (IOException e) { Log.e("Migration", "Failed to migrate preferences", e); } } } public static boolean verifyPath(SharedPreferences prefs, File expectedDir, String prefsName) { try { // 使用反射获取实际文件路径 Field fileField = prefs.getClass().getDeclaredField("mFile"); fileField.setAccessible(true); File actualFile = (File) fileField.get(prefs); File expectedFile = new File(expectedDir, prefsName + ".xml"); return expectedFile.equals(actualFile); } catch (Exception e) { return false; } } } ``` ### 关键注意事项 1. **权限问题**: - 自定义路径需要适当的文件系统权限 - 外部存储需要 `WRITE_EXTERNAL_STORAGE` 权限(Android 9 及以下) - Android 10+ 需要使用分区存储兼容方案 2. **安全性**: - 避免将敏感数据存储在外部目录 - 使用 `Context.MODE_PRIVATE` 确保文件安全 - 考虑文件加密 3. **性能影响**: - 自定义实现可能比系统实现慢 - 频繁读写应考虑异步操作 - 使用内存缓存减少 IO 4. **兼容性**: - 不同 Android 版本路径处理差异 - 厂商定制 ROM 的特殊行为 - 多用户环境路径变化 ###
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值