欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Spi機制在Android開發(fā)的應用示例詳解

 更新時間:2022年08月04日 14:18:21   作者:Pika  
這篇文章主要為大家介紹了Spi機制在Android開發(fā)的應用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪

Spi機制介紹

SPI 全稱是 Service Provider Interface,是一種將服務接口與服務實現分離以達到解耦、可以提升程序可擴展性的機制。嘿嘿,看到這個概念很多人肯定是一頭霧水了,沒事,我們直接就可以簡單理解為是一種反射機制,即我們不需要知道具體的實現方,只要定義好接口,我們就能夠在運行時找到一個實現接口的類,我們具體看一下官方定義

舉個例子

加入我是一個庫設計者,我希望把一個接口暴露給使用者實現具體的邏輯,那么我肯定不能夠寫死實現類對吧,不然我們怎么擴展嘛!比如我們有以下接口

package com.example.newtestproject
interface TestSpi {
    fun getSpi();
}

如果我在使用的過程中,想不關心具體的實現類/又或者想兼容多個實現,那么怎么辦呢?(比如日常開發(fā)的gradle,如果我想兼容多個agp版本在自己庫的運行,一個個寫死肯定是一個非常糟糕的實現,如果出了新版本,那么我們還要更改代碼),我們能不能只定義一個規(guī)范,即上面的TestSpi就可以不關心以后的擴展呢?很簡單,我們只需要在resource/META-INF/services目錄下定義一個以該接口為名稱的文件,文件內容是具體的接口實現類即可,如圖

內容是實現類的全名稱,假如我們有以下實現類

class TheTestSpi:TestSpi {
    override fun getSpi() {
        Log.i("hello","i am the interface implementation from TheTestSpi")
    }
}

那么文件只需要寫入com.example.newtestproject.TheTestSpi即可。

在我們想要用到TestSpi的功能的時候,就可以通過以下方式進行使用,從而不用關心具體的實現,達到了解耦合的目的

// spi test
val load = ServiceLoader.load(TestSpi::class.java)
load.forEach {
    it.getSpi()
    if(it is TheTestSpi){
        Log.i("hello","theTestSpi")
    }
}

ServiceLoader.load

從上面我們可以看到,最關鍵的是調用了ServiceLoader.load方法,這個就是Spi具體的實現了,本質是什么呢?相信都能夠猜到了,其實就是反射,我們來跟一下源代碼

public static <S> ServiceLoader<S> load(Class<S> service) {
    // 獲取了一個當前類的classloader,做好了準備
    ClassLoader cl = Thread.currentThread().getContextClassLoader();
    return ServiceLoader.load(service, cl);
}

緊接著就會調用到

private boolean hasNextService() {
    if (nextName != null) {
        return true;
    }
    if (configs == null) {
        try {
            String fullName = PREFIX + service.getName();
            if (loader == null)
                configs = ClassLoader.getSystemResources(fullName);
            else
                configs = loader.getResources(fullName);
        } catch (IOException x) {
            fail(service, "Error locating configuration files", x);
        }
    }
    while ((pending == null) || !pending.hasNext()) {
        if (!configs.hasMoreElements()) {
            return false;
        }
        pending = parse(service, configs.nextElement());
    }
    nextName = pending.next();
    return true;
}

我們可以看到,執(zhí)行的時候有這么一句String fullName = PREFIX + service.getName();,很容易想到,如果我們要反射的話,是不是需要類全稱,PREFIX 其實就是

private static final String PREFIX = "META-INF/services/";

可以看到,之所以我們需要在resource下定一個文件路徑是META-INF/services,是因為在ServiceLoader中定義好了,所以ServiceLoader會按照約定的路徑,去該文件夾下查找service.getName()(TestSpi)的實現類,最后通過

