React-Native開發之原生模塊封裝(Android)升級版

 

 本文主題:如何實現原生代碼的複用,即如何將原生模塊封裝。javascript

(尊重勞動成果,轉載請註明出處:http://blog.csdn.net/qq_25827845/article/details/52862892前端

 

       有時候咱們的應用須要進行訪問原平生臺系統的API接口,可是React Native可能尚未封裝相應功能組件。還有可能咱們須要

去複用一些原生java代碼而不是讓JavaScript從新去實現一遍。或者咱們可能須要些一些更加高級的功能代碼,所線程相關的。例如:

圖片處理,數據庫以及一些高級功能擴展之類的。


       React Native平臺的開發其實自己也是可讓你寫純原生代碼而且還可讓你訪問原平生臺的功能。這是一個比較高級的功能不

過官方仍是不推薦你在平時開發中使用這樣的開發形式。可是若是你具有這樣的開發能力,也是仍是不錯的。特別當在React Native

暫時未提供部分原生功能或者模塊,那麼你能夠使用這樣的方法進行封裝擴展。今天咱們就來看一下原生組件的封裝擴展方法。
 java

      整體來講,就是當咱們須要複用部分原生代碼時,好比複用一個原生方法,此時就須要將原生方法進行封裝,只暴露出一個接口react

讓React-Native調用。本博客以一個Toast消息來做用案例來說解如何封裝原生模塊。android

 

步驟:git

  • 用Android Studio打開一個已經存在的RN項目,即用AS打開 項目文件夾/android/build.gradle文件。

 

  • 在Android原生這邊建立一個類繼承ReactContextBaseJavaModule,這個類裏邊放咱們須要被RN調用的方法,將其封裝成一個原生模塊。

 

  • 在Android原生這邊建立一個類實現接口ReactPackage包管理器,並把第二步建立的類加到原生模塊(NativeModule)列表裏。

 

  • 將第三步建立的包管理器添加到ReactPackage列表裏(getPackage方法裏)

 

  • 在RN中去調用原生模塊,必須import    NativeModule模塊。

 

 

        首先你們確定已經安裝好了Android Studio,打開build.gradle文件以後,會發現其實連android/app文件夾也一併打開了。其中java文件夾中存放原生代碼,也就是將咱們要複用的原生代碼放進來。你們能夠打開  項目文件夾/android/app自行查看各級目錄。在默認的包下,建立上邊第二步和第三步所需的類。截圖以下:github

 

 

 

 

MyNativeModule.java代碼以下:shell

package com.reactnative;

import android.widget.Toast;
import android.content.Context;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

/**
 * Created by Administrator on 2016/10/18.
 */

public class MyNativeModule extends ReactContextBaseJavaModule {


  private Context mContext;

  public MyNativeModule(ReactApplicationContext reactContext) {
    super(reactContext);

    mContext = reactContext;
  }

  @Override
  public String getName() {

    //返回的這個名字是必須的,在rn代碼中須要這個名字來調用該類的方法。
    return "MyNativeModule";
  }


  //函數不能有返回值,由於被調用的原生代碼是異步的,原生代碼執行結束以後只能經過回調函數或者發送信息給rn那邊。


  @ReactMethod
  public void rnCallNative(String msg){

    Toast.makeText(mContext,msg,Toast.LENGTH_SHORT).show();

  }



}


      本類中存放咱們要複用的原生方法,繼承了ReactContextBaseJavaModule類,而且實現了其getName()方法,構造方法也是必須的。按着Alt+Enter程序會自動提示。接着定義了一個方法,該方法必須使用註解@ReactMethod標明,說明是RN要調用的方法。數據庫

 

MyReactPackage.java代碼以下:react-native

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 Administrator on 2016/10/18.
 */

public class MyReactPackage implements ReactPackage {

  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

    List<NativeModule> modules=new ArrayList<>();
   //將咱們建立的類添加進原生模塊列表中
    modules.add(new MyNativeModule(reactContext));
      return modules;
  }


  @Override
  public List<Class<? extends JavaScriptModule>> createJSModules() {

    //返回值須要修改
    return Collections.emptyList();
  }


  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {

    //返回值須要修改
    return Collections.emptyList();
  }
}


 

MainApplication.java代碼以下:

package com.reactnative;

import android.app.Application;
import android.util.Log;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;

import java.util.Arrays;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    protected boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
          //將咱們建立的包管理器給添加進來
            new MyReactPackage()
      );
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
      return mReactNativeHost;
  }
}


