通過高德地圖學(xué)習(xí)RN與Android原生的混合開發(fā)

在原生的開發(fā)中使用高德或者百度地圖實現(xiàn)定位等相關(guān)功能是每一個小伙伴必備的技能粘衬,那么現(xiàn)在我要在RN中實現(xiàn)定位的功能該怎么做呢项玛?一看到需要實現(xiàn)這樣的功能貌笨,第一反應(yīng)就是打開高德開放平臺看看是否有相關(guān)的SDK,可是翻來翻去也只看到了Android原生的實現(xiàn)方式稍计。但是躁绸,RN可以實現(xiàn)與原生的混合開發(fā),那么是不是可以嘗試下在RN頁面調(diào)用原生里面的定位功能呢臣嚣?

嘗試開始(內(nèi)心還是有點小激動...):

1.在Android工程中完成相關(guān)配置

此步驟跟原生開發(fā)沒有任何的區(qū)別净刮,可以參照高德開放平臺,此處不再介紹硅则。

<meta-data 
     android:name="com.amap.api.v2.apikey"                          
     android:value="you key" />

2.創(chuàng)建原生模塊

原生模塊是一個繼承于ReactContextBaseJavaModule的Java類

創(chuàng)建一個繼承ReactContextBaseJavaModule的Java類淹父,并在此類中實現(xiàn)定位功能,先上代碼:


public class AMapLocationModule extends ReactContextBaseJavaModule {    
      private ReactApplicationContext mReactApplicationContext;       
      private AMapLocationClient mLocationClient = null;    
      // 定位回調(diào)監(jiān)聽器   
      private AMapLocationListener mAMapLocationListener = new AMapLocationListener() {     
          @Override     
          public void onLocationChanged(AMapLocation aMapLocation) { 
               // 實例化一個回調(diào)給RN端的map對象
               WritableMap params = Arguments.createMap();           
               if (aMapLocation != null) {               
                   if (aMapLocation.getErrorCode() == 0) {                    
                        // 定位成功                                  
                        params.putString("address", aMapLocation.getAddress());               
                    } else {                    
                        // 定位失敗                                      
                        params.putBoolean("result", false);                
                    }          
               }           
               // 發(fā)送給RN端  
               sendEvent("onLocationChanged", params);       
            }    
       };    
       private void sendEvent(String eventName, @Nullable WritableMap params) {        
              if (mReactApplicationContext != null) {         
                    mReactApplicationContext                       
                            .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)                   
                            .emit(eventName, params);        
              }    
        }    
        public AMapLocationModule(ReactApplicationContext reactContext) {        
               super(reactContext);        
               // 在構(gòu)造函數(shù)中實例化定位功能的相關(guān)對象怎虫,并設(shè)置相關(guān)的定位配置暑认,跟原生實現(xiàn)定位功能一樣配置
               mReactApplicationContext = reactContext;        
               mLocationClient = new AMapLocationClient(reactContext);    
               mLocationClient.setLocationListener(mAMapLocationListener);    
               AMapLocationClientOption clientOption = new AMapLocationClientOption();        
               clientOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Battery_Saving); // 低功耗模式, 只使用wifi        
               clientOption.setOnceLocation(true);        
               mLocationClient.setLocationOption(clientOption);    
        }   
        @Override    
        public String getName() {       
             return "AMapLocation";   //此Name在RN端標(biāo)記這個模塊,不能與已有的Name發(fā)生沖突
        }    
        @ReactMethod    //  可以被RN端調(diào)用的方法
        public void destory() {       
               if (mLocationClient != null) {          
                    mLocationClient.stopLocation(); // 停止定位   
                    mLocationClient.onDestroy(); // 銷毀定位      
               }    
         }    
         @ReactMethod   // 可以被RN端掉用的方法大审,開啟定位
         public void startLocation() {   
               if (mLocationClient != null) {   
                   mLocationClient.startLocation();       
               }   
         }
}

在上述的代碼中蘸际,必須注意以下幾個點:

  • getName()方法
    這是ReactContextBaseJavaModule的派生類必須實現(xiàn)的方法。只要返回一個字符串名稱徒扶,在RN端才能找到這個模塊粮彤,需要防止命名沖突。還有一點姜骡,如果名稱中有RCT作為前綴导坟,會被自動忽略移除的,比如RCTAMapLocation圈澈,那么在RN端通過React.NativeModules.AMapLocation就能訪問這個模塊了惫周。
  • ReactMethod注解
    只有使用了此注解的public void方法才能被RN端訪問,RN端可以通過此方法的參數(shù)向原生端傳遞數(shù)據(jù)康栈。上面代碼中递递,RN端能夠訪問startLocation()方法以及destory()方法喷橙。還有一點需要注意的是,RN端訪問原生是異步進(jìn)行的漾狼。
  • sendEvent ()方法
    只要原生中的方法通過ReactMethod注解重慢,RN端就能夠訪問得到,那么我們要在RN端啟動定位服務(wù)這個功能應(yīng)該已經(jīng)得到了實現(xiàn)逊躁,那么在RN端如何監(jiān)聽定位的結(jié)果呢似踱?如何將定位的結(jié)果傳遞給RN端呢?其實通過sendEvent()方法就能夠得到實現(xiàn)稽煤。
    在這肯定會有一個疑問核芽,為什么僅僅通過這么一個方法就能夠讓RN端監(jiān)聽并接收原生端的數(shù)據(jù)呢?
    其實實現(xiàn)這個功能還是需要借助RCTDeviceEventEmitter這個玩意的酵熙。通過代碼中的sendEvent()方法可以看到轧简,通過reactContext來獲得RCTDeviceEventEmitter的引用,然后調(diào)用RCTDeviceEventEmitter中的emit()方法匾二。這樣在RN端就可以添加監(jiān)聽回調(diào)事件哮独。
  • WritableMap()方法
    顧名思義這是一個可寫的map類,通過它察藐,我們可以通過鍵值對的方式將需要發(fā)送給RN端的信息封裝起來皮璧,并通過emit ()方法發(fā)送出去。其實還有一個ReadableMap用來存放RN傳遞給原生的數(shù)據(jù)分飞。

