Android绕过微信包名限制对接微信登录和支付

  • 前言
  • 最终效果
  • 原理
  • 代码
    • 通过 APT 生成 WXEntryActivity 文件
    • 对接微信SDK
  • End

前言

Android对接微信登录和支付几乎是现在所有的商用Android APP都需要做的一个东西,不过每次开发我们都需要去新建微信官方要求的指定包名+Activity名字,这个还是有点烦的。下面我将通过 APT 封装一个可以绕过微信包名限制的微信登录和支付功能。(其实也不算是真的绕过啦)

最终效果

先上最后封装好我调用起微信登录的代码:

public class MainActivity extends AppCompatActivity {private Button button;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);button = findViewById(R.id.button);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {WeChatAPI.getInstance().setSingInCallBack(new IWXSignCallBack() {@Overridepublic void signSuccess(String info) {Toast.makeText(MainActivity.this, "info: " + info, Toast.LENGTH_SHORT).show();}}).signIn();}});}
}

效果还可以吧,只需要加入几行代码就可以调起微信登录 。

原理

这里用到的和 ButterKnife 一样的技术叫做编译时注解的技术即 APT ,代码在编译时会扫描AbstractProcessor的所有子类,并且调用这些子类的process函数,在这个函数就会将所有的代码元素传递进来。此时我们只需要在这个process函数中获取所有添加了某个注解的元素,然后对这些元素进行操作,使之能够满足我们的需求,这样我们就可以在编译期对源代码进行处理,例如生成新的类等。在运行时,我们通过一些接口对这些新生成的类进行调用以此完成我们的功能

emmmmm 很抽象,其实我也觉得挺抽象的,我们直接看代码吧~

代码

代码我们分出两块讲: 通过 APT 生成 WXEntryActivity 文件 以及 对接微信SDK

通过 APT 生成 WXEntryActivity 文件

我们封装这个东西就是为了方便还有通用性,我们用了组件化的思想,用两个 Module 完成我们这个功能所以我们要新建两个 Java Library 的 Module 分别为:annotation(放注解类),compile (放对注解类的处理)。

  • 新建Module
    新建两个 Java Library 分别为: annotation( 放注解类 ),compile ( 放对注解类的处理)。

注意这里新建 Java Library 而不是 Android Library 是因为我们的注解这里其实会用到很多标准 Java SDK的一些注解类。其中一些是 Android Library 里面没有”。

画面效果不好,意思意思就行了
​​
​​

  • 添加依赖
    在 annotation Module 的 gradle 文件中添加支持中文的依赖
apply plugin: 'java-library'dependencies {implementation fileTree(dir: 'libs', include: ['*.jar'])//添加支持中文tasks.withType(JavaCompile){options.encoding='UTF-8'}
}sourceCompatibility = "1.7"
targetCompatibility = "1.7"

在 compile Module 的 gradle 中添加一下依赖

apply plugin: 'java-library'dependencies {implementation fileTree(include: ['*.jar'], dir: 'libs')api project(':annotation')//生成 java library 的一个工具api 'com.squareup:javapoet:1.10.0'//注解 processor 类并生成 META-INF 的配置信息api 'com.google.auto.service:auto-service:1.0-rc4'annotationProcessor'com.google.auto.service:auto-service:1.0-rc4'//添加支持中文tasks.withType(JavaCompile){options.encoding='UTF-8'}
}sourceCompatibility = "1.7"
targetCompatibility = "1.7"
  • 创建微信登录注解类
    在 annotations Module 下创建 EntryGeneral 注解
@Target(ElementType.TYPE)//作用于类和接口
@Retention(RetentionPolicy.SOURCE)//只在源码阶段
public @interface EntryGeneral {String packageName();//要继承的类Class<?> entryTemplete();
}
  • 写一个 AbstractProcessor 的子类
    在 compile Module 下写一个 AbstractProcessor 的子类并添加我们的注解元素,对注解元素进行扫描操作然后再生成我们指定包名下的指定名文件。

注意这里要记得在 compile Module 下的依赖文件引入 annotation Module

