360手機衛士插件化RePlugin今日開源

寫在前面

「RePlugin將在6月底開源,這將是咱們獻給安卓世界最好的禮物。」當咱們宣佈這一消息時,心中的激動,無以言表。是的,三年的「厚積」,現在的「薄發」,看似平凡的話,實際上卻飽含了咱們太多的激動、辛酸與淚。git

那麼今天,咱們就來詳細的和您聊一聊,這個從2014年中旬,正式在手機衛士上啓用,並即將開源的360 RePlugin,究竟能爲咱們,更爲您能帶來什麼。github

GitHub地址:https://github.com/Qihoo360/RePlugin。歡迎您爲RePlugin項目加Star、發送Pull Request,提Issue。咱們會竭盡所能回答您們的疑惑。安全

RePlugin是什麼

RePlugin是一套完整的、穩定的、適合全面使用的,佔坑類插件化方案。其主要優點有:框架

  • 極其靈活:主程序無需升級(無需在Manifest中預埋組件),便可支持新增的四大組件,甚至全新的插件;
  • 很是穩定:Hook點僅有一處(Classloader)。其崩潰率作到僅爲「萬分之一」,並完美兼容市面上近乎全部的Android ROM;
  • 特性豐富:支持近乎全部在「單品」開發時的特性,包括靜態Receiver、Task-Affinity、自定義Theme、進程坑位、AppCompat等;
  • 進程任意:可以讓各組件跑在UI、常駐,甚至是「任意坑位進程」;
  • 易於集成:不管插件仍是主程序,只需「數行」就能完成接入;
  • 自由隔離:想隔離就隔離(如不穩定或佔資源的插件,易於釋放),不想隔離的模塊就混用(如各類基礎、UI插件,都跑在UI進程內,性能優異);
  • **管理成熟:擁有成熟穩定的「插件管理方案」,支持插件安裝、升級、卸載、版本管理,甚至包括進程通信、協議版本、安全校驗等
  • 數億支撐:有360手機衛士龐大用戶量作支撐。 
    截止2017年6月底,RePlugin的:
  • 插件數已達102個(其中,核心基礎插件57個)。
  • 插件佔應用比(指把代碼資源鋪開,插件佔整個應用的比例)高達83%。
  • 年發版次數高達596次(平均每一個工做日發版2-3次)。 
    此外,目前360公司幾乎全部的億級用戶量的APP,以及多款主流第三方APP,都採用了RePlugin方案。

圖片描述
RePlugin的核心優點ide

圖片描述
RePlugin與現有插件化框架的對比組件化

爲何要作RePlugin

插件化的好處

在講述咱們團隊爲什麼要在2013年末,設計一套屬於本身的插件化以前,咱們先來簡單談談,有了插件化方案後,能爲咱們帶來多大的便利,它究竟解決了什麼問題。性能

對於用戶而言

  • 一切按需:利用插件化方案,可讓您的應用變得「小而精」。只有當用戶須要使用某個特定功能時,才能夠下載並開啓,且能夠隨時卸載插件。這不只能夠減少APK大小、節省流量,還可明顯的減小內存、內部存儲佔用,將更多空間讓給珍貴的相片、文檔等資料;
  • 隨時體驗新版:不用去應用市場等到大包升級,用戶能夠隨時體驗到新版的應用。如今紅極一時的插件化、動態化(RN類)、熱更新技術,都或多或少的在圍繞此點而展開,可見其對用戶帶來的巨大價值。

對於開發者而言

  • 發版靈活:不用等市場上線,等用戶主動升級,結果錯過寶貴的時機。插件化方案可以讓您作到「隨時發版」,不受「發版窗口期」的限制。甚至可針對不一樣地域、不一樣用戶羣、不一樣時段來更新,且能夠快速驗證本身的構想;
  • 組織結構靈活:一旦發版變得足夠靈活,則組織結構上就能夠由原來的「統一做戰」變成「百團做戰」,每一個團隊都在開發「本身的插件單品」,制定本身的發版計劃;
  • 模塊思惟:可讓團隊造成「模塊意識」。固然,插件間、插件與宿主間容許有適度的耦合,但不會是「毫無控制」的那種。這讓開發者們意識到,咱們之間是「插件間的協定」,而非「同一屋檐下,隨便胡來」,迫使團隊以全新又合適的方式來開發應用;
  • Android原生優點:和動態化(RN類)不一樣,您可使用最熟悉的Java/Kotlin語言,及各類原生API來開發您的插件。這使得應用能和系統更「契合」,充分利用原生的各類優點,且在性能上幾乎感覺不到影響。

如上所述,不管是對用戶,仍是對開發者而言,使用插件化框架都是大有益處的,理應作到「飛入尋常應用家」。學習

