遵循开闭原则实现通过Intent控制页面执行的具体逻辑

项目背景

在app开发中经常遇到的一个问题是Activity的重用。对于很多页面布局相同,但是业务逻辑上有少量不同的页面。我们希望能够重复使用一个Activity来减少工作量。一种实现方式是,通过Intent传一个值给Activity,并在Activity中通过switch语句判断该运行哪个具体的业务逻辑。如下:

val intent = Intent(context, DemoActivity::java.class)
intent.putExtra(KEY_MODEL, MODEL_A)
intent.putExtra(KEY_PARAMS_A, "abc")
startActivity(intent)

val intent = Intent(context, DemoActivity::java.class)
intent.putExtra(KEY_MODEL, MODEL_B)
intent.putExtra(KEY_PARAMS_B0, "hello word")
intent.putExtra(KEY_PARAMS_B1, 10)
startActivity(intent)


class DemoActivity : AppCompactActivity {

	onCreate(savedInstanceState : Bundle?) {
		super.onCreate(savedInstanceState)
		val model = intent.getIntExtra(KEY_MODEL)
		
		
		when(model) {
			MODEL_A->{
				val paramsA = intent.getStringExtra(KEY_PARAMS_A)
			}//执行A逻辑
			MODEL_B->{
				val paramsB0 = intent.getStringExtra(KEY_PARAMS_B0)
				val paramsB1 = intent.getIntExtra(KEY_PARAMS_B1, -1)
			}//执行B逻辑
			MODEL_C->{}//执行C逻辑
		}
	}
}

上述方法在逻辑不复杂时,实现方便快捷。但是该方法存在如下问题:
1)违反开闭原则:目前项目中存在A、B、C三种业务逻辑,如果后续需求迭代,需要添加新的业务逻辑D。则需要对DemoActivity页面进行修改才能满足要求
2)每个业务逻辑都有可能有自己特定的输入参数,上述实现思路在调用时,需要在调用时根据不同的业务逻辑传递不同的参数,稍不谨慎可能会造成参数漏传
3)多种业务逻辑的代码存在于同一个页面中,给后续代码维护带来相当大的麻烦,特别是在多种复杂业务逻辑的情况下。

还有一种思路,是在onActivityResult方法中返回DemoActivity的通用结果,然后在onActivityResult中处理。这样实现仍然存在局限性。假设一个项目中有多个地方需要调用该DemoActivity,并执行A逻辑。那么就需要在多处实现onActivityResult并重复对A逻辑代码块的调用。给代码维护带来不便。

设计思路

解决多个业务逻辑分离的问题,我们很容易联想到策略模式。为每一种业务逻辑定义一个策略类,分别实现各个业务逻辑。又由于是Android开发,将一个自定义的策略类通过Intent发送给定义好的Activity,我们可以将策略类继承自Parcelable。

撸代码

策略类基类

@UiThread
class BaseAction() : Parcelable {

	open fun onCreate(activity : ActionActivity) {}

	open fun run(activity : ActionActivity, data : Any?)

	open fun onDestroy(activity : ActionActivity) {}

	constructor(parcel : Parcel)

	override fun writeToParcel(parcel: Parcel, flags: Int) {}

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<BaseAction> {
        override fun createFromParcel(parcel: Parcel): BaseAction{
            return BaseAction(parcel)
        }

        override fun newArray(size: Int): Array<BaseAction?> {
            return arrayOfNulls(size)
        }
    }
}

onCreate、onDestroy方法分别于Activity的onDestroy方法和onDestroy方法绑定。使得Action可以获取Activity的生命周期。当然也可以实现onStart、onStop、onPause、onResum等Activity生命周期方法的绑定
run方法的data参数由Activity传递进来。根据具体的情况可以定义成需要的数据类型,这里定义为Any?

Activity实现

class ActionActivity : AppCompatActivity {

	private var action : BaseAction?
	
	onCreate(savedInstanceState : Bundle?) {
		super.onCreate(savedInstanceState)
		...
		
		action = intent.getParcelableExtra(KEY_ACTION)
		action?.onCreate(this)
	}

	onStart() {
		super.onStart()
		doAction()
	}
	