@AutoService(Processor.class)
public class WeChatProcessor extends AbstractProcessor {@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {//扫描注解并生成我们的微信登录入口文件generateEntryCode(roundEnvironment);return false;}//设置为最大版本@Overridepublic SourceVersion getSupportedSourceVersion() {return SourceVersion.latestSupported();}/*** 获取到注解的名字** @return Set*/@Overridepublic Set<String> getSupportedAnnotationTypes() {Set<String> types = new LinkedHashSet<>();for (Class<? extends Annotation> annotation : getSupportedAnnotations()) {types.add(annotation.getCanonicalName());}return types;}/*** 获取到注解类列表** @return Set*/private Set<Class<? extends Annotation>> getSupportedAnnotations() {Set<Class<? extends Annotation>> annotations = new LinkedHashSet<>();annotations.add(EntryGeneral.class);return annotations;}/*** 扫描解析我们注解类的 方法等** @param env        RoundEnvironment* @param annotation 注解类* @param visitor    可以理解成生成文件的这么一个类*/private void scan(RoundEnvironment env,Class<? extends Annotation> annotation,AnnotationValueVisitor visitor) {for (Element typeElement : env.getElementsAnnotatedWith(annotation)) {//获取到该声明上所添加的注解的实际值final List<? extends AnnotationMirror> annotationMirrors =typeElement.getAnnotationMirrors();for (AnnotationMirror annotationMirror : annotationMirrors) {final Map<? extends ExecutableElement, ? extends AnnotationValue> elementValue= annotationMirror.getElementValues();for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry: elementValue.entrySet()) {//将获得的注解上的实际的值给visitorentry.getValue().accept(visitor, null);}}}}/*** 生成 微信入口 文件** @param env RoundEnvironment*/private void generateEntryCode(RoundEnvironment env) {EntryVistor entryVistor = new EntryVistor();//processingEnv 是父类过来的entryVistor.setmFiler(processingEnv.getFiler());scan(env, EntryGeneral.class, entryVistor);}}

注意: @AutoService(Processor.class) 这个注解一定要加,auto-services是一个注解处理器,会在编译时为该module生成声明文件。

上面代码提到的 EntryVistor 是 SimpleAnnotationValueVisitor7 的子类。

关于 SimpleAnnotationValueVisitor7 官方的解释是这样的:

A simple visitor for annotation values with default behavior appropriate for the RELEASE_7 source version. Visit methods call defaultAction passing their arguments to defaultAction’s corresponding parameters.

我个人的理解就是它的作用就是可以访问到注解里的值

下面是 EntryVistor 的代码

public final class EntryVistor extends SimpleAnnotationValueVisitor7<Void, Void> {private Filer mFiler = null;private TypeMirror mTypeMirror = null;private String mPackageName = null;public void setmFiler(Filer mFiler) {this.mFiler = mFiler;}@Overridepublic Void visitString(String s, Void aVoid) {mPackageName = s;return aVoid;}@Overridepublic Void visitType(TypeMirror typeMirror, Void aVoid) {mTypeMirror = typeMirror;generateJavaCode();return aVoid;}/*** build WXEntryActivity.java*/private void generateJavaCode() {final TypeSpec targetActivity =TypeSpec.classBuilder("WXEntryActivity").addModifiers(Modifier.PUBLIC).addModifiers(Modifier.FINAL).superclass(TypeName.get(mTypeMirror)).build();final JavaFile javaFile = JavaFile.builder(mPackageName + ".wxapi", targetActivity).addFileComment("微信入口文件").build();try {javaFile.writeTo(mFiler);} catch (IOException e) {e.printStackTrace();System.out.println("build WXEntryActivity.java failed");}}}

JavaFile.builder(String packageName, TypeSpec typeSpec); packageName: 包名 typeSpec : 一个 class 文件的定义

到这里我们就完成了我们通过 APT 生成 WXEntryActivity.java 文件。

