react native集成微信sdk 实现一次订阅功能

1,216 阅读7分钟

网上有大部分很多微信sdk集成的文档和爬坑经过,但很少是react native,大部分都是原生的接入,我用我的血汗教训踩坑之路,希望能帮助大家快速定位解决问题。 我们rn是需要接安卓+ios,按照我自己的尿性我肯定先讲一下较为简单的ios接入。

给大家个tips,官方接入是有demo的!一定要配合着demo看 demo地址

ios接入

文档地址

  1. 相信大部分公司都会选择使用CocoaPods安装对应的ios包(CocoaPods是专门为iOS工程提供第三方依赖库的管理工具,有需要了解点击=》pod官网
    我们根据文档在podfile中加入代码
pod 'WechatOpenSDK'

然后cd到ios目录下执行pod install就oj8k

  1. Xcode设置URL

scheme(路径左边目录选中项目根文件,右边顶部选项栏选中info,正中选项中下拉选中URL Type,identifier输入包名,URL scheme输入注册的id。如果多个id可以用逗号隔开(如下图

解释:接下来这一步是为了让实现跳转微信app并带去注册的id。众所周知iOS是一个自闭的系统,应用之间是不能互相存储,读取文件。为了满足应用的通讯,URL Scheme来实现了这个功能。通过各个APP设计的符合苹果的统一规范的URL Scheme,Url Scheme 是可以用来在各种应用之间传递信息。浅析URLScheme在iOS中存在的意义

Xcode设置URL scheme

  1. 借鉴demo,复制==WXApiManager==到目录中,我们需要其进行发送wx请求成功后回调的处理。其本身头文件已然import WXApi.h 头文件,并增加 WXApiDelegate 协议。这里因为注册方法多次调用,我们可以抽象一个方法放在WXApiManager中,AppDelegate中的注册调用WXApiManager的方法
+ (BOOL) registerApp {
  NSString* appId = xxx;
  return [WXApi registerApp:appId];
}
  1. 第三步到AppDelegate的didFinishLaunchingWithOptions注册id.
//头部引入
#import "WXApiManager.h"

//didFinishLaunchingWithOptions中加入
//向微信注册
[WXApiManager registerApp];
return YES;
  
//并且最后实现代理WXApiDelegate
//重写handleOpenURL和openURL方法
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
  return  [WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]];
}

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
  return [WXApi handleOpenURL:url delegate:[WXApiManager sharedManager]];
}

配置完成我们开始开发

这里先理一理我们的调用过程

  1. 用户点击js处理事件对原生发起请求
  2. 原生响应调用wx sdk 的sendReq跳转到微信,弹出对应模版的微信信息接受提示
  3. 用户点击确认则跳回app,触发回调onResp返回openid等信息,这个openid就是我们需要的用户唯一标示
  4. js调用后台提供的接口,后台根据openid发送订阅信息到对应微信用户。
好我们来看看到底每个步骤怎么达成,存在什么坑:

第一步:js发送事件到原生,毫无疑问我们用官方推荐的方式RCT_EXPORT_METHOD()就ok(原生模块)[reactnative.cn/docs/native…]。

但这里我有一个坑,我这边是js发送的scene和temleteId给原生事件,但这个过程中我js发送的scene本来为1,却不知道为啥变成一串不认得的数字,这个是我oc在处理sence值时不经意进行了值转换,我是在抓包中才发现scene变化,解决方案是在原生中写死这个scene。

这里我们还可以用RCTPromiseResolveBlock设置promise,当发送wxapi sendReq的时候我们可以捕捉到返回值,异常可以进行抛出,await事件进行loading或其余操作。 贴上代码

RCT_EXPORT_METHOD(sendSubscribeRequest:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
  dispatch_sync(dispatch_get_main_queue(), ^{
    WXSubscribeMsgReq *req = [[WXSubscribeMsgReq alloc] init];
    req.scene = 1;
    req.reserved = @"xxx";
    req.templateId = @"xxx";
    BOOL result = [WXApi sendReq:req];
    result ? resolve(@{@"code": @0}) : reject(@"1", @"error", nil);
  });
}

//解释
dispatch_sync 和 dispatch_async 区别:

dispatch_async(queue,block) async 异步队列,dispatch_async 函数会立即返回, block会在后台异步执行。

dispatch_sync(queue,block) sync 同步队列,dispatch_sync 函数不会立即返回,即阻塞当前线程,等待 block同步执行完成。

GCD Queue 分为三种:

1,The main queue :主队列,主线程就是在个队列中。

2,Global queues : 全局并发队列。

3,用户队列:是用函数 dispatch_queue_create 创建的自定义队列

第二步:第二部只要保证好scene写死,URL scheme配置等前置条件设置妥当基本可以跳转成功

第三步:在WXApiDelegate实现的,onResp模仿demo写法就ok,拿到了回调我们需要将数据传回到js进行请求,这里官方推荐用的方式是RCTEventEmitter,实现suppportEvents方法并调用self sendEventWithName,我跟着官网做完之后发现,无法让WXApiManager的回调执行我所定义的,返回到js的方法,这里我换了一种方法,用了之前我们公司自己封装好的RCTDeviceEventEmitter公共方法。

