ReactNative封裝iOS/Android原生組件

簡介

為了提高代碼的復(fù)用,減少重復(fù)工作,我們經(jīng)常會把比較獨立的功能模塊封裝成組件或者library

在iOS我們通常封裝為靜態(tài)庫(.framework和.a),

在Android中我們通常封裝為Android Library(gradle模塊),

在JavaScript中我們通常封裝成node package,

而在ReactNative中我們要封裝與原始有關(guān)的庫我們需要,結(jié)合iOS靜態(tài)庫(.a)、Android Library和node package一起封裝成ReactNative用的node package。

目錄結(jié)構(gòu)

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包的配置文件

源碼地址

制作

裝備工作

創(chuàng)建ReactNative工程

我們需要先創(chuàng)建一個ReactNative工程,使用如下命令創(chuàng)建。

$ react native init A7011CreatNativeModule

創(chuàng)建存放封裝庫的目錄

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

封裝iOS庫

步驟

1. 靜態(tài)庫創(chuàng)建

由于ReactNative的組件都是一個個靜態(tài)庫,我們發(fā)布到npm給別人使用的話,也需要建立靜態(tài)庫。我們使用xcode建立靜態(tài)庫,取名為Testmodule。建立之后,我們將創(chuàng)建的靜態(tài)庫中的文件全部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. 引入到目標(biāo)工程

使用xcode打開A7011CreatNativeModule/ios/下的iOS工程,將BGNativeModuleExample靜態(tài)庫工程拖動到工程中的Library中。

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

    因為,不拖到RN工程中去的話,用的RN庫中的API,就會報錯,所以需要RN工程的基礎(chǔ)環(huán)境。

  2. 為什么這里要先把靜態(tài)庫放到node_modules/Testmodule/ios,再拖到RN工程中去呢?

    那是因為,如果直接把創(chuàng)建的靜態(tài)庫的TestModule.xcodeproj文件拖到工程中去,無法編輯這個靜態(tài)庫,只有在node_modules/Testmodule/ios才能編輯。目前還不知道原因

3. 編寫原生代碼

咱們這里主要是演示做封裝RN的node package,所以就封裝一個最簡單的組件,一個背景色為紅色的View。

  1. 創(chuàng)建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. 創(chuàng)建橋接類

    // 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. 需要更復(fù)雜的交互,可以參考React Native官網(wǎng)的iOS原生模塊原生UI組件

4. React組件創(chuàng)建
  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創(chuàng)建index.js文件,導(dǎo)出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封裝靜態(tài)庫的時候,經(jīng)常會用到需要引入的系統(tǒng)庫和第三方庫;系統(tǒng)庫引入很簡單,只要在TestModule.xcodeproj->Build Phases->Link Bnary with Libraries添加系統(tǒng)庫就行;而第三方庫有可能是.a也有可能是.framework,.a拷貝到靜態(tài)庫根目錄里面,接下就和系統(tǒng)庫添加一樣了,而.framework就復(fù)雜了,他不能直接引用到封裝的靜態(tài)庫中,需要引用到目標(biāo)iOS工程中去才可以。

橋接ViewController

封裝Android庫

步驟

1. 創(chuàng)建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依賴我們新創(chuàng)建的Library。
  • 我們還需要讓新創(chuàng)建的Library依賴react native,和上面差不多,只需要在我們新創(chuàng)建的testmodule-wyh下的build.gradle中的dependencies添加一行compile "com.facebook.react:react-native:+"就行了。
3. 編寫原生代碼

Android原生代碼的編寫,前兩步和iOS步驟類似,多了一個創(chuàng)建ReactPackage實現(xiàn),并在MainApplication的getPackages方法里面注冊。

  1. 創(chuàng)建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. 創(chuàng)建橋接類

    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. 需要創(chuàng)建一個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. 需要更復(fù)雜的交互,可以參考React Native官網(wǎng)的android原生模塊原生UI組件

4. 組件創(chuàng)建使用

之后的步驟在封裝iOS庫的4、5、6步已經(jīng)做了

5. 運行效果

直接在點擊run,看結(jié)果


image

注意

Gradle問題
  1. com.android.tools.build:gradle工具也有2升級為3以后,dependencie依賴由compile變?yōu)閕mplementation,而用React Native init ProjectName創(chuàng)建的rn項目還是用的compile,所以創(chuàng)建的時候要調(diào)整Gradle.build的dependencie和minSdkVersion以適用目標(biāo)項目。
  2. 一定要記得創(chuàng)建ReactPackage的接口實現(xiàn)類,這樣當(dāng)執(zhí)行react-native link testmodule-wyh才會自動添加到依賴。

整理成node package

  1. 將封裝的Android library里面的所以文件移到node_modules/Testmodule/android里面,形成的目錄結(jié)構(gòu)如下:

    .
    ├── 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創(chuàng)建一個package.json文件,全部使用默認(rèn)項就行,這樣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有關(guān)的代碼全部刪除

  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項目根目錄執(zhí)行如下指令

    $ 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. 默認(rèn)iOS和Android的library都要在根目錄,如果不在根目錄需要在packag.json指明如下

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

上傳代碼庫

GitHub,創(chuàng)建一個代碼倉庫,將node package 代碼上傳,然后在package.json添加repository如下:

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

repository 屬性不寫也可以,但是最好建一個 github 項目然后把地址寫進來,方便以后維護。

發(fā)布

注冊npm賬號

發(fā)布node package到npm服務(wù)器上,需要npm的賬號,注冊哪怕npm賬號有兩種形式

  • npm官網(wǎng)注冊

  • 使用命令行注冊

    $ npm adduser
    Username: XXX # 賬戶名
    Password: *** # 密碼
    Email: (this IS public) XXX@XXX.com # 郵箱
    

如果用命令行注冊的話,最好是在npm官網(wǎng)驗證一下

登錄npm賬戶

首次需要登錄,npm login(需要輸入用戶名,密碼,還有郵箱) 存儲證書到本地,后面就不需要每次都登錄的

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

開始發(fā)布

$ npm publish
+ testmodule-wyh@1.0.2

發(fā)布時報錯

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

出現(xiàn)原因:使用的是淘寶源cnpm,登陸到的是cnpm

解決方法:切換到npmjs的網(wǎng)址,代碼如下:

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

發(fā)布完成之后,如果還想回到之前的cnpm,使用下面的命令

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

npm 安裝git項目的幾種方式

1. 直接通過用戶名安裝

#   直接利用用戶名與倉庫名進行安裝
$ npm install yiifaa/yii-es6-amd
#   或者為了提醒自己,加上github前綴進行區(qū)分
$ npm install github:yiifaa/yii-es6-amd1234

2. 通過地址安裝

#   這樣適合安裝公司內(nèi)部的git服務(wù)器上的項目
$ 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

參考文章

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,119評論 25 708
  • 道路蜿蜒 前程似錦 一個星期以來,簡素莫名的給自己增加了許多壓力,焦慮感十足。主要是來源于向左而生你007-49...
    簡素七年閱讀 383評論 0 1

友情鏈接更多精彩內(nèi)容