然而,在實際調查過程當中,咱們卻發現了一個和這些好處徹底不匹配的奇怪現象。到底是什麼呢?插件

既然插件化這麼好,爲何……

雖然我事先已作好功課,然而在一次技術大會上的調查結果卻讓人大跌眼鏡——在參會的200多位安卓開發者中,僅有不足5%的比例,使用了插件化方案。超過九成的開發者,目前上沒有將插件化應用在軟件開發之中。設計

實際上,這和咱們在線下觀察到的結果基本吻合。結合以前的調查,咱們發現,有三大挑戰制約了插件化在Android開發界的普及:

  • 不夠穩定:目前有不少比較靈活的插件化框架,雖然支持特性衆多,但因Hook點較多,因此不是很是穩定。所以不少大型項目不是很願意用它們來開發插件,擔憂出現應用崩潰、插件沒法正常使用等問題。
  • 不夠靈活:有一些相對穩定的插件化框架,又存在「不夠靈活、自由」的問題,一旦插件有較大改動,如新增Activity、Service、進程等,就須要主程序發版,更不用說能作到「一年前的主程序,無需升級,能夠用新插件和組件」。也所以,不少項目的「接入動機」也就大打折扣了。
  • 功能豐富項目專用:目前市面上的插件化方案,大多僅在功能豐富的大型項目中,才被考慮使用,且多用於邊緣功能,好比「紅包」、「天氣」、「搖一搖」等,他們認爲只有「非核心」模塊,纔會考慮作成插件。這也使得插件化的應用範圍很是狹窄。

然而,經過咱們多年的實踐證實,以上三大挑戰,實際上是能夠被攻克的。這也是咱們今天要爲您介紹的,360手機衛士首款Android開源項目——RePlugin。

既然這麼大膽,那麼,咱們到底是怎麼作到的呢?

咱們是怎麼作的

「不夠穩定」怎麼破?

前文提到,不夠穩定的主要緣由是Hook了太多。那麼市面上比較靈活的插件化框架,究竟Hook了哪些呢?

圖片描述

注意:這裏所說的「Hook」是指經過Java反射手段,獲取並修改與系統Server等交互的Internal API,來讓框架正常工做的行爲,如上面所列部分。正常狀況下的反射(例如反射類內部本身的字段)不屬於Hook。

看似靈活,然而下列三種狀況,將頗有可能致使插件甚至應用,完全不能工做:

  • Android升級:既然是內部 API,那麼Android天然不會認爲是「不能修改的」,一旦系統升級時作了改動,輕則功能不正常,重則直接Crash。例如,有的插件化框架曾遇到在Android 7.0上出現異常,必須升級主程序才能解決的事故,歷歷在目。
  • ROM修改:比Android升級更可怕的,是第三方ROM對內部API的「各類改」。這個適配難度是可想而知的。例如自定義Resource、自定義WiFiService等形成的「插件化血案」,不一而足。
  • 使用不當:「常在河邊站,哪有不溼鞋」,Hook的點多了,一旦對某一點的實現原理理解不透而出了錯。結果,前功盡棄不說,還可能出現更嚴重,且更難以察覺的崩潰事故,細思恐極。

基於上述的狀況,咱們團隊在2014年初,研究全新佔坑插件化框架(注意,此時DroidPlugin類方案尚未出現)時,就定了個「小目標」:讓Hook越少越好。通過一次次的研究討論,最終肯定只Hook一個點:ClassLoader,且要求「堅持到底」,全部改動都是基於此來展開。

對咱們而言,這是里程碑式的決定,即使到如今來看也是如此。

惟一Hook——ClassLoader

圖片描述

修改ClassLoader的點其實不難,如上圖所示逐步反射便可。然而須要注意的是,這個ClassLoader必定得繼承自PathClassLoader,防止Android 7.x因使用addDexPath而有問題。

除此以外,此ClassLoader所在位置也很是穩定。目前來看,從Android 2.1至今都沒有發生過位置、名稱上的變化,能夠長期使用。

關於這一點,咱們以後會有一篇文章來詳述,敬請期待。

「不夠靈活」怎麼破?

前文提到,就目前市面上的插件化框架而言,若作的足夠穩定,則多少會失去一些靈活性。對於咱們擁有這麼多模塊的產品而言,這一樣也是不可接受的。

爲此,咱們在「堅持一個Hook點」原則的前提下,經過不斷創新,最終解決了上述問題。

咱們的核心思路,是2015年之後纔開始「老生常談」的一個詞,那就是:坑位。

坑位方案思想

