DUICUO

Atlas: Taobao ネイティブコンテナ化フレームワークと考慮事項

先日終了したYunqiカンファレンスにおいて、Taobaoはモバイルコンテナ化フレームワーク「Atlas」を2017年初頭にオープンソース化すると発表しました。チームはこれまでもこのフレームワークに関する情報を外部関係者と共有しており、外部からの注目も高まっていました。そして今、ついにオープンソース化が実現します。

本稿では、Atlasの設計コンセプトと、Taobaoにおけるコンテナ化、コンポーネント化、ダイナミクスに関する考え方を紹介します。主な内容は、2016年杭州雲奇カンファレンスにおいて、アリババのシニアテクニカルエキスパートである倪盛華(Xuanli)氏が発表した内容に基づいています。

アトラスとは何ですか?

2013年、タオバオは「キャリア戦略」の策定に伴い、事業開発人員が倍増しました。従業員数は100人未満から4~5倍に増加し、取扱商品数も大幅に増加しました。これは、クライアント全体のアーキテクチャとリリーススケジュールに大きな課題をもたらしました。以前のタオバオクライアントの基本フレームワークであったAtlasは、大幅な再構築を経て、現在のAtlasへと発展しました。

Atlasは、主にコンポーネント化、ダイナミズム、分離をサポートするAndroidクライアントコンテナ化フレームワークです。プロジェクトのコーディングフェーズ、APKランタイム、そしてその後の運用・保守における様々な問題へのエンジニアの対応をサポートします。

  • プロジェクトフェーズでは、各モジュールが独立して動作し、プロジェクトを独立して開発およびデバッグできるようになります。
  • 実行時には、コンポーネントのライフサイクルとクラスの分離などのメカニズムの完全なマッピングが実装されます。
  • 運用・保守期間中は、迅速な増分アップデートおよび修復機能を提供し、迅速なアップグレードを可能にします。

Atlasは、エンジニアリングフェーズと運用フェーズの両方で機能するフレームワークです。運用フェーズのシンプルさと安定性を確保するために、エンジニアリングフェーズにある程度の機能を持たせているのが特徴です。

現在、AtlasはTaobaoのアプリで広く利用されています。60以上のビジネスコンポーネント、20の共同チーム、そして数百万行に及ぶコードがAtlas上で実行されています。迅速なイテレーション機能により、アプリケーションのリリースサイクルは月次リリースから週次リリース、そしてオンデマンドリリースへと進化し、過去6ヶ月だけでも446回のリリースが行われています。さらに、Atlas自体は約90クラスと非常に軽量で、あらゆる規模のアプリ開発をサポートしています。Taobaoのような大規模アプリからAliHealthのような比較的小規模なアプリまで、あらゆるアプリがこのフレームワークを使用しています。安定性も実証されており、Android 4.x以降をサポートしています。Taobao全体のクラッシュ率は0.05%前後で安定しており、コンテナ関連のクラッシュは1%未満です。

この意味で、Atlasが最初に解決する必要があった問題は、大規模チーム内でのコラボレーションの問題であり、並行開発、迅速なイテレーション、そしてエンジニアリングの分離が求められました。次に解決すべき問題は、クライアント側の動的なアップデートでした。Taobaoの内部的な解決策はコンポーネント化でした。

Atlasコンポーネントベースの実装

業界ではプラグインアーキテクチャとして知られるコンポーネント化は、現在のプラグインアーキテクチャとはいくつかの点で異なります。コンポーネント化では、各コンポーネントの機能を理解する必要があり、より標準化された設計が可能になります。

(Taobao APKパッケージのディレクトリ構造)

これはTaobaoモバイル用のAPKパッケージです。トップレベルのディレクトリは標準のAPKと全く同じです。アプリには多数の.soファイルが含まれています。解凍すると、完全なAPKと似た構造になりますが、単独では実行できません。多くのプラグインパッケージとの違いは、実行時にコンテナ全体で実行され、各コンポーネントが独立したバンドルになっていることです。