3. 注冊模塊

在第二個步驟中悴务,我們將可以被RN調(diào)用的原生模塊給創(chuàng)建好了,為了讓RN端可以識別此模塊譬猫,就少不了注冊這一個步驟讯檐。
注冊模塊相對簡單一點,創(chuàng)建一個實現(xiàn)ReactPackage接口的Java類染服,實現(xiàn)相應(yīng)的方法就OK了别洪,下面看下代碼:

public class AMapLocationReactPackage implements ReactPackage {    
      @Override    
      public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) { 
             List<NativeModule> list = new ArrayList<>();       
             list.add(new AMapLocationModule(reactContext));        
             return list;   
      }    
      @Override   
      public List<Class<? extends JavaScriptModule>> createJSModules() {       
             return Collections.emptyList();   
      }   
      @Override    
      public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {  
             return Collections.emptyList();  
      }
}

最后需要記住在Application里面ReactNativeHostgetPackages()方法里面添加上AMapLocationReactPackage實例。

AMapLocationReactPackage這個類中柳刮,需要實現(xiàn)三個方法:

  • createNativeModules()方法
    此方法是向ReactPackage中添加可以被RN端調(diào)用的原生模塊挖垛。在第二個步驟中創(chuàng)建AMapLocationModule模塊就是為了讓RN端調(diào)用的,所以需要在此方法進(jìn)行注冊诚亚。
  • createJSModules ()方法
    此方法是添加可以讓原生調(diào)用的Js模塊,這邊沒有此需求午乓。所以就傳遞個空集合給ReactPackage站宗。
  • createViewManagers() 方法
    此方法是添加可以讓RN端調(diào)用的原生View組件,比如項目中原本就有一個寫的很nice的原生自定義View益愈,不想在RN端重新去寫梢灭,就可以通過實現(xiàn)一個ViewManager派生類夷家,并通過createViewManagers注冊,就能在RN進(jìn)行調(diào)用敏释。

到這Android原生端的代碼就告一段落了库快,來個中場休息吧(啦啦隊該上場...),接下來實現(xiàn)下RN端的代碼就能夠知道我這個嘗試是否能夠成功了钥顽。

4.在RN端獲取模塊

還記得在創(chuàng)建模塊的時候講過义屏,getName()方法返回了一個字符串,那么在RN端通過此字符串就能獲得相對應(yīng)的模塊的引用了蜂大。所以此例子中闽铐,在RN端通過NativeModules.AMapLocation就能獲取剛剛創(chuàng)建的模塊實例,這意味著現(xiàn)在已經(jīng)可以調(diào)用模塊中通過ReactMethod注解的方法了奶浦,為了更好的使用兄墅,我做了個簡單的封裝。

import {NativeModules, DeviceEventEmitter} from 'react-native';
const location = NativeModules.AMapLocation;
export default class AMapLocation {    
       // 開啟定位
       static startLocation() {        
            location.startLocation();    
       }    
       // 關(guān)閉定位  
       static destory() {        
            location.destory();    
       }
       // 注冊定位回調(diào)監(jiān)聽   
       static addEventListener(handler) {        
            const listener = DeviceEventEmitter.addListener(      
                            'onLocationChanged',   // 與模塊中的eventName保持一致  
                             handler       // 回調(diào)函數(shù)澳叉,在此函數(shù)接收定位信息
                             );        
            return listener;    
       }
}
  • addEventListener()方法
    這是一個注冊回調(diào)監(jiān)聽方法隙咸,也就是為了接收從原生端傳遞過來的定位信息。在原生端通過RCTDeviceEventEmitteremit()方法來發(fā)送事件的成洗,RN端同樣是通過 DeviceEventEmitter (模塊名的RCT前綴會被自動移除)來添加監(jiān)聽事件五督。需要傳遞一個與原生相同的eventName字符串以及一個回調(diào)方法。

5.在RN端開啟定位服務(wù)并接收定位數(shù)據(jù)