MainActivity.java代碼以下:

package com.reactnative;

import com.facebook.react.ReactActivity;

public class MainActivity extends ReactActivity {

    /**
     * Returns the name of the main component registered from JavaScript.
     * This is used to schedule rendering of the component.
     */
    @Override
    protected String getMainComponentName() {
        return "reactNative";
    }
}


 

 

接着咱們須要編寫index.android.js文件

代碼以下:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  NativeModules,
  View
} from 'react-native';

class reactNative extends Component {
  render() {
    return (
      <View style={styles.container}>

        <Text style={styles.welcome}
        onPress={this.call_button.bind(this)}
        >     
          React Native 調用原生方法!
        </Text>

        <Text style={styles.instructions}>
          To get started, edit index.android.js
        </Text>

        <Text style={styles.instructions}>
          Double tap R on your keyboard to reload,{'\n'}
          Shake or press menu button for dev menu
        </Text>

      </View>
    );
  }
   
   call_button(){

   	 NativeModules.MyNativeModule.rnCallNative('調用原生方法的Demo'); 
   }

}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

AppRegistry.registerComponent('reactNative', () => reactNative);



       咱們將一個文本框綁定了一個事件,首先import導入NativeModule,當點擊文本時將會調用call_button()方法。而後傳入一個上下文對象,也就是一句話,經過調用原生方法rnCallNative( )方法來實現。


 

來分析一下程序運行流程:

(1)在配置文件AndroidManifest.xml中,android:name=".MainApplication",則MainApplication.java會執行。

(2)在MainApplication.java中,有咱們建立的包管理器對象。程序加入MyReactPackage.java中。

(3)在MyReactPackage.java中,將咱們本身建立的模塊加入了原生模塊列表中,程序進入MyNativeModule.java中。

(4)在MyNativeModule.java中,有咱們須要被複用的原生方法rnCallNative( )。

 

 

程序運行結果以下所示,當點擊第一行文本時,出現Toast消息。

 

 

 

封裝原生方法升級篇:


(1)如何封裝複雜方法,實現更多的功能?

       在上文中,咱們封裝了一個簡單的方法—彈出Toast 提醒框。可是你們看到可能很鬱悶,內心想,我TM 要封裝的方法也不會這麼簡單呀,能不能封裝點複雜的方法,用來實現更多的功能?(畢竟是咱們想要複用的方法,確定實現了不少比較牛逼的功能。)

答:能!!!


如何實現呢?以上面所述項目爲例講解。

  • 首先,將原生的java 文件複製到RN項目中存放原生代碼的位置,以下圖所示。
  • 而後,在MyNativeMoudle.java中寫一個能夠被RN調用的方法,以註解@ReactMethod代表。
  • 其次,在上一步所述方法內部能夠任意調用原生方法,實現更加複雜的功能。
  • 最後,在RN中調用第二步所寫的方法。

 

Demo以下:

ManiActivity.java

package com.firstproject;

import com.facebook.react.ReactActivity;

public class MainActivity extends ReactActivity {

    /**
     * Returns the name of the main component registered from JavaScript.
     * This is used to schedule rendering of the component.
     */
    @Override
    protected String getMainComponentName() {
        return "FirstProject";
    }
}

 

ManiApplication.java

package com.firstproject;

import android.app.Application;
import android.util.Log;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;

import java.util.Arrays;
import java.util.List;

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
    @Override
    protected boolean getUseDeveloperSupport() {
      return BuildConfig.DEBUG;
    }

    @Override
    protected List<ReactPackage> getPackages() {
      return Arrays.<ReactPackage>asList(
          new MainReactPackage(),
            new MyPackage()
      );
    }
  };

  @Override
  public ReactNativeHost getReactNativeHost() {
      return mReactNativeHost;
  }
}


MyModule.java

package com.firstproject;

import android.content.Context;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;

/**
 * Created by Administrator on 2016/10/30.
 */

public class MyModule extends ReactContextBaseJavaModule {

  //創建上下文對象
  public Context myContext;

  public MyModule(ReactApplicationContext reactContext) {

    super(reactContext);

    myContext=reactContext;
  }

  @Override
  public String getName() {

    return "MyModule";
  }

  @ReactMethod
  public void showTime()
  {
    new Test().getTime(myContext);
  }




}


MyPackage.java

package com.firstproject;

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 Administrator on 2016/10/30.
 */

public class MyPackage implements ReactPackage {
  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {

    List<NativeModule> modules=new ArrayList<>();
    modules.add(new MyModule(reactContext));

    return modules;
  }

