阿里云推送react-native android最佳实践

1,463 阅读5分钟

最近在写阿里云推送相关的东西,看了阿里云移动推送+ReactNative最佳实践之后,跟着写越写越不对劲,明显省略了亿点点细节。我发现大多数文档就算跟着一步步去做,还是会有报错,都是省略了细节,于是重复的坑越踩越多,特此记录一下此次踩坑记录。

demo中copy一下,啊,不是,借鉴一下,是每个程序员都具备的良好品质👍。查看使用Demo快速体验移动推送,将demo下载下来, 打开react-native的demo选择文件目录:mpush_reactnative_android_demo/AwesomeProject.

安装依赖,执行yarn android命令,于是你就会得到这样的错误提示:

将文件android/app/build.gradle里的debug内容换一下

signingConfigs {
    debug {
        storeFile file('D:\桌面\key\Test.jks')
        storePassword "test1234"
        keyAlias "test"
        keyPassword "test1234"
    }
}
// 替换成
signingConfigs {
    debug {
        storeFile file('debug.keystore') // 主要是这句
        storePassword 'android'
        keyAlias 'androiddebugkey'
        keyPassword 'android'
    }
}

重新yarn android一下,怀着期待的心情发现它又报错啦:

image.png

看提示就知道local.properties未设置,将local.properties放到android文件夹下,里面是本地sdk的位置

image.png

再次运行yarn android

程序终于运行成功啦🎉,然后按照链接文档的那样,找到android/app/src/main/AndroidManifest.xml,将appkey和appsecret设置成自己创建好的emas项目

<!-- 请填写你自己的- appKey -->
<meta-data
    android:name="com.alibaba.app.appkey"
    android:value="********" />
<!-- 请填写你自己的appSecret -->
<meta-data
    android:name="com.alibaba.app.appsecret"
    android:value="********" />

重新yarn android一下,在emas控制台上新建消息通知,满心期待的看着app的界面........................................................................................好吧,等了很久没什么反应,应该是哪儿出问题了。 控制台输入adb shell input keyevent 82, 选择debug,看到react-native-debugger控制台有个这样的错误:

image.png 研究了半天,看了github提交的issue也没有这个问题,心里感觉这个方向应该没必要研究下去了。于是开始读源码。找到android/app/src/main/java/com/MainApplication.java里的这一段方法。

/**
 * 注册通知
 */
public void initCloudChannel() {
    File is_privacy = new File(ContextCompat.getDataDir(this).getAbsolutePath(), ContVar.P_FILE);
    if (!is_privacy.exists()) return;
    if (pushInit) return;
    pushInit = true;

    if (BuildConfig.DEBUG) {
        //仅适用于Debug包,正式包不需要此行
        PushServiceFactory.getCloudPushService().setLogLevel(CloudPushService.LOG_DEBUG);
    }
    PushServiceFactory.getCloudPushService().register(this.getApplicationContext(), new CommonCallback() {
        @Override
        public void onSuccess(String s) {
            pushInit = true;
            WritableMap params = Arguments.createMap();
            params.putBoolean("success", true);
            PushModule.sendEvent("onInit", params);
            initCS();
        }

        @Override
        public void onFailed(String s, String s1) {
            pushInit = false;
            WritableMap params = Arguments.createMap();
            params.putBoolean("success", false);
            params.putString("errorMsg", "errorCode:" + s + ". errorMsg:" + s1);
            PushModule.sendEvent("onInit", params);
        }
    });
}

反复🤔了一下,在主目录的index.android.js文件找到这段

componentDidMount() {
 DeviceEventEmitter.addListener('onMessage', this.onMessage);
 DeviceEventEmitter.addListener('onNotification', this.onNotification);
 DeviceEventEmitter.addListener('onNotificationOpened', this.onNotificationOpened);
 DeviceEventEmitter.addListener('onNotificationRemoved', this.onNotificationRemoved);
 DeviceEventEmitter.addListener('onSysNoticeOpened', this.onSysNoticeOpened);
}

因为项目是react-native,链接文档下面这个是原生java输出,react里是看不到是否是集成成功的。

image.png

动动脑筋学委想了一下,我把第二段代码改成了这样:

componentDidMount() {
 DeviceEventEmitter.addListener('onInit', (params)=> {
     console.log('----------init', params);
 });
 DeviceEventEmitter.addListener('onMessage', this.onMessage);
 DeviceEventEmitter.addListener('onNotification', this.onNotification);
 DeviceEventEmitter.addListener('onNotificationOpened', this.onNotificationOpened);
 DeviceEventEmitter.addListener('onNotificationRemoved', this.onNotificationRemoved);
 DeviceEventEmitter.addListener('onSysNoticeOpened', this.onSysNoticeOpened);
}