  • 使用 @EntryGeneral
    我们随便新建一个类或者接口注解都行,这里我就新建一个 @WXEntry 的注解
@EntryGeneral(packageName = "你的包路径",//你的app项目包路径entryTemplete = WeChatSignTemple.class //你要继承的类
)
public @interface WXSignEntry {}

注意: 在你这个注解所在的 module 引入 compile 和 annotation 的 module

annotationProcessor project(':compile')
api project(':annotation')

好了,见证奇迹的时刻到了。我们 build 一下项目。

build 完看一下 app -> build -> source -> apt -> debug -> 包名 -> wxapi 有没有 WXEntryActivity.java 文件。不知道你们有没有反正我有,截图为证。

我们来看一下我们生成的 WXEntryActivity 是怎样的

WXEntryActivity 继承了我们自己写的 WeChatSignTemple ,所以这里就很好理解了,我们在 WeChatSignTemple 写的在 WXEntryActivity 就可以实现了。

上面代码所提到的 WeChatSignTemple 这个类是真正你对微信进行处理的类,也就是我们原先放到 WXEntryActivity 这个类里的东西。

接下来就可以好好来写我们的这个 WeChatSignTemple 类了

对接微信SDK

  • 添加微信相关的依赖
    在我们 app 的 gradle 里导入我们的微信依赖以及 网络依赖(这个个人喜欢就行)
// 微信相关
api 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:+'
  • 整体项目结构
    我们先捋一捋我们的 app module 的项目结构
  • ConstantUtils 这个用来获取到全局的Context 记得要在 Application 中初始化
  • WXSignEntry 微信登录注解,直接在我们要使用到的 Activity 加上这个注解
  • BaseWeChatEntryActivity 微信入口类的基类(后面我们的微信登录,支付和分享都要继承到),做绑定 IWXAPIEventHandler 的
  • IWXSignCallBack 微信登录回调接口
  • WeChatSignTemple WXEntryActivity 的父类,做微信登录的具体操作
  • Config 微信配置信息 APPID 之类的
  • WeChatApi 调用微信相关东西的工具类(可以获取到IWXAPI 设置登录回调函数 IWXSignCallBack ,发起登录等等)

接下来具体讲一下类

  • WeChatAPI
public class WeChatAPI {private IWXAPI iwxapi;//微信登录回调接口private IWXSignCallBack weChatSignInCallBack;private static final class Holder {private static final WeChatAPI WECHAT_API = new WeChatAPI();}public static WeChatAPI getInstance() {return Holder.WECHAT_API;}private WeChatAPI() {//实例化IWXAPIiwxapi = WXAPIFactory.createWXAPI(ConstantUtils.getAPPContext(), Config.APP_ID, true);iwxapi.registerApp(Config.APP_ID);}// 获取到 IWXAPIpublic final IWXAPI getWXAPI() {return iwxapi;}//设置微信登录回调接口public WeChatAPI setSingInCallBack(IWXSignCallBack weChatSignInCallBack) {this.weChatSignInCallBack = weChatSignInCallBack;return this;}//获取到微信登录回调接口public IWXSignCallBack getWeChatSignInCallBack() {return weChatSignInCallBack;}/*** 发起登录*/public void signIn() {final SendAuth.Req req = new SendAuth.Req();req.scope = "snsapi_userinfo";req.state = "random_state";iwxapi.sendReq(req);}
}

这个类主要是做发起登录并且设置微信登录接口回调作用的(后期可以加入支付分享相关的)

  • BaseWeChatEntryActivity
public abstract class BaseWeChatEntryActivity extends AppCompatActivity implements IWXAPIEventHandler {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);WeChatAPI.getInstance().getWXAPI().handleIntent(getIntent(), this);}@Overrideprotected void onNewIntent(Intent intent) {super.onNewIntent(intent);setIntent(intent);WeChatAPI.getInstance().getWXAPI().handleIntent(getIntent(), this);}
}

