Android- change the way of thinking to achieve H5 to wake up the App and jump to the specified page

1. How does H5 wake up the App

There are many ways for H5 to wake up the App, but generally the front-end will adopt the form of URL Scheme in order to be compatible with both Android and iOS. The following will focus on this method. A URL Scheme is composed of the following parts:

[scheme]://[host]:[port][path]?[参数]

Description and examples

  • schemeIt is negotiated with the front end. Generally, the English name of the company can be used, such as Taobao taobao. Here is an example defined asabc
  • hostIt is a host, can write the company domain name, and can be multi-level, here is an example defined asabc.com
  • portIt is a port, which can be defined casually, here is an example defined as8088
  • pathIs the path, you can define it casually, here is an example defined as/router
  • 参数It is the parameter brought by the front end to the App. Here, it is defined astoken=123&data={这是一个json字符串}

Then the URL Scheme spelled out is:

abc://abc.com:8088/router?token=123&data={这是一个json字符串}

In the project, you need to define an Activity that can be opened externally according to the above protocol, and register it in the manifest file as follows:

<!--H5唤起App的中转页面-->
<activity
    android:name=".activity.H5WakeUpNativeActivity"
    android:exported="true"
    android:screenOrientation="portrait"
    tools:ignore="AppLinkUrlError">
    <intent-filter>
        <data
            android:host="abc.com"     //这里的参数和前端定义的保持一致
            android:path="/router"     //这里的参数和前端定义的保持一致
            android:port="8088"        //这里的参数和前端定义的保持一致
            android:scheme="abc" />    //这里的参数和前端定义的保持一致

        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <action android:name="android.intent.action.VIEW" />
    </intent-filter>
</activity>

With this Activity, you can consider subsequent operations.

Two, ideas

There may be the following problems when waking up the app:
1. The app may or may not be started. If not, you need to start the app first. After starting the app, you can jump, and the jump data needs to be saved.
2. If the app is already in the background, it needs to determine whether the user has logged in. If the target page of the jump needs to log in, it needs to log in first before jumping to the target interface, and the jump data needs to be saved.
3. There are many target activities that you want to jump to, so you need to find a place that is responsible for jumping and maintenance.

If it is too troublesome to use data transparent transmission and ARouter broadcast notification continue (postCard), you can try other ideas.

Because in general, the App exists in MainActivity, so the jump and maintenance are placed in MainActivity.

The apps we develop generally distinguish between two situations. One is that you need to log in before entering MainActivity, and the other is that you don’t need to log in before entering MainActivity. You only need to log in when you encounter specific operations.

Let's look at it separately.

3. Preparation

1. The project uses ARouter to realize the jump, otherwise the componentized project cannot get the Activity classes and objects of other modules.
2. The project maintains a globally callable ViewModel object. You can read an article I wrote before " How does Android design a globally callable ViewModel object? " This article uses the second method in the blog. (It is recommended to click to take a look, otherwise you may not understand the following content).
3. Agree on the format of the JSON data passed in with the front end, take the following parsed results as an example:

/**
 * H5唤醒App时带过来的数据
 * 原始Json数据:{"path":"AC001","data":{"id":13786,"user_id":56129},"code":200,"msg":""}
 */
data class H5ParamsBean(
    val path: String = "", //跳转的路径标识
    val code: Int = 0,
    val msg: String = "",
    var data: DataBean? = null, 
) 

data class DataBean(
    var id: Int = 0, 
    var user_id: Int = 0
)

Where paththe field jumps to should be discussed with front-end colleagues, for example:

//从 h5 页面经过路由activity跳转至修改密码界面
const val H5_ROUTER_UPDATE_PASSWORD_ACTIVITY = "AC001"

//从 h5 页面经过路由activity跳转至忘记密码界面
const val H5_ROUTER_FORGOT_PASSWORD_ACTIVITY = "AC002"

//从 h5 页面经过路由activity跳转至注销账号界面
const val H5_ROUTER_CANCEL_ACCOUNT_ACTIVITY = "AC003"

//从 h5 页面经过路由activity跳转至意见反馈界面
const val H5_ROUTER_FEED_BACK_ACTIVITY = "AC004"

Change the above data according to your actual needs. They are listed here for the convenience of explanation.

Let's get to the point.

4. You need to log in to enter the App of MainActivity

Let’s start with apps that need to log in to enter MainActivity, and then talk about apps that can enter MainActivity without logging in.

After getting the data passed by the front end, set the data to the global callable ViewModelobject , and then subscribe to this , remember that the value should be set to 1, because when the App is not started, it is to set the value first and then subscribe , so data replay should be set to 1.EventViewModelSharedFlow--->h5WakeUpMutableSharedFlowMainActivitySharedFlowSharedFlowreplay(replay)

