xposed是一个可以修改app内部逻辑的框架,基于xposed我们可以实现很多有趣的功能。但使用xposed框架有一个很麻烦的问题,那就是需要对手机进行root。虽然出现了太极等框架使得我们无需root也可以使用许多现成的xposed模块,但如果我们想自己写一个模块练练手的话,按照网上编写模块的方法是适配不了太极的。那这样才能免root运行自己编写的xposed模块呢?这可以使用github上的xpatch项目。xpatch可以重新签名打包Apk文件,使重打包后的Apk能加载安装在系统里的Xposed插件,从而实现免Root Hook任意App。
开始正式编写xposed模块前需要一些配置,大家可以参考https://www.colabug.com/5324975.html和https://blog.csdn.net/qq_37567866/article/details/83019971这两篇博文。
下面正式进入我们的重点,实现指定好友发布新朋友圈时弹出提示的功能。整个代码的逻辑非常简单,每隔一定时间(代码中是10分钟)刷新一次朋友圈,判断关注的好友是否发布了新朋友圈。如果是的话,在你下一次打开微信时,就会弹出提示,如下图所示。
这里主要有几个问题,第一个是如何刷新朋友圈?由于朋友圈是私有协议,伪造请求十分困难,我采用的是直接打开朋友圈的方式进行刷新,也就是每隔一定时间模块就会自动打开朋友圈。但为了尽量减少干扰,比如我们锁屏的时候也许不希望手机突然亮起来,并显示出朋友圈的界面。那怎样才能既实现刷新朋友圈的功能,朋友圈又不会突然显示出来呢。打开朋友圈可以通过startactivity方法实现,但退出朋友圈就不能等到页面加载完再退出。如果能够在朋友圈已经刷新并写入手机数据库,但还未在界面上显示出来的时候退出最好不过了,这样手机就不会突然亮起来了。
通过打印堆栈,我发现刷新朋友圈的时候有一个a方法非常关键(微信的源码是经过混淆的,可以通过jadx反混淆勉强看一下,也可以通过打印堆栈查看方法调用顺序)。每次刷新朋友圈,a方法都会被调用一次。于是我hook了a方法,在a方法被调用后马上退出朋友圈,发现果然达到了刷新朋友圈但手机不亮屏的效果。
XposedHelpers.findAndHookMethod(snsModelYClass, lpparam.classLoader, "a", int.class,int.class,
int.class,String.class,Class.forName("com.tencent.mm.network.q"),byte[].class,new XC_MethodHook() {
@Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
if(snsLaunchByXP){ //如果是xposed启模块启动的朋友圈,自己打开朋友圈则不会执行下列代码
Log.d("myLog", "a()方法被调用");
snsActivity.finish();
Log.d("myLog", "snsActivity.finish()"); //关闭朋友圈
snsOn = false;
Log.d("myLog", "snsOn false");
snsLaunchByXP = false;
}
}
});
第二个问题,如何判断是不是关注的好友发布的朋友圈?刷新朋友圈的时候,新朋友圈信息写入手机数据库会调用insertWithOnConflictMethod这个方法。hook这个方法,读取它的参数可以获取朋友圈发布者的微信id。判断该id是不是自己关注好友的id即可。我发现如果有的好友更改了自己的微信id,但实际上他的微信id依旧是之前的默认id。那我们怎么查看这个默认id呢,可以下载微续这款软件登录自己微信,就可以查看好友的微信id了。
public void handleLoadPackage(XC_LoadPackage.LoadPackageParam lpparam) throws Throwable {
XposedHelpers.findAndHookMethod(dataBaseName, lpparam.classLoader, insertWithOnConflictMethod, String.class, String.class, ContentValues.class, int.class, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(XC_MethodHook.MethodHookParam param) throws Throwable {
if (param.args[0].equals("SnsInfo")) {
ContentValues contentValues = ((ContentValues) param.args[2]);
int time = contentValues.getAsInteger("createTime"); //获取朋友圈发布时间
String name = contentValues.getAsString("userName"); //获取朋友圈发布者微信id
if(name.equals("wxid_xxx")) friend1 = true; //wxid_xxx改成自己想要关注的好友的微信id即可
else if(name.equals("wxid_yyy")) friend2 = true;
else if(name.equals("wxid_zzz")) friend3 = true;
long msgCreatetime = Long.parseLong(time + "") * 1000L;
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String snsDate = format.format(new Date(msgCreatetime));
Log.d("myLog:", "insert:" + snsDate + " " + name); //打印朋友圈发布时间和发布者
}
}
});
第三个问题,如何定时刷新朋友圈?这里我用的是java的timer定时器。也许使用android的service会更好一些,以后有时间可以尝试一下。
public final void schedule() {
timerTask = new TimerTask() {
@Override
public void run() {
try {
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
if(!pm.isScreenOn()){ //黑屏时才运行程序,以免影响手机使用
if(snsOn && snsActivity != null){ //有时网络不佳,刷新朋友圈时a方法并没有调用,朋友圈因此没有关闭,下一次刷新朋友圈时先关闭之前打开的activity。
snsActivity.finish();
snsOn = false;
}
else {
Intent intent = new Intent(context, Class.forName(snsActivityName));
snsLaunchByXP = true;
context.startActivity(intent); //打开朋友圈的activity
snsOn = true;
Log.d("myLog", "start sns");
}
}
else{
Log.d("myLog","screen on");
}
}
catch(ClassNotFoundException e){
}
}
};
timer.schedule(timerTask,20000,600000); //每隔10分钟运行一次
Log.d("myLog","timer.schedule(timerTask)");
}
最后一个问题是怎样弹出提示,我直接用了android的toast,简单方便。大家可以尝试在状态栏弹出提示。
XposedHelpers.findAndHookMethod(launchActivityName, lpparam.classLoader, onResumeMethod, new XC_MethodHook() {
@Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
Log.d("myLog","launchActivity.onResume");
Context con = (Context)param.thisObject;
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
if(pm.isScreenOn()) {
if (friend1){
Toast.makeText(con, "你关注的大姐有新朋友圈了!", Toast.LENGTH_LONG).show();
friend1 = false;
}
if (friend2){
Toast.makeText(con, "你关注的二姐有新朋友圈了!", Toast.LENGTH_LONG).show();
friend2 = false;
}
if (friend3){
Toast.makeText(con, "你关注的好友有新朋友圈了!", Toast.LENGTH_LONG).show();
friend3 = false;
}
}
}
});
这就是整个代码的主要内容了,第一次写xposed模块,不足之处,多多指教。模块已经上传到github(Moments_reminder),我测试的微信版本是7.0.3,应该7以上都可以。