第四步:就是js获取微信回调的参数发送请求,贴上代码:

import { DeviceEventEmitter } from 'react-native';
//...
componentDidMount() {
    this.listener = DeviceEventEmitter.addListener('getWxCallback', data => {
      // handle event.
      console.log(data, '==========DeviceEventEmitter back data');
      this.sendWxReq(data);
    });
  }

安卓

安卓对比ios着实要麻烦,从一开始接触rn,进入原生的坑之后一直对安卓开发敬而远之,总会出现莫名其妙的bug,无论是运行环境和开发体验都。。。 让我们先进入安卓的环境配置,照旧先按照官网的配

相关文档

首先我们先解决发送的问题,我们按rn官网的发送方案=》RCTDeviceEventEmitter
照旧也是引入promise方便监测微信接口调用情况和方便报错,这一步应该相对简单。

// 发送wx订阅方法
@ReactMethod
public void sendSubscribeRequest(final Promise promise){
    SubscribeMessage.Req req = new SubscribeMessage.Req();
    req.scene = 1;
    req.templateID = Constant.WX_SUBSCRIBE_TEMPLATE_ID;
    req.reserved = "xxx";
    IWXAPI api = WXHelper.getInstance();
    Boolean result = api.sendReq(req);
    if (result) {
        WritableMap info = Arguments.createMap();
        info.putString("code", "0");
        promise.resolve(info);
        return;
    }
    promise.reject("1", "error");
}

我们按照demo引入WXEntryActivity文件,文件一定要放在名叫wxapi的文件夹下

注意文件存放的路径,必须在打包的包名applicationId对应的文件目录下(这个applicationId可以在项目build.gradle中的defaultConfig下的applicationId看到。

wx

// 这里是根据demo和官方工作人员对于存在的bug修复的方案
// 这一步设置比较关键,不然调不起WXEntryActivity的回调
// activity的android:name这一个我看很多设置是不写的包名,但我发现不写无法回调成功,一定要写上
//因为我们这边打包不止一个包名,所以用${applicationId}指代,而且对应的wxapi文件夹需要存放到对应的路径上,不然返回无法回调成功!

 <activity
    android:name="${applicationId}.wxapi.WXEntryActivity"
    android:exported="true"
    android:taskAffinity="${applicationId}"
    android:launchMode="singleTask"
    android:theme="@style/Theme.AppCompat.Light.NoActionBar">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

来看一下整理后的WXEntryActivity文件:

package com.xxx;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.WritableMap;
import com.tencent.mm.opensdk.constants.ConstantsAPI;
import com.tencent.mm.opensdk.modelbase.BaseReq;
import com.tencent.mm.opensdk.modelbase.BaseResp;
import com.tencent.mm.opensdk.modelbiz.SubscribeMessage;
import com.tencent.mm.opensdk.openapi.IWXAPI;
import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler;
import static com.xxx.xxxmodule.wechatModule.sendEvent; // 获取微信订阅回调的处理事件

// 实现IWXAPIEventHandler接口,微信发送的请求将回调到onReq方法,发送到微信请求的响应结果将回调到onResp方法
public class WXEntryBaseActivity extends Activity implements IWXAPIEventHandler{
	private static final String TAG = "WXEntryBaseActivity";

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		IWXAPI api =  WXAPIFactory.createWXAPI(this, Constants.APP_ID, false);
		//优化后,注册api提取成一个公共方法
		//IWXAPI api = WXHelper.getInstance();
		api.handleIntent(getIntent(), this);
	}

	@Override
	protected void onNewIntent(Intent intent) {
		super.onNewIntent(intent);
		IWXAPI api = WXHelper.getInstance();
		setIntent(intent);
		api.handleIntent(intent, this);
		Log.d(TAG, "onNewIntent: ");
	}

    //不写会报错。
	@Override
	public void onReq(BaseReq req) {
		Log.d(TAG, "onReq: ");
		finish();
	}

    // 回调成功回到这里来
	@Override
	public void onResp(BaseResp resp) {
		Log.d(TAG, "onResp: ");
		if (resp.getType() == ConstantsAPI.COMMAND_SUBSCRIBE_MESSAGE) {
			SubscribeMessage.Resp subscribeMsgResp = (SubscribeMessage.Resp) resp;
			String openId = subscribeMsgResp.openId;
			if (openId != null) {
			    // 包装一个存储参数对象
				WritableMap params = Arguments.createMap();
				params.putString("openId", openId);
				params.putString("templateId", subscribeMsgResp.templateID);
				params.putInt("scene", subscribeMsgResp.scene);
				// 调用我们的发送方法,这里我是将数据发送到js,再进行调用后台接口发送微信消息到对应微信用户
				sendEvent(params);
			}
		}
		finish();
	}
}

发送的事件

public static void sendEvent(WritableMap params) {
    mContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("getWxCallback",params );
}

js调用跟ios的保持一致即可。

第一次在掘金上发文,小陈也是react native的新手,学习过程中略感社区文档和高质量指导文章的稀少,希望能跟大家一起讨论学习,向高级前端架构进阶