モジュールの観点から見ると、Taobao APKは2つのレイヤーに分けられます。上層は、QRコードスキャン、レビュー、商品詳細といった分割されたビジネスバンドルで構成されています。これらのビジネスバンドルは相互に機能を呼び出し、他のビジネスユニットにルーティングできます。下層は、ネットワークライブラリや画像ライブラリなど、ビジネスユニットに様々な機能を提供する共有基盤ミドルウェアです。これらはコンテナ内で一元管理されています。このアプローチは、パッケージサイズを最小限に抑え、パフォーマンスを向上させるという利点があります。

このセクションでは、5 つのレイヤーに分かれた Atlas の全体的な設計の概要を説明します。

*** 層(Hack層と呼んでいます)には、OS Hackツールキットと検証ツールが含まれています。ここでシステムの機能を拡張し、いくつかのセキュリティチェックを実行します。

2 番目のレイヤーは、コンテナ インフラストラクチャ フレームワークである Bundle Framework で、Bundle 管理、読み込み、ライフサイクル、セキュリティなどの最も基本的な機能の一部を提供します。

3 番目のレイヤーはランタイム管理レイヤーです。これには、呼び出し時に簡単に取得できるようにすべてのバンドルとその機能をリストするマニフェスト、すべてのバンドルのバージョンを管理するバージョン管理、バンドルがコンテナ フレームワーク上で実行されるようにシステムのランタイム環境をプロキシする業界の一部のプラグイン フレームワークに似たプロキシ、およびエンジニアリング フェーズで開発とデバッグを容易にするデバッグおよび監視ツールが含まれます。

4 番目のレイヤーはビジネス レイヤーで、フレームワークのライフサイクル、構成ファイル、ユーティリティ ライブラリなど、ビジネス側へのいくつかのインターフェースを公開します。

最上位層はアプリケーション アクセス層で、ここにビジネス ロジック コードが含まれています。

そのため、Atlasはフレームワークとして比較的包括的な機能を提供します。ビジネス層の開発者は、フレームワークのライフサイクルの様々な段階でカスタムアクションを実行できるだけでなく、システム、フレームワーク、さらには他のコンポーネントによって提供される機能を自由に呼び出すこともできます。

コンポーネント化技術の詳細

上記はコンテナ層の概要です。以下では具体的な詳細について説明します。

バンドルのライフサイクルは、きめ細かなノードで提供されます。例えば、以下はバンドルのロードから実行までのライフサイクルです。

  • `startInstall`: 読み込みを開始します。この時点で、フレームワークはファイルのコピー、ライブラリの解放、バンドルの読み込みなどのタスクを実行します。
  • インストール済み: 読み込みが完了しました。この時点で、フレームワークはリソースパスを挿入し、クラスローダーを作成します。
  • 解決済み: 解析が完了すると、フレームワークはコンポーネント構成が有効かどうか、および解析できるかどうかを確認します。
  • active: コンポーネントを実行します。つまり、コンポーネント バンドルの実行を開始します。
  • 開始しました: プロセスは成功しました。

コンポーネント化における最初の課題は、マニフェストの処理です。これは、ホストマニフェスト、Aarマニフェスト、コンポーネントマニフェストなど、多くのソースが存在するためです。また、各コンポーネントのマニフェストは頻繁に変更されるため、柔軟な処理が求められます。そこで、プロジェクト開発中にすべてのマニフェストをマージするアプローチが取られます。バンドルの依存関係は、依存関係の調停が必要となるため、個別にマージされる点に注意してください。最後に、各バンドルのマージされたマニフェストを解析し、バンドル全体のBundleInfoList(前述のバンドル情報リスト)を取得します。

2つ目はクラスローディングです。これは、Delegate ClassLoaderを使用してコンポーネントクラスを動的にロードします。Delegate ClassLoaderはまずホストバンドルのPathClassLoaderを参照し、次に前述のBundleListに基づいて対応するBundleClassLoaderを見つけます。

3つ目はリソースです。システムのリソースを独自のDelegateResourcesに置き換えます。バンドルのリソースはインストール時にAssertPathに一つずつ追加されます。バンドルの追加順序は固定されていないため、パーティション分割を行わないとリソース検索エラーが発生します。

