Android Scheme方案

在Android開發(fā)的過程中兄墅,很多活動頁面都使用H5進行快速開發(fā)并隨時更新,這樣的話H5與原生的交互就不可避免

H5相關除了Webview類以外诈泼,在使用的過程中還需要用到別的類:
1.WebSettings 從WebView中get膝迎,進行一些設置攘须,比如開啟js支持摧扇,viewport支持(該配置幫助H5進行不同屏幕分辨率的適配)圣贸,使用網頁自身縮放,dom存儲扛稽,數據庫吁峻,多窗口等
2.WebViewClient 主要功能組件,頁面加載完成庇绽,頁面結束锡搜,頁面錯誤等回調橙困,最主要的是通過shouldOverrideUrlLoading方法對url進行攔截以進行客戶端需要的一些操作瞧掺,也在該方法中進行scheme的調用,另外還有其他的資源攔截方法可以用來緩存
3.WebChromeClient 處理關于外層的一些事物凡傅,如網站標題辟狈,Icon,加載進度夏跷,定位權限請求等

在H5與原生的交互中哼转,原生調用H5比較容易,直接使用以下的代碼即可

WebView.loadUrl("javascript:XXX")

對于H5調用來說一般有兩種方法槽华,一個是通過webview.addJavascriptInterface(Object,String)和@JavascriptInterface注解使H5可以直接調用原生的某些方法壹蔓,但這樣做的局限性比較大,不太推薦

另一種方法就是使用scheme:

Android Scheme主要是用來H5與原生頁面的交互猫态,而且這種使用url的交互也可以用于原生佣蓉,后端返回不同的鏈接客戶端做出不同的操作披摄,非常靈活,在信鴿的推送點擊中也很好用

對于這個需求主要分為以下幾個步驟:

1.監(jiān)聽webview發(fā)出的請求
2.對該請求進行解析
3.對解析結果進行相應的操作

1.監(jiān)聽并攔截webview的請求

對于webview請求相關的回調都存在于WebViewClient中

需要完成H5調用需要H5發(fā)起一個url請求勇凭,客戶端在WebViewClient.shouldOverrideUrlLoading()方法中進行攔截

該方法分為兩個版本疚膊,如下:

@RequiresApi(Build.VERSION_CODES.N)//這個注解只是個提示,起不到過濾版本的作用虾标,還是需要進行版本判斷寓盗,否則高版本會兩個方法都調用,N以下版本沒有這個方法
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        val url = request.url.toString()
        if (url.startsWith(HTTP_URL_PREFIX)) {//不攔截http請求璧函,這里不攔截的話會在同一個webview中打開新的頁面傀蚌,如有需求可以進行攔截并使其打開新的頁面
            return super.shouldOverrideUrlLoading(view, request)
        }
        return SchemeUtil.openScheme(activity!!, url, fragment)//處理其他請求
    }
    return false//返回false不攔截
}
 
@Suppress("deprecation")
override fun shouldOverrideUrlLoading(view: WebView?, url: String): Boolean {
    if(Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
        if (url.startsWith(HTTP_URL_PREFIX)) {
            return super.shouldOverrideUrlLoading(view, url)
        }
        return SchemeUtil.openScheme(activity!!, url, fragment)
    }
    return false
}

2.解析請求

第一步里過濾了http請求不進行scheme的相關流程

所以需要進行scheme調用的就前端和客戶端約定一套schme,可以使用 項目名://+方法名+.項目名?json_params={} 格式柳譬,加兩個項目名來過濾以防錯誤調用(保險的話可以用更復雜的名字或者加密喳张,一般不太需要)

然后就是對某個具體的scheme進行解析:

