前端知识 | React-Native如何自己撸第三方库之android篇

在使用 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 加入如下代码

             android:name="com.baidu.location.f"

            android:enabled="true"

             android:process=":remote">


             android:name="com.baidu.lbsapi.API_KEY"

             android:value="GN1lCG*************4I&&s" />

添加权限

```

```

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号和大体定位时间到loc-bugs@baidu.com,会有人追查原因");

            } 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 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  createViewManagers(ReactApplicationContext reactContext) {

        return Collections.emptyList();

    }


    @Override

    public List  createNativeModules(

            ReactApplicationContext  reactContext) {

        List 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 返回值。

更多特性敬请期待。


-END- 

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,392评论 25 707
  • React Native开发中常用三方组件大全 作者整理的一套常用的React Native开发中使用到的三方组件...
    光强_上海阅读 21,109评论 6 95
  • 我的花园中央是一座海 无所事事,她雕刻水 我的杏树没有姿势 冬天结果夏天疯 玫瑰一夜一枯萎 掉下委屈的刺 这春天的...
    我是不是蝎大人阅读 199评论 0 1
  • 生活在小县城的我,觉得买了房有房住,工作稳定已经很不错,所以一直没有想过去投资买第二套房子,已没有想过要如何奋斗让...
    学着努力8e8o阅读 136评论 0 0
  • 1大约是在三年前开始,我做着美梦,写着一些随心之笔,喝着苦茶,妄想成为那些隐士大师之中的一员。还未到二十的年纪,我...
    木起心落阅读 901评论 0 0