First look at the code for H5 to wake up the native Activity:

/**
 * H5跳转到原生页面
 * 具体传参如下:
 * abc://abc.com:8088/router?token="123"&data={这是一个json字符串}
 */
class H5WakeUpNativeActivity :
    BaseActivity() {
    
    

    override fun onCreate(savedInstanceState: Bundle?) {
    
    
        super.onCreate(savedInstanceState)
        //唤醒App
        wakeUpApp()
    }

    private fun wakeUpApp() {
    
    
        //判断MainActivity是否在栈中
        isMainActivityRunning().also {
    
     isRunning ->
            val paramsBean = getParamsBean()
            if (isRunning) {
    
       //MainActivity在栈中
                //eventViewModel就是全局可调用的viewmodel的对象,下同
                //设置唤醒时携带的值
                eventViewModel.setH5WakeUpValue(paramsBean)
            } else {
    
       //MainActivity不在栈中
                eventViewModel.setH5WakeUpValue(paramsBean)
                //唤醒App
                ARouter.getInstance().build(ARouterPath.StartActivity).navigation()
            }
        }
        finish()
    }

    //获取H5传递过来的值并解析成Bean类
    private fun getParamsBean(): H5ParamsBean? {
    
    
        val uri = intent.data
        if (isRecognizable(uri)) {
    
    
            uri?.getQueryParameter("data")?.apply {
    
    
                if (isNotEmpty()) {
    
    
                    try {
    
    
                        return GsonUtils.fromJson(this, H5ParamsBean::class.java)
                    } catch (exception: JsonSyntaxException) {
    
    
                        LogUtils.d("H5WakeUpNativeActivity JsonSyntaxException")
                    }
                }
            }
        }
        return null
    }

    //是不是需要这个类解析的scheme
    private fun isRecognizable(uri: Uri?): Boolean {
    
    
        return if (uri != null) {
    
    
            val url: String = uri.toString()
            return url.startsWith("abc://abc.com:8088/router?")
        } else {
    
    
            false
        }
    }

    /**
     * 判断MainActivity是否在运行
     */
    private fun isMainActivityRunning(): Boolean {
    
    
        //使用的是AndroidUtilCode库的Api表示感谢
        ActivityUtils.getActivityList().apply {
    
    
            if (isNotEmpty()) {
    
    
                forEach {
    
    
                    if (it::class.java.simpleName == "MainActivity") {
    
    
                        return true
                    }
                }
            }
        }
        return false
    }
}

The logic here is relatively simple:
1. Take the string passed by H5 and parse it into dataclasses . 2. Set the data to . 3. If it is not in the call stack, start it .JsonStringBean
SharedFlow--->h5WakeUpMutableSharedFlow
MainActivityApp

Most of the above codes are for parsing data, no matter what wake-up method is used, it basically needs to be written.

The following key EventViewModelcode:

class EventViewModel : ViewModel() {
    
    
   
   //与viewModel生命周期关联的协程
   private val coroutineScope: CoroutineScope
       get() = viewModelScope
   
   /**
    * H5唤醒App的跳转的Flow
    */
   private val h5WakeUpMutableSharedFlow: MutableSharedFlow<H5ParamsBean> =
       MutableSharedFlow(replay = 1)       <--------注意这里


   /**
    * 设置H5唤醒携带的值
    */
   fun setH5WakeUpValue(bean: H5ParamsBean?) {
    
    
       bean?.apply {
    
    
           h5WakeUpMutableSharedFlow.tryEmit(bean)
       }
   }

   /**
    * 开始订阅,获取H5唤醒携带的值
    */
   fun getH5WakeUpValue(method: (bean: H5ParamsBean) -> Unit) {
    
    
       coroutineScope.launch(Dispatchers.IO) {
    
    
           h5WakeUpMutableSharedFlow.collect {
    
    
               method.invoke(it)
           }
       }
   }
}

Subscribe in MainActivity

eventViewModel.getH5WakeUpValue {
    
    
    //TODO 跳转到具体Activity的业务代码
}

The advantages of the above methods are:
1. Concise, except for parsing data, the actual code is just a little bit.
2. Let MainActivitythe subscription solve the problem of whether to log in, because only by logging in can you enter MainActivity.
3. The ease SharedFlowof use replay=1solves Appthe problems of starting value transfer and login value transfer. Because the setting data comes first and MainActivitythe subscription comes later, MainActivityyou can still get the data when you enter.

5. You can enter the App of MainActivity without logging in