//第一步解析url獲取參數,protocolStr為完成的scheme鏈接美澳,fragment為發(fā)起請求的fragment销部,對于webview來說可以在創(chuàng)建webviewclient時將activity和fragment傳進來方便后續(xù)使用
//這個方法就是將scheme解析為{String方法名}和{JSONObject參數}
    fun openScheme(context: Context,protocolStr:String,fragment: Fragment?){
        //防抖
        if(System.currentTimeMillis() - lastTime < GAP_OF_SAME_SCHEME
            && TextUtils.isEmpty(protocolStr)
            && protocolStr == lastHost)return
        lastTime = System.currentTimeMillis()
        lastHost = protocolStr
 
 
        /**
         *     foo://example.com:8042/over/there?name=ferret&psw=123#nose
         *     \_/   \______________/\_________/ \_________________/ \__/
         *      |           |            |                |           |
         *    scheme     authority       path           query      fragment
         *
         *   
         */
        try {
            val uri = URI(protocolStr)
            val host = uri.authority
            val query = protocolStr.substring(protocolStr.indexOf('?') + 1)
            var paramObj:JSONObject? = null
            if(query!=null) {
                val paramStr = query.split("&")
                val paramMap = HashMap<String, String>()
                for (pairStr in paramStr) {
                    val pair = pairStr.split(Regex("="),2)
                    if (pair.size == 2) {
                        paramMap[pair[0]] = StringUtil.decodeString(pair[1])
                    }
                }
                // 獲取json_params參數
                val json = paramMap[mSchemeKeyParams]
                paramObj = if (json == null) null else JSONObject(json)
            }
            var type:String? = null
            if (host!=null && host.indexOf(".") != -1){
                type = host.substring(0,host.indexOf("."))
            }
            openScheme(context,type,paramObj,fragment)
        }catch (e: Exception){
            e.printStackTrace()
        }
    }

3.對解析結果進行處理

這里拿到方法名和參數以后就可以進行對應的操作

有很多方法可以進行,最直接的方法可以使用switch-case在原地進行處理制跟,但這樣就是造成一個類過于龐大導致可讀性可維護性下降舅桩,而且即使調用別的類的方法也需要每次添加scheme都在這里修改,不符合開閉原則

這里推薦使用反射雨膨,具體操作如下:

1.新建一個scheme處理接口擂涛,比如:

interface ISchemeFilter {
    fun process(context: Context, paramObj: JSONObject?, fragment: Fragment?)
}

2.創(chuàng)建一個上面接口的實現類,比如:

class OpenXXXFilter:ISchemeFilter {
    override fun process(context: Context, paramObj: JSONObject?, fragment: Fragment?) {
        XXXActivity.open(context)
        //如果簡單的操作在這里可以完成則到此結束聊记,如果不行則使用context和fragment進行相應方法的調用
    }
}

3.建立一個Map撒妈,以方法名為鍵,具體實現類的類名 //這個可以不用排监,可以直接使用方法名作為類名狰右,但是不太靈活

4.通過方法名查詢到對應的類名,并使用該類名創(chuàng)建實例并調用其process方法舆床,具體代碼如下:

val schemeFilterClassName = schemeMap[type]
schemeFilterClass = Class.forName(schemeFilterClassName!!)
val constructor = schemeFilterClass!!.getConstructor()
val schemeFilter = constructor.newInstance() as ISchemeFilter
schemeFilter.process(context, paramObj, fragment)
//注意判空與try-catch
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末棋蚌,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子挨队,更是在濱河造成了極大的恐慌谷暮,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件盛垦,死亡現場離奇詭異湿弦,居然都是意外死亡,警方通過查閱死者的電腦和手機腾夯,發(fā)現死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門颊埃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赌蔑,“玉大人,你說我怎么就攤上這事竟秫⊥薰撸” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵肥败,是天一觀的道長趾浅。 經常有香客問我,道長馒稍,這世上最難降的妖魔是什么皿哨? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮纽谒,結果婚禮上证膨,老公的妹妹穿的比我還像新娘。我一直安慰自己鼓黔,他們只是感情好央勒,可當我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著澳化,像睡著了一般崔步。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上缎谷,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天井濒,我揣著相機與錄音,去河邊找鬼列林。 笑死瑞你,一個胖子當著我的面吹牛,可吹牛的內容都是我干的希痴。 我是一名探鬼主播者甲,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼润梯!你這毒婦竟也來了过牙?” 一聲冷哼從身側響起甥厦,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤纺铭,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后刀疙,有當地人在樹林里發(fā)現了一具尸體舶赔,經...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年谦秧,在試婚紗的時候發(fā)現自己被綠了竟纳。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片撵溃。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖锥累,靈堂內的尸體忽然破棺而出缘挑,到底是詐尸還是另有隱情,我是刑警寧澤桶略,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布语淘,位于F島的核電站,受9級特大地震影響际歼,放射性物質發(fā)生泄漏惶翻。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一鹅心、第九天 我趴在偏房一處隱蔽的房頂上張望吕粗。 院中可真熱鬧,春花似錦旭愧、人聲如沸颅筋。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽垃沦。三九已至,卻和暖如春用押,著一層夾襖步出監(jiān)牢的瞬間肢簿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工蜻拨, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留池充,地道東北人。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓缎讼,卻偏偏與公主長得像收夸,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子血崭,可洞房花燭夜當晚...
    茶點故事閱讀 44,914評論 2 355