在应用 ReactNative 开发设计全过程中,因为开发设计中采用的語言是 JS,而假如必须保持百度定位或是新浪微博共享等第三方 SDK 时,通常会先到找有木有适用 React Native 的三方库。虽然绝大多数常用功能已经被民间艺人或官方团队实现,但是一旦遇到未被 RN 开发的就只能撸起袖子自己干了。ReactNative 官方文档也介绍了 Native Modules(下称‘原生模块’)的使用方法,它能用于访问原生平台的 API。
本文主要提供 Android 平台的原生模块打包教程。
原生模块的使用方法:
以官网文档列举的 Toast 模块使用方法为例,其主要步骤为:
第一步:
使用 Android Studio 打开 RN 项目的 android 文件,新建 Android Library 命名为 react-native-toast
图1 新建Android Library
第二步:
在 react-native-toast 的 build.gradlew 中添加RN依赖
dependencies { .... compile "com.facebook.react:react-native:+" //here } |
在 app 的 build.gradlew 中引入这个库
dependencies { .... compile project(':react-native-toast') //here } |
第三步:
前面的环境都配置好后就开始新建2个 java 文件:
继承 ReactContextBaseJavaModule 的 java 类 Module.java
和注册模块用的 Package.java。
具体代码在官网有详细提供,本文不重点阐述
思考:
如果按照 RN 文档方法,在项目的 android 根文件夹下添加原生模块,会出现两个问题:一是,如果要实现的原生模块多了,Android Library 也会递增,目录容易混乱难以管理;二是,这样的原生模块不容易复用,其它项目没法直接使用这个原生模块。
通行的做法是,将实现后原生模块打包好,然后存放在项目的 node_modules 的文件内,供 APP 调用。
本文重点:
本文将以百度定位的 Android SDK 为例,介绍如何将其打包为一个名为 “react-native-baidu-location” 的原生模块库(library),供 RN APP 调用。
这个例子相比一般的原生模块开发(RN文档示例)会多一项工作,即引入 SDK,流程大致如下:
·创建一个 RN 项目 Demo,用于调试。
·初始化 react-native-baidu-location 包。
·根据官网文档对 Android 平台配置、引入 SDK、写代码。
初始化原生模块库
先创建一个 RN 项目 Demo
安装 react-native-create-library(类似的工具还有react-native-create-bridge)
npm install -g react-native-create-library |
在 node_modules 文件夹下创建你的三方库
react-native-create-library react-native-baidu-location |
之后进行本文之前的第三步,第一步和第二步已经帮我们实现,只需要根据自己需求修改构建工具的版本 .grandle 和 buildToolsVersion。
打包 Android 部分(重难点)
因为以百度定位为例,所以需要申请为百度开发者,下载基础定位 SDK,具体步骤请使用 baidu,本文不必阐述,主要使用下载后解压包内叫 libs 的文件夹。
使用 Android Studio 打开 react-native-baidu-location 内的 android 文件和 RN 项目Demo 下的 android 文件,因为这里是 android 打包教程,所以其他平台教程请期待下篇《React-Native 如何自己撸第三方库之 ios 篇》
1.React-native-baidu-location - android
将 libs 文件复制到 android 文件下
build.gradle 加入如下代码
android {
.... sourceSets { main { jniLibs.srcDirs = ['libs'] } } } |
2.Demo - android
根据百度文档,配置相关服务,ak 秘钥和权限
AndroidManifest.xml 加入如下代码
<service
android:name="com.baidu.location.f" android:enabled="true" android:process=":remote"></service> <meta-data android:name="com.baidu.lbsapi.API_KEY" android:value="GN1lCG*************4I&&s" /> |
添加权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/> <!-- 这个权限用于进行网络定位--> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"></uses-permission> <!-- 这个权限用于访问GPS定位--> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission> <!-- 用于访问wifi网络信息,wifi信息会用于进行网络定位--> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission> <!-- 获取运营商信息,用于支持提供运营商信息相关的接口--> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"></uses-permission> <!-- 这个权限用于获取wifi的获取权限,wifi信息会用来进行网络定位--> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission> <!-- 用于读取手机当前的状态--> <uses-permission android:name="android.permission.READ_PHONE_STATE"></uses-permission> <!-- 写入扩展存储,向扩展卡写入数据,用于写入离线定位数据--> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"></uses-permission> <!-- 访问网络,网络定位需要上网--> <!-- SD卡读取权限,用户写入离线定位数据--> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"></uses-permission> |
3. 编写 Native 模块
和前文的第三步一样使用固定写法:
1. 建一个中间访问对象,
2. 建一个 ReactPackage 对象.
3. 将 ReactPackage 对象加载到 MainApplication 中.
LocationActivity
package com.location;
import android.util.Log; import com.baidu.location.BDLocation; import com.baidu.location.BDLocationListener; import com.baidu.location.LocationClient; import com.baidu.location.LocationClientOption; import com.baidu.location.Poi; import com.facebook.react.bridge.Callback; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.ReactContextBaseJavaModule; import com.facebook.react.bridge.ReactMethod; import java.util.List; /** * Created by luokun on 2018/1/31. */ public class LocationActivity extends ReactContextBaseJavaModule { public LocationClient mLocationClient = null; private Callback locationCallback; public LocationActivity(ReactApplicationContext reactContext) { super(reactContext); } @ReactMethod public void startLocation( Callback locationCallback) { this.locationCallback = locationCallback; mLocationClient = new LocationClient(getReactApplicationContext()); //声明LocationClient类 mLocationClient.registerLocationListener(myListener); //注册监听函数 LocationClientOption option = new LocationClientOption(); option.setLocationMode(LocationClientOption.LocationMode.Hight_Accuracy );//可选,默认高精度,设置定位模式,高精度,低功耗,仅设备 option.setCoorType("bd09ll");//可选,默认gcj02,设置返回的定位结果坐标系 int span = 0; option.setScanSpan(span);//可选,默认0,即仅定位一次,设置发起定位请求的间隔需要大于等于1000ms才是有效的 option.setIsNeedAddress(true);//可选,设置是否需要地址信息,默认不需要 option.setOpenGps(true);//可选,默认false,设置是否使用gps option.setLocationNotify(true);//可选,默认false,设置是否当gps有效时按照1S1次频率输出GPS结果 option.setIsNeedLocationDescribe(true);//可选,默认false,设置是否需要位置语义化结果,可以在BDLocation.getLocationDescribe里得到,结果类似于“在北京天安门附近” option.setIsNeedLocationPoiList(true);//可选,默认false,设置是否需要POI结果,可以在BDLocation.getPoiList里得到 option.setIgnoreKillProcess(false);//可选,默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认不杀死 option.SetIgnoreCacheException(false);//可选,默认false,设置是否收集CRASH信息,默认收集 option.setEnableSimulateGps(false);//可选,默认false,设置是否需要过滤gps仿真结果,默认需要 mLocationClient.setLocOption(option); mLocationClient.start(); Log.e("tag","ok"); } /** * 定位回掉 */ public BDLocationListener myListener = new BDLocationListener() { @Override public void onReceiveLocation(final BDLocation bdLocation) { Log.e("TGA", "回掉+1"); //Receive Location final StringBuffer sb = new StringBuffer(256); sb.append("time : "); sb.append(bdLocation.getTime()); sb.append("nerror code : "); sb.append(bdLocation.getLocType()); sb.append("nlatitude : "); sb.append(bdLocation.getLatitude()); sb.append("nlontitude : "); sb.append(bdLocation.getLongitude()); sb.append("nradius : "); sb.append(bdLocation.getRadius()); if (bdLocation.getLocType() == BDLocation.TypeGpsLocation) {// GPS定位结果 sb.append("nspeed : "); sb.append(bdLocation.getSpeed());// 单位:公里每小时 sb.append("nsatellite : "); sb.append(bdLocation.getSatelliteNumber()); sb.append("nheight : "); sb.append(bdLocation.getAltitude());// 单位:米 sb.append("ndirection : "); sb.append(bdLocation.getDirection());// 单位度 sb.append("naddr : "); sb.append(bdLocation.getAddrStr()); sb.append("ndescribe : "); sb.append("gps定位成功"); } else if (bdLocation.getLocType() == BDLocation.TypeNetWorkLocation) {// 网络定位结果 sb.append("naddr : "); sb.append(bdLocation.getAddrStr()); //运营商信息 sb.append("noperationers : "); sb.append(bdLocation.getOperators()); sb.append("ndescribe : "); sb.append("网络定位成功"); } else if (bdLocation.getLocType() == BDLocation.TypeOffLineLocation) {// 离线定位结果 sb.append("ndescribe : "); sb.append("离线定位成功,离线定位结果也是有效的"); } else if (bdLocation.getLocType() == BDLocation.TypeServerError) { sb.append("ndescribe : "); sb.append("服务端网络定位失败,可以反馈IMEI号和大体定位时间到[email protected],会有人追查原因"); } else if (bdLocation.getLocType() == BDLocation.TypeNetWorkException) { sb.append("ndescribe : "); sb.append("网络不同导致定位失败,请检查网络是否通畅"); } else if (bdLocation.getLocType() == BDLocation.TypeCriteriaException) { sb.append("ndescribe : "); sb.append("无法获取有效定位依据导致定位失败,一般是由于手机的原因,处于飞行模式下一般会造成这种结果,可以试着重启手机"); } sb.append("nlocationdescribe : "); sb.append(bdLocation.getLocationDescribe());// 位置语义化信息 List<Poi> list = bdLocation.getPoiList();// POI数据 if (list != null) { sb.append("npoilist size = : "); sb.append(list.size()); for (Poi p : list) { sb.append("npoi= : "); sb.append(p.getId() + " " + p.getName() + " " + p.getRank()); } } locationCallback.invoke(sb.toString()); } }; @Override public String getName() { return "Location"; } } |
LocationPackage
package com.location;
import com.facebook.react.ReactPackage; import com.facebook.react.bridge.JavaScriptModule; import com.facebook.react.bridge.NativeModule; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.uimanager.ViewManager; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Created by luokun on 2018/1/31. */ public class LocationPackage implements ReactPackage { @Override public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { return Collections.emptyList(); } @Override public List<NativeModule> createNativeModules( ReactApplicationContext reactContext) { List<NativeModule> modules = new ArrayList<>(); modules.add(new LocationActivity(reactContext)); // return modules; } } |
Index.js文件
'use strict';
import { NativeModules } from 'react-native'; export default NativeModules.Location; |
恭喜完成!
至于怎么使用,最好写一个 README.md,要‘装逼’咱们就来全套。写个使用文档,让我们搬运工们用得顺手。(笑笑就好:))
使用方法:
自动 link
React-native-baidu-location link |
或者手动 link
/android/settings.gradle
include ':react-native-baidu-location'
project(':react-native-baidu-location').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-baidu-ocr/android') |
/android/app/build.gradle
dependencies {
... compile project(':react-native-baidu-ocr'') // 加入 ... } |
js 使用方法
Import Location from ‘react-native-baidu-location’
... ... componentDidMount() { Location.startLocation((location)=> { console.log(location) }); } |
当你看到这里的时候并且自己照着写了一遍,那么恭喜你成功开发出第三方 rn 库了,甚至可以上传到 npm 上开源。
上传成功后,其它项目就可以通过 npminstall 来使用这个库。
总结:
要打包 React Native 的原生模块,你最好有以下知识储备:
有 RN 开发经验。如 RN 文档所说,原生模块是该框架的高级特性,当然是有一定的经验更易理解。
了解 Java 和 Objective-C, 至少能看懂这两种代码。和官网教程对比上述代码虽然多但是很多都是固定写法。
基本实现原理是通过回调使 JavaScript 能够访问到 java 返回值。
本文来源于:前端知识 | React-Native如何自己撸第三方库之android篇-变化吧门户
特别声明:以上文章内容仅代表作者本人观点,不代表变化吧门户观点或立场。如有关于作品内容、版权或其它问题请于作品发表后的30日内与变化吧联系。
- 赞助本站
- 微信扫一扫
-
- 加入Q群
- QQ扫一扫
-
评论