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原生实现思路一样,代码风格都很接近,接收起来相对容易.同样实现流式的问题也就转化换成了:

  1. 怎么把原生代码的回调转化成Future流
  2. 怎么处理原生回调哪些异常场景.

把原生代码的回调转化成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代码:

@Override

publicvoidonMethodCall(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 -> {

// 订单确认异常

}

总结

  1. 基于同样一个业务场景,整体代码结构dart和koltin二者相似
  2. 把异步callback转化成同步代码的方式相似
  3. 异常机制处理相似

后续

  1. 把iOS端留下的问题补充好.
  2. 整理下Flutter的事件模型.

以上是 Flutter Future 回调地狱的一种解决思路 的全部内容, 来源链接: utcz.com/a/30547.html

回到顶部