圖片描述

  • 非坑位方案:標準的一一對應關係。例如,插件有個XXXActivity,那麼主程序則要求必須也有個XXXActivity(名字未必同樣)來對應。 
    一旦插件要添加一個新的Activity,則對應的,主程序也必須得添加,不然就沒法使用這個Activity。
  • 準坑位方案:有的(如2013年的咱們)會經過Fragment來模擬Activity,從而實現必定程度上的靈活。 
    然而真的遇到大需求,如和其它應用Activity的交互等,就侷限百出,很難稱得上是完整坑位思想。
  • 坑位方案:能夠作到「一對多」的關係。例如插件有個XXXActivity,則運行時,主程序能夠將本身的N1ST1對應到這個XXXActivity上。一旦該Activity退出,則N1ST1就「空閒」出來。而當YYYActivity進來後,又會從新佔用N1ST1。這樣就能夠作到一個坑位(如N1ST1)對應多個實體。 
    此外,這個YYYActivity既能夠是已有的,也能夠是插件新增的。這樣不管插件如何升級,主程序均可以不用動,便可支持新的Activity。

固然,咱們當初設計坑位思想(恕我再強調,是2014年初)時,也毫不僅僅針對Activity,而是整個四大組件,甚至到了後期,連Theme、進程、Task-Affinity等都作到了「坑位化」,只不過實現方式各異而已。

因篇幅所限,這裏僅以經常使用的Activity來簡述。有關更詳細的內容,歡迎繼續關注咱們的《RePlugin深度剖析》系列文章。

Activity坑位

圖片描述

目前市面上的完整坑位方案,Hook的地段能夠說是「各有千秋」,從AMS、Instrumentation到ContextImpl都有,並以此讓插件變得更靈活。

而咱們的方案和他們有些不一樣:除了ClassLoader是Hook的,其他一概不須要。那麼咱們到底是如何開啓一個插件的Activity呢?

簡單來講,咱們有五個核心步驟:

  • 記錄:經過PM.startActivity方法來「記錄」到要打開的Activity的名字;
  • 尋找坑:經過一系列流程來找到一個可用的坑位(如N1ST1)並記錄;
  • 開啓坑:經過系統的startActivity來直接打開這個坑位(注意,此處沒有作Hook);
  • 攔截:當系統調到咱們的HostClassLoader(惟一Hook點)時,咱們「攔了一道」,找到此坑位(N1ST1)對應的真正的Activity(XXXActivity);
  • 返回:加載插件並獲取這個真正的Activity的Class對象並返回給系統。

其中,PM.startActivity能夠由插件/宿主直接調用。若在插件內部,則能夠直接經過startActivity方法來打開,更爲方便。

固然,這只是核心思路,而每一步咱們都會作各免費催收系統軟件種邏輯處理,尤爲是「尋找坑」一節,這也是咱們的核心之一。

Activity分層坑位

找坑是有很是多的注意點:

  • 從「分層」上看,則須要支持:各類LaunchMode、全部透明/非透明的Theme、TaskAffinity坑位、進程坑位等,甚至AppCompat的狀況也要考慮。
  • 從「管理」**上看,又須要考慮到坑位回收釋放,坑位分配,甚至坑位不足時的處理策略等,不一而足。

篇幅所限,之後會寫詳細介紹,敬請期待。

向完美前進——動態編譯方案

經過剛纔的敘述,像PM.startActivity等確實可經過一些方法,來讓插件「無成本使用」。可是,像Provider的調用(本質是IContentProvider),Service的stopSelf(是final的),以及因涉及坑位分配,而不得不需複寫相應方法的Activity等。這裏面存在兩個矛盾點:

  • 若不Hook,則必需要插件開發者「自行處理」,稍顯繁瑣,不夠完美;
  • 一旦Hook(如嘗試Hook AMS、IContentProvider等),又破壞了咱們堅持的「1 Hook原則」,進而擔憂將來出現兼容性問題

利弊相間,使人頭疼。

針對這個問題,咱們的核心理念是:「毫不在Hook及穩定性上作任何妥協」,轉而創新性的作一套「動態編譯方案」,力圖從「編譯期」來解決這個難題。

大致而言,就是把一些咱們認爲須要開發者修改的類和方法,藉助神奇的JavaAssist來作自動化修改,這樣可節省開發者的改動成本,達到想要的效果。

一圖以蔽之: 
圖片描述

而作到了這一點之後,你會發現,下面的「夢想」就變成了現實:

圖片描述

例如,有個名叫「360桌面」的應用,它想把本身變成「插件」跑起來。那麼,有了「動態編譯方案」,結合「插件類庫」和框架的支持,最終的效果是——只需改幾行Gradle,就能直接生成一個APK。這個APK:

  • 既能夠做爲插件直接跑在主程序中;
  • 又能夠做爲單品直接安裝到設備中。

是的,就是這麼的神奇!

就這些了?

固然遠不止這些。我在曾演示過一段視頻,將龐大又複雜的360桌面變成插件,運行在360手機衛士中。