接下來就在RN端開啟個定位服務(wù)泌枪,看看能不能成功開啟并且接受到當(dāng)前的地址信息概荷。


import AMapLocation from '../modules/AMapLocation';

componentDidMount() {    
      this.listener = AMapLocation.addEventListener(this._onLocationChanged);    // 注冊監(jiān)聽
      AMapLocation.startLocation(); // 開啟定位
}
componentWillUnmount() {    
      AMapLocation.destory();    
      this.listener.clear();
}
_onLocationChanged = (data)=> {   
     if (data && data.result) {        
           console.log(data.address);        
     }
};

很常規(guī)的做法,在componentDidMount ()方法中注冊監(jiān)聽并開啟定位碌燕;在componentWillUnmount()方法中關(guān)閉定位误证,清除監(jiān)聽事件;
_onLocationChanged()方法中接收數(shù)據(jù)修壕。

6.驗證結(jié)果(雞凍ing...)

歷經(jīng)千辛萬苦終于到了這一步了愈捅,那么就來看下log吧。

log.png

地址信息從logo中顯示出來了慈鸠,也就意味著這次嘗試成功了蓝谨!

6.總結(jié)

通過此次嘗試差不多也就學(xué)會了RN與原生的一種混合開發(fā)方式,那就來讓我們回憶一下混合開發(fā)大致的步驟吧青团。

  • 在Android端繼承ReactContextBaseJavaModule創(chuàng)建一個原生模塊譬巫,并在原生模塊中通過ReactMethod注解暴露出可以讓RN端調(diào)用的方法,并在getName() 方法中返回一個模塊名稱字符串督笆;
  • 在模塊中通過RCTDeviceEventEmitteremit() 方法實現(xiàn)向RN端發(fā)送事件芦昔;
  • 通過實現(xiàn)ReactPackage接口注冊模塊,需要區(qū)別三個createXXX()方法對應(yīng)的使用方式娃肿,同時不要忘記在Application中添加package實例咕缎;
  • 在RN端可以利用對應(yīng)的模塊名稱調(diào)用暴露的方法珠十,同時可以通過DeviceEventEmitter注冊回調(diào)的監(jiān)聽事件;
  • 需要記住RN中調(diào)用原生的方法都是異步進(jìn)行的凭豪;
  • 混合開發(fā)中還可以通過Promises來實現(xiàn)回調(diào)函數(shù)焙蹭。

通過原生向RN發(fā)送事件的方法實現(xiàn)了高德地圖的定位功能,這是一次成功的嘗試嫂伞,但這才邁出了第一步孔厉。。末早。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末烟馅,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子然磷,更是在濱河造成了極大的恐慌郑趁,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件姿搜,死亡現(xiàn)場離奇詭異寡润,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)舅柜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評論 3 392
  • 文/潘曉璐 我一進(jìn)店門梭纹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人致份,你說我怎么就攤上這事变抽。” “怎么了氮块?”我有些...
    開封第一講書人閱讀 162,577評論 0 353
  • 文/不壞的土叔 我叫張陵绍载,是天一觀的道長。 經(jīng)常有香客問我滔蝉,道長击儡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評論 1 292
  • 正文 為了忘掉前任蝠引,我火速辦了婚禮阳谍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘螃概。我一直安慰自己矫夯,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,189評論 6 388
  • 文/花漫 我一把揭開白布吊洼。 她就那樣靜靜地躺著训貌,像睡著了一般。 火紅的嫁衣襯著肌膚如雪融蹂。 梳的紋絲不亂的頭發(fā)上旺订,一...
    開封第一講書人閱讀 51,155評論 1 299
  • 那天,我揣著相機(jī)與錄音超燃,去河邊找鬼区拳。 笑死,一個胖子當(dāng)著我的面吹牛意乓,可吹牛的內(nèi)容都是我干的樱调。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼届良,長吁一口氣:“原來是場噩夢啊……” “哼笆凌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起士葫,我...
    開封第一講書人閱讀 38,903評論 0 274
  • 序言:老撾萬榮一對情侶失蹤乞而,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后慢显,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體爪模,經(jīng)...
    沈念sama閱讀 45,319評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,539評論 2 332
  • 正文 我和宋清朗相戀三年荚藻,在試婚紗的時候發(fā)現(xiàn)自己被綠了屋灌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,703評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡应狱,死狀恐怖共郭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情疾呻,我是刑警寧澤除嘹,帶...
    沈念sama閱讀 35,417評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站罐韩,受9級特大地震影響憾赁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜散吵,卻給世界環(huán)境...
    茶點故事閱讀 41,013評論 3 325
  • 文/蒙蒙 一龙考、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧矾睦,春花似錦晦款、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至赁温,卻和暖如春坛怪,著一層夾襖步出監(jiān)牢的瞬間淤齐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評論 1 269
  • 我被黑心中介騙來泰國打工袜匿, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留更啄,地道東北人。 一個月前我還...
    沈念sama閱讀 47,711評論 2 368
  • 正文 我出身青樓居灯,卻偏偏與公主長得像祭务,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子怪嫌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,601評論 2 353

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