RN與原生通訊(安卓篇)

明天和意外你永遠(yuǎn)都不知道哪一個先來,編程界亦是如此。例如某個已經(jīng)有原生代碼開發(fā)模塊的項目要求用RN擴(kuò)張某些功能儿奶;又例如沾凄,RN中未封裝到的組件非得求助于原生代碼梗醇。所以RN與原生代碼通訊對于混合編程是至關(guān)重要的。為了實現(xiàn)兩者之間的通信撒蟀,facebook也提供了三種通信方式叙谨。

看內(nèi)容.jpg
  • RCTDeviceEventEmitter消息機(jī)制:由Native主導(dǎo)控制,可以任意時刻傳遞
  • Callback回調(diào)方式:由js代碼調(diào)用保屯,原生代碼返回手负。
  • Promise機(jī)制方式:由js調(diào)用,只是每次使用都需要調(diào)用配椭。

一虫溜、RN調(diào)用安卓代碼(簡單)

RN調(diào)用安卓原生的代碼,大致分為如下幾步股缸。
1衡楞、用Android Studio打開一個已經(jīng)創(chuàng)建好的RN項目,選擇android/build.gradle文件敦姻。

01.jpg

2瘾境、創(chuàng)建一個類繼承ReactContextBaseJavaModule,在該類中我們應(yīng)該要暴露出一些讓RN調(diào)用的方法,封裝成一個原生模塊镰惦。

  • 新建一個類MyReactPackage繼承ReactContextBaseJavaModule
02.jpg
public class MyNativeModule extends ReactContextBaseJavaModule{
}
  • 實現(xiàn)getName方法迷守。該方法用于返回RN代碼需要尋找的類的名稱。(alt+enter快捷鍵可快速實現(xiàn)父類方法)
 @Override
    public String getName() {
        // 一定要有名字 RN代碼要通過名字來調(diào)用該類的方法
        return "ToastModule";
    }
  • 實現(xiàn)類的構(gòu)造方法旺入。在這里將傳入的上下文賦值給類內(nèi)部私有的上下文
 // 創(chuàng)建一個上下文兑凿,放到構(gòu)造函數(shù)中凯力,得到reactContext
    private ReactApplicationContext mContext;

    public MyNativeModule(ReactApplicationContext reactContext){

        super(reactContext);

        mContext = reactContext;
    }
  • 創(chuàng)建暴露給RN調(diào)用的方法。但是要用注釋符號@ReactMethod修飾礼华。
 //方法不能返回值 因為被調(diào)用的原生代碼是異步的 原生代碼執(zhí)行結(jié)束之后只能通過回調(diào)函數(shù)或者發(fā)送消息給RN
    @ReactMethod
    public void rnCallNative(String msg){
        //這個方法是說彈出一個彈窗到界面Toast.makeText(mContext,msg,Toast.LENGTH_LONG).show();
    }

3咐鹤、在原生代碼中創(chuàng)建一個類實現(xiàn)接口ReactPackage包管理器,并且把第二步已經(jīng)創(chuàng)建好的類加入到原生模塊列表里圣絮。

實現(xiàn)接口ReactPackage的方法祈惶,當(dāng)然我們現(xiàn)在只需要創(chuàng)建模塊,所以在其他的實現(xiàn)先直接返回空集合即可扮匠。而在createNativeModules方法中捧请,要先聲明一個裝有原生模塊的列表。之后將原生模塊加入到列表里棒搜。

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<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }

}

4疹蛉、將創(chuàng)建好的包管理器添加到ReactPackage列表里,也就是MainApplication代碼中

在類里找到方法getPackages方法,將包管理器添加進(jìn)去帮非。

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

5.在RN代碼中用NativeModules組件去調(diào)用原生模塊

  • 導(dǎo)入組件
import {
    AppRegistry,
    StyleSheet,
    Text,
    View,
    NativeModules,
} from 'react-native';
  • 設(shè)置方法調(diào)用原生代碼
 call_button(){
        NativeModules.ToastModule.rnCallNative('RN與安卓開發(fā)');
    }
  • 布置UI
    在render方法里面設(shè)置當(dāng)用戶點擊文字時氧吐,調(diào)用自定義的方法call_button。并且以這種形式創(chuàng)建的方法需要進(jìn)行綁定
render() {
        return(
            <View style={styles.container}>
                <Text  onPress={this.call_button.bind(this)}>測試原生通訊</Text>
            </View>
        );
    }
  • 處理好樣式
const styles = StyleSheet.create({
    container: {
        flex:1,
        backgroundColor:'deeppink',
        flexDirection:'row',
        justifyContent:'center',
        alignItems:'center'    },
});