It is a little troublesome to enter MainActivity without logging in, because you need to monitor the login status, but EventViewModelit can be easily solved in . Create a StateFlowmonitoring login state, and then let it link with the one we defined above SharedFlow--->h5WakeUpMutableSharedFlow. When the user logs in successfully, continue to jump the unprocessed jump. The code is as follows:

class EventViewModel : ViewModel() {
    
    

    private var mH5ParamsBean: H5ParamsBean? = null

    //与viewModel生命周期关联的协程
    private val coroutineScope: CoroutineScope
        get() = viewModelScope

    /**
     * 全局的登录状态
     */
    private val loginMutableStateFlow: MutableStateFlow<LoginState> =
        MutableStateFlow(LoginState.NOT_LOGGED_IN)    //默认未登录
    val loginStateFlow: StateFlow<LoginState> = loginMutableStateFlow

    /**
     * H5唤醒App的跳转
     */
    private val h5WakeUpMutableSharedFlow: MutableSharedFlow<H5ParamsBean> =
        MutableSharedFlow(replay = 1)

    /**
     * 设置登录的状态。登录/退出登录都需要设置。LoginState自定义登录的枚举类。
     */
    fun setLoginState(value: LoginState) {
    
    
        loginMutableStateFlow.value = value
    }

    /**
     * 设置H5唤醒携带的值
     */
    fun setH5WakeUpValue(bean: H5ParamsBean?) {
    
    
        bean?.apply {
    
    
            h5WakeUpMutableSharedFlow.tryEmit(bean)
        }
    }

    /**
     * 获取H5唤醒携带的值
     */
    fun getH5WakeUpValue(method: (bean: H5ParamsBean) -> Unit) {
    
    
        coroutineScope.apply {
    
    
            launch(Dispatchers.IO) {
    
    
                //H5唤醒原生页面的事件回调
                h5WakeUpMutableSharedFlow.collect {
    
    
                    when (it.path) {
    
    
                        "AC006" -> {
    
       //需要登录才能跳转的页面 AC006是举例
                            if (loginStateFlow.value == LoginState.LOGGED_IN) {
    
      //已经登录
                                method.invoke(it)
                            } else {
    
        //未登录
                                //将值存起来
                                mH5ParamsBean = it
                                //跳转到登录页面
                                ARouter.getInstance().build(ARouterPath.LoginActivity).navigation()
                            }
                        }
                        else -> {
    
       //不需要登录就能跳转的页面
                            method.invoke(it)
                        }
                    }
                }
            }
            launch(Dispatchers.IO) {
    
    
                //登录状态的事件回调
                loginStateFlow.collect {
    
     state ->
                    when (state) {
    
    
                        LoginState.LOGGED_IN -> {
    
        //用户已登录
                            //用户已登录,继续之前的跳转动作
                            mH5ParamsBean?.apply(method).also {
    
    
                                //跳转后参数设置为null
                                mH5ParamsBean = null
                            }

                        }
                        LoginState.NOT_LOGGED_IN -> {
    
       //用户未登录

                        }
                    }
                }
            }
        }
    }
}

After the user successfully logs in on the login page, modify StateFlowthe status value of the login, and continue to complete the unfinished jump event, so that the whole event runs through. There is no need to use broadcasting in the ARouter interceptor so cumbersome.

Extension :
① What if the user cancels the login and needs to clear the jump? Add the status of the user canceling the login in the monitoring loginStateFlow, call back in time, and set the data to null.

② What should I do if H5 needs to wake up multiple page jumps at one time? That is the strength of Flow, which distributes the data one by one and jumps one by one.

at last

If you want to become an architect or want to break through the 20-30K salary range, then don't be limited to coding and business, but you must be able to select models, expand, and improve programming thinking. In addition, a good career plan is also very important, and the habit of learning is very important, but the most important thing is to be able to persevere. Any plan that cannot be implemented consistently is empty talk.

If you have no direction, here I would like to share with you a set of "Advanced Notes on the Eight Major Modules of Android" written by the senior architect of Ali, to help you organize the messy, scattered and fragmented knowledge systematically, so that you can systematically and efficiently Master the various knowledge points of Android development.
img
Compared with the fragmented content we usually read, the knowledge points of this note are more systematic, easier to understand and remember, and are arranged strictly according to the knowledge system.

Welcome everyone to support with one click and three links. If you need the information in the article, you can directly scan the CSDN official certification WeChat card at the end of the article to get it for free↓↓↓

PS: There is also a ChatGPT robot in the group, which can answer your work or technical questions
picture

Guess you like

Origin blog.csdn.net/weixin_43440181/article/details/131723061