Android多種支付方式的實(shí)現(xiàn)示例
1.場景
App 的支付流程,添加多種支付方式,不同的支付方式,對應(yīng)的操作不一樣,有的會跳轉(zhuǎn)到一個新的webview,有的會調(diào)用系統(tǒng)瀏覽器,有的會進(jìn)去一個新的表單頁面,等等。
并且可以添加的支付方式也是不確定的,由后臺動態(tài)下發(fā)。
如下圖所示:
根據(jù)上圖 ui 理一下執(zhí)行流程:
- 點(diǎn)擊不同的添加支付方式 item。
- 進(jìn)入相對應(yīng)的添加支付方式流程(表單頁面、webview、彈框之類的)。
- 在第三方回調(diào)里面根據(jù)不同的支付方式執(zhí)行不同的操作。
- 調(diào)用后臺接口查詢添加是否成功。
- 根據(jù)接口結(jié)果展示不同的成功或者失敗的ui.
2.以前的實(shí)現(xiàn)方式
用一個 Activity 承載,上述所有的流程都在 Activity 中。Activity 包含了列表展示、多種支付方式的實(shí)現(xiàn)和 ui。
偽代碼如下:
class AddPaymentListActivity : AppCompatActivity(R.layout.activity_add_card) { private val addPaymentViewModel : AddPaymentViewModel = ... override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) addPaymentViewModel.checkPaymentStatusLiveData.observer(this) { isSuccess -> // 從后臺結(jié)果判斷是否添加成功 if (isSuccess) { addCardSuccess(paymentType) } else { addCardFailed(paymentType) } } } private fun clickItem(paymentType: PaymentType) { when (paymentType) { PaymentType.ADD_GOOGLE_PAY -> //執(zhí)行添加谷歌支付流程 PaymentType.ADD_PAY_PEL-> //執(zhí)行添加PayPel支付流程 PaymentType.ADD_ALI_PAY-> //執(zhí)行添加支付寶支付流程 PaymentType.ADD_STRIPE-> //執(zhí)行添加Stripe支付流程 } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) when (resultCode) { PaymentType.ADD_GOOGLE_PAY -> { // 根據(jù)第三方回調(diào)的結(jié)果,拿到key // 根據(jù)key調(diào)用后臺的Api接口查詢是否添加成功 } PaymentType.ADD_PAY_PEL -> // 同上 // ... } } private fun addCardSuccess(paymentType: PaymentType){ when (paymentType) { PaymentType.ADD_GOOGLE_PAY -> // 添加對應(yīng)的支付方式成功,展示成功的ui,然后執(zhí)行下一步操作 PaymentType.ADD_PAY_PEL-> // 同上 // ... } } private fun addCardFailed(paymentType: PaymentType){ when (paymentType) { PaymentType.ADD_GOOGLE_PAY -> // 添加對應(yīng)的支付方式失敗,展示失敗的ui PaymentType.ADD_PAY_PEL-> // 同上 // ... } } enum class PaymentType { ADD_GOOGLE_PAY, ADD_PAY_PEL, ADD_ALI_PAY, ADD_STRIPE } }
雖然看起來根據(jù) paymentType 來判斷,邏輯條理也還過得去,但是實(shí)際上復(fù)雜度遠(yuǎn)遠(yuǎn)不止如此。
• 不同的支付方式跳轉(zhuǎn)的頁面相差很大。
• 結(jié)果的回調(diào)獲取也相差很大,并不是所有的都在onActivityResult中。
• 成功和失敗實(shí)際上也不能統(tǒng)一來處理,里面包含很多的if…else…判斷。
• 如果支付方式是后臺動態(tài)下發(fā)的,處理起來判斷邏輯就更多了。
此外,最大的問題:擴(kuò)展性問題。
當(dāng)新來一種支付方式,例如微信支付之類的,改動代碼就很大了,基本就是將整個Activity中的代碼都要改動??梢哉f上面這種方式的可擴(kuò)展性為零,就是簡單的將代碼都揉在一起。
3.優(yōu)化后的代碼
要想實(shí)現(xiàn)高內(nèi)聚低耦合,最簡單的就是套用常見的設(shè)計模式,回想一下,發(fā)現(xiàn)策略模式+簡單工廠模式非常這種適合這種場景。
先看下優(yōu)化后的代碼:
class AddPlatformActivity : BaseActivity() { private var addPayPlatform: IAddPayPlatform? = null private fun addPlatform(payPlatform: String) { // 將后臺返回的支付平臺字符串變成枚舉類 val platform: PayPlatform = getPayPlatform(payPlatform) ?: return addPayPlatform = AddPayPlatformFactory.getCurrentPlatform(this, platform) addPayPlatform?.getLoadingLiveData()?.observe(this@AddPlatformActivity) { showLoading -> if (showLoading) startLoading() else stopLoading() } addPayPlatform?.addPayPlatform(AddCardParameter(platform)) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) // 將onActivityResult的回調(diào)轉(zhuǎn)接到需要監(jiān)聽的策略類里面 addPayPlatform?.thirdAuthenticationCallback(requestCode, resultCode, data) } }
4.策略模式
意圖: 定義一系列的算法,把它們一個個封裝起來, 并且使它們可相互替換。
主要解決: 在有多種算法相似的情況下,使用if…else所帶來的復(fù)雜和難以維護(hù)。
何時使用: 一個系統(tǒng)有許多許多類,而區(qū)分它們的只是他們直接的行為。
如何解決: 將這些算法封裝成一個一個的類,任意地替換。
關(guān)鍵代碼: 實(shí)現(xiàn)同一個接口。
**優(yōu)點(diǎn): **
1、算法可以自由切換。
2、避免使用多重條件判斷。
3、擴(kuò)展性良好。
缺點(diǎn):
1、策略類會增多。
2、所有策略類都需要對外暴露。
**使用場景: **
1、如果在一個系統(tǒng)里面有許多類,它們之間的區(qū)別僅在于它們的行為,那么使用策略模式可以動態(tài)地讓一個對象在許多行為中選擇一種行為。
2、一個系統(tǒng)需要動態(tài)地在幾種算法中選擇一種。
3、如果一個對象有很多的行為,如果不用恰當(dāng)?shù)哪J剑@些行為就只好使用多重的條件選擇語句來實(shí)現(xiàn)。
5.需要實(shí)現(xiàn)的目標(biāo)
5.1 解耦宿主 Activity
現(xiàn)在宿主Activity中代碼太重了,包含多種支付方式實(shí)現(xiàn),還有列表ui的展示,網(wǎng)絡(luò)請求等。
現(xiàn)在目標(biāo)是將 Activity 中的代碼拆分開來,讓宿主 Activity 變得小而輕。
如果產(chǎn)品說新增一種支付方式,只需要改動很少的代碼,就可以輕而易舉的實(shí)現(xiàn)。
5.2 抽取成獨(dú)立的模塊
因為公司中有可能存在多個項目,支付模塊的分層應(yīng)該處于可復(fù)用的層級,以后很有可能將其封裝成一個獨(dú)立的 mouble,給不同的項目使用。
現(xiàn)在代碼全在 Activity 中,以后若是抽取 mouble 的話,相當(dāng)于整個需求重做。
5.3 組件黑盒
"組件黑盒"這個名詞是我自己的一個定義。大致意思:
將一個 View 或者一個類進(jìn)行高度封裝,盡可能少的暴露public方法給外部使用,自成一體。
業(yè)務(wù)方在使用時,可以直接黑盒使用某個業(yè)務(wù)組件,不必關(guān)心其中的邏輯。
業(yè)務(wù)方只需要一個簡單的操作,例如點(diǎn)擊按鈕調(diào)用方法,然后邏輯都在組件內(nèi)部實(shí)現(xiàn),組件內(nèi)處理外部事件的所有操作,例如:Loading、請求網(wǎng)絡(luò)、成功或者失敗。
業(yè)務(wù)方都不需要知道組件內(nèi)部的操作,做到宿主和組件的隔離。
當(dāng)然這種處理也是要分場景考慮的,其中一個重點(diǎn)就是這個組件是偏業(yè)務(wù)還是偏功能,也就是是否要將業(yè)務(wù)邏輯統(tǒng)一包進(jìn)組件,想清楚這個問題后,才能去開發(fā)一個業(yè)務(wù)組件。
因為添加支付方式是一個偏業(yè)務(wù)的功能,我的設(shè)計思路是:
外部 Activity 點(diǎn)擊添加對應(yīng)的支付方式,將支付方式的枚舉類型和支付方式有關(guān)的參數(shù)通過傳遞,然后不同的策略類組件執(zhí)行自己的添加邏輯,再通過一層回調(diào)將第三方支付的回調(diào)從 Activity 中轉(zhuǎn)接過來,每個策略類內(nèi)部處理自己的回調(diào)操作,具體的策略類自己維護(hù)成功或者失敗的ui。摘自https://xuyisheng.top/author/xuyisheng/
6.具體實(shí)現(xiàn)
6.1 定義頂層策略接口
interface IAddPayPlatform { fun addPayPlatform(param: AddCardParameter) fun thirdAuthenticationCallback(requestCode: Int?, resultCode: Int?, data: Intent?) fun addCardFailed(message: String?) fun addCardSuccess() }
6.2 通用支付參數(shù)類
open class AddCardParameter(val platform: PayPlatform) class AddStripeParameter(val card: Card, val setPrimaryCard: Boolean, platform: PayPlatform) : AddCardParameter(platform = PayPlatform.Stripe)
因為有很多種添加支付方式,不同的支付方式對應(yīng)的參數(shù)都不一樣。
所以先創(chuàng)建一個通用的卡片參數(shù)基類AddCardParameter, 不同的支付方式去實(shí)現(xiàn)不同的具體參數(shù)。這樣的話策略接口就可以只要寫一個添加卡片的方法addPayPlatform(param: AddCardParameter)。
6.3 Loading 的處理
因為我想實(shí)現(xiàn)的是黑盒組件的效果,所有添加卡片的loading也是封裝在每一個策略實(shí)現(xiàn)類里面的。
Loading的出現(xiàn)和消失這里有幾種常見的實(shí)現(xiàn)方式:
• 傳遞BaseActivity的引用,因為我的loading有關(guān)的方法是放在BaseActivity中,這種方式簡單但是會耦合BaseActivity。
• 使用消息事件總線,例如EventBus之類的,這種方式解耦強(qiáng),但是消息事件不好控制,還要添加多余的依賴庫。
• 使用LiveData,在策略的通用接口中添加一個方法返回Loading的LiveData, 讓宿主Activity自己去實(shí)現(xiàn)。
interface IAddPayPlatform { // ... fun getLoadingLiveData(): LiveData<Boolean>? }
6.4 提取BaseAddPayStrategy
因為每一個添加卡的策略會存在很多相同的代碼,這里我抽取一個BaseAddPayStrategy來存放模板代碼。
需要實(shí)現(xiàn)黑盒組件的效果,宿主Activity中都不需要去關(guān)注添加支付方式是不是存在網(wǎng)絡(luò)請求這一個過程,所以網(wǎng)絡(luò)請求也分裝在每一個策略實(shí)現(xiàn)類里面。
abstract class BaseAddPayStrategy(val activity: AppCompatActivity, val platform: PayPlatform) : IAddPayPlatform { private val loadingLiveData = SingleLiveData<Boolean>() protected val startActivityIntentLiveData = SingleLiveData<Intent>() override fun getLoadingLiveData(): LiveData<Boolean> = loadingLiveData protected fun startLoading() = loadingLiveData.setValue(true) protected fun stopLoading() = loadingLiveData.setValue(false) private fun reloadWallet() { startLoading() // 添加卡片完成后,重新刷新錢包數(shù)據(jù) } override fun addCardSuccess() { reloadWallet() } override fun addCardFailed(message: String?) { stopLoading() if (isEWalletPlatform(platform)) showAddEWalletFailedView() else showAddPhysicalCardFailedView(message) } /** * 添加實(shí)體卡片失敗展示ui */ private fun showAddPhysicalCardFailedView(message: String?) { showSaveErrorDialog(activity, message) } /** * 添加實(shí)體卡片成功展示ui */ private fun showAddPhysicalCardSuccessView() { showCreditCardAdded(activity) { activity.setResult(Activity.RESULT_OK) activity.finish() } } private fun showAddEWalletSucceedView() { // 添加電子錢包成功后的執(zhí)行 activity.setResult(Activity.RESULT_OK) activity.finish() } private fun showAddEWalletFailedView() { // 添加電子錢包失敗后的執(zhí)行 } // ---默認(rèn)空實(shí)現(xiàn),有些支付方式不需要這些方法--- override fun thirdAuthenticationCallback(requestCode: Int?, resultCode: Int?, data: Intent?) = Unit override fun getStartActivityIntent(): LiveData<Intent> = startActivityIntentLiveData }
6.5 具體的策略類實(shí)現(xiàn)
通過傳遞過來的AppCompatActivity引用獲取添加卡片的ViewModel實(shí)例AddPaymentViewModel,然后通過AddPaymentViewModel去調(diào)用網(wǎng)絡(luò)請求查詢添加卡片是否成功。
class AddXXXPayStrategy(activity: AppCompatActivity) : BaseAddPayStrategy(activity, PayPlatform.XXX) { protected val addPaymentViewModel: AddPaymentViewModel by lazy { ViewModelProvider(activity).get(AddPaymentViewModel::class.java) } init { addPaymentViewModel.eWalletAuthorizeLiveData.observeState(activity) { onSuccess { addCardSuccess()} onError { addCardFailed(it.detailed) } } } override fun thirdAuthenticationCallback(requestCode: Int?, resultCode: Int?, result: Intent?) { val uri: Uri = result?.data ?: return if (uri.host == "www.xxx.com") { uri.getQueryParameter("transactionId")?.let { addPaymentViewModel.confirmEWalletAuthorize(platform.name, it) } } } override fun addPayPlatform(param: AddCardParameter) { startLoading() addPaymentViewModel.addXXXCard(param) } }
7.簡單工廠進(jìn)行優(yōu)化
因為我不想在Activity中去引用每一個具體的策略類,只想引用抽象接口類IAddPayPlatform, 這里通過一個簡單工廠來優(yōu)化。
fun setCurrentPlatform(activity: AppCompatActivity, payPlatform: PayPlatform): IAddPayPlatform? { return when (payPlatform) { PayPlatform.STRIPE -> AddStripeStrategy(activity) PayPlatform.PAYPAL -> AddPayPalStrategy(activity) PayPlatform.LINEPAY -> AddLinePayStrategy(activity) PayPlatform.GOOGLEPAY -> AddGooglePayStrategy(activity) PayPlatform.RAPYD -> AddRapydStrategy(activity) else -> null } }
8.再增加一種支付方式
如果再增加一種支付方式,宿主Activity中的代碼都可以不要改動,只需要新建一個新的策略類,實(shí)現(xiàn)頂層策略接口即可。
這樣,不管是刪除還是新增一種支付方式,維護(hù)起來就很容易了。
策略模式的好處就顯而易見了。
以上就是Android多種支付方式的實(shí)現(xiàn)示例的詳細(xì)內(nèi)容,更多關(guān)于Android支付方式的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android 對話框(Dialog)大全示例(建立你自己的對話框)
android開發(fā)中,對話框的使用還是很平凡的,本篇文章介紹了Android 對話框的實(shí)例,詳細(xì)的介紹了多種對話框的方法,有興趣的可以了解一下。2016-11-11Android Studio里如何使用lambda表達(dá)式
這篇文章主要介紹了Android Studio里如何使用lambda表達(dá)式,需要的朋友可以參考下2017-05-05Android開發(fā)中總結(jié)的Adapter工具類【附完整源碼下載】
這篇文章主要介紹了Android開發(fā)中總結(jié)的Adapter工具類,簡單說明了Adapter的功能,并結(jié)合實(shí)例形式分析了Adapter工具類的相關(guān)使用方法,并附帶完整源碼供讀者下載參考,需要的朋友可以參考下2017-11-11Android使用文件進(jìn)行數(shù)據(jù)存儲的方法
這篇文章主要介紹了Android使用文件進(jìn)行數(shù)據(jù)存儲的方法,較為詳細(xì)的分析了Android基于文件實(shí)現(xiàn)數(shù)據(jù)存儲所涉及的相關(guān)概念與使用技巧,具有一定參考借鑒價值,需要的朋友可以參考下2015-09-09Android通知欄增加快捷開關(guān)的功能實(shí)現(xiàn)教程
對于Android來說其中一項很方便的操作便是下拉菜單,下拉菜單欄可以快捷打開某項設(shè)置,這篇文章主要給大家介紹了關(guān)于Android通知欄增加快捷開關(guān)的功能實(shí)現(xiàn),需要的朋友可以參考下2023-01-01