至此末盔,基本的RN調(diào)用安卓原生代碼的方式就得以實現(xiàn)筑舅。再從RN的角度來回看整個過程。RN調(diào)用原生的方法陨舱,此時安卓的application就會啟動翠拣,完成之后它會去找Package的列表,進(jìn)而找到自己創(chuàng)建的列表游盲。而在組件的列表里面有一個原生模塊列表误墓,到自己的模塊列表里面調(diào)用模塊里的方法就完成了調(diào)用。

效果圖如下:

03.jpg

二益缎、RN用消息機(jī)制方式與安卓原生代碼切換

實現(xiàn)效果:在原生代碼中添加一個按鈕谜慌,當(dāng)用戶從RN界面調(diào)用原生代碼就會進(jìn)入到原生代碼開發(fā)的界面中,而點擊原生代碼中的按鈕就會返回到RN界面莺奔。


樹自行車分割線

接上一節(jié)的代碼欣范。
1、在與MainApplication同級的目錄下創(chuàng)建一個Activity令哟。Activity是android系統(tǒng)最小的調(diào)度單位恼琼。

04.jpg

創(chuàng)建名稱為MyActivity的空活動。它會幫助我們生成一個自動布局文件做布局的工作屏富。(此時若遇到錯誤晴竞,可以選擇build->clean)

05.jpg

2、command+enter點擊進(jìn)入activity_my中狠半,此時會打開布局文件噩死。將左下角的Design切換成Text文件颤难。在該文件中,為原生界面創(chuàng)建一個按鈕并且布局甜滨。


06.jpg

xmlns:android表示設(shè)置xmlns的命名空間乐严,沒有這句話就無法設(shè)置屬性的約束。
在該界面上創(chuàng)建一個按鈕衣摩,為按鈕綁定一個方法onBack。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:layout_width = "match_parent"
    android:layout_height="match_parent"
    xmlns:android = "http://schemas.android.com/apk/res/android"
    >

    <Button
        android:text="goBack"
        android:onClick="onBack"
        android:layout_centerInParent="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        />
</RelativeLayout>

3捂敌、回到新創(chuàng)建的MyActivity代碼艾扮,實現(xiàn)onBack方法。

  //點擊按鈕占婉,直接完成
    public void onBack(View v){
        finish();
    }

4泡嘴、到MyNativeModule原生模塊中去實現(xiàn)Activity

上文中已經(jīng)說過,Activity是android系統(tǒng)的最小調(diào)度單位逆济,而Intent則是安卓的進(jìn)程之間酌予、activity之間、線程之間交換數(shù)據(jù)的載體奖慌。

 //方法不能返回值 因為被調(diào)用的原生代碼是異步的 原生代碼執(zhí)行結(jié)束之后只能通過回調(diào)函數(shù)或者發(fā)送消息給RN
    @ReactMethod
    public void rnCallNative(String msg){

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

        Intent intent = new Intent(mContext,MyActivity.class);  //創(chuàng)建一個意圖抛虫,意圖是android進(jìn)程之間、線程之間简僧、交換數(shù)據(jù)的載體
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);    //一定要加上這句
        mContext.startActivity(intent);
    }

效果圖如下:

07RN與原生切換.gif

三建椰、RN用Promise機(jī)制與安卓原生代碼通信

使用Promise機(jī)制也是RN與原生通信的一種方式。在原生代碼的MyNativeModule文件中創(chuàng)建橋接方法岛马。當(dāng)橋接的原生方法的最后一個參數(shù)是一個Promise對象棉姐,那么該方法會返回一個JS的Promise對象給與之對應(yīng)的js方法。

與上文類似啦逆,需要暴露給RN的方法不能有返回值伞矩,并且要以注釋@ReactMethod標(biāo)識。

  @ReactMethod
    public void rnCallNative_promise(String msg,Promise promise){
        Toast.makeText(mContext,msg,Toast.LENGTH_SHORT).show();
        //得到組件名稱
        String componentName = getCurrentActivity().getComponentName().toString();
        promise.resolve(componentName);
    }

原生代碼已經(jīng)實現(xiàn)夏志。接下來要實現(xiàn)的就是RN的代碼乃坤。在RN中創(chuàng)建一個方法,這個方法內(nèi)部使用NativeModules組件來調(diào)用原生模塊提供的名稱盲镶,進(jìn)而找到要調(diào)用的原生方法侥袜。原生方法最后一個參數(shù)是一個promise,所以在js中用.then的方法實現(xiàn)即可溉贿。

 callAndroid_promise(){
        NativeModules.ToastModule.rnCallNative_promise('promise調(diào)用原生').then(
                (msg) => {
                    console.log('promise收到消息:'+msg);
                }
            ).catch(
                (err)=>{
                    console.log(err);
                }
            )
    }