思路是先判断是否初始化成功再考虑是不是接收方法的问题,因为第一段java有PushModule.sendEvent("onInit", params), 这个方法可以在第二段react里用我加的这段代码监听到,这里不懂可以去看react和原生通信相关知识, 然后重新yarn android一下,结果不出所料,没有任何打印信息。继续修改第一段java代码

/**
 * 注册通知
 */
public void initCloudChannel() {
    File is_privacy = new File(ContextCompat.getDataDir(this).getAbsolutePath(), ContVar.P_FILE);
    // if (!is_privacy.exists()) return;
    if (pushInit) return;
    pushInit = true;

    if (BuildConfig.DEBUG) {
        //仅适用于Debug包,正式包不需要此行
        PushServiceFactory.getCloudPushService().setLogLevel(CloudPushService.LOG_DEBUG);
    }
    PushServiceFactory.getCloudPushService().register(this.getApplicationContext(), new CommonCallback() {
        @Override
        public void onSuccess(String s) {
            pushInit = true;
            WritableMap params = Arguments.createMap();
            params.putBoolean("success", true);
            PushModule.sendEvent("onInit", params);
            initCS();
        }

        @Override
        public void onFailed(String s, String s1) {
            pushInit = false;
            WritableMap params = Arguments.createMap();
            params.putBoolean("success", false);
            params.putString("errorMsg", "errorCode:" + s + ". errorMsg:" + s1);
            PushModule.sendEvent("onInit", params);
        }
    });
}

将第一个return注释掉,可能是没有执行到sendEvent这一步。继续yarn android. 出乎意料的没有任何输出,🤯,这么看来,控制台报错有可能就是onInit监听有问题了。再再思考了一下,将第一段代码改成了这样:

/**
 * 注册通知
 */
public void initCloudChannel() {
    File is_privacy = new File(ContextCompat.getDataDir(this).getAbsolutePath(), ContVar.P_FILE);
    // if (!is_privacy.exists()) return;
    if (pushInit) return;
    pushInit = true;

    if (BuildConfig.DEBUG) {
        //仅适用于Debug包,正式包不需要此行
        PushServiceFactory.getCloudPushService().setLogLevel(CloudPushService.LOG_DEBUG);
    }
    PushServiceFactory.getCloudPushService().register(this.getApplicationContext(), new CommonCallback() {
        @Override
        public void onSuccess(String s) {
            pushInit = true;
            WritableMap params = Arguments.createMap();
            params.putBoolean("success", true);
            Toast.makeText(MainApplication.this, "success", Toast.LENGTH_SHORT).show();
            PushModule.sendEvent("onInit", params);
            initCS();
        }

        @Override
        public void onFailed(String s, String s1) {
            pushInit = false;
            WritableMap params = Arguments.createMap();
            params.putBoolean("success", false);
            params.putString("errorMsg", "errorCode:" + s + ". errorMsg:" + s1);
            Toast.makeText(MainApplication.this, "fail", Toast.LENGTH_SHORT).show();
            PushModule.sendEvent("onInit", params);
        }
    });
}

Toast是原生android的弹窗,自创的react-native原生java的"debug",记得要在上方导入,再次yarn android

import android.widget.Toast

终于输出了。。。根据使用Demo快速体验移动推送,在控制台新建通知信息,然后静静等待片刻,即可收到控制台设置好的消息,这里需要注意的是demo的GET DEVICE_ID点击后有deviceID的大概率能正常收到消息,但是Mac的虚拟器有时候deviceId为null.是收不到消息的,这又是另一个大坑,emm,其次控制台设置的PackageName需要和项目android/app目录下的build.gradle里的applicationId设置的一致。

费尽心思终于,这个demo跑起来了。。。emm...具体项目里如何重写,虽然我已经集成进项目,但是文章只能有时间再写了。

最后的最后,老实说,实现这些功能是一件很有趣的事儿,但是有时候因为工作,或者生活中各种压力,会对自己热爱的事情有厌倦,这时候最好的做法就是停下脚步想一想自己想要的究竟是什么,放慢之后会发现,喜欢的还是会喜欢,热爱的还是会热爱,有遗憾就去争取,尽力之后就不要后悔,做自己很重要。实话实说代码很有趣,工作很无趣,两者并不是互斥的,希望我们都能因为热爱去做一些事。

2023-06-15 更新 家人们,谁懂啊,6月12号还有react-native阿里云推送最佳实践的官方文档,现在官网已经删除了,只能移步至客户端插件,使用官方推荐的aliyun-react-native-push,好像是这个,但是文档...还是不全,建议有需求的直接用插件好了。。。。踩坑文档等我最近有时间再更吧,大概吧,哈哈哈哈...