應大家的要求,我研究了一下相互傳值操作
一、uni-app 跳轉Android原生界面(Activity)並傳值
- 前端傳值操作
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
</head>
<body>
<input type="button" value="js start native Activity" onclick="jsCallNativeActivity()"/>
</body>
<script type="text/javascript">
function jsCallNativeActivity(){
//獲取宿主上下文
var main = plus.android.runtimeMainActivity();
//通過反射獲取Android的Intent對象
var Intent = plus.android.importClass("android.content.Intent");
//通過宿主上下文創建 intent
var intent = new Intent(main.getIntent());
//設置要開啓的Activity包類路徑 com.HBuilder.integrate.MainActivity換掉你自己的界面
intent.setClassName(main, "com.HBuilder.integrate.MainActivity");
//開啓新的任務棧 (跨進程)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//向原生界面傳值操作
intent.putExtra("uni_key","來自uniapp的值");
//開啓新的界面
main.startActivity(intent);
}
</script>
</html>
做Android的都知道intent.putExtra(key,value)就是通過Intent進行不同組件之間傳值操作,前端開發人員我建議直接封裝成json傳值,intent.putExtra(“uni_json_key”,"{key,“value1”}");
- Android端接收傳值
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent mIntent = getIntent();
if (mIntent!=null){
//獲取Intent,通過key獲取對應的值
String uniValue = mIntent.getStringExtra("uni_key");
Toast.makeText(this, "uniValue="+uniValue, Toast.LENGTH_SHORT).show();
}
}
}
效果如下:
二、uni-app 跳轉Android原生界面(Activity)並傳值,並返回uni-app時帶返回值
- uni-app開啓android 原生界面,並請求返回值
function jsCallNativeActivity(){
//獲取宿主上下文
var main = plus.android.runtimeMainActivity();
//通過反射獲取Android的Intent對象
var Intent = plus.android.importClass("android.content.Intent");
//通過宿主上下文創建 intent
var intent = new Intent(main.getIntent());
//設置要開啓的Activity包類路徑 com.HBuilder.integrate.MainActivity換掉你自己的界面
intent.setClassName(main, "com.HBuilder.integrate.MainActivity");
//開啓新的任務棧 (跨進程)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//uni向android原生界面傳值
intent.putExtra("uni_key","來自uniapp的值");
//請求碼保證了,開始的新界面和返回的是同一個操作
var CODE_REQUEST=1000
//採用startActivityForResult開啓新的界面,當界面關閉時可以處理返回結果, CODE_REQUEST請求碼是唯一標識
main.startActivityForResult(intent,CODE_REQUEST);
//設置原生界面返回後的回調操作
main.onActivityResult = function(requestCode, resultCode, data) {
if (requestCode == CODE_REQUEST) {
alert(requestCode); //這個是正確的 1000
alert(resultCode); //始終都是0
alert(data); //彈出 undefined
}
}
}
- Android端點擊按鈕,關閉原生界面,返回值
public void backValue(View view) {
Intent mIntent = new Intent();
mIntent.putExtra("Native_RESULT_Key", "來自原生界面的返回值");
setResult(Activity.RESULT_OK, mIntent);
finish();
}
上面一頓操作後,理論上應該是可以返回值的,但是實際上除了requestCode是正確的,其他的值都是錯誤的,爲什麼呢?我跟了安卓這邊的官方demo源碼,發現有bug,onActivityResult方法居然在開啓新界面的時候就被調用,在返回的時候設置 setResult(Activity.RESULT_OK, mIntent);後onActivityResult壓根就沒有走,所以這樣resultCode和data都沒有被正常賦值,如下圖log
可以看到官方在這裏挖了一個坑,多少人跳了進去,爲什麼他的生命週期方法會調用錯亂呢?uni-app的demo是這麼寫的,用一個代理類去處理所有的事件操作
只能看Android的官方源碼
也就是在我們開啓原生的activity重新開始交互時,將在onResume()之前調用onActivityResult將上個界面的值返回,現在onActivityResult沒有執行,說明uni-app在某些地方做了方法攔截,導致Android生命週期方法回調異常;所以這個問題需要uni-app官方處理,把Android的生命週期理順
我個人提出的解決方案就是採用EventBus去手動調用SDK_WebApp中的onActivityResult,具體如下
1、app.gradle 依賴 implementation 'org.greenrobot:eventbus:3.0.0'
2、創建DataSynEvent
public class DataSynEvent {
public int requestCode;
public int resultCode;
public Intent data;
public DataSynEvent(int requestCode, int resultCode, Intent data) {
this.requestCode = requestCode;
this.resultCode = resultCode;
this.data = data;
}
}
3、SDK_WebApp.java修改
public class SDK_WebApp extends Activity implements IActivityDelegate {
private static final String TAG = "SDK_WebApp";
....
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
....
//註冊EventBus
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy: ");
mEntryProxy.onStop(this);
//解綁EventBus
EventBus.getDefault().unregister(this);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Log.d(TAG, "onActivityResult: " + data + ",requestCode=" + requestCode + ",resultCode=" + resultCode);
super.onActivityResult(requestCode, resultCode, data);
if (data!=null){
//第一次啓動的時候調用這個方法data肯定爲null.所以減少調用次數
mEntryProxy.onActivityExecute(this, SysEventType.onActivityResult, new Object[]{requestCode, resultCode, data});
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onDataSynEvent(DataSynEvent event) {
Log.d(TAG, "onDataSynEvent: ");
//手動調用
onActivityResult(event.requestCode, event.resultCode, event.data);
}
}
4、修改原生界面的返回方法
public void backValue(View view) {
Intent data= new Intent();
data.putExtra("Native_RESULT_Key", "來自原生界面的返回值");
//用EventBus替換setResult(Activity.RESULT_OK,data);
EventBus.getDefault().post(new DataSynEvent(1000,Activity.RESULT_OK,data));
finish();
}
5、修改前端的接收方法
function jsCallNativeActivity(){
//獲取宿主上下文
var main = plus.android.runtimeMainActivity();
//通過反射獲取Android的Intent對象
var Intent = plus.android.importClass("android.content.Intent");
//通過宿主上下文創建 intent
var intent = new Intent(main.getIntent());
//設置要開啓的Activity包類路徑 com.HBuilder.integrate.MainActivity換掉你自己的界面
intent.setClassName(main, "com.HBuilder.integrate.MainActivity");
//uni向android原生界面傳值
intent.putExtra("uni_key","來自uniapp的值");
//開啓新的任務棧 (跨進程)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//請求碼保證了,開始的新界面和返回的是同一個操作
var CODE_REQUEST=1000
//採用startActivityForResult開啓新的界面,當界面關閉時可以處理返回結果, CODE_REQUEST請求碼是唯一標識
main.startActivityForResult(intent,CODE_REQUEST);
//設置原生界面返回後的回調操作
main.onActivityResult = function(requestCode, resultCode, data) {
if (requestCode == CODE_REQUEST) {
alert(requestCode); //這個是正確的 1000
alert(resultCode); //這個是正確的 -1
alert(data.getStringExtra("Native_RESULT_Key")); //彈出 來自原生界面的返回值
}
}
}
6、上效果
*看到官方論壇有人問main
是啥東西,其實就是通過反射獲取的宿主Activity的一個實例對象,也就是com.HBuilder.integrate.SDK_WebApp
*
2020年5月12日補充
關於通信,uni-app官方在2020年四月底給出了相關api 宿主 App 向小程序發送事件
- Android 平臺API
DCUniMPSDK.getInstance().sendUniMPEvent(event, data)
- 參數說明
參數 | 類型 | 必填 | 說明 |
---|---|---|---|
event | String | yes | 觸發事件的event |
data | String或者JSON | yes | 事件攜帶的參數 |
- 返回值
類型 | 說明 |
---|---|
boolean | true表示事件通知成功。false表示失敗。可通過log查看。 |