React-Native之Android:原生界面與React界面的相互調用

這裏原生界面是指用佈局文件實現或java代碼實現view的Activity,React界面是指用ReactJS實現的界面的Activity。

從某種角度看,React只是充當了Android裏的view層,因此原生界面與React界面的相互調用及數據傳遞同原生界面之間的互動基本是一致的。

下面是我對兩種界面的相互調用和數據傳遞的一種實現嘗試,不一定是最有效率或最佳的,純當練習和探索而已。

一、原生界面調用React界面

1.只是啓動React界面的話很簡單,同原生界面間的啓動一樣,直接用startActivity即可。

2.在啓動React界面時傳遞數據給React界面。

先來捋下思路,原生界面啓動是沒問題的,同原生一樣;關鍵是React界面如何獲取到傳遞過來的值:從前面的學習中我們知道原生模塊是有很大自由度的,只要能得到React界面所在activity就可以順着得到傳遞的來的intent,而JS端想得到數據就要利用回調函數了。

接下來是實現思路,從後往前來:首先先要按之前構建原生模塊的步驟一步步來構建我們這次需要的功

public class MyIntentModuleextendsReactContextBaseJavaModule
	@ReactMethod
	public void getDataFromIntent(Callback successBack,Callback erroBack){
	try{
		Activity currentActivity = getCurrentActivity();
		String result = currentActivity.getIntent().getStringExtra("result");//會有對應數據放入
		if (TextUtils.isEmpty(result)){
			result = "No Data";
	}
	successBack.invoke(result);
	}catch (Exception e){
		erroBack.invoke(e.getMessage());
	}
}
`
```
publicclassMyReactPackageimplementsReactPackage	
	@Override
	public List createNativeModules(ReactApplicationContext reactContext) {
		return Arrays.asList(
		new MyIntentModule(reactContext)
	);
}
```
```
publicclassMyReactActivityextendsReactActivity
	@Override
	protectedListgetPackages(){
		returnArrays.asList(
		newMainReactPackage(),
		newMyReactPackage()
	);
}
```
在JS端配置相應代碼
.......
classmyreactactivity extendsReact.Component{
	constructor(props){
	super(props);//這一句不能省略,照抄即可
	this.state={
		TEXT:'Input Text',//這裏放你自己定義的state變量及初始值
	};
}
	render(){
	return(
	style={{height:40,borderColor:'gray',borderWidth:1,}}
	onChangeText={(text)=>this.setState({text})}
	value={this.state.TEXT}/>
	)
}
	componentDidMount(){   //這是React的生命週期函數,會在界面加載完成後執行一次
	React.NativeModules.MyIntentModule.getDataFromIntent(
	(successMsg)=>{
	this.setState({TEXT:successMsg,}); //狀態改變的話重新繪製界面
},
(erroMsg)=>{alert(erroMsg)}
);
}
}
.......
最後在原生界面將數據存放在intent裏,然後用startActivity啓動;
Intentintent=newIntent(this,MyReactActivity.class);
intent.putExtra("result","haha");
startActivity(intent,10);
3.原生界面啓動React界面後等待回調數據。
在原生應用裏是通過intent來傳遞數據,有 startActivityForResult 和onAcitvityResult來滿足啓動和回調,回傳數據的用setResult設置intent。
同上面一樣,由於可以得到當前activity,這些問題也迎刃而解了。
先在MyIntentModule裏添加方法
@ReactMethod
publicvoidfinishActivity(Stringresult){
	ActivitycurrentActivity=getCurrentActivity();
	Intentintent=newIntent();
	intent.putExtra("result",result);
	currentActivity.setResult(11,intent);
	currentActivity.finish();
}
在JS端配置相應代碼
React.NativeModules.MyIntentModule.finishActivity(this.state.TEXT);
至於原生界面裏代碼就略去不寫了,會安卓的人都可以寫出來了。
二、React界面調用原生界面
1.只是啓動原生界面
安卓中啓動activity有隱式和顯式,這裏只考慮顯示啓動。顯式啓動需要指定目的activity的類,而我們雖然可以定製原生模塊供JS調用,但並不能直接指定類或字節碼爲參數。一些特殊的情況下時可以用swtich的思路來解決,適用性更好的方法有沒有呢,如何用允許的參數來得到目的類呢。這裏就要要到反射了。
先在MyIntentModule裏添加方法
@ReactMethod
publicvoidstartActivityByString(StringactivityName){
	try{
		ActivitycurrentActivity=getCurrentActivity();
		if(null!=currentActivity){
			ClassaimActivity=Class.forName(activityName);
			Intentintent=newIntent(currentActivity,aimActivity);
			currentActivity.startActivity(intent);
		}
	}catch(Exceptione){
		thrownewJSApplicationIllegalArgumentException(
		"Could not open the activity : "+e.getMessage());
	}
}
在JS裏調用
React.NativeModules.MyIntentModule..startActivityByString("com.example.SecondActivity")
2.啓動原生界面並傳遞進數據
這和上面相比就是多了幾個參數的事,沒有啥好講的,只是只能傳遞基本數據類型的參數。要想做到和原生一樣在intent裏任意傳遞基本數據和類還做不到,而且要想自己用的方便一點的話還需要再考慮封裝模塊方法,在此不贅述。
3.React界面啓動原生界面後等待回調數據
啓動原生界面是同2一樣的,問題在於原生界面返回的數據是在React界面所在Activity的onActvityResult裏接受的,而JS端接受原生模塊傳回的數據是通過調用原生模塊方法時設置的回調函數接受到的,如何將onActivityResult裏的數據實時傳遞到原生模塊裏方法設置的回調函數裏呢?如果不考慮這些回調只是把數據存在變量或者文件裏,用一個類似messageQueue的思路應該可以解決,不過那樣需要可能效率上不太高,我們自己實現起來也不太方便。幸運的是在Java裏已經有BlockingQueue了。
先在MyReactActivity裏添加代碼
public static ArrayBlockingQueue myBlockingQueue = new ArrayBlockingQueue(1);
	@Override
	publicvoidonActivityResult(intrequestCode,intresultCode,Intentdata){
		super.onActivityResult(requestCode,resultCode,data);
		if(resultCode==RESULT_OK&&requestCode==100){
			Stringresult=data.getStringExtra("result");
		if(!TextUtils.isEmpty(result)){
			MyConstants.myBlockingQueue.add(result);
		}else{
			MyConstants.myBlockingQueue.add("無數據傳回");
	}
}
}else{
MyConstants.myBlockingQueue.add("沒有");
}
}
在MyIntentModule裏添加方法
@ReactMethod
publicvoidstartActivityForResult(StringactivityName,intrequestCode,CallbacksuccessCallback,CallbackerroCallback){
	try{
		ActivitycurrentActivity=getCurrentActivity();
		if(null!=currentActivity){
			ClassaimActivity=Class.forName(activityName);
			Intentintent=newIntent(currentActivity,aimActivity);
			currentActivity.startActivityForResult(intent,requestCode);
			Stringresult=MyConstants.myBlockingQueue.take();
			successCallback.invoke(result);
		}
		}catch(Exceptione){
			erroCallback.invoke(e.getMessage());
			thrownewJSApplicationIllegalArgumentException(
			"Could not open the activity : "+e.getMessage());
	}
}
在JS端調用
React.NativeModules.MyIntentModule.startActivityForResult(
	"com.example.SecondActivity",100,
	(successMsg)=>{
		this.setState({TEXT:successMsg,});
	},
	(erroMsg)=>{alert(erroMsg)}
);
原生界面的代碼也要記得寫,不過也沒啥好說的了,按原生安卓一樣就可以了。



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章