圖片描述

試想,一個桌面插件涉及到的功能是「方方面面」的,小到TaskDesription和SO的使用,大到四大組件、Task-Affinity坑位、靜態Receiver和進程坑位的處理,都需一一兼顧。因此,要作到這一點,毫不僅僅是前面所說的那幾點就能搞定的。

固然,「讓360桌面變成插件」,還不是最有意義的。真正讓RePlugin變得更有意義的,就是拿咱們的360手機衛士來「開刀」,讓數百個——甚至說,近乎一切——的模塊,都成爲RePlugin的插件,並完美的運行起來。

「功能豐富項目專用」怎麼破?

前文提到,之因此「功能豐富項目專用」,主要和目前市面上的插件化方案的定位有關,以致於開發者認爲:「插件 = 免安裝」、「基礎放在主程序裏更放心」、「插件開發成本高」等。

然而,仔細分析深層緣由後發現,其實最爲核心的緣由,是插件化和相關框架(包括熱更新方案等)的「定位」不一樣。我知道的有:

  • 組件化:以Atlas/ACDD爲表明,官方的定義是「在運行環境中按需地去完成各個bundle的安裝,加載類和資源」,以解決大團隊協做時的各類難題,提供了熱修復能力。其依賴「編譯期」較多但很穩定。而大組件的添加,則仍須要主程序發版才能解決,畢竟如官方所述,「組件化 ≠ 插件化」;
  • 動態插件化:以Dynamic-Load-Apk爲表明,其目的是解決發版、升級時的問題。大多采用「非佔坑」思路,添加新組件時,仍是會要求升級主程序。此類方案較多,且是插件化的「鼻祖」了,值得咱們尊敬與學習;
  • 免安裝應用:以DroidPlugin爲表明,官方的定義是「能夠在無需安裝、修改的狀況下運行APK文件」。其場景比較相似於「應用分身」 
    此外,此項目是由咱們360公司的手機助手團隊研發,在2015年中旬發佈。
  • 熱修復:以Tinker/Robust爲表明,目的是以最小的代價來快速打各類Patch,讓應用可以持續更新。其核心優點是「無需重啓進程就能打補丁」。一樣,新添加的大塊功能,則仍是須要升級主程序的,畢竟這不是「熱修復」的主要目標。

那麼,咱們是這三種目標之一嗎?

顯然,都不是。那麼,咱們的目標到底是什麼?

答案:全面插件化

咱們的目標只有一個:全面插件化。

  • 全面插件化:以RePlugin爲表明。其目的是「儘量多的讓模塊變成插件」,並在很穩定的前提下,儘量像開發「單品」那樣靈活,並享受插件化方案帶來的各類好處。

也就是說,不管是UI、核心業務、合做插件、後臺服務,仍是基礎功能,均可以變成插件,並在RePlugin框架內穩定又靈活的運行起來。甚至,不只大項目能用,小項目——甚至只是個計算器——均可以使用RePlugin來提高本身的靈活性,並最終實現「插件滿天下」的神奇效果。

而這一點,則是咱們,和目前市面上大多數插件化框架的主要差別。

目前衛士插件的現狀

目前手機衛士已有的插件,能夠分爲如下幾類,供各App開發者參考:

  • UI插件:如首頁(是的,你沒看錯)、體檢、信息流等;
  • 業務插件:如清理、騷擾攔截、懸浮窗等;
  • 合做插件:如程序鎖、免費WiFi、安全桌面等;
  • 後臺插件:如Push、服務管理、Protobuf等;
  • 基礎插件:如安全WebView、分享、定位等、

而這樣的插件,咱們有102個。能夠想見,一旦這些插件不能用,那麼手機衛士瞬間變空殼。三年已過,回頭想一想,值得回味。

一塊兒創造更高的價值

說到這兒,讓我想起了Lody(VirtualApp做者,高中大牛)在一次採訪時說過一段話:

「插件化技術的成熟程度雖然在最近幾年呈上升趨勢,可是整體而言仍然處於初、中級階段。App沙盒技術的出現就是插件化發展的創新和第一階段的產物。在將來,我相信不少插件化技術會被更多的應用,若是插件化穩定到了必定的程度,甚至能夠顛覆App開發的方式。」

這,其實也是RePlugin的終極價值,那就是——讓插件化能「飛入尋常應用家」,作到穩定、靈活、自由,大小項目兼用。

固然,在「全面插件化」甚至「全民插件化」的道路上,咱們還有太多的路要走,而如此龐大又複雜的RePlugin,是十多位研發人員共同努力的成果,且獲得了部門領導,和公司技術委員會的大力支持。我相信,RePlugin的開源,是一場新的開始

相關文章
相關標籤/搜索