Android模塊化及RN調(diào)用原生
先老實(shí)交代下背景,這篇講的是兩個(gè)問(wèn)題。這兩個(gè)問(wèn)題是自己被問(wèn)到的,但是講的很含糊,想必問(wèn)我的人也不滿意吧。究其原因:語(yǔ)言組織、表達(dá)、緊張。都是淚,下面說(shuō)說(shuō)這兩個(gè)問(wèn)題吧。
Q1:android模塊化遇到的問(wèn)題?
按照原始MVP的架構(gòu),隨著業(yè)務(wù)越來(lái)越復(fù)雜、基礎(chǔ)組件越來(lái)越多。業(yè)務(wù)與業(yè)務(wù)之間還有很強(qiáng)的耦合。就變成這樣

使用模塊化之后,變成這樣

很明顯清晰了不少。那么模塊化又存在哪些問(wèn)題呢?
- 最常見(jiàn)的R文件問(wèn)題。
我當(dāng)時(shí)說(shuō)的時(shí)候:整體與個(gè)體之間沖突,findbyid常用的ButterKnife不能用,因?yàn)闀?huì)有沖突等。module的application和主app的application沖突。
總之沒(méi)有很系統(tǒng)的講出來(lái),估計(jì)對(duì)方也不耐煩了。尷了個(gè)噶。接下來(lái)正規(guī)系統(tǒng)的講下:
Android開(kāi)發(fā)中,各個(gè)資源文件都是放在res目錄中,在編譯過(guò)程中,會(huì)生成R.java文件。R文件中包含有各個(gè)資源文件對(duì)應(yīng)的id,這個(gè)id是靜態(tài)常量,但是在Library Module中,這個(gè)id不是靜態(tài)常量,那么就會(huì)出現(xiàn)找不到id或者id沖突這樣的問(wèn)題。因此常用的ButterKnite也不能使用
解決方案:
- 重新一個(gè)Gradle插件,生成一個(gè)R2.java文件,這個(gè)文件中各個(gè)id都是靜態(tài)常量,這樣ButterKnite就可以正常使用了,通過(guò)R2獲取到id。
- 使用Android系統(tǒng)提供的最原始的方式,直接用findViewById以及setOnClickListener方式。
- 設(shè)置項(xiàng)目支持Databinding,然后使用Binding中的對(duì)象,但是會(huì)增加不少方法數(shù),同時(shí)Databinding也會(huì)有編譯問(wèn)題和學(xué)習(xí)成本。目前項(xiàng)目中沒(méi)有使用Databinging,這里也沒(méi)有太多的發(fā)言權(quán)
- xml沖突,資源沖突問(wèn)題
解決辦法:
- 對(duì)各個(gè)模塊的資源名字添加前綴,比如user模塊中的登錄界面布局為activity_login.xml,那么可以寫(xiě)成這樣us_activity_login.xml。
- application的xml只能查查哪些屬性沖突,借助網(wǎng)上資料了。
總結(jié)
模塊化架構(gòu)主要思路就是分而治之,把依賴整理清楚,減少代碼冗余和耦合,在把代碼抽取到各自的模塊后,了解各個(gè)模塊的通信方式,以及可能發(fā)生的問(wèn)題,規(guī)避問(wèn)題或者解決問(wèn)題。最后為了開(kāi)發(fā)和調(diào)試方便,開(kāi)發(fā)一些周邊工具,幫助開(kāi)發(fā)更好的完成任務(wù)。
Q2 ReactNative如何調(diào)用Android原生模塊
先來(lái)說(shuō)說(shuō)我的回答:android原生模塊實(shí)現(xiàn)ReactContext,通過(guò)它的回調(diào)使用原生模塊的方法?,F(xiàn)在回想太通俗了。下面正規(guī)說(shuō)下:
- 創(chuàng)建一個(gè)原生模塊
首先我們需要?jiǎng)?chuàng)建一個(gè)原生模塊,這個(gè)原生模塊是一個(gè)繼承ReactContextBaseJavaModule的Java類,它可以實(shí)現(xiàn)一些JavaScript所調(diào)用的原生功能.
public class RnTest extends ReactContextBaseJavaModule {
public RnTest(ReactApplicationContext reactContext) {
super(reactContext);
}
// ReactContextBaseJavaModule要求派生類實(shí)現(xiàn)getName方法。這個(gè)函數(shù)用于返回一個(gè)字符串
// 這個(gè)字符串用于在JavaScript端標(biāo)記這個(gè)原生模塊
@Override
public String getName() {
return "ToastByAndroid";
}
// 獲取應(yīng)用包名
// 要導(dǎo)出一個(gè)方法給JavaScript使用,Java方法需要使用注解@ReactMethod
@ReactMethod
public void getPackageName() {
String name = getReactApplicationContext().getPackageName();
Toast.makeText(getReactApplicationContext(),name,Toast.LENGTH_LONG).show();
}
}
- 注冊(cè)模塊
要使JavaScript端調(diào)用到原生模塊還需注冊(cè)這個(gè)原生模塊。需實(shí)現(xiàn)一個(gè)類實(shí)現(xiàn)ReactPackage接口
public class ExampleReactPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new RnTest(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文件中的getPackages方法中,實(shí)例化上面的注冊(cè)類
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
// 實(shí)例化注冊(cè)類
new ExampleReactPackage());
}
};
-
js調(diào)用android原生
- 引入模塊
import { NativeModules } from 'react-native'; - 使用方法
- 引入模塊
// 這里的ToastByAndroid即為1.創(chuàng)建一個(gè)原生模塊中g(shù)etName()方法返回的字符串
var rnToastAndroid = NativeModules.ToastByAndroid;
rnToastAndroid.getPackageName();
3. 回調(diào)函數(shù)
提供給js調(diào)用的原生android方法的返回類型必須是void,React Native的跨語(yǔ)言訪問(wèn)是異步進(jìn)行的,所以想要給JavaScript返回一個(gè)值的唯一辦法是使用回調(diào)函數(shù)或者發(fā)送事件
android主動(dòng)向rn發(fā)送消息
既然都說(shuō)道js向原生調(diào)用了,那么也說(shuō)說(shuō)原生向js調(diào)用。
根據(jù)上面,可以想到的思路就是:通過(guò)reactContext獲取到j(luò)sModule,然后像eventbus一樣發(fā)生事件,js方獲取事件。
- 創(chuàng)建一個(gè)原生模塊,發(fā)送消息方法
public static void sendEvent(ReactContext reactContext, String eventName, int status)
{
System.out.println("reactContext="+reactContext);
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName,status);
}
```
2. RN端代碼
DeviceEventEmitter.addListener(eventName, (reminder) => {
console.log(reminder):
});
```
- rn調(diào)用android模版
const RNBridgeModule = NativeModules.RNBridgeModule;
nativeLanuchApp(message) {
RNBridgeModule.nativePlayVideo(message);
}
<TouchableOpacity onPress={() => {
this.nativeLanuchApp("111");
}} >
<Text >
try
</Text>
</TouchableOpacity>
```