[Xposed开发] Xposed模块使用XSharedPreferences进行存储数据
Posted SkyHandCsdn
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[Xposed开发] Xposed模块使用XSharedPreferences进行存储数据相关的知识,希望对你有一定的参考价值。
一、简介
正常在编写模块的时候,我们想给模块增加一些开关,例如 模块是否开启 这个功能,这时候我们就得需要将开关状态给保存下来,然后在hook的时候拿到这个状态判断是否执行下去。 数据的保存方法有多种,这次是介绍XSharedPreferences
(以下简称xsp)。
正常使用android开发的时候我们使用SharedPreferences
(以下简称sp),sp的工作流程是在对应app的内部数据目录下创建以map键值对为内容的xml文件,一般路径为data/data/app包名/shared_prefs/包名.xml
,于是这里就有个问题,这个目录和文件只能app自己能访问,其他app不能直接访问,然后这里还会有个坑,详情看第三点。
但是得益于我们是Xposed模块的原因,在hook方法时候利用SELinuxHelper.getAppDataFileService()
获取AppData的文件服务功能,然后利用getFileInputStream
方法即可获取对应app的文件的文件流。
源码如下,在xsp里面的loadFromDiskLocked
方法里面:
而且这整个流程,Xposed开发者robv大佬已经帮我们封装好了xsp,在xposed的api的jar包里面的包名路径为:de.robv.android.xposed.XSharedPreferences
。
在模块里面读取sp数据的时候就可以用这个类。 读取的用法基本和sp一模一样,里面是一个map类型的键值对数据。
因为创建sp文件的时候是创建到模块app里面的数据目录,所以,创建写入的时候就在模块app里面进行正常的sp读取写入。
二、用法
又上面的介绍得知,用法分两种:
- 模块的hook方法里面使用
- 模块app里面使用
2.1 Hook里使用
2.1.1 初始化
在handleLoadPackage
里面判断了包名之后,进行new实例化一个XSharedPreferences
,参数传入需要起的xml文件名的名字。创建出来的xml文件的路径为:data/data/<packageName>/shared_prefs/<packageName>_preferences.xml
。 以下以微信为例:
public class TestHook implements IXposedHookLoadPackage
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable
if(lpparam.packageName.equals("com.tencent.mm"))
//com.skyhand.testmm为当前模块的包名。可以利用BuildConfig.APPLICATION_ID获取当前包名(需要build之后才有)
XSharedPreferences xsp = new XSharedPreferences("com.skyhand.testmm");
2.1.2 读取数据
读取数据和和sp一模一样,第一个参数为key,第二个参数为默认值
基本类型:
XSharedPreferences xsp = new XSharedPreferences(packageName);
//获取布尔类型值
boolean boolV = xsp.getBoolean("key",false);
//获取整数类型值
int intV = xsp.getInt("key",0);
//获取浮点数类型值
float floatV = xsp.getFloat("key",0f);
//获取长整形类型值
long longV = xsp.getLong("key",0L);
//获取字符串类值
String stringV = xsp.getString("key","");
特殊类型:
//获取Set<String>类型值
Set<String> defSet = new HashSet<String>();
Set<String> setV = xsp.getStringSet("key", defSet);
//获取当前xml的File
File fileV = xsp.getFile();
//获取当前xml的全部值,Map类型
Map<String,?> mapV = xsp.getAll();
2.1.3 封装用法
上面演示api的使用,但是一般是封装成一个静态类来使用,如下的XSPUtils
。
更深一层的时候可以封装成枚举,这里就不演示了。
public class XSPUtils
public static XSharedPreferences xsp;
/**
* 初始化,在handleLoadPackage里面使用
* @param packageName 包名
*/
public static void initXSP(String packageName)
XSPUtils.xsp = new XSharedPreferences(packageName);
public static boolean getBoolean(String key,boolean def)
return xsp.getBoolean(key,def);
public static String getString(String key,String def)
return xsp.getString(key,def);
public static int getInt(String key,int def)
return xsp.getInt(key,def);
public static float getFloat(String key,float def)
return xsp.getFloat(key,def);
public static long getLong(String key,long def)
return xsp.getLong(key,def);
初始化的时候就在handleLoadPackage
hook方法里面:
public class TestHook implements IXposedHookLoadPackage
@Override
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable
if(lpparam.packageName.equals("com.tencent.mm"))
//初始化xsp,com.skyhand.testmm为当前模块的包名
XSPUtils.initXSP("com.skyhand.testmm");
//获取一个数据
boolean isOpen = XSPUtils.getBoolean("open",false);
2.2 模块App里面使用
在模块的App里面使用就是参考正常的sp用法,因为可以正常读取自己data目录的文件。
这里给个简单的例子,例如封装了一个SPUtils.java
:
package com.store;
import android.content.Context;
import android.content.SharedPreferences;
public class SPUtils
public static SPUtils xsp;
public SharedPreferences sp;
private SPUtils()
public static synchronized SPUtils getInstance()
if (xsp == null)
xsp = new SPUtils();
return xsp;
/**
* 初始化
* @param context
*/
public void init(Context context)
sp = context.getSharedPreferences(context.getPackageName(), Context.MODE_PRIVATE);
/**
* 下面的是读取数据
* @param key
* @param def
* @return
*/
public static String getString(String key, String def)
return SPUtils.getInstance().sp.getString(key, def);
public static int getInt(String key, int def)
return SPUtils.getInstance().sp.getInt(key, def);
public static float getFloat(String key, float def)
return SPUtils.getInstance().sp.getFloat(key, def);
public static long getLong(String key, long def)
return SPUtils.getInstance().sp.getLong(key, def);
public static boolean getBoolean(String key, boolean def)
return SPUtils.getInstance().sp.getBoolean(key, def);
/**
* 下面是保存数据
* @param key
* @param v
* @return
*/
public static boolean setString(String key, String v)
return SPUtils.getInstance().sp.edit().putString(key, v).commit();
public static boolean setInt(String key, int v)
return SPUtils.getInstance().sp.edit().putInt(key, v).commit();
public static boolean setBoolean(String key, boolean v)
return SPUtils.getInstance().sp.edit().putBoolean(key, v).commit();
public static boolean setFloat(String key, float v)
return SPUtils.getInstance().sp.edit().putFloat(key, v).commit();
public static boolean setLong(String key, long v)
return SPUtils.getInstance().sp.edit().putLong(key, v).commit();
然后在模块app里面使用的时候大概这样子:
public class TestMainActivity extends Activity
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化Sp
SPUtils.getInstance().init(this);
Switch switchView = (Switch) findViewById(R.id.SwOpen);
switchView.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener()
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked)
//保存到sp
SPUtils.setBoolean("open", isChecked);
);
三、坑
使用Xsp的情况,targetSdkVersion必须设置27或者27以下,还需要在主app里面设置可读写,例如SettingsActivity在onPause的时候可以可读写
class SettingsActivity : AppCompatActivity()
/**
* 重点设置:xml文件可读写,不然模块里面的Xsp获取不到
*/
override fun onPause()
super.onPause()
LogH.log("设置本地xp文件可读写")
Xsp.setPreferenceWorldWritable(this)
Xsp里面的方法:
/**
* 设置sp文件可读写
*/
@SuppressLint("SetWorldWritable", "SetWorldReadable")
fun setPreferenceWorldWritable(context: Context, name: String = "")
var spName = name;
if (spName == "")
spName = context.packageName + "_preferences"
var file: File? = getSharedPreferencesFile(context, spName)
if (file != null && !file.exists()) return
for (i in 0..2)
file?.setExecutable(true, false)
file?.setWritable(true, false)
file?.setReadable(true, false)
file = file?.parentFile
if (file == null) break
private fun getSharedPreferencesFile(context: Context, name: String): File
val dataDir = ContextCompat.getDataDir(context)
val prefsDir = File(dataDir, "shared_prefs")
return File(prefsDir, "$name.xml")
四、总结
模块使用xsp进行数据存储,hook的情况和模块自身使用的情况分开处理,hook时候使用xsp,模块自身正常使用sp,就是以下的对应:
- xsp: hook里面使用
- sp: 模块app里面使用
因为:xsp和sp他们操作的都是同一个sp的xml文件。
五、拓展
在模块的app里面使用xsp和sp的时候,很多开发者喜欢使用PreferenceFragment
或者PreferenceActivity
,然后把Key写成枚举enum,让xsp和sp同时使用。
这里就不给例子了,作为作业,自己查询其它资料。
以上是关于[Xposed开发] Xposed模块使用XSharedPreferences进行存储数据的主要内容,如果未能解决你的问题,请参考以下文章