到渲染方法中枫吧,調(diào)用方法

 <Text style={styles.welcome} onPress={this.callAndroid_promise.bind(this)}>Promise通信</Text>

給Text組件設(shè)置樣式

 welcome: {
        fontSize: 16,
        textAlign: 'left',
        margin: 10
    }

結(jié)果圖如下:

08.jpg

用Debug進(jìn)行調(diào)試,得到結(jié)果如下:

09.jpg

四宇色、RN用callback回調(diào)方式與安卓原生代碼通信

按照上文中提到的方式九杂,在原生模塊中暴露一個橋接方法給RN調(diào)用颁湖。
參數(shù)傳入一個成功的回調(diào)和一個失敗的回調(diào)。

@ReactMethod
    public void measureLayout(Callback errorCallback,Callback successCallback){
        try {
            successCallback.invoke(100,100,200,200); //調(diào)用回調(diào)函數(shù)例隆,返回結(jié)果
        }catch (IllegalViewOperationException e){
            errorCallback.invoke(e.getMessage());
        }
    }

在js中實現(xiàn)回調(diào)方法甥捺。同樣是通過NativeModules組件尋找到橋接名稱ToastModule,進(jìn)而找到想要調(diào)用的方法镀层。拿到返回的參數(shù)镰禾,做功能處理。

 callAndroid_callback(){
        NativeModules.ToastModule.measureLayout(
            (msg)=>{
                console.log(msg);
            },
            (x,y,width,height)=>{
                console.log('x坐標(biāo):'+x+'y坐標(biāo):'+y+'高:'+height+'寬'+width);
            }
        )
    }
10.jpg

在Debug調(diào)試下唱逢,點擊callback通信文字吴侦,可以看到如下結(jié)果:

11.jpg

在使用回調(diào)函數(shù)時會呈現(xiàn)出某些缺點,比如說每次調(diào)用只應(yīng)當(dāng)調(diào)用一次坞古,多次調(diào)用可能會出現(xiàn)意想不到的結(jié)果备韧,并且用這種方法安卓原生代碼是無法主動發(fā)送信息給RN側(cè)的。而消息機(jī)制的方式就可以進(jìn)行消息的互相傳遞痪枫。


小孩與狗童真分隔線
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末织堂,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子奶陈,更是在濱河造成了極大的恐慌易阳,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件尿瞭,死亡現(xiàn)場離奇詭異闽烙,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)声搁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評論 2 384
  • 文/潘曉璐 我一進(jìn)店門黑竞,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人疏旨,你說我怎么就攤上這事很魂。” “怎么了檐涝?”我有些...
    開封第一講書人閱讀 156,674評論 0 345
  • 文/不壞的土叔 我叫張陵遏匆,是天一觀的道長。 經(jīng)常有香客問我谁榜,道長幅聘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評論 1 283
  • 正文 為了忘掉前任窃植,我火速辦了婚禮帝蒿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巷怜。我一直安慰自己葛超,他們只是感情好暴氏,可當(dāng)我...
    茶點故事閱讀 65,404評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著绣张,像睡著了一般答渔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上侥涵,一...
    開封第一講書人閱讀 49,749評論 1 289
  • 那天沼撕,我揣著相機(jī)與錄音,去河邊找鬼独令。 笑死端朵,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的燃箭。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼舍败,長吁一口氣:“原來是場噩夢啊……” “哼招狸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起邻薯,我...
    開封第一講書人閱讀 37,662評論 0 266
  • 序言:老撾萬榮一對情侶失蹤裙戏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后厕诡,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體累榜,經(jīng)...
    沈念sama閱讀 44,110評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年灵嫌,在試婚紗的時候發(fā)現(xiàn)自己被綠了壹罚。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,577評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡寿羞,死狀恐怖猖凛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情绪穆,我是刑警寧澤辨泳,帶...
    沈念sama閱讀 34,258評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站玖院,受9級特大地震影響菠红,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜难菌,卻給世界環(huán)境...
    茶點故事閱讀 39,848評論 3 312
  • 文/蒙蒙 一试溯、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧扔傅,春花似錦耍共、人聲如沸烫饼。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽杠纵。三九已至,卻和暖如春钩骇,著一層夾襖步出監(jiān)牢的瞬間比藻,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評論 1 264
  • 我被黑心中介騙來泰國打工倘屹, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留银亲,地道東北人。 一個月前我還...
    沈念sama閱讀 46,271評論 2 360
  • 正文 我出身青樓纽匙,卻偏偏與公主長得像务蝠,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子烛缔,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,452評論 2 348

推薦閱讀更多精彩內(nèi)容