此文主要介绍 React Native SDK for OSS的方方面面,包括相关基本概念、项目背景、项目方案、环境搭建运行、使用姿势、注意事项等。文末的附件可运行Example Zip压缩包和针对新手的入门实用文章《从0开始搭建React Native for OSS项目》。
背景
OSS支持React Native SDK,主要有以下几个原因。
- React Native跨平台开发技术目前在移动开发领域仍然是一个非常热门的技术,在国内外互联网公司中都有广泛使用,比如:Facebook、微软、Airbnb、百度、腾讯、京东、爱奇艺等科技公司。此外从我们的ali-oss-js-sdk上的issue上也可以看到,从2015年底到现在有接近10个issue是关于支持react native平台的。近日我们把这些issue合并成一个并统一支持,旨在希望能够更好的服务于使用React Native开发移动应用的客户,同时准备把项目开源出去从而聆听更多客户的声音,也可以让更多的开发者加入我们,持续改善OSS服务。
- AWS在9个月前正式支持React Native平台,最近AWS也专门开发了aws-amplify-react-native包用于支持React Native平台。近期关注React Native官网的人会发现,其Blog专栏里专门写了一边文章《Using AWS with React Native》,由此可以看出AWS对于支持React Native平台的重视,此外国内的七牛云也单独开发了用于支持react-native的SDK包,因此阿里云OSS有必要去支持React Native平台。
- 通过github和npm包仓库调查,其实部分开发者已经针对自身业务场景需求或多或少地在React native平台上支持某一个API,但都不够全面,而且缺乏长期维护的动力,不能服务于更多的开发者。因此我们决定统一开发来支持react native平台,为更多的开发者提供标杆,也可以让更多有需求的开发者参与加入。
什么是React Native
React Native是facebook团队于2015年推出的移动端跨平台解决方案,它主张的理念是learn once ,run anywhere。React Native本质上和React是两个不同的东西。React Native旨在让移动开发者通过简捷的前端语法写出Native级别的性能体验,其页面性能介于H5和原生页面之间,而且更加接近原生页面,完美兼顾性能与动态性,因此受到很多移动开发者的青睐,很多团队也基于此造就符合自身业务需求的轮子。
React Native无论是在Andoird、iOS还是浏览器都需要一个JS的运行环境, 在iOS上直接使用内置的javascriptcore,实现JS与OC的通信,最终渲染成原生的iOS页面。 在Android 通过JNI调用C++代码通过javascriptcore来执行JS,实现JS与JAVA的通信,最终渲染成原生安卓页面。 此外react-native还集成了安卓或者iOS其他开源组件库,具体的原理细节读者可参考文档末尾贴出的链接,React Native的整个架构图如下。
React Native SDK for OSS方案
从React Native的学习调研、环境搭建到OSS支持React Native平台到代码文档编写完成,整个过程持续一周多,目前项目正在申请走开源流程,可查看项目ali-oss-rn-sdk仓库。中间方案经历一次变革。目前整体开发方案采用方案二。
方案一
从nodejs、browserjs的角度通过不断替换包、新增包的方式转换成React Native平台需要的RN.js, 早期调研经过排查定位到具体的包和不兼容的代码,列出部分如下:
1. xml2js => react-native-xml2js
2. client中createRequest mimie.lookup == > getType
3. stream =>stream-browserify
4. http => stream-http
5. https=>https-browserfiy
6. shim/xhr.js constants =>constants-browserify
7. assert代码块注释lib/bucket.js
8. Bufffer: shims/xhr.js libclient.js common/signUtils
9. timer模块等等
缺点
经过大量调试解决了包依赖和不兼容报错,但是需要改动大量的已有包和代码。通过这种方式生产的RN包,对于JS开发者来说完全是黑盒,改造后的JS也不一定是React Native平台真正需要的JS,同时对于React Native平台对应的Native(Android、IOS)端执行的逻辑完全搞不清楚。就算生成RN需要包,还是需要在Native(Android和IOS)端执行。
方案二
从安卓SDK和IOS SDK角度出发,在已有SDK的基础上,通过React Native插件的方式,再封装一个React Native的包,底层调用Native(Android、IOS) API,上层通过React Native插件的方式暴漏JS API,以npm包的形式发出,这种方式可行更高,更靠谱,思路更清晰。目前所有代码都是基于这种方式开发。
针对方案二我们以具体的asyncUpload接口为例来阐述如何实现兼容安卓、iOS的API,安卓和iOS端需要统一暴漏Native模块RNAliyunOSS供JS调用。
安卓
安卓端需要新建RNAliyunOssModule类继承ReactContextBaseJavaModule,同时重写getName方法,getName的返回值为'RNAliyunOSS',这个就是安卓暴漏给JS的模块名,如果需要安卓暴漏方法给JS需要在暴漏的方法前加上注解@ReactMethod,可参考下面代码,具体代码可以参考官方代码
public class RNAliyunOssModule extends ReactContextBaseJavaModule {
private OSS mOSS;
public RNAliyunOssModule(ReactApplicationContext reactContext) {
super(reactContext);
}
//返回给JS调用的模块名
@Override
public String getName() {
return "RNAliyunOSS";
}
//返回给JS模块调用的方法
@ReactMethod
public void initWithServerSTS(final String server, String endPoint, ReadableMap configuration) {
/<strong>这里写调用安卓代码授权的逻辑</strong>/
}
@ReactMethod
public void asyncUpload(String bucketName, String ossFile, String sourceFile,ReadableMap options, final Promise promise) {
/**这里写调用安卓代码上传的逻辑*/
}
}
此外RNAliyunOssModule也需要在MainApplication.java中进行注册
public class MainApplication extends Application implements ReactApplication {
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new RNAliyunOssPackage()
);
}
/*<strong>各种代码</strong>*/
}
iOS
新建文件RNAliyunOSS.m和RNAliyunOSS.h文件,RNAliyunOSS.m文件里通过RCT_EXPORT_MODULE()模块名给JS,通过 RCT_REMAP_METHOD暴漏方法名给JS,可参考如下代码:RNAliyunOSS.m
#import "RNAliyunOSS.h"
#import <React/RCTLog.h>
#import <React/RCTConvert.h>
@implementation RNAliyunOSS
RCT_REMAP_METHOD(asyncUpload, asyncUploadWithBucketName:(NSString *)bucketName objectKey:(NSString *)objectKey filepath:(NSString *)filepath options:(NSDictionary*)options resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject){
/**oc native code*/
}
RCT_EXPORT_MODULE()
@end
JS
JS端只需引入react-native NativeModules模块就可引用上述安卓和iOS端暴漏的RNAliyunOSS模块和上面的方法asyncUpload,参考代码如下
import {DeviceEventEmitter, NativeEventEmitter, NativeModules, Platform} from "react-native";
const {RNAliyunOSS} = NativeModules;
export default AliyunOSS = {
initWithServerSTS(server, endPoint, configuration = conf) {
RNAliyunOSS.initWithServerSTS(server, endPoint, configuration);
},
/**
* Asynchronously uploading
*/
asyncUpload(bucketName, objectKey, filepath,options) {
return RNAliyunOSS.asyncUpload(bucketName, objectKey, filepath,options);
}
};
React Native SDK for OSS的使用姿势
- 导入aliyun-oss-rn-sdk包
import AliyunOSS from 'aliyun-oss-rn-sdk'
- 初始化授权
目前提供四种方式,包括initWithPlainTextAccessKey、initWithImplementedSigner、initWithSecurityToken、initWithServerSTS建议initWithServerSTS, 其中server为本地鉴权服务器。
- 调用API,以upload接口为例,并监听上传事件
console.log(res)
}).catch((error) => {
console.log(error)
})
//监听上传事件
const uploadProgress = p => console.log(p.currentSize / p.totalSize);
AliyunOSS.addEventListener('uploadProgress', uploadProgress);
如何运行附件Example工程
- 开启鉴权server,可以参考ali-oss-rn-sdk项目下的script文件,修改config.json里的accessKeyId、accessKeySecret和RoleArn,然后运行node index.js默认开启9000端口 2.项目下运行npm install,然后运行react-native link
- 打开项目下的app.js,在initWithServerSTS函数中修改server地址为本地鉴权server地址
- 打开项目目录运行 npm run start
- android studio打开android文件下的安卓项目编译运行或者运行react-native run-android命令,成功运行后可看到界面
- 使用xcode打开ios文件夹下的iOS项目编译运行或者运行react-native run-ios 命令,成功运行后可看到界面
Reference
- ali-oss-rn-sdk官方仓库:http://gitlab.alibaba-inc.com/oss/aliyun-oss-rn-sdk/tree/develop
- react: https://reactjs.org/
- react-native: http://facebook.github.io/react-native/
- react-native大致原理:https://www.jianshu.com/p/5cc61ec04b39
- react-native通信原理:https://www.jianshu.com/p/17d6f6c57a5c
- rn android native modules: http://facebook.github.io/react-native/docs/native-modules-android.html
- rn ios native modules:http://facebook.github.io/react-native/docs/native-modules-ios.html