React Native 調(diào)用原生Android、iOS模塊實(shí)現(xiàn)撥號(hào)功能
一 前言
由于前幾個(gè)月公司2.0項(xiàng)目開(kāi)發(fā)技術(shù)選型為React Native,技術(shù)部相關(guān)人員開(kāi)始學(xué)習(xí)React Native相關(guān)的技術(shù)智政,筆者是一名Android開(kāi)發(fā)者掏缎,下文所描述的React Native調(diào)用Android/iOS模塊中關(guān)于iOS的部分如有誤的地方阐斜,請(qǐng)指出衫冻。為了讓從Android或iOS學(xué)習(xí)React Native的同志更加清楚的了解另一移動(dòng)端,筆者盡可能寫(xiě)的詳細(xì)點(diǎn)谒出。
二 效果
下面兩張圖分別為iOS和Android上效果圖隅俘,其中iOS效果圖中點(diǎn)擊電話號(hào)碼會(huì)打印log,并不會(huì)調(diào)起iOS撥號(hào)界面笤喳,因?yàn)閕OS模擬器不支持此功能为居,所以要想看效果只能用真機(jī)查看。這里打印log是為了證明React Native成功調(diào)起了原生iOS模塊功能莉测。
三 實(shí)現(xiàn)方案
關(guān)于調(diào)用撥號(hào)功能以及調(diào)用瀏覽器颜骤、短信、郵箱等功能捣卤,可有兩種實(shí)現(xiàn)方案忍抽。
一種是按照React Native提供的調(diào)用原生的過(guò)程方案來(lái)調(diào)用,這種適合大部分React Native調(diào)用原生功能的需求董朝,掌握這種后鸠项,基本以后再有調(diào)用原生需求即可按照此過(guò)程方案解決,此文也會(huì)選用這種方案進(jìn)行描述子姜。
另一種是React Native幫我們封裝的Linking模塊可以實(shí)現(xiàn)這類的需求祟绊,這種相比上一種來(lái)說(shuō)相對(duì)簡(jiǎn)單,主要適用于調(diào)用原生的電話哥捕、短信牧抽、郵箱、瀏覽器等功能遥赚。
四 實(shí)現(xiàn)原生Android模塊
1.在自己新建的Reacat Native項(xiàng)目中android/app/src/main/java/xxx(項(xiàng)目包名)/ 目錄下(為了和其他文件分離扬舒,筆者又在此目錄下新建一個(gè)native文件夾),需要新建一個(gè)java類文件凫佛,例如文件名為CallPhoneModule.java,這個(gè)java類一定要繼承RN提供的ReactContextBaseJavaModule抽象類,然后實(shí)現(xiàn)其構(gòu)造函數(shù)讲坎,其中的參數(shù)要為ReactApplicationContext reactContext。
public class CallPhoneModule extends ReactContextBaseJavaModule {
public CallPhoneModule(ReactApplicationContext reactContext) {
super(reactContext);
}
}
2.然后實(shí)現(xiàn)NativeModule中定義的getName()方法愧薛,返回一個(gè)String類型字符串晨炕,這個(gè)返回結(jié)果將要在JavaScript中使用,例如返回“CallPhoneModule”毫炉,則可以在JavaScript中通過(guò)React.NativeModules.CallPhoneModule調(diào)用瓮栗。注意,如果返回的字符串中有RCT前綴,則會(huì)自動(dòng)移除RCT前綴费奸。例如返回“RCTCallPhoneModule”,則在JavaScript中依然可以通過(guò)React.NativeModules.CallPhoneModule調(diào)用鲸郊。CallPhoneModule繼承ReactContextBaseJavaModule,ReactContextBaseJavaModule繼承BaseJavaModule货邓,BaseJavaModule實(shí)現(xiàn)了NativeModule接口,這是CallPhoneModule與NativeModule的關(guān)系四濒。
@Override
public String getName() {
return "CallPhoneModule";
}
3.然后在CallPhoneModule類中寫(xiě)一個(gè)方法换况,這個(gè)方法提供給JavaScript調(diào)用,例如方法名為callPhone盗蟆,里面?zhèn)鬟fString類型參數(shù)(非必須)戈二,特別的這個(gè)方法要使用@ReactMethod注解,以及返回類型必須為void喳资。然后在callPhone方法中寫(xiě)入要實(shí)現(xiàn)的功能觉吭,這里寫(xiě)入了撥號(hào)功能的實(shí)現(xiàn)。
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneString));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.reactContext.startActivity(intent);
CallPhoneModule.java文件的全部代碼如下:
package com.zhuku02;
import android.content.Intent;
import android.net.Uri;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.lang.String;
public class CallPhoneModule extends ReactContextBaseJavaModule {
public ReactApplicationContext reactContext;
public CallPhoneModule(ReactApplicationContext reactContext) {
super(reactContext);
this.reactContext = reactContext;
}
@ReactMethod
public void callPhone(String phoneString) {
Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:" + phoneString));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.reactContext.startActivity(intent);
}
@Override
public String getName() {
return "CallPhoneModule";
}
}
4.新建一個(gè)java類文件仆邓,例如文件名為CallPhoneReactPackage.java鲜滩,這個(gè)類必須實(shí)現(xiàn)ReactPackage接口,然后實(shí)現(xiàn)createViewManagers节值、createNativeModules兩個(gè)方法徙硅,特別的要在createNativeModules方法的返回值中add進(jìn)剛才新建的CallPhoneModule類。
CallPhoneReactPackage.java全部代碼如下:
package com.zhuku02;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import com.zhuku02.CallPhoneModule;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CallPhoneReactPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new CallPhoneModule(reactContext));
return modules;
}
}
5.最后在MainApplication.java文件中的getPackages方法中加上剛才新建的CallPhoneReactPackage類搞疗。至此嗓蘑,原生Android模塊書(shū)寫(xiě)完畢。關(guān)于JavaScript調(diào)用原生Android模塊代碼會(huì)在文末和調(diào)用原生iOS一起寫(xiě)出匿乃。
修改后的MainApplication.java文件代碼如下:
package com.zhuku02;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
import com.zhuku02.CallPhoneReactPackage;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new CallPhoneReactPackage()
);
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
五 實(shí)現(xiàn)原生iOS模塊
1.在自己新建的Reacat Native項(xiàng)目中ios/xxx(項(xiàng)目包名)/ 目錄下桩皿,需要新建一個(gè)后綴為m和一個(gè)后綴為h的文件。為了和其他文件分離以及和Android保持一致幢炸,筆者又在此目錄下新建一個(gè)native文件夾泄隔。在Xcode的此文件夾上右鍵New File,然后在彈出的頁(yè)面中Cocoa Touch Class選項(xiàng)輸入文件名阳懂,這樣會(huì)建立出相同文件名的m和h文件梅尤。如果New File時(shí),分別選擇Objective-C File和Header File岩调,則這兩個(gè)文件名要相同巷燥。例如文件名稱為CallPhoneModuleIos。
2.在CallPhoneModuleIos.h文件中号枕,類要實(shí)現(xiàn)RN提供的RCTBridgeModule協(xié)議缰揪。RCT是ReaCT的縮寫(xiě),React Native中Object-C相關(guān)的命名均以RCT開(kāi)頭。RCTBridgeModule是定義好的protocol钝腺,實(shí)現(xiàn)該協(xié)議的類抛姑,會(huì)自動(dòng)注冊(cè)到Object-C對(duì)應(yīng)的Bridge中。Object-C Bridge上層負(fù)責(zé)與Object-C通信艳狐,下層負(fù)責(zé)和JavaScript Bridge通信定硝,而JavaScript Bridge負(fù)責(zé)和JavaScript通信。這樣,通過(guò)Object-C Bridge和JavaScript Bridge就可以實(shí)現(xiàn)JavaScript和Object-C的相互調(diào)用毫目。
CallPhoneModuleIos.h文件如下:
#import <UIKit/UIKit.h>
#import <React/RCTBridgeModule.h>
#import <React/RCTLog.h>
@interface CallPhoneModuleIos : NSObject <RCTBridgeModule>
@end
3.CallPhoneModuleIos.m文件中蔬啡,類需要包含RCT_EXPORT_MODULE()宏,作用是自動(dòng)注冊(cè)一個(gè)module镀虐。這個(gè)宏可以添加一個(gè)參數(shù)箱蟆,用來(lái)指定在JavaScript調(diào)用這個(gè)模塊的名字,類似于上文中說(shuō)的getName()方法刮便。如果不添加這個(gè)參數(shù)空猜,則默認(rèn)就是這個(gè)類的名字。
4.然后需要在此類中寫(xiě)一個(gè)方法恨旱,提供給RN調(diào)用辈毯,方法通過(guò)RCT_EXPORT_METHOD()宏來(lái)實(shí)現(xiàn)。
RCT_EXPORT_METHOD(callPhone: (NSString *)phone){
NSLog(@"======%@",phone)窖杀;
}
CallPhoneModuleIos.m完整代碼:
#import "CallPhoneModuleIos.h"
#import <Foundation/Foundation.h>
@implementation CallPhoneModuleIos
RCT_EXPORT_MODULE(CallPhoneModuleIos);
RCT_EXPORT_METHOD(callPhone: (NSString *)phone){
NSLog(@"======%@",phone);
//去掉注釋漓摩,下面代碼就是實(shí)現(xiàn)撥號(hào)功能代碼,但還未真機(jī)測(cè)試
// NSMutableString * str = [[NSMutableString alloc] initWithFormat:@"telprompt://%@",phone];
// [[UIApplication sharedApplication] openURL:[NSURL URLWithString:str]];
}
// -(dispath_queue_t)methodQueue{
// return dispath_get_main_queue();
// }
@end
至此入客,則原生iOS代碼書(shū)寫(xiě)完成管毙,現(xiàn)在即將開(kāi)始調(diào)用。
六 React Native調(diào)用Android桌硫、iOS原生模塊
為了在JavaScript端同時(shí)訪問(wèn)Android夭咬、iOS原生模塊更加方便,筆者把原生模塊的調(diào)用封裝在一個(gè)JavaScript文件中铆隘,例如文件名為CallPhone.js,這樣在需要調(diào)用的地方直接調(diào)用此JavaScript文件既可卓舵,同時(shí)在此文件中,處理好Android膀钠、iOS掏湾、Web(若有)的分別調(diào)用。
import {Platform, NativeModules} from 'react-native';
var module = null;
if (Platform.OS == 'ios') {
module = NativeModules.CallPhoneModuleIos;
} else if (Platform.OS == 'android') {
module = NativeModules.CallPhoneModule;
} else if (Platform.OS == 'web') {
//暫未實(shí)現(xiàn)web功能
}
export default module;
然后在JavaScript文件中這樣調(diào)用:
import CallPhone from '../../native/CallPhone';
CallPhone.callPhone('4007773177');
到這里肿嘲,整篇文章就結(jié)束了融击,疑問(wèn)、建議或者指教歡迎討論雳窟。