	onDestroy() {
		super.onDestroy()
		action?.onDestroy()
	}
	
	private fun doAction() {
		action?.run(this, null)
	}
}

doAction方法根据具体的情况触发,例如用户点击某个按钮,或者其他操作后执行。这里为了说明方便,在Activity#onStart方法中触发

具体业务A的Action

class AModelAction(val value : Int) : BaseAction() {

	override fun run(activity : ActionActivity, data : Any?) {
		Log.i("action", "run in action A, value= $value")
	}

	constructor(parcel : Parcel) : this(parcel.readInt())

	override fun writeToParcel(parcel: Parcel, flags: Int) {
		super.writeToParcel(parcel, flags)
		parcel.writeInt(this.value)
	}

    companion object CREATOR : Parcelable.Creator<AModelAction> {
        override fun createFromParcel(parcel: Parcel): AModelAction{
            return AModelAction(parcel)
        }

        override fun newArray(size: Int): Array<AModelAction?> {
            return arrayOfNulls(size)
        }
    }
}

这里AModelAction有一个参数value,在实现parcelable时,需要实现该参数的读写过程。在run方法种调用打印一行log。注意CRATOR对象的方法一定要指定具体的Action类型,否则intent.getParcelableExtra方法返回的类型会与intent.putParcelableExtra的不一致

同样可以实现BModelAction

class BModelAction(val value0 : String, val value1 : Int) : BaseAction() {

	override fun run(activity : ActionActivity, data : Any?) {
		Log.i("action", "run in action B, value= $value0, $value1")
	}

	constructor(parcel : Parcel) : this(parcel.readString(), parcel.readInt())

	override fun writeToParcel(parcel: Parcel, flags: Int) {
		super.writeToParcel(parcel, flags)
		parcel.writeString(this.value0)
		parcel.writeInt(this.value1)
	}

    companion object CREATOR : Parcelable.Creator<BModelAction> {
        override fun createFromParcel(parcel: Parcel): BModelAction{
            return BModelAction(parcel)
        }

        override fun newArray(size: Int): Array<BModelAction?> {
            return arrayOfNulls(size)
        }
    }
}

在BModelAction中传递一个String参数和一个Int参数。同样可以定义更多的BaseAction子类

启动ActionActivity并执行AModelAction

val intent(context, ActionActivity::java.class)
intent.putExtra(KEY_ACTION, AModelAction(10))
startActivity(intent)

可以看大日志输出:
run in action A, value= 10

启动ActionActivity并执行BModelAction

val intent(context, ActionActivity::java.class)
intent.putExtra(KEY_ACTION, BModelAction("hello word", 12))
startActivity(intent)

调用后的日志输出:
run in action B, value= hello word, 12

可以看出,该实现方案,在启动页面和指定具体业务逻辑时非常的清晰。并且不同业务逻辑都分开定义在具体的策略类中。现在在Activity内仅提供一个触发条件的控制。我们也可以在Action的onCreate方法中修改触发条件,实现更加灵活的业务场景,例如:

class CModelAction() : BaseAction() {

    override fun onCreate(activity : ActionActivity) {
    	activity.findViewById<Button>(R.id.btDoAction).setOnClickListener{
    		activity.doAction()
    	}
    }
	override fun run(activity : ActionActivity, data : Any?) {
		Log.i("action", "run in action C= $data")
	}

	constructor(parcel : Parcel) : this(parcel.readString())

	override fun writeToParcel(parcel: Parcel, flags: Int) {
		super.writeToParcel(parcel, flags)
		parcel.writeString(this.value)
	}

    companion object CREATOR : Parcelable.Creator<CModelAction> {
        override fun createFromParcel(parcel: Parcel): CModelAction{
            return CModelAction(parcel)
        }

        override fun newArray(size: Int): Array<CModelAction?> {
            return arrayOfNulls(size)
        }
    }
}

策略C添加了一个通过点击按钮的触发条件。
我们在项目开发中,总是在不断的提炼自己的代码,使得代码越来越精简和可靠。针对上述的问题,小伙伴们是否还能够给出更加理想的解决方案呢,欢迎留言。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值