美文网首页React NativeiOS笔记React-Native相关
ReactNative封装iOS/Android原生组件

ReactNative封装iOS/Android原生组件

作者: kyleBoy | 来源:发表于2018-07-20 16:29 被阅读30次

    简介

    为了提高代码的复用,减少重复工作,我们经常会把比较独立的功能模块封装成组件或者library

    在iOS我们通常封装为静态库(.framework和.a),

    在Android中我们通常封装为Android Library(gradle模块),

    在JavaScript中我们通常封装成node package,

    而在ReactNative中我们要封装与原始有关的库我们需要,结合iOS静态库(.a)、Android Library和node package一起封装成ReactNative用的node package。

    目录结构

    testmodule-wyh
    .
    ├── android   // 存放Android library
    │   ├── build
    │   ├── build.gradle
    │   ├── libs
    │   ├── proguard-rules.pro
    │   ├── src
    │   └── testmodule.iml
    ├── component // 存放js桥接过来的组件以及api
    │   └── TestView.js
    ├── index.js
    ├── ios // 存放iOS library
    │   ├── TestModule
    │   └── TestModule.xcodeproj
    └── package.json // node包的配置文件
    
    

    源码地址

    制作

    装备工作

    创建ReactNative工程

    我们需要先创建一个ReactNative工程,使用如下命令创建。

    $ react native init A7011CreatNativeModule
    

    创建存放封装库的目录

    $ cd A7011CreatNativeModule/node_modules
    $ mkdir testmodule-wyh
    $ cd testmodule-wyh
    $ mkdir ios
    $ mkdir android
    $ mkdir component
    

    封装iOS库

    步骤

    1. 静态库创建

    由于ReactNative的组件都是一个个静态库,我们发布到npm给别人使用的话,也需要建立静态库。我们使用xcode建立静态库,取名为Testmodule。建立之后,我们将创建的静态库中的文件全部copy到node_modules/Testmodule/ios目录下。 ios文件目录如下:

    testmodule-wyh
    .
    ├── android   // 存放Android library
    ├── component // 存放js桥接过来的组件以及api
    └── ios // 存放iOS library
        ├── Testmodule
        │   ├── Testmodule.h
        │   └── Testmodule.m
        └── TestModule.xcodeproj
    
    2. 引入到目标工程

    使用xcode打开A7011CreatNativeModule/ios/下的iOS工程,将BGNativeModuleExample静态库工程拖动到工程中的Library中。

    image-20180713184731573
    1. 为什么要拖到RN工程中呢?

      因为,不拖到RN工程中去的话,用的RN库中的API,就会报错,所以需要RN工程的基础环境。

    2. 为什么这里要先把静态库放到node_modules/Testmodule/ios,再拖到RN工程中去呢?

      那是因为,如果直接把创建的静态库的TestModule.xcodeproj文件拖到工程中去,无法编辑这个静态库,只有在node_modules/Testmodule/ios才能编辑。目前还不知道原因

    3. 编写原生代码

    咱们这里主要是演示做封装RN的node package,所以就封装一个最简单的组件,一个背景色为红色的View。

    1. 创建TestView Class

         // TestView.h
         #import <UIKit/UIKit.h>
         
         @interface TestView : UIView
             
         @end
         
             
         // TestView.m
         #import "TestView.h"
         
         @implementation TestView
         - (instancetype)init
         {
             self = [super init];
             if (self) {
                 self.backgroundColor = [UIColor redColor];
             }
             return self;
         }
         
         @end    
      
    2. 创建桥接类

      // TestViewManager.h
      #import <React/RCTBridgeModule.h>
      
      @interface TestViewManager : RCTViewManager <RCTBridgeModule>
      
      @end
          
          
      // TestViewManager.m
      #import "TestViewManager.h"
      #import <React/RCTViewManager.h>
      #import "TestView.h"
      
      @interface TestViewManager : RCTViewManager
      @end
      
      @implementation TestViewManager
      
      RCT_EXPORT_MODULE()
      
      - (UIView *)view
      {
          return [[TestView alloc] init];
      }
      
      @end
      
      
    3. 需要更复杂的交互,可以参考React Native官网的iOS原生模块原生UI组件

    4. React组件创建
    1. 接下来你需要一些Javascript代码来让这个视图变成一个可用的React组件:

      // A7011CreatNativeModule/node_modules/testmodule-wyh/component/TestView.js
      // TestView.js
      import {requireNativeComponent} from 'react-native';
      
      const TestView = requireNativeComponent('TestView', null);
      export default TestView;
      
    2. 在路径A7011CreatNativeModule/node_modules/testmodule-wyh创建index.js文件,导出TestView组件

      // index.js
      import TestView from './component/TestView'
      
      export default TestView;
      
    5. 组件使用

    在RN工程里面的App组件中,使用这个组件

    // App.js
    import React, {Component} from 'react';
    // ......
    export default class App extends Component<Props> {
      render() {
        return (
          <View style={styles.container}>
          // ......
            <TestView style={{width: 100, height: 100}} /> // TestView组件
          </View>
        );
      }
    }
    // ......
    });
    
    
    6. 运行效果
    image

    注意

    引入第三方库

    iOS封装静态库的时候,经常会用到需要引入的系统库和第三方库;系统库引入很简单,只要在TestModule.xcodeproj->Build Phases->Link Bnary with Libraries添加系统库就行;而第三方库有可能是.a也有可能是.framework,.a拷贝到静态库根目录里面,接下就和系统库添加一样了,而.framework就复杂了,他不能直接引用到封装的静态库中,需要引用到目标iOS工程中去才可以。

    桥接ViewController

    封装Android库

    步骤

    1. 创建Android library

    用Android studio打开RN工程中的Android工程,新建file->new->new module..->Android library,并命名为testmodule

    image-20180716164450271
    2. RN的Android工程中配置模块
    • app工程中的build.gradle文件中的dependencies添加一行compile project(':testmodule-wyh'),让主工程app依赖我们新创建的Library。
    • 我们还需要让新创建的Library依赖react native,和上面差不多,只需要在我们新创建的testmodule-wyh下的build.gradle中的dependencies添加一行compile "com.facebook.react:react-native:+"就行了。
    3. 编写原生代码

    Android原生代码的编写,前两步和iOS步骤类似,多了一个创建ReactPackage实现,并在MainApplication的getPackages方法里面注册。

    1. 创建TestView Class

      package com.example.testmodule;
      
      import android.content.Context;
      import android.graphics.Color;
      import android.view.View;
      
      public class TestView extends View {
      
          public TestView(Context context) {
              super(context);
              this.setBackgroundColor(Color.rgb(255,0,0));
          }
      }
      
    2. 创建桥接类

      package com.example.testmodule;
      
      import com.facebook.react.uimanager.SimpleViewManager;
      import com.facebook.react.uimanager.ThemedReactContext;
      
      public class TestViewManager extends SimpleViewManager<TestView> {
          public static final String REACT_CLASS = "TestView";
      
          @Override
          public String getName() {
              return REACT_CLASS;
          }
      
          @Override
          protected TestView createViewInstance(ThemedReactContext reactContext) {
              return new TestView(reactContext);
          }
      }
      
      
    3. 需要创建一个Package注册类

      package com.example.testmodule;
      
      import com.facebook.react.ReactPackage;
      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;
      
      public class TestPackage implements ReactPackage {
          @Override
          public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
              return Collections.emptyList();
          }
      
          @Override
          public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
              List<ViewManager> modules = new ArrayList<>();
              modules.add(new TestViewManager());
              return modules;
          }
      }
      
    4. 在MainApplication.java中注册这个package

      package com.a7011creatnativemodule;
      // ......
      public class MainApplication extends Application implements ReactApplication {
        private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
         //.......
          @Override
          protected List<ReactPackage> getPackages() {
            return Arrays.<ReactPackage>asList(
                new MainReactPackage(),
                new TestPackage(),
                // .......
            );
          }
       //.......
        };
      
        //.......
      }
      
      
    5. 需要更复杂的交互,可以参考React Native官网的android原生模块原生UI组件

    4. 组件创建使用

    之后的步骤在封装iOS库的4、5、6步已经做了

    5. 运行效果

    直接在点击run,看结果


    image

    注意

    Gradle问题
    1. com.android.tools.build:gradle工具也有2升级为3以后,dependencie依赖由compile变为implementation,而用React Native init ProjectName创建的rn项目还是用的compile,所以创建的时候要调整Gradle.build的dependencie和minSdkVersion以适用目标项目。
    2. 一定要记得创建ReactPackage的接口实现类,这样当执行react-native link testmodule-wyh才会自动添加到依赖。

    整理成node package

    1. 将封装的Android library里面的所以文件移到node_modules/Testmodule/android里面,形成的目录结构如下:

      .
      ├── android
      │   ├── build.gradle
      │   ├── libs
      │   ├── proguard-rules.pro
      │   ├── src
      │   ├── androidTest
      │   ├── main
      │   │   ├── AndroidManifest.xml
      │   │   ├── java
      │   │   │   └── com
      │   │   │       └── example
      │   │   │           └── testmodule
      │   │   │               ├── TestPackage.java
      │   │   │               ├── TestView.java
      │   │   │               └── TestViewManager.java
      │   │   └── res
      │   │       ├── drawable
      │   │       └── values
      │   │           └── strings.xml
      │   └── test
      │   └── testmodule.iml
      ├── component
      │   └── TestView.js
      ├── index.js
      ├── ios
      │   ├── TestModule
      │   │   ├── TestView.h
      │   │   ├── TestView.m
      │   │   ├── TestViewManager.h
      │   │   └── TestViewManager.m
      │   └── TestModule.xcodeproj
      
    2. 在路径node_modules/Testmodule-wyh/使用npm init创建一个package.json文件,全部使用默认项就行,这样Testmodule-wyh node package就封装完成了。

      // package.json
      {
        "name": "testmodule-wyh",
        "version": "1.0.0",
        "description": "test",
        "main": "index.js",
        "directories": {
          "lib": "lib"
        },
        "scripts": {
          "test": "echo \"Error: no test specified\" && exit 1"
        },
        "author": "",
        "license": "ISC"
      }
      
      

    使用

    1. testmodule-wyh node package拷贝到桌面,然后将iOS和Android项目中与Testmodule-wyh有关的代码全部删除

    2. 在RN项目中package.json中使用本地路径的方式引用制作的testmodule-wyh node package

      // package.json
      {
        "name": "A7011CreatNativeModule",
        "version": "0.0.1",
        "private": true,
        "scripts": {
          "start": "node node_modules/react-native/local-cli/cli.js start",
          "test": "jest"
        },
        "dependencies": {
          "react": "16.4.1",
          "react-native": "0.56.0",
          "testmodule": "file:/Users/wangyinghui/Desktop/testmodule-wyh",
        },
        "devDependencies": {
          "babel-jest": "23.4.0",
          "babel-preset-react-native": "^5",
          "jest": "23.4.0",
          "react-test-renderer": "16.4.1"
        },
        "jest": {
          "preset": "react-native"
        }
      }
      
    3. 在RN项目根目录执行如下指令

      $ yarn install
      $ react-native link testmodule-wyh
      
    4. 看到如下提示表示成功

      image
    5. 运行iOS和Android查看效果

      $ react-native run-android
      $ react-native run-ios
      

    注意事项

    1. iOS和Android的命名不一定要和封装的node package名一直。

    2. 默认iOS和Android的library都要在根目录,如果不在根目录需要在packag.json指明如下

      {
        "name": "react-native-maps",
        // ...
        "rnpm": {
          "android": {
            "sourceDir": "./lib/android"
          }
        }
      }
      

    上传代码库

    GitHub,创建一个代码仓库,将node package 代码上传,然后在package.json添加repository如下:

    "repository" :  
      { 
          "type" : "git",
          "url" : "https://github.com/<yourusername>/testmodule-wyh.git"  
      }  
    

    repository 属性不写也可以,但是最好建一个 github 项目然后把地址写进来,方便以后维护。

    发布

    注册npm账号

    发布node package到npm服务器上,需要npm的账号,注册哪怕npm账号有两种形式

    • npm官网注册

    • 使用命令行注册

      $ npm adduser
      Username: XXX # 账户名
      Password: *** # 密码
      Email: (this IS public) XXX@XXX.com # 邮箱
      

    如果用命令行注册的话,最好是在npm官网验证一下

    登录npm账户

    首次需要登录,npm login(需要输入用户名,密码,还有邮箱) 存储证书到本地,后面就不需要每次都登录的

    $ npm login
    Username: wangyinghui
    Password: 
    Email: (this IS public) iyinghui@163.com
    Logged in as wangyinghui on http://registry.npmjs.org/.
    

    开始发布

    $ npm publish
    + testmodule-wyh@1.0.2
    

    发布时报错

    1. cnpm造成的报错

    $ npm publish
    npm ERR! registry error parsing json
    npm ERR! publish Failed PUT 413
    npm ERR! Unexpected token < in JSON at position 0
    npm ERR! <html>
    npm ERR! <head><title>413 Request Entity Too Large</title></head>
    npm ERR! <body bgcolor="white">
    npm ERR! <center><h1>413 Request Entity Too Large</h1></center>
    npm ERR! <hr><center>nginx/1.4.6 (Ubuntu)</center>
    npm ERR! </body>
    npm ERR! </html>
    npm ERR! 
    
    npm ERR! A complete log of this run can be found in:
    npm ERR!     /Users/wangyinghui/.npm/_logs/2018-07-13T02_27_29_082Z-debug.log
    

    出现原因:使用的是淘宝源cnpm,登陆到的是cnpm

    解决方法:切换到npmjs的网址,代码如下:

    $ npm config set registry http://registry.npmjs.org/
    

    发布完成之后,如果还想回到之前的cnpm,使用下面的命令

    $ npm config set registry https://registry.npm.taobao.org
    

    npm 安装git项目的几种方式

    1. 直接通过用户名安装

    #   直接利用用户名与仓库名进行安装
    $ npm install yiifaa/yii-es6-amd
    #   或者为了提醒自己,加上github前缀进行区分
    $ npm install github:yiifaa/yii-es6-amd1234
    

    2. 通过地址安装

    #   这样适合安装公司内部的git服务器上的项目
    $ npm install git+https://git@github.com/yiifaa/yii-es6-amd.git#v1.0.0
    #   或者以ssh的方式
    $ npm install git+ssh://git@github.com/yiifaa/yii-es6-amd.git#v1.0.0
    

    参考文章

    相关文章

      网友评论

        本文标题:ReactNative封装iOS/Android原生组件

        本文链接:https://www.haomeiwen.com/subject/dzdqmftx.html