Xposed框架中XSharePreference的使用
本文博客地址:https://blog.csdn.net/QQ1084283172/article/details/81194406
在Xposed框架的模块编写中,通常希望我们自己写的Android界面控制部分能够对Xposde框架的模块进行配置,也就说在当前Android应用中写的SharePreference文件,在其他Android应用加载该Xposde模块时,在执行的Xposed模块代码中能够访问到前面提到的SharePreference配置文件。当然了将配置文件写在Android应用的sd卡路径下是可以的,但是到了Android系统7.0的高版本时,基于sd卡文件的访问权限问题,使得这种方法不是很好使,当然了基于SharePreference配置文件的数据共享方法也不好使。这里只是学习一下Xposed框架中XSharePreference的使用方法,具体遇到的问题就是《一个基于xposed和inline hook的一代壳脱壳工具》中,关于Xposed模块共享配置文件问题的讨论,总之呢,到了Android 7.0版本的系统,基于权限和安全性的考虑,这两种方法都不是最佳的共享数据的通用方法。
一、普通情况下,Android应用跨进程共享文件数据的方法,可以参考博客《读、写其他应用程序的SharedPreferences》的讲解。
要读、写其他Android应用的SharedPreferences文件,需要共享该SharedPreferences文件的Android应用程序指定相应的访问权限,例如:MODE_WORLD_READABLE表明该SharedPreferences可被其他应用程序读取;指定MODE_WORLD_WRITEABLE,表明该SharedPreferences可被其他程序写入。
操作步骤:
1、创建其他Android应用程序对应的Context。
2、调用上面Context的getSharedPreferences(String name,int mode),获取相应的SharedPreferences对象。
3、如果需要写入数据,调用SharedPreferences的edit()方法获取相应的Editor即可。
摘抄一下作者冯健-developer大大的博客代码,进行演示说明:
共享SharedPreferences配置文件数据的Android应用:
获取共享的SharedPreferences配置文件数据的Android应用:
但是这个Android跨进程共享SharedPreferences文件数据的方案,在Android的高版本系统上并不好使,具体的可以参考博客《SharedPreferences多进程解决方案》的描述,google官方还是推荐使用ContentProvider的方法进行替代,具体的内容如下:
二、Xposed框架下,Android应用与编写的Xposed模块进行SharedPreferences文件数据共享的实现。
对于Android应用与编写的Xposed模块进行SharedPreferences文件数据的共享,Xposed框架提供了XSharePreference的解决方法,不过需要说明的Xposed框架提供的XSharePreference只能实现SharedPreferences文件数据共享的读,不支持跨进程SharedPreferences文件数据共享的写。SharePrefernce实际上是对应路径/data/data/<Package Name>/shared_prefs/<Package Name>_preferences.xml的文件,里面按键值对的形式存放。XSharedPreferences做的工作实际上就是根据<Package Name>自己去获取和解析这个配置文件,然后读出该配置文件中的数据存放在一个HashMap当中,这些可以从XSharedPreferences的源码中了解到。
package de.robv.android.xposed;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import android.preference.PreferenceManager;
import android.util.Log;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import de.robv.android.xposed.services.FileResult;
/**
* This class is basically the same as SharedPreferencesImpl from AOSP, but
* read-only and without listeners support. Instead, it is made to be
* compatible with all ROMs.
*/
public final class XSharedPreferences implements SharedPreferences {
private static final String TAG = "XSharedPreferences";
private final File mFile;
private final String mFilename;
private Map<String, Object> mMap;
private boolean mLoaded = false;
private long mLastModified;
private long mFileSize;
/**
* Read settings from the specified file.
* @param prefFile The file to read the preferences from.
*/
public XSharedPreferences(File prefFile) {
mFile = prefFile;
mFilename = mFile.getAbsolutePath();
startLoadFromDisk();
}
/**
* Read settings from the default preferences for a package.
* These preferences are returned by {@link PreferenceManager#getDefaultSharedPreferences}.
* @param packageName The package name.
*/
public XSharedPreferences(String packageName) {
// 调用XSharedPreferences(String packageName, String prefFileName)函数
this(packageName, packageName + "_preferences");
}
/**
* Read settings from a custom preferences file for a package.
* These preferences are returned by {@link Context#getSharedPreferences(String, int)}.
* @param packageName The package name.
* @param prefFileName The file name without ".xml".
*/
public XSharedPreferences(String packageName, String prefFileName) {
// Environment.getDataDirectory()获取Android系统的/data目录
// 在/data/data/路径下创建共享xml文件
mFile = new File(Environment.getDataDirectory(), "data/" + packageName + "/shared_prefs/" + prefFileName + ".xml");
// 获取文件全路径
mFilename = mFile.getAbsolutePath();
startLoadFromDisk();
}
/**
* Tries to make the preferences file world-readable.
*
* <p><strong>Warning:</strong> This is only meant to work around permission "fix" functions that are part
* of some recoveries. It doesn't replace the need to open preferences with {@code MODE_WORLD_READABLE}
* in the module's UI code. Otherwise, Android will set stricter permissions again during the next save.
*
* <p>This will only work if executed as root (e.g. {@code initZygote()}) and only if SELinux is disabled.
*
* @return {@code true} in case the file could be made world-readable.
*/
@SuppressLint("SetWorldReadable")
public boolean makeWorldReadable() {
if (!SELinuxHelper.getAppDataFileService().hasDirectFileAccess())
return false; // It doesn't make much sense to make the file readable if we wouldn't be able to access it anyway.
if (!mFile.exists()) // Just in case - the file should never be created if it doesn't exist.
return false;
// 设置文件为全局可读
return mFile.setReadable(true, false);
}
/**
* Returns the file that is backing these preferences.
*
* <p><strong>Warning:</strong> The file might not be accessible directly.
*/
public File getFile() {
return mFile;
}
private void startLoadFromDisk() {
synchronized (this) {
mLoaded = false;
}
new Thread("XSharedPreferences-load") {
@Override
public void run() {
synchronized (XSharedPreferences.this) {
loadFromDiskLocked();
}
}
}.start();
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private void loadFromDiskLocked() {
if (mLoaded) {
return;
}
Map map = null;
FileResult result = null;
try {
result = SELinuxHelper.getAppDataFileService().getFileInputStream(mFilename, mFileSize, mLastModified);
if (result.stream != null) {
map = XmlUtils.readMapXml(result.stream);
result.stream.close();
} else {
// The file is unchanged, keep the current values
map = mMap;
}
} catch (XmlPullParserException e) {
Log.w(TAG, "getSharedPreferences", e);
} catch (FileNotFoundException ignored) {
// SharedPreferencesImpl has a canRead() check, so it doesn't log anything in case the file doesn't exist
} catch (IOException e) {
Log.w(TAG, "getSharedPreferences", e);
} finally {
if (result != null && result.stream != null) {
try {
result.stream.close();
} catch (RuntimeException rethrown) {
throw rethrown;
} catch (Exception ignored) {
}
}
}
mLoaded = true;
if (map != null) {
mMap = map;
mLastModified = result.mtime;
mFileSize = result.size;
} else {
mMap = new HashMap<>();
}
notifyAll();
}
/**
* Reload the settings from file if they have changed.
*
* <p><strong>Warning:</strong> With enforcing SELinux, this call might be quite expensive.
*/
public synchronized void reload() {
if (hasFileChanged())
startLoadFromDisk();
}
/**
* Check whether the file has changed since the last time it has been loaded.
*
* <p><strong>Warning:</strong> With enforcing SELinux, this call might be quite expensive.
*/
public synchronized boolean hasFileChanged() {
try {
FileResult result = SELinuxHelper.getAppDataFileService().statFile(mFilename);
return mLastModified != result.mtime || mFileSize != result.size;
} catch (FileNotFoundException ignored) {
// SharedPreferencesImpl doesn't log anything in case the file doesn't exist
return true;
} catch (IOException e) {
Log.w(TAG, "hasFileChanged", e);
return true;
}
}
private void awaitLoadedLocked() {
while (!mLoaded) {
try {
wait();
} catch (InterruptedException unused) {
}
}
}
/** @hide */
@Override
public Map<String, ?> getAll() {
synchronized (this) {
awaitLoadedLocked();
return new HashMap<>(mMap);
}
}
/** @hide */
@Override
public String getString(String key, String defValue) {
synchronized (this) {
awaitLoadedLocked();
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
/** @hide */
@Override
@SuppressWarnings("unchecked")
public Set<String> getStringSet(String key, Set<String> defValues) {
synchronized (this) {
awaitLoadedLocked();
Set<String> v = (Set<String>) mMap.get(key);
return v != null ? v : defValues;
}
}
/** @hide */
@Override
public int getInt(String key, int defValue) {
synchronized (this) {
awaitLoadedLocked();
Integer v = (Integer)mMap.get(key);
return v != null ? v : defValue;
}
}
/** @hide */
@Override
public long getLong(String key, long defValue) {
synchronized (this) {
awaitLoadedLocked();
Long v = (Long)mMap.get(key);
return v != null ? v : defValue;
}
}
/** @hide */
@Override
public float getFloat(String key, float defValue) {
synchronized (this) {
awaitLoadedLocked();
Float v = (Float)mMap.get(key);
return v != null ? v : defValue;
}
}
/** @hide */
@Override
public boolean getBoolean(String key, boolean defValue) {
synchronized (this) {
awaitLoadedLocked();
Boolean v = (Boolean)mMap.get(key);
return v != null ? v : defValue;
}
}
/** @hide */
@Override
public boolean contains(String key) {
synchronized (this) {
awaitLoadedLocked();
return mMap.containsKey(key);
}
}
/** @deprecated Not supported by this implementation. */
@Deprecated
@Override
public Editor edit() {
// 不支持共享配置文件的写操作
throw new UnsupportedOperationException("read-only implementation");
}
/** @deprecated Not supported by this implementation. */
@Deprecated
@Override
public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
throw new UnsupportedOperationException("listeners are not supported in this implementation");
}
/** @deprecated Not supported by this implementation. */
@Deprecated
@Override
public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
throw new UnsupportedOperationException("listeners are not supported in this implementation");
}
}
XSharedPreferences的api使用说明:https://api.xposed.info/reference/de/robv/android/xposed/XSharedPreferences.html
1.使用 XSharedPreferences(String packageName) 获取共享配置文件SharedPreferences的数据。
构造函数的源码如下,在构建XSharedPreferences对象设置默认的xml文件的文件名称为packageName+"_preferences",很显然构造函数XSharedPreferences(String packageName)最终是调用的构造函数XSharedPreferences(String packageName, String prefFileName)来实现的,只是传给该构造函数的第2个参数是默认的packageName+"_preferences"。
XSharedPreferences(String packageName)函数的使用说明:
保存数据到共享配置文件SharedPreferences中,使用的场景是自己写的Xposed框架一般都会做个界面,继承Activity的,保存数据是直接正常保存,唯一注意的就是初始化SharedPreferences的时候,权限要写成:Activity.MODE_WORLD_READABLE。这样的话,在其他的Xposed模块才能访问到这个共享的配置文件SharedPreferences,但是在Android 7.0的系统上还是会存在问题,不好使。
// 创建共享配置文件的时,设置为Activity.MODE_WORLD_READABLE模式
SharedPreferences sp = getActivity()
.getSharedPreferences(getPackageName() + "_preferences", Activity.MODE_WORLD_READABLE);
SharedPreferences.Editor editor = sp.edit();
// 保存数据到共享配置文件中
editor.putString("test_put", "test_put").commit();
在编写的Xposed模块中,对共享的SharedPreferences文件数据进行访问:
// 其中"com.xxx.yyy"为共享SharedPreferences文件数据的Android应用的包名
XSharedPreferences intance = new XSharedPreferences("xxx.yyy.zzz");
String strHello = intance.getString("test_put", "");
2.使用XSharedPreferences(String packageName, String prefFileName)获取共享配置文件SharedPreferences的数据。
构造函数的源码如下:
构造函数的使用说明:
数据的保存:
SharedPreferences sp = getActivity().getSharedPreferences("config", Activity.MODE_WORLD_READABLE);
SharedPreferences.Editor editor = sp.edit();
editor.putBoolean("isOk", true).commit();
数据的访问:
在XSharePreference初始化完成后,一定要设置为可读取即makeWorldReadable()。XSharedPreferences是只读的,并不允许进行写操作,从源码中实现中就可以看出来,代码来自于博客《Xposed中的XSharePreference的使用》。
public class PreferenceUtils {
public static final String AUTO_ALL ="auto_all";
public static final String REPLY_CONTENT ="reply_content";
public static final String REPLY_ORDER ="reply_order";
public static final String REPLY_ROBOT ="reply_robot";
public static final String REPLY_DESCRIBE ="reply_describe";
private static XSharedPreferences intance = null;
public static XSharedPreferences getIntance(){
if (intance == null){
intance = new XSharedPreferences("com.ydscience.fakemomo","config");
intance.makeWorldReadable();
}else {
intance.reload();
}
return intance;
}
public static boolean openAll(){
return getIntance().getBoolean(AUTO_ALL,false);
}
public static String replyContent(){
return getIntance().getString(REPLY_CONTENT,"请设置自动回复内容");
}
}
提供一个Xposed框架中XSharePreference使用的例子,来源于看雪论坛《一个基于xposed和inline hook的一代壳脱壳工具》下面关于该脱壳插件改进讨论的例子代码,在这个基础上根据需求进行修改即可:
/**
* Created by virjar on 2018/2/24.<br>
* 原方案,在宿主apk无sd卡权限时,无法hook无法读取配置,所以使用XSharePreference来处理多apk配置通信问题,这是因为shardPreference可以开放读取权限,绕开了不同app私有文件通信问题
*/
class XSharePreferenceConfig {
private static final String pluginPackage = "com.smartdone.dexdump";
private static final String configName = "dumpapps";
//可写的XSharedPreferences对象,这个可以使用在插件自己的apk里面使用
private SharedPreferences writeXSharedPreferences = null;
//只读的XSharedPreferences,这个可以让插件在宿主Android应用里面访问
private SharedPreferences readXSharedPreferences = null;
@SuppressLint("WorldReadableFiles")
void initWriteSharedPreference(Context pluginHostContext) {
if (!StringUtils.equals(pluginHostContext.getApplicationInfo().packageName, pluginPackage)) {
throw new IllegalStateException("can not create write sharedPreference in host app");
}
if (writeXSharedPreferences != null) {
return;
}
writeXSharedPreferences = pluginHostContext.getSharedPreferences(configName, Context.MODE_WORLD_READABLE);
}
void initReadSharePreference() {
if (readXSharedPreferences != null) {
return;
}
readXSharedPreferences = new XSharedPreferences(pluginPackage, configName);
}
private SharedPreferences getBindSharePreference() {
if (writeXSharedPreferences != null) {
return writeXSharedPreferences;
}
if (readXSharedPreferences != null) {
return readXSharedPreferences;
}
throw new IllegalStateException("not inited");
}
// 向共享配置文件中添加数据
void addOne(String name) {
SharedPreferences bindSharePreference = getBindSharePreference();
Set<String> stringSet = Sets.newHashSet(Splitter.on(",").splitToList(bindSharePreference.getString(configName, "")));
stringSet.add(name);
bindSharePreference.edit().putString(configName, Joiner.on(",").join(stringSet)).apply();
}
// 从共享配置文件中移除数据
void removeOne(String name) {
SharedPreferences bindSharePreference = getBindSharePreference();
Set<String> stringSet = Sets.newHashSet(Splitter.on(",").splitToList(bindSharePreference.getString(configName, "")));
stringSet.remove(name);
bindSharePreference.edit().putString(configName, Joiner.on(",").join(stringSet)).apply();
}
// 获取共享的配置文件中的数据
Set<String> getConfig() {
return Sets.newHashSet(Splitter.on(",").splitToList(getBindSharePreference().getString(configName, "")));
}
static boolean contains(Set<String> lists, String name) {
if (lists == null) {
return false;
}
for (String l : lists) {
if (l.equals(name)) {
return true;
}
}
return false;
}
}
比较不错的博客参考链接,很多知识点都是结合Xposed的api参考文档和源码以及这些博客搬运过来:
Xposed的api参考文档: https://api.xposed.info/reference/packages.html
《Xposed Xposed读取SharedPreferences》
Xposed框架中XSharePreference的使用的更多相关文章
- 三星5.0以上机器最简单激活Xposed框架的经验
对于喜欢钻研手机的哥们来说,经常会接触到XPOSED框架及种类繁多功能强大的模块,对于5.0以下的系统版本,只要手机能获得ROOT权限,安装和激活XPOSED框架是比较轻易的,但随着系统版本的更新,5 ...
- 三星5.0以上设备最完美激活XPOSED框架的经验
对于喜欢钻研手机的小伙伴来说,常常会接触到Xposed框架以及种类繁多功能强大的模块,对于5.0以下的系统版本,只要手机能获得Root权限,安装和激活Xposed框架是异常简易的,但随着系统版本的不断 ...
- 华为6.0系统设备最完美激活Xposed框架的经验
对于喜欢玩手机的伙伴而言,经常会使用上Xposed框架及种种功能强大的模块,对于5.0以下的系统版本,只要手机能获得root权限,安装和激活Xposed框架是非常轻松的,但随着系统版本的迭代,5.0以 ...
- oppo7.0系统手机(亲测有效)激活Xposed框架的流程
对于喜欢钻研手机的朋友而言,很多时候会使用到xposed框架及种类繁多功能极强的模块,对于5.0以下的系统版本,只要手机能获得root权限,安装和激活xposed框架是异常简单的,但随着系统版本的升级 ...
- oppo6.0系统设备(亲测有效)激活xposed框架的流程
对于喜欢研究手机的伙伴来说,常常会用到xposed框架以及种类繁多功能强大的模块,对于5.0以下的系统版本,只要手机能获得Root权限,安装和激活xposed框架是异常简易的,但随着系统版本的不断升级 ...
- 魅族5.0以上设备(亲测有效)激活Xposed框架的流程
对于喜欢研究手机的哥们来说,大多时候会使用到Xposed框架及种种功能强大的模块,对于5.0以下的系统版本,只要手机能获得ROOT权限,安装和激活Xposed框架是比较简单的,但随着系统版本的持续更新 ...
- 华为7.0系统最简单激活xposed框架的流程
对于喜欢搞机的哥们而言,很多时候会接触到Xposed框架及其种类繁多功能无敌的模块,对于5.0以下的系统版本,只要手机能获得root权限,安装和激活Xposed框架是异常简易的,但随着系统版本的不断更 ...
- 安卓9.0系统机器(亲测有效)激活Xposed框架的步骤
对于喜欢玩手机的哥们来说,经常会用到xposed框架及其种类繁多功能无敌的模块,对于5.0以下的系统版本,只要手机能获得root权限,安装和激活xposed框架是非常简便的,但随着系统版本的持续更新, ...
- 荣耀5.0以上手机(亲测有效)激活xposed框架的经验
对于喜欢搞机的朋友而言,大多时候会使用到xposed框架及其种类繁多功能强悍的模块,对于5.0以下的系统版本,只要手机能获得Root权限,安装和激活xposed框架是非常简便的,但随着系统版本的不断迭 ...
随机推荐
- JavaScript实现动态添加员工
html代码: <div id="empAdd"> <fieldset> <legend><strong>添加员工</stro ...
- 剑指 Offer 60. n个骰子的点数 + 动态规划 + 空间优化
剑指 Offer 60. n个骰子的点数 Offer_60 题目详情 题解分析 package com.walegarrett.offer; /** * @Author WaleGarrett * @ ...
- Ubuntu小配置
Ubuntu 拍摄快照 在虚拟机安装好.配置号后各拍摄一次快照,并存储. 可在虚拟机出错后回滚 Root用户 Ubuntu默认不能以 Root用户身份直接登录 因此,正常操作时在需要调用 root权限 ...
- 漫漫Java路1—基础知识—初涉java
前言 主学信息安全,在编程的路上还是一个孩子,还在一步一步探索,有些东西可能是站在自己的位置思考的,很可能会出现一些啼笑皆非的错误,如果有误,还希望各位斧正. Java安装 jdk的安装 甲骨文官网选 ...
- TensorFlow学习(1)
初识TensorFlow 一.术语潜知 深度学习:深度学习(deep learning)是机器学习的分支,是一种试图使用包含复杂结构或由多重非线性变换构成的多个处理层对数据进行高层抽象的算法. 深度学 ...
- ResNet的个人总结
ResNet可以说是我认真读过的第一篇paper,据师兄说读起来比较简单,没有复杂的数学公式,不过作为经典的网络结构还是有很多细节值得深究的.因为平时不太读英文文献,所以其实读的时候也有很多地方不是很 ...
- POJ1979_Red and Black(JAVA语言)
思路:bfs裸题. 对这种迷宫问题的bfs,我们把坐标点用一个class来存储,并放入队列进行求解. //一直接收不了输入,找了一个多小时的问题,居然是行和列搞反了ORZ Red and Black ...
- P1781_宇宙总统(JAVA语言)
//水题 题目背景 宇宙总统竞选 题目描述 地球历公元6036年,全宇宙准备竞选一个最贤能的人当总统,共有n个非凡拔尖的人竞选总统,现在票数已经统计完毕,请你算出谁能够当上总统. 输入输出格式 输入格 ...
- java 基础知识储备
初始JAVA JAVA 帝国的诞生 1972年C诞生 贴近硬件,运行极快,效率极高. 操作系统,编译器,数据库,网络系统等 指针和内存管理 1982年C++诞生 面向对象 兼容C 图形领域.游戏等 纵 ...
- [Azure Devops] 使用 Azure Pipelines 实现 CI
1. 什么是 Azure Pipelines Azure Pipelines 会自动构建和测试代码项目,以将其提供给其他人.它适用于任何语言或项目类型.Azure Pipelines 结合了持续集成 ...