When Android is developed, we use different architectural solutions (patterns). For example, Mvp , Mvvm , Mvi , etc ... Each of these patterns solves several important problems, and since they are not perfect, they leave us some unsolved problems. For example, these tasks include navigation within the application (routing), transferring information from the screen to the screen (speaking the screen, I mean Activity, Fragment or View), Saving the application state when the configuration change changes.
In our company, we also faced these problems, some were solved in an easy way, but the first of them did not find a specific solution, having tried various methods of its solution, we wrote our own Flowzard library.
In our company we use Mvp architecture. To have maximum flexibility in showing, changing and transferring data between screens, we try to follow the principle called Single-responsibility principle . The principle says that each module should solve a specific task. In our case, the screen should be isolated from the global task and should solve its specific task to show / receive information. He does not need to know about other screens at all. So we can achieve maximum flexibility. Below is an example of setting up and using the library.
Creating flow
class MainFlow(flowManager: FlowManager) : Flow(flowManager) { // flow override fun onCreate(savedInstance: DataBunch?, data: DataBunch?) { super.onCreate(savedInstance, data) } }
Creating a flow navigator
Navigators perform two functions: Create flow containers (Activity, Fragment, View) for transitions between flow and screens for transitions within flow.
class DefaultFlowNavigator(activity: AppCompatActivity) : SimpleFlowNavigator(activity){ // flow Activity override fun getActivityIntent(id: String, data: Any?): Intent { return when (id) { Flows.SIGN_UP -> Intent(activity, SignupActivity::class.java) else -> throw RuntimeException("Cannot find activity for id=$id") } } }
Attaching to an Activity
To associate an Activity with Flow, we inherit FlowActivity and provide a Navigator, in our case DefaultFlowNavigator.
class MainActivity : FlowActivity() { override val navigator: Navigator get() = DefaultFlowNavigator(this) }
Creating FlowManager
class DefaultFlowManager : FlowManager() { // (main) flow override fun createMainFlow(): Flow { return MainFlow(this) } // flow override fun createFlow(id: String): Flow { return when (id) { Flows.SIGN_UP -> SignupFlow(this) else -> throw RuntimeException("Cannot find flow for id=$id") } } } // FlowManager Application class App : Application(), FlowManagerProvider { private val flowManager = DefaultFlowManager() override fun getProvideManager(): FlowManager { return flowManager } }
Messaging between flow and screen
When you press the login button, the activation sends a message to main flow. Flow creates a SIGN_UP flow and waits for a response from it. With a successful login, SIGN_UP flow sends the result to the main flow and is called onFlowResult: MainFlow with the code and the result object. Main flow checks if the result is correct then sends a message back to the activation that the user has successfully logged in.
class MainFlow(flowManager: FlowManager) : Flow(flowManager) { companion object { const val LOGIN_REQUEST_CODE = 1 } // override fun onMessage(code: String, message: Any) { super.onMessage(code, message) if (code == "main" && message == "login") { newFlow(Flows.SIGN_UP, LOGIN_REQUEST_CODE) } } // flow override fun onFlowResult(requestCode: Int, result: Result) { super.onFlowResult(requestCode, result) if (requestCode == LOGIN_REQUEST_CODE && result is Result.SUCCESS) { sendMessageFromFlow("main", true) } } } class MainActivity : FlowActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) loginButton.setOnClickListener { // “login” “main” flow.sendMessage("main", "login") } // “main” setMessageListener("main") { if (it is Boolean && it) { statusTextView.text = "Logined" } } } }
Since Android saves the Activity and Fragment stacks, the created flows with these containers will save and restore their state. With the View container, you will need to write your custom FlowManager as the library does not yet have such a manager. In the next update there will be a feature for saving intermediate data from flow.
Since I didn’t want a lot of code in the article, I will limit myself to this example. Here is a link to the repository for a detailed study of the library.
Source: https://habr.com/ru/post/435248/
All Articles