private S nextService() {
    if (!hasNextService())
        throw new NoSuchElementException();
    String cn = nextName;
    nextName = null;
    Class&lt;?&gt; c = null;
    try {
        c = Class.forName(cn, false, loader);
    } catch (ClassNotFoundException x) {
        fail(service,
             // Android-changed: Let the ServiceConfigurationError have a cause.
             "Provider " + cn + " not found", x);
             // "Provider " + cn + " not found");
    }
    .....

Class.forName(cn, false, loader) 去進行了類查找操作,因為我們通過foreach去遍歷,其實就是通過迭代器去獲取了每個實例,所以才能夠調用到了實現類TheTestSpi的getSpi() 方法。

在Android中的應用

SPI機制在很多庫的設計上都有應用,比如Coroutine(kotlin協(xié)程庫)就有用到,比如我們經常用到的 MainCoroutineDispatcher,如果我們希望一個任務運行在主線程,在Android就可以通過handler的方式去向主線程post一個消息,那如果在其他環(huán)境呢?

kotlin不僅僅是想在android的世界立足,還有很多比如如果在native環(huán)境呢?在服務器環(huán)境呢?多平臺環(huán)境呢(如KMM),那就不一定有Handler這個概念對不對!但是都有一個主線程的概念,所以Coroutine把這部分就通過SPI的方式去實現了,如

定義了這個接口 MainDispatcherFactory::class.java 用于給具體環(huán)境的主線程實現類進行實現,具體的實現類就是

internal class AndroidDispatcherFactory : MainDispatcherFactory {
    override fun createDispatcher(allFactories: List&lt;MainDispatcherFactory&gt;) =
        HandlerContext(Looper.getMainLooper().asHandler(async = true))
    override fun hintOnError(): String? = "For tests Dispatchers.setMain from kotlinx-coroutines-test module can be used"
    override val loadPriority: Int
        get() = Int.MAX_VALUE / 2
}

可以看到,在Android中就通過了Handler去實現,最后我們可以在源碼中看到,SPI相關的注冊信息

總結

通過SPI技術去實現的解耦合工作的出色工程還有很多很多,比如我們用的APT,還有didi開源的Booster,都有用到這方面的知識。

以上就是Spi機制在Android開發(fā)的應用示例詳解的詳細內容,更多關于Android開發(fā)Spi機制的資料請關注腳本之家其它相關文章!

相關文章

  • Android自定義View之酷炫數字圓環(huán)

    Android自定義View之酷炫數字圓環(huán)

    這篇文章主要為大家詳細介紹了Android自定義View之酷炫數字圓環(huán),實現效果很酷,,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-01-01
  • Android TextView多文本折疊展開效果

    Android TextView多文本折疊展開效果

    這篇文章主要為大家詳細介紹了Android TextView多文本折疊展開效果的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • Android自定義View實現打字機效果

    Android自定義View實現打字機效果

    最近在做Android開發(fā)的時候,需要做類似于打字機打字的效果,字一個一個地蹦出來,顯示每一個字都帶有打字的聲音?,F在分享給大家,有需要的可以參考借鑒。
    2016-08-08
  • flutter傳遞值到任意widget(當需要widget嵌套使用需要傳遞值的時候)

    flutter傳遞值到任意widget(當需要widget嵌套使用需要傳遞值的時候)

    這篇文章主要介紹了flutter傳遞值到任意widget(當需要widget嵌套使用需要傳遞值的時候),本文給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下
    2019-07-07
  • Android實現銀行卡號掃描識別功能

    Android實現銀行卡號掃描識別功能

    這篇文章主要為大家詳細介紹了Android實現銀行卡號掃描識別功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-09-09
  • android仿愛奇藝加載動畫實例

    android仿愛奇藝加載動畫實例

    這篇文章主要介紹了android仿愛奇藝加載動畫實例,小編覺得挺不錯的,現在就分享給大家,也給大家做個參考。
    2016-10-10
  • android實現音樂播放器

    android實現音樂播放器

    這篇文章主要為大家詳細介紹了android實現音樂播放器,擁有播放、暫停、重新播放和停止等功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • 深入Android Handler,MessageQueue與Looper關系

    深入Android Handler,MessageQueue與Looper關系

    這篇文章主要介紹了深入Android Handler,MessageQueue與Looper關系,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-08-08
  • 詳解Android JS相互調用

    詳解Android JS相互調用

    這篇文章主要為大家介紹了Android與JS兩者之間的相互調用,感興趣的小伙伴們可以參考一下
    2016-01-01
  • Android多返回棧技術

    Android多返回棧技術

    本文將詳情講解用戶通過系統(tǒng)返回按鈕導航回去的一組頁面,在開發(fā)中被稱為返回棧 (back stack)。多返回棧即一堆 "返回棧",對多返回棧的支持是在 Navigation 2.4.0-alpha01 和 Fragment 1.4.0-alpha01 中開始的,有興趣的話一起參與學習
    2021-08-08

最新評論