  @Override
  public List<Class<? extends JavaScriptModule>> createJSModules() {
    return Collections.emptyList();
  }

  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }
}


Test.java做爲咱們要複用的原生類,裏邊有要複用的原生方法。

package com.firstproject;

import android.content.Context;
import android.util.Log;
import android.widget.Toast;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * Created by Administrator on 2016/10/30.
 */

public class Test {


     public void getTime(Context ctx)
     {
       SimpleDateFormat formatDate=new SimpleDateFormat("yyyy年MM月dd日  HH:mm:ss");
       Date date=new Date(System.currentTimeMillis());   //獲取當前時間
       String s=formatDate.format(date);

         Log.e("HHH",s);
       Toast.makeText(ctx,s,Toast.LENGTH_SHORT).show();

     }

}


index.android.js以下:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  NativeModules,
  View
} from 'react-native';

export default class FirstProject extends Component {
    render() {
    return (
      <View style={styles.container}>

        <Text style={styles.welcome}
        //給此處的文字綁定一個事件,其中callNative爲要調用的方法名。
        onPress={this.callNative.bind(this)}
        >
          點擊此處文字調用原生方法!
        </Text>

        <Text style={styles.instructions}>
          此Demo演示如何調用Android原生中的複雜方法。
        </Text>

      </View>
    );
  }

   callNative()
   {
     NativeModules.MyModule.showTime();
   }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

AppRegistry.registerComponent('FirstProject', () => FirstProject);


程序運行結果以下:


         在上邊的Demo中,主要是原生類Test.java。咱們在要被RN調用的方法中調用原生類中的方法。和最開始的案

不一樣的是,這裏說明了,方法能夠調用,咱們N多原生類均可以直接粘貼複製過來。這樣就能夠實現調用複雜方

實現強大功能了。

 

 

注意:當Android原生中涉及到權限的使用時,記得在AndroidManifest.xml中添加相應權限,以下圖所示。




(2)如何實現數據從Android 原生回調到RN前端界面?

       咱們都知道,要被RN調用的方法必須是void 類型,即沒有返回值,可是項目中不少地方都須要返回數據。那怎

麼實現呢?

       如圖所示:咱們定義一個方法,使用Callback, 在這個方法中,創建而且開啓一個線程,

使用callback. invoke( XXXX)實現數據向RN前端的傳遞。

       其中,MyMainActivity.java文件爲咱們的一個原生類,按照上面的要求複製到RN項目中存放原生代碼的地

方。而ReceiveData爲這個類的一個變量。


如何在RN項目中調用?


代碼以下:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 * @flow
 */

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  NativeModules,
  View,
  TouchableOpacity
} from 'react-native';

export default class lh extends Component {
	constructor(props) {
    super(props);
    this.state = {
        global: '這個是預約的接受信息',
    }
}
  render() {
  
  	var Globle="null";
    return (
 
      <View style={styles.container}>
    
        <TouchableOpacity style={styles.button1}
        onPress={this.call_button_show.bind(this)}>
        <Text style={styles.welcome}
        >
        顯示信息
        </Text>
        </TouchableOpacity>

      <Text style={styles.welcome} >
        {this.state.global}
      </Text>
     
     
      </View>
    );
  }
 
 
 call_button_show(){
    Globle="null";

 	NativeModules.MyModule.getResult((result)=>{this.setState({global:result,});});
 }
 
 
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
    button1:{
    height: 40,
    width: 100,
    marginTop:1,
    backgroundColor:'gray',
    },
  welcome: {
    fontSize: 20,
    textAlign: 'center',
    margin: 10,
  },
  instructions: {
    textAlign: 'center',
    color: '#333333',
    marginBottom: 5,
  },
});

AppRegistry.registerComponent('lh', () => lh);


       當咱們點擊按鈕時,將會調用getResult方法,而且將ReceiveData變量的值傳遞給RN前端的result變量中,利用

setState來實現界面的更新。

 

        至此,咱們實現了RN複用原生代碼,即將原生模塊封裝成一個接口,在RN中調用。而且能夠封裝更加複雜

的方法,同時實現了數據回調,即將數據從原生模塊中傳遞到RN前端。

 

程序源代碼1下載,請見GitHub:https://github.com/chaohuangtianjie994/ReactNative-call-NativeMethod 

Demo2的改動不大,你們能夠自行改動哦。

 若是對你有幫助,記得點贊哦大笑

相關文章
相關標籤/搜索