html调用deeplink,如何优雅地从浏览器打开本地应用deeplink
相信Android開發者都會知道,在應用內跳轉頁面,我們肯定會用到 Intent。使用 Intent 跳轉頁面有顯式跳轉和隱式跳轉兩種方式。
Intent intent = new Intent();
intent.setClass(this, MainActivity.class);
startActivity(intent);
典型的顯式跳轉就如同上面的代碼一樣,如果需要攜帶數據的話,我們也有兩種方式。一方面我們可以調用 Intent 的 putExtra 系列方法,通過鍵值對的方式把數據傳遞過去。Intent 支持傳遞的數據包括基本類型數據、字符串、序列化以及各自的數組,有意思的是 Intent 支持 Parcelable數組 但不支持 Serializable數組。然后在跳轉的頁面通過相應的 get方法 把數據拿出來即可。
另一方面,我們也可以用 Intent 的 setData方法 將統一資源標識符Uri傳遞過去,然后在接收端解析 Uri 來拿出數據。對于一個 Uri,主要由以下幾部分組成:
scheme://host:port/path?query
相應地,我們便能從 Uri 中把各個部分的信息拿出來。
而隱式跳轉則是通過 Intent 的 action 來實現。典型的便是我們在桌面點擊了應用的圖標,然后應用啟動進入我們的第一個頁面。相信大家都知道對于應用自動的第一個頁面,我們在 AndroidManifest.xml 文件中對 Activity 注冊時,會有這么一段代碼。
實際上應用從點擊圖標到啟動第一個頁面的過程相當復雜,如果大家有興趣的話可以去看老羅的博客:
Android應用程序啟動過程源代碼分析
http://blog.csdn.net/luoshengyang/article/details/6689748
從源代碼的分析來看,桌面圖標保存了相應應用的啟動 intent,該 intent 保存了 AndroidManifest.xml 的信息。因此我們自定義隱式啟動頁面時,只需要在 AndroidManifest.xml 中相應的 Activity 配置相應的 action 信息,然后調用的時候設置相應的 action 便可啟動相應 Activity。
比如我們之前開發應用時,想要調用系統撥打電話應用,我們并不知道撥打應用 Activity 的具體信息,所以只有通過隱式方式來調用。
嗯,有了這些基礎知識,我們便可以來實現瀏覽器打開本地App了。
瀏覽器打開本地APP
常規下,瀏覽器打開一個 url 要么是 http 要么是 https,那么,我們要怎么讓瀏覽器能識別到一個 url 是指向我們的應用的呢?那我們來看看 Url 吧,仔細看看一個 url 鏈接其實就是一個 Uri,有scheme,有host,有query等等。scheme 實際上相當于一種協議,比如 http,系統識別到這個協議便會交由網絡部分去處理這個請求。
那么系統能否識別自定義的 scheme 呢?答案是肯定的。我們可以在自己的應用內自定義一種 scheme,系統安裝我們的應用后便會在某個地方進行 scheme 注冊,這樣當有相應的 scheme 請求的時候,便能將請求導向到我們的應用。有意思的是如果在多個應用中配置了相同的 scheme 的話,從瀏覽器請求時便會提示用戶選擇某一個應用來打開。
那么,我們來看看基本做法怎么做吧。
配置要打開的頁面,比如我們在應用的啟動頁面配置相應的scheme
配置瀏覽器訪問的url
myapp://?id=1
為了簡單,就沒有配置 host 信息,讀者可以自己將 host 加上即可。這樣,當瀏覽器訪問這個 url 的時候便會打開我們的App。那么怎么拿傳遞的數據呢,不要方,看代碼。
之所以要作一個判斷,是為了區別該界面是否是由瀏覽器打開的,然后我們便可以通過 Uri 的 getQueryParameter 來獲取傳遞的信息了,當然,你還可以拿到 Uri 的其他信息,這里就不說明了。
至此,通過瀏覽器打開本地App功能算是完成了。相信讀者看到這里也會說,這么簡單,我分分鐘便能搞定。沒錯,做到這個地步,相信只要有點Android開發知識的同學都能分分鐘搞定,那么我在這里提幾個問題:
假如我們從瀏覽器打開了應用,按下HOME鍵,然后又從桌面點擊了應用圖標,這時候會發生什么?
假如應用之前已經打開了某個頁面,然后我們又從瀏覽器重新打開了應用,這時候按返回鍵,我們還能回到之前打開的頁面么?
我們通過瀏覽器打開頁面一般都會是二級甚至三級頁面,如果之前沒有打開過應用,那么直接按返回鍵就會退出應用,這似乎用戶體驗不太友好,怎么解決?
如果你能把這三個問題解決了,那么從瀏覽器打開本地應用就算掌握了。哈哈,沒那么簡單吧,要解答上面三個問題,必須熟練掌握 Activity 的啟動方式并靈活應用,下面我來介紹我的方案吧。
仿知乎瀏覽器打開本地應用
其實我上面拋出的三個問題就是從知乎的體驗來提出的,通過瀏覽器打開知乎的某個問題的回答頁面,假如之前知乎是打開的,則按返回鍵能直接回到之前的頁面,如果之前知乎沒有打開過,則按返回鍵會回到主頁面。當通過瀏覽器打開頁面后,按HOME鍵之后再重新點擊圖標,知乎會直接打開到瀏覽器打開的那個頁面。所以知乎是完美解決了以上三個問題的。那么,我們來看如果不做任何處理,會是什么效果。
由于手機界面截圖太大了,就不貼圖了,我們從打印日志來看界面的啟動情況吧。分別在 onCreate方法 和 onResume方法 中放入日志打印代碼。
首先完全退出應用,然后通過瀏覽器調用 url 喚起界面,然后按下HOME鍵,點擊圖標重新進入,我們可以得到如下所示的日志
可以看出點擊圖標后會重新創建應用,之前在瀏覽器打開的界面找不到了。同樣對于第二個問題,如果我們不做任何處理,瀏覽器喚起的頁面按返回鍵是回不到之前打開的界面的。那么,這是為什么呢?
篇幅所限,就不介紹 Activity 的四種啟動方式了。首先我們來了解一下任務棧這個概念吧。默認情況下,如果沒有對 Activity 設置 TaskAffinity屬性,一個應用的所有 Activity 都是運行在同一個任務棧的,任務棧的名稱為應用的 PackageName。如果從應用A啟動應用B的某個 Activity C,則 C 會運行在 A 的任務棧中。說到這里,相信大家應該明白為啥了吧。
從桌面啟動的應用運行在應用本身的任務棧中,而從瀏覽器打開的界面則運行在瀏覽器的任務棧中,兩個任務棧是分開的,所以在情景一,會重新創建出新的任務棧來打開應用,而在情景二中,由于瀏覽器的后臺任務棧是桌面,在瀏覽器的任務棧中按返回鍵當然不能回到本地應用的任務棧咯而是回到桌面。
那么,知道了問題的原因,怎么來解決這個問題呢?由于從桌面點擊應用會創建自己的應用棧,那么如果我們可以把瀏覽器任務棧中的界面移動到應用本身的任務棧中,則不就解決第一個問題了么。那么怎么將 Activity 從其他任務棧中移到自己的任務棧中呢?方法很簡單,只需要在相應的 Activity 中配置 allowTaskReparenting屬性 為?true即可。
android:allowTaskReparenting="true"
設置了該屬性的 Activity 在應用真正啟動時,會將在其他任務棧中移動到自己的任務棧中來,由于移動過來的界面處與棧頂,所以會直接顯示之前在瀏覽器中打開的界面,是不是很厲害。
對于場景二,應用自己打開的 Activity 在自己的任務棧中,由于我們沒辦法把已啟動的 Activity 移動到瀏覽器的任務棧中,所以只有另辟蹊徑。我們知道瀏覽器喚起的那個界面如果不做任何處置,則會在瀏覽器的任務棧中新建 Activity,那么我們是不是可以指定喚起的 Activity 的打開方式為 SingleTask 呢,因為對于 SingleTask 類型啟動的界面來說,如果在本任務棧中不存在對應的 Activity 的話,會在新的任務棧中新建 Activity。所以當瀏覽器喚起界面時,由于瀏覽器任務棧中沒有對應 Activity,所以會在本地應用所在任務棧去創建 Activity,這樣就鏈接到了本地應用的任務棧,并且將本來處于在后臺任務棧的應用任務棧移動到了前臺任務棧。
由于 Android 的返回機制是只有在某個任務棧為空時,才會退到上一個任務棧,所以按返回鍵的效果便是在本地應用棧中退出相應的 Activity,直到任務棧為空時,再把瀏覽器所在的任務棧移動到前臺。當然有個小問題是如果我們在配置文件中聲名 Activity 為 SingleTask 啟動方式,如果該任務棧中存在相應的 Activity,則會把該 Activity 之上的所有 Activity 清除掉。而通過給 Intent 配置 New_Task 的 flag 方式默認不會清除 Activity 之上的其他 Activity,除非添加了 Clear_Top 的 flag。由于我們不能控制瀏覽器設置的 Intent,所以沒法添加 New_Task 的 flag,所以直接在配置文件中設置 SingleTask 并行不通。
考慮到應用一般都會有啟動頁,并且啟動后會自動 finish,那么我們可以利用該頁面做做文章。我們通過瀏覽器打開啟動頁,然后在啟動頁面通過 New_Task 的方式去啟動主頁面,再逐級打開二級、三級頁面。這樣既不會清楚已經打開的 Activity,又可以實現任務棧的移動。法很簡單,只需要在啟動頁面的 onCreate方法 中根據 Intent 的類型做頁面跳轉即可。
這樣便解決了第二個問題,在瀏覽器中按返回鍵能回到之前打開的頁面。對于第三個問題,就更簡單了,我們只需要有個 AppManager類 來管理已經打開的界面,然后在啟動頁面判斷應用是否有打開過的頁面,如果有,則直接跳轉到需要喚起的界面,如果之前沒有打開界面,則先跳轉到主界面,再跳轉到需要喚起的界面,這樣按返回鍵還能回到主界面不至于直接退出應用。這里值得注意的是跳轉到主界面不要新建Intent,直接沿用獲得的Intent重新設置跳轉信息即可。思路比較簡單,就不再贅述了。
這樣三個問題都解決了,我們來簡單回味一下整個優化過程:
通過設置 android:allowTaskReparenting=”true” 屬性將其他任務棧中存在的 Activity 在應用啟動時移動到自己的任務棧中,實現按HOME鍵啟動應用能回到瀏覽器喚起的頁面。
通過 Splash 頁面做過渡,通過 New_Task 的方式啟動瀏覽器喚起的頁面,使得在瀏覽器中按返回鍵能接著本地應用已打開的頁面。
通過判斷 Activity 的數量決定是否是直接喚起頁面還是先喚起主界面再打開需要打開的界面使得按返回鍵不至于直接從二級或者三級界面退出應用,提高用戶體驗。
文字寫得有點啰嗦,那來一張簡單明了的圖吧。按照這個設計流程,并且在 AndroidManifest.xml 中將 MainActivity 以及瀏覽器需要喚起的 Activity 設置 android:allowTaskReparenting=”true” 屬性,你也可以實現知乎那樣的瀏覽器喚起應用。
能看到這里的都是真愛啊,希望大家在Android的道路上越走越遠,越走越遠,越走越遠!
總結
以上是生活随笔為你收集整理的html调用deeplink,如何优雅地从浏览器打开本地应用deeplink的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 老外大赞OPPO Find N2 Fli
- 下一篇: 跨界“潮”出圈!盘点五款经典联名手机 你