さらに、DalvikとARTではリソースの参照プロセスが異なります。また、Xiaomiのようなシステムは独自のリソースを書き換えるため、AssetsPathを末尾または先頭に追加することで、異なるモデルに適応する必要があります。システムのAssetManagerはシングルトンであり、デフォルトでは末尾に追加されます。先頭に追加する場合は、AssetsManagerオブジェクトを再作成する必要があります。同様に、メインDEXを動的にデプロイする場合、元のリソースを置き換えるには、挿入順序と参照順序が一致している必要があります。

また、resourceTable が更新されるたびに、apk リソースとランタイム システム リソース (Webview やバンドル リソースなど) が正常に追加され、一意であり、正しい順序になっていることを確認する必要があります。

異なるバンドルのリソースでは、名前の競合が発生する可能性があります。各バンドルに異なるIDを割り当てるという比較的シンプルな方法を採用することで、すべてのビジネスリソースの競合を防ぎ、プロジェクトフェーズ中に問題を解決しました。コードの多くの部分で、リフレクションを使用してリソース全体を呼び出しています。これはシステム5.0以降では問題なく動作し、最初のインスタンスのみを検索します。ビジネスコードについては、以前と同じです。

コンポーネントベースのパフォーマンスに関しては、オンデマンドロードを導入しました。Taobao APKには70以上のバンドルが含まれていますが、各ユーザーが必要とするのは5~10個程度なので、すべてのバンドルをロードする必要はありません。バンドルは独立しており、4つのネイティブAndroidコンポーネントを介して連携することで、より効率的な分離を実現しています。すべてのエントリポイントはBundleInfolistに基づいています。このリスト情報によってコンポーネントを含むバンドルが決定され、ロードが必要な場合はinstallやdexoptなどの操作が実行されます。

さらに、コンポーネント依存性の問題に対処するため、2つの新しいコンポーネント形式が定義されました。Awb(ビジネスバンドル)とsolib(soライブラリ)です。前者はAARと整合性がありますが、ローカルライブラリを追加せず、ビルドプロセス中に依存関係の調停を行います。後者はネイティブsoライブラリの依存関係です。Awbは基本的にAARと同じですが、サフィックスが変更されているだけです。パッケージがホストバンドルに配置されている場合はAARを使用し、コンポーネントバンドルの場合はAwbを使用してください。

ビジネスバンドルの依存関係については、ビルドフェーズでホストバンドルとビジネスバンドル、そしてそれらの依存関係を個別にパッケージ化します。その後、最短パスと***宣言原則に従ってツリーアービトレーションを実行し、各バンドルに必要な依存関係を取得します。パッケージ化の過程で、依存ライブラリはそれぞれのバンドルに配置されます。

***これはAPKビルドを指し、大幅に変更しました。上図の左側は、処理、コンパイル、署名を含む標準的なAPKビルドプロセスを表しています。私たちの違いは、特別な処理を必要とするAwbを追加している点です。Awbリソースは、ホストのresource.ap_とパッケージレベルのリソースに基づいて構築されます。Rファイルは、バンドルRリソースとホストのRリソースから生成されます。次に、Aaptを修正して各AWBに異なるpackageIdを割り当て、統一された難読化を行い、各A​​WBのDexファイルを生成し、APKにパッケージ化、署名、libsへのコピー、.soファイルへの名前変更を行い、最後にTaobao APKにマージしました。これが私たちのコンポーネント化プロセス全体です。

アトラスダイナミクス

コンテナフレームワークにおいて、コンポーネント化とダイナミズムは相互に補完し合います。コンポーネントは単に分離問題を解決するだけですが、パッケージをいつでもリリースしたい場合、コンテナフレームワークは動的な機能を備えている必要があります。Atlasのコンポーネント化が完了した後、ダイナミズムのサポートを追加しました。ダイナミズムの利点は2つあります。1つ目はパッケージサイズが縮小されるため、実行後に一部のパッケージをアプリケーションにダウンロードできることです。2つ目は、動的なリリースとバグ修正機能を提供することです。

増分動的スキーム

Atlasは、主に動的なビジネス展開と問題解決を目的とした動的展開機能を提供します。Taobaoが独自に開発した差分アルゴリズムをベースとし、メインバンドルはClassLoaderメカニズムを、ビジネスバンドルは差分マージをベースとすることで、あらゆるビジネスタイプをサポートします。

