Flutter Future 回调地狱的一种解决思路
背景
尝试使用Flutter来开发双端应用,实现订阅功能. 之前用kotlin实现过一次可以参考: koltin回调地狱的一种解决思路.
实现过程
UI
这个UI比较简单
订阅逻辑
同样使用流式来实现,主动抛出异常来中断流.
void _goBuy(SubscriptModel model) async {setState(() {
_isloading = true;
});
// 购买前先确认是不是已经付费过,但是本地订单丢失了
await FlutterInappPurchase
.instance
// 1. 建立和GP/AppStore的连接
.initConnection
// 2. 查询历史缓存订单,看看是否已经是vip了. 换机器可以restore,没必要走网络查询
.then((it) {
if (it == null) {
throw InitExection;
}
return FlutterInappPurchase.instance
.getAvailablePurchases()
.then((historyItems) => model.checkHistorySubscript(historyItems))
.then((item) {
if (item != null) {
model.updatePurchased(item);
throw VipStateException;
}
});
})
// 3. 获取可购买产品列表
.then((it) {
return FlutterInappPurchase.instance
.getSubscriptions(List.filled(1, this._productID))
.then((items) => items
.firstWhere((item) => item.productId == this._productID));
})
// 4. 购买
.then((item) {
return FlutterInappPurchase.instance
.requestSubscription(this._productID)
.then((itemStr) => PurchasedItem.fromJSON(jsonDecode(itemStr)));
})
// 5.校验
.then((purchaseItem) => model.checkSubscript(purchaseItem))
// 6.客户端确认订单
.then((purchaseItem) {
model.updatePurchased(purchaseItem);
SubscriptModel.log(StatisticConstant.SUCCESS);
FlutterInappPurchase.instance.consumePurchaseAndroid(
purchaseItem.purchaseToken, purchaseItem.isAcknowledgedAndroid);
})
// 异常处理
.catchError((e) {
switch (e.runtimeType) {
case PlatformException:
String method = e.code;
if (method == "service_error") {
_dismissLoading(
'''Google services are unavailable. Install "GoogleService" and communicate again.''');
} elseif (method == "buyItemByType") {
_dismissLoading("");
}
break;
case VipStateException:
SubscriptModel.data[StatisticConstant.REASON] = 'isVip';
_dismissLoading('''Already is VIP. No repurchase required.''');
break;
case InitExection:
_dismissLoading(
'''Google services are unavailable. Install "GoogleService" and communicate again.''');
break;
}
})
.whenComplete(() => {_dismissLoading("")});
}
这里实现过程和kotlin原生实现思路一样,代码风格都很接近,接收起来相对容易.同样实现流式的问题也就转化换成了:
- 怎么把原生代码的回调转化成Future流
- 怎么处理原生回调哪些异常场景.
把原生代码的回调转化成Future
在kotlin,已经试用使用通道把java回调转化成suspend函数. 这里也是一样的,只是多了一层dart封装.例如上面的initConnection方法先申明为一个Future函数
Future<String> get initConnection async {if (_platform.isAndroid) {
await _setPurchaseListener();
String result =
await _channel.invokeMethod('initConnection').catchError((e) {
returnnull;
});
return result;
} elseif (_platform.isIOS) {
await _setPurchaseListener();
finalString result =
await _channel.invokeMethod('canMakePayments').catchError((e) {
returnnull;
});
return result;
}
throw PlatformException(
code: _platform.operatingSystem, message: "platform not supported");
}
iOS和android分别实现initConnection 和 canMakePayments方法. 因为flutter是单线程模型,await和kotlin协程的await一样是非阻塞的,原生实现功能后可以直接利用方法通道,结束await.
方法通道的原生java代码:
@OverridepublicvoidonMethodCall(final MethodCall call, final Result result){
if (call.method.equals("initConnection")) {
billingClient = BillingClient.newBuilder(reg.activity()).setListener(purchasesUpdatedListener)
.enablePendingPurchases()
.build();
billingClient.startConnection(new BillingClientStateListener() {
@Override
publicvoidonBillingSetupFinished(BillingResult billingResult){
int responseCode = billingResult.getResponseCode();
if (responseCode == BillingClient.BillingResponseCode.OK) {
result.success("Billing client ready");
} else {
result.error(call.method, "responseCode: " + responseCode, "");
}
}
});
}
}
这里result就是起了通道的作用.
异常机制
Future回调有个catchError函数,用法和kotlin的Exceptionhandler相似.比较下两种代码
.catchError((e) {switch (e.runtimeType) {
case PlatformException:
String method = e.code;
if (method == "service_error") {
_dismissLoading(
'''Google services are unavailable. Install "GoogleService" and communicate again.''');
} elseif (method == "buyItemByType") {
_dismissLoading("");
}
break;
case VipStateException:
SubscriptModel.data[StatisticConstant.REASON] = 'isVip';
_dismissLoading('''Already is VIP. No repurchase required.''');
break;
case InitExection:
_dismissLoading(
'''Google services are unavailable. Install "GoogleService" and communicate again.''');
break;
}
})
vs
when (throwable) {is UserCancleException -> {
// 用户取消
}
is SubscriptProductException -> {
// 订阅购买异常
}
is InitException -> {
// GP初始化异常 一般是没有装GP
}
is RepeateSubscription -> {
// 重复购买
}
is NoProducteException -> {
// 找不到要购买的商品信息
}
is AcknowException -> {
// 订单确认异常
}
总结
- 基于同样一个业务场景,整体代码结构dart和koltin二者相似
- 把异步callback转化成同步代码的方式相似
- 异常机制处理相似
后续
- 把iOS端留下的问题补充好.
- 整理下Flutter的事件模型.
以上是 Flutter Future 回调地狱的一种解决思路 的全部内容, 来源链接: utcz.com/a/30547.html