關於Android路由的實現

  先說一下背景,目前有需求從外部包括其餘應用和WEB跳轉到咱們本身的APP,就這麼個簡單的需求……java

  要實現這種外部跳轉的功能,咱們能夠理解爲打算跳轉的一方有多少方式通知到APP進行相對的響應行爲。因此,若是是應用之間的跳轉,則有多種,你能夠直接經過包名和具體的類名去打開已經exported=true的Activity,又或者直接經過Android的廣播通知進行相關的APP,又或者經過自定義的URL去打開應用。可是若是涉及到Web打開外部應用的話,目前只有一種辦法,那就是自定義應用的URL進行攔截,系統會自動調起相應的組件響應這個URL。android

  可是,要作這種需求,不多會僅僅是完成對外部的支持而已,一般也要進行必定的內部邏輯跳轉映射。因此要作這種需求一般分爲兩個種,一種是對內的(應用內部本身的跳轉邏輯),一種對外的(其餘應用以及Web跳轉邏輯)。git

  咱們先說一下對外的情形,因爲考慮到統一性,咱們目前只有URL這種手段可使用了。下面咱們一一來講github

  一、對外跳轉說明微信

  1.一、關於URL的說明。restful

  首先,咱們得了解一下URL,這裏直接引用 https://en.wikipedia.org/wiki/URL 的說明。爲了方便說明,我稍稍修改一下,大概的格式以下:app

  scheme:[//host[:port]][/path][?query][#fragment]ide

  首先,scheme是必須的,其餘的都是沒必要須的,可是對於跳轉來講,顯然不可能,由於你要從這個url中取出跳轉相關的信息。因此,一般必定要要有host和query。咱們常常看到一些開源的路由實現,都會支持所謂的restful風格的url,好比:wytings://app/{city}/{id} ,但我我的認爲是沒有必要的。主要是由於這種外部跳轉的行爲,一般量比較少,其次應該儘可能統一併且方便,而不是爲了追求各類技術炫酷…我刻意看了微信的scheme就甚合我意~都是相似於這種格式:weixin://qrscan?a=1&b=2測試

  咱們進行一下概括,就能夠進行應用的URL定義了,首先scheme是必須項,看我的和公司要求,好比接下來要舉的例子,我定義的scheme爲wytings,而後支持的模塊都集中於host字段,具體參數則所有經過query補充。好比:wytings://user?uin=10000 打開我的頁面,wytings://stockDetail?marketcode=hk&stockcode=00376 打開股票詳情頁面等等。ui

  要是對外部的支持,一般咱們不會對每個要支持的Activity都進行相應的intent-filter限制,而是定義一個公共的Activity進行全部外部請求的攔截形如:

 1     <activity
 2             android:name=".activity.SchemeFilterActivity"
 3             android:exported="true"
 4             android:theme="@android:style/Theme.NoDisplay">
 5 
 6             <intent-filter>
 7 
 8                 <action android:name="android.intent.action.VIEW" />
 9 
10                 <category android:name="android.intent.category.DEFAULT" />
11                 <category android:name="android.intent.category.BROWSABLE" />
12 
13                 <data android:scheme="wytings" />
14 
15             </intent-filter>
16 
17             <intent-filter
18                 android:autoVerify="true"
19                 tools:targetApi="m">
20                 <action android:name="android.intent.action.VIEW" />
21 
22                 <category android:name="android.intent.category.DEFAULT" />
23                 <category android:name="android.intent.category.BROWSABLE" />
24 
25                 <data
26                     android:host="native.app.wytings.com"
27                     android:scheme="http" />
28                 <data
29                     android:host="native.app.wytings.com"
30                     android:scheme="https" />
31             </intent-filter>
32         </activity>

  咱們對這個Activity的定義進行一下說明:

  a、android:exported這個屬性其默認是false就是對外不開放,咱們必需要設置爲true,由於咱們要讓外部可以對其進行訪問。

  b、android:theme="@android:style/Theme.NoDisplay" 因爲是做爲攔截的Activity,因此,不必展現,可是這個NoDisplay的theme要求必須在onResume前finish掉Activity,不然要報錯。

  c、第一個intent-filter自定義scheme爲wytings,也就是攔截該類URL。

  d、第二個scheme爲http,可是加了特別的host=nativ.app.wytings.com,進一步詳細攔截url爲:http://nativ.app.wytings.com 的url。爲何要攔截這種url,一般狀況下不用,可是特殊狀況下,有時候自定義的scheme可能失效,因此而外再加層保障,固然,也要與調用方預約好url格式,好比:http://nativ.app.wytings.com/stockDetail?marketCode=hk&stockCode=00376,因爲host已經被定義爲別的,因此咱們把具體模塊定義在path裏面,參數依然保留在query中。

  再來看看SchemeFilterActivity的實現狀況:

  

/**
 * Created by rex on 06/10/2017.
 *
 * @author wytings@gmail.com
 */

public class SchemeFilterActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Uri uri = getIntent().getData();
        Log.i("wytings", "uri = " + uri);

        String scheme = uri.getScheme();
        if ("http".equals(scheme) || "https".equals(scheme)) {
            String routeModule = uri.getLastPathSegment();
            if (!TextUtils.isEmpty(routeModule)) {
                RouteManager.getInstance().build(routeModule + "?" + uri.getQuery()).go(this);
            }
        } else {
            RouteManager.getInstance().build(uri.toString()).go(this);
        }

        finish();
    }

}

  大致就是攔截,而後經過內部的RouteManager進行解析處理跳轉。RouteManager怎麼處理和實現就太細節了,總的來講,這個Manager的職責就是把URL翻譯成具體的Intent,而後啓動相應的Activity。有興趣的同窗能夠本身去看看本篇文章的全部源碼:

  https://github.com/wytings/AndroidRoute

  

  二、對內跳轉說明

  因爲是應用內的實現,因此基本上,你想怎麼實現就怎麼實現。可是,不管多麼變幻莫測,都繞不開一個核心那就是創建路由映射關係,打開相關頁面,取出請求參數這三大步驟。咱們逐個來分析一下。

  2.一、創建路由映射關係

  這個是爲了可以知道特定的url到底應該展現哪一個頁面。一般創建一個Map,而後查找。

  2.三、打開相關頁面

  在Android中,打開一個頁面老是有本身的一套邏輯,系統那一套則是經過Intent去啓動相應的組件展現。

  2.四、取出參數

  這個步驟,仍是基於系統的Intent方式,要經過intent.getXXXExtra來取出相關參數。

  這麼一看好像,也沒什麼難度。也確實沒什麼難度,就單純實現功能來講。那難點在哪呢?難點在於你決定使用註解去作這件事……爲何要用註解?由於爲了哪一丁點潔癖,解藕的潔癖。結果掉進坑裏了…

  用註解理論上,也還好,遍歷反射嘛,並且我我的測試了一下,就目前的機器真的感覺不出來。固然再怎麼樣,也沒在編譯時直接生成相關代碼來得快卻是真的……

   因而乎,進入第三個大難題,那就是進行編譯時生成代碼,相似於ButterKnife同樣,在編譯期就生成相關代碼,而不是在運行時經過反射來給變量賦值。

  這裏就涉及到一個東西,那就是Java 的 AbstractProcessor,這個類是在編譯時生成代碼最關鍵的類。要講解這個得再開一篇《關於Java註解實現編譯時生成代碼》的文章了。同窗們能夠網上搜索一下基本知識,而後再看這個項目中的代碼,我本身也看了不少關於註解的文章,可是很遺憾,我沒看到哪篇是值得捧的,一樣也沒看到那篇值得噴的……我如今也沒時間專門寫篇關於註解的文章,可是能夠給個方向,那就是先學會調試,AnnotatioProcessor的調試,跟普通java調試有點區別(本身google一下),而後就能夠本身摸索了。另外,我審視了一下,我寫的Annotation compiler仍是蠻清晰的,你也能夠試着看看。

  最後,再說一遍,項目地址:AndroidRoute

  有興趣的同窗,本身跑一下,勝讀十年書!  

相關文章
相關標籤/搜索