さらに、Atlasは迅速な障害修復を目的としたプラグインとしてAndfixもサポートしています。Andfixはネイティブフックをベースとし、主にメソッドの変更を行います。実際には、両者を併用することも可能です。プロジェクトのビルドフェーズで調整を行うことで、単一のコードベースで両方のソリューションとして機能させることができます。

自社開発の動的デプロイメント機能は、以下の手順で動作します。まず、Dex Patchを生成するために、Dexのバイトコードを修正し、DexファイルをSmali形式に変換します。その後、ClassDef構造とClassDataMethod構造を解析し、クラスの削除、追加、変更を行います。次に、Diff処理によって差分ファイルを取得し、最後にMerge処理によってパッチを生成します。

次に、リソースパッチ全体の生成は、ビジネスバンドルとメインバンドルの2つの部分に分かれています。ビジネスバンドルは継続的な読み込みプロセスであり、実装が比較的簡単で、MD5 diff/BSDiffを使用して取得できます。メインバンドルの場合、すべてのリソースがベースパッケージ内になければならないというAndroidの制限により、新しいリソースを追加しても効果がありません。そのため、パッケージ化中に多くの空リソースを予約しておくというアプローチがあります。既存のリソースの更新は、リソースの上書きによって行われます。

新しいビジネスロジックが追加されると、新しいアクティビティが追加されます。私たちのアプローチは、まずマニフェストにStubActivityを埋め込み、次にInstrumentation.execStartActivity() ステージで置き換え、同時にIntent setFlagを使用してアクティビティの起動モードをシミュレートし、startActivityで続行することです。その後、System_serverプロセスがプロセスを処理し、ActivityStackを更新してバインダーを作成し、ActivityThreadにインスタンスを作成するように通知します。ActivityThreadのハンドラーでこれをインターセプトし、ActivityInfoなどの情報を更新して、対象のアクティビティを作成します。

さらに、エンジニアリングの実践では、パッチ生成にはDexとリソースのベースラインが関係するため、APKパッケージがリリースされるたびにAP(ベースラインパッケージ)をMavenに同時に公開します。APベースラインパッケージには、ベースラインに影響を与えるすべてのファイルが含まれており、最初のファイルはAndroid APK、2番目のファイルはMapping.txt、最後のファイルはDependency.txtです。これにより、ビルドプロセス全体が非常に高速になります。

そのため、バージョンアップへのアプローチも異なります。例えば、Taobaoアプリの詳細ページを今日更新する必要がある場合、新しいバージョンがリリースされます。このバージョンはアプリストアにリリースされたバージョンではなく、パッチパッケージである可能性があります。一方、ビジネスバージョンの動的展開では、5.3.0から5.3.1、そして5.3.2へと同期的に展開します。この方法の利点は、コンテナバージョンがアップグレードされていない限り、そしてニーズがある限り、パッチを継続的にアップグレードでき、シームレスな増分アップグレードを実現できることです。

周囲の最適化ポイント

周辺の最適化についてお話しします。なぜ今になってオープンソースについて話しているのでしょうか?その過程で、かなりの数の問題に直面しました。

重要なのは、バンドル内の重複リソースを統合することです。ホスト固有の問題により、画像リソースを含む競合が避けられないことが分かりました。画像リソースはホスト全体のカテゴリに配置します。

2つ目は、バンドルの依存関係検証です。以前はコードをコンパイルすることで検証していましたが、現在はバイナリコードを使用しているため、この問題は環境に残ります。そのため、APIがバンドルに影響を与えるかどうかを確認します。

3 つ目は、Taobao Mobile があまりにも多くのミドルウェア クラス ライブラリに依存しているため、Taobao Mobile 自体が多数のメソッドで肥大化していたため、クラス ライブラリを「スリム化」しました。そのため、パッケージ化中にクラス ライブラリをトリミングするプロセスを実行して、メソッドの数を最適化しました。

4 番目は、依存関係、具体的には依存関係クエリ ライブラリによって発生します。

5 番目に、Dex ファイルの作成などの難読化マッピングを実行します。

***は現在オープンソース化の準備段階にあります。エンジニアリングおよび運用フェーズでオープンソース化し、クラウドサービスを通じてメカニズムを提供します。Alibaba Baichuanは、迅速な生成、展開、ロールバック、監視機能など、Atlasの研究開発サポート機能を提供します。