这个是我们所有的微信相关(登录,支付,分享)的基类,做绑定 IWXAPIEventHandler 的。
IWXAPIEventHandler 提供了两个接口分别是

  1. void onReq(BaseReq var1); //微信发送请求到APP后的回调
  2. void onResp(BaseResp var1); //APP发送请求到微信后的回调
  • WeChatSignTemple
    在这个接口写具体的微信登录相关的操作
 @Overrideprotected void onResume() {super.onResume();//再次进入这个页面直接消失finish();//无动画效果overridePendingTransition(0, 0);}@Overridepublic void onReq(BaseReq baseReq) {}//APP发送请求到微信后的回调@Overridepublic void onResp(BaseResp baseResp) {if (baseResp.errCode == BaseResp.ErrCode.ERR_OK) {getAccessToken(((SendAuth.Resp) baseResp).code);} else {Toast.makeText(this, "微信授权登录失败", Toast.LENGTH_SHORT).show();finish();}}//获取到tokenprivate void getAccessToken(String code) {OkHttpUtils.get("https://api.weixin.qq.com/sns/oauth2/access_token?APPID=" + Config.APP_ID +"&secret=" + Config.APP_SECRET +"&code=" + code +"&grant_type=authorization_code").tag(this).execute(new StringCallback() {@Overridepublic void onSuccess(String s, Call call, Response response) {try {JSONObject object = new JSONObject(s);String acceeeToken = object.getString("access_token");String open_id = object.getString("openid");getInfo(acceeeToken, open_id);} catch (JSONException e) {e.printStackTrace();Toast.makeText(WeChatSignTemple.this, "微信授权登录失败", Toast.LENGTH_SHORT).show();}}@Overridepublic void onError(Call call, Response response, Exception e) {Toast.makeText(WeChatSignTemple.this, "微信授权登录失败", Toast.LENGTH_SHORT).show();}});}//获取用户数据private void getInfo(String at, String open_id) {OkHttpUtils.get("https://api.weixin.qq.com/sns/userinfo?access_token="+ at + "&openid=" + open_id).tag(this).execute(new StringCallback() {@Overridepublic void onSuccess(String s, Call call, Response response) {WeChatAPI.getInstance().getWeChatSignInCallBack().signSuccess(s);}@Overridepublic void onError(Call call, Response response, Exception e) {super.onError(call, response, e);Toast.makeText(WeChatSignTemple.this, "微信授权登录失败", Toast.LENGTH_SHORT).show();}});}

这里发起了两次网络请求(这个没办法微信就是这么要求,包括传那些数据)所以我们的微信授权登录是会相对慢一点的。

我们到这里就基本完成了我们的博客的 title 说的东西啦~

对了别忘记在 app 的 AndroidManifest.xml 加入 WXEntryActivity(如果有用到支付也需要增加.wxapi.WXPayEntryActivity 这个 Activity ,别忘记了)

<activityandroid:name=".wxapi.WXEntryActivity"android:exported="true"android:label="微信回调Activity"android:launchMode="singleTop"android:screenOrientation="portrait"android:theme="@style/Theme.AppCompat.Translucent"><intent-filter><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.DEFAULT" /></intent-filter></activity>

还有别忘记添加网络权限

<uses-permission android:name="android.permission.INTERNET"/>

Theme.AppCompat.Translucent 是我自己写的一个透明主题

在style.xml加入

<!--透明Activity-->
<style name="Theme.AppCompat.Translucent"><item name="android:windowNoTitle">true</item><item name="android:windowBackground">@android:color/transparent</item><item name="android:colorBackgroundCacheHint">@null</item><item name="android:windowIsTranslucent">true</item><item name="android:windowAnimationStyle">@android:style/Animation</item>
</style>

好了然后就回到我们开头的代码了

  • MainActivity
public class MainActivity extends AppCompatActivity {private Button button;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);button = findViewById(R.id.button);button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {WeChatAPI.getInstance().setSingInCallBack(new IWXSignCallBack() {@Overridepublic void signSuccess(String info) {Toast.makeText(MainActivity.this, "info: " + info, Toast.LENGTH_SHORT).show();}}).signIn();}});}
}

只需要简单的在 Activity 设置登录回调发起登录,简简单单几句代码就可以完成。

End

其实微信支付和分享其实也差不多了,只是时间的问题,就不赘述啦~
鉴于很多人说编译出来并没有对应的文件,这个链接是完成了第一步的源码,我更希望大家能自己弄出来的~
链接:https://pan.baidu.com/s/1Sdc2JDC9yuQzxZT6kqmWiA
提取码:9x90

Android绕过微信包名限制对接微信登录和支付相关推荐

  1. 【Android 应用开发】Android 工程修改包名流程 ( 修改 applicationId | 修改 package | 修改 R 资源引用 | 修改 BuildConfig 引用 )

    文章目录 一. Android 工程修改包名流程 二. 修改 applicationId 三. 修改 package 包名 四. AndroidManifest.xml 清单文件组件 五. 修改 R ...

  2. android工程改包名及多工程设置

    android工程更改包名: 1. 先改manifest中的包名 2. src目录上点右键,选择refactor->rename,改包名,然后选择continue. 3. 改了包名之后,要注意包 ...

  3. Android Studio(五):修改Android Studio项目包名

    Android Studio相关博客: Android Studio(一):介绍.安装.配置 Android Studio(二):快捷键设置.插件安装 Android Studio(三):设置Andr ...

  4. Android Studio 修改包名

    Android Studio 修改包名 1. 介绍 2. 操作概述 3. 图片解说 1. 介绍 根据目录层次修改包名(包括公司名). 目的:将 kirin 公司改成 kk. 参考:AndroidStu ...

  5. Android开发时包名、签名、渠道和版本号的易坑点(转)

    本文中总结一下 Android 开发中容易被忽视的一些注意事项吧: 一.谨慎选择包名 包名 (Package Name) 就相当于一款应用在户口本上登记的名字,是系统用来区分不同应用的字段.重复的包名 ...

  6. Android实现根据包名跳转各大应用市场

    目录 前言: 1.MartetUtils:各大应用市场工具类 2.使用实例代码,这里以跳转qq为例: 3.实现的效果截图如下: 4.总结:目前已经实现华为.小米.oppo.vivo.魅族.联想.应用宝 ...

  7. Android通过包名启动应用微信qq淘宝

    step1: 获取应用列表,亲测可用,找了几个小时才找到能用的 package com.example.myapplicationimport android.content.pm.PackageMa ...

  8. android studio 修改包名_android逆向笔记之初学者常用adb命令

    android逆向常用命令笔记 1.如何导出已安装apk? a.列出已经安装的包 | grep -i 关键字 b.找出安装路径 adb shell pm path 包名 c.拉下来: adb pull ...

  9. android自动填充包名,debug/release 修改包名,取不同包名下的agconnect-services.json 文件...

    问题描述 我在打多渠道包的时候,我需要区分debug版本,release版本,其中涉及到包名的不同,我使用release编译的时候,发现如下错误信息.这个原因是因为你的agconnect-servic ...

最新文章

  1. python中为什么无法导入类_ImportError:无法导入名称类型
  2. protel快捷键大全
  3. 【小白学PyTorch】扩展之Tensorflow2.0 | 21 Keras的API详解(上)卷积、激活、初始化、正则...
  4. Object family 在Object search中的default逻辑
  5. 字符串_月隐学python第7课
  6. 二次元带音乐404源码
  7. [经典控件]按钮和菜单
  8. [.net 面向对象程序设计进阶] (15) 缓存(Cache)(二) 利用缓存提升程序性能
  9. emoji.php,简单的处理emoji的PHP类库
  10. 回调地狱[Callback Hell]
  11. 第52届格莱美大奖完全获奖名单
  12. linux C获取本地IP地址
  13. 2019 全年中国马拉松赛事日历表
  14. 自己动手丰衣足食之轮播图一动态修改marginTop属性实现轮播图
  15. 笔记本电脑计算机的配置表,笔记本组装配置清单_笔记本电脑配置单及价格
  16. unity3D-游戏/AR/VR在线就业班 C#入门访问修饰符学习笔记
  17. python算法专项(六)——Tensorflow原理基础,三层网络
  18. 连接策略与搜索引擎优化
  19. 嵌入式程序设计学习(1)
  20. 微信摇一摇php,微信“摇一摇”功能是怎么实现的?

热门文章

  1. aspose-slides-22.5-jdk16
  2. 如何提高Windows 系统性能
  3. 猴子吃桃问题 (15分) (递归)
  4. [M1]Daily Scum 10.11
  5. 前端图片放大缩小/比例自适应/打码/码大小可调整
  6. js 通过 exceljs 和 canvas 实现导出带水印的 excel 表
  7. Linux升级glibc
  8. 2022年Github学生包白嫖!
  9. 决策树与XGBOOST
  10. Twilight的卡牌游戏