2017/02/18

Dagger2. MultibindingでComponentを綺麗に仕上げる

はじめに

2017年に入ってDagger2もバージョン2.9を迎えました.
Androidでも使われることが多いDIフレームワークも, バージョンを重ねるごとに便利なAPIが増えています.

本稿はAndroidアプリを例に, Activityに依存するComponentからインジェクションする方法について, 過去のAPIを使用した方法と, 新しいAPIを使用した方法とで比較を行い, より綺麗なインジェクションを実現していきます.

今回紹介する内容+αとソースコードはGitHubにもアップしていますのでそちらもあわせてご覧ください :)

goto GitHub pages

Subcomponent. 親と子の密結合問題

Androidでは, コンポーネントをアクティビティの単位で分割することがよくあります(e.g. MainActivityComponent, SettingActivityComponent, etc.)
そのようなコンポーネントが依存性の解決にアプリケーションスコープのオブジェクトを必要とする場合や, 他のコンポーネントからも参照される共有オブジェクトを持つ場合にSubcomponentとして定義されることがあります.
サブコンポーネントの仕組みは, それぞれのいくつかのコンポーネントが持つ共通部分をまとめて定義したり, スコープの観点からみた”親-子”を定義したりするのに大変便利です.
ただ, 古いバージョンのDaggerではサブコンポーネントから依存される親コンポーネントは, サブコンポーネントのクラスを知っている必要があり, 親と子の結合度が高くなってしまう問題がありました.

@Component(...)
public interface AppComponent {
  // 親であるAppComponentは子にあたるサブコンポーネントを全て知っておく必要がある :(
  MainActivityComponent plus(MainActivityModule module);
  SettingActivityComponent plus(SettingActivityModule module);

この問題はDagger2.7で追加された@Modulesubcomponent属性を使うことで解決できます.

Module.subcomponents

@Modulesubcomponent属性はサブコンポーネントの親を指定するための新しい手段を提供します.

subcomponent属性を持つモジュールは親コンポーネントと子コンポーネントの関係を築く橋渡し役になります.
このモジュールは親コンポーネントの@Component(modules=...)に定義されることで, 親コンポーネントに属することになります.

// subcomponent属性には対象となるサブコンポーネントクラスを定義する  
@Module(subcomponents = {MainComponent.class, SubComponent.class})
public abstract class ActivityBindingModule {
}

// 親コンポーネントのmodulesに上記モジュールを追加する
@Component(modules = {AppModule.class, ActivityBindingModule.class})
public interface AppComponent { … }

この方法を使う場合は@Module(subcomponents={...})で指定したサブコンポーネントをどのように構築するのかを定義するコンポーネントビルダーが必要になります.
サブコンポーネントの内部インタフェースとしてビルダーを新たに宣言し, これに@Component.Builderアノテーションをつけます.
また, (サブ)コンポーネントビルダーにはいくつかの実装ルールがあるのでそれに従います.

@Subcomponent(modules = MainModule.class)
public interface MainComponent {
  @Subcomponent.Builder interface Builder {
    Builder activityModule(MainModule module);
    MainComponent build();
  }

サブコンポーネントのビルダーはDaggerによってプライベートなインナークラスとして自動生成されます.
必要なビルダーはActivityBindingModuleをインストールした親コンポーネントから提供されます.

@Component(modules = { AppModule.class, ActivityBindingModule.class })
public interface AppComponent {
  MainComponent.Builder mainComponentBuilder();
  SettingComponent.Builder settingComponentBuilder();

ここまでの変更によって, 親コンポーネントが抱えていたサブコンポーネントとの密結合関係がコンポーネントビルダーとの密結合関係に変わりました.
まだ, 親コンポーネントはサブコンポーネントのことを知っている状態で, サブコンポーネントが増えると親コンポーネントもあわせて修正しないといけません.

次はマルチバインディングの機能を使ってこの問題に対処します.

Multibinding. コンポーネントマップの自動生成

Dagger2.4から, 生成したオブジェクトをコレクションにバインドするマルチバインディング機能が追加されました.
これにより, 値がプリセットされたSetMapをインジェクションできるようになりました.
次はこのマルチバインディングを使ってサブコンポーネントビルダーのマップコレクションを作り, 親コンポーネントとコンポーネントビルダーの関係を疎にして, サブコンポーネントを柔軟に追加できる仕組みを作っていきます.

まずはじめに, マルチバインディングでマップに格納するKeyValueを決めておきます.
Androidではコンポーネットをアクティビティ単位で分割することが多いのでKeyにはアクティビティのクラスオブジェクトを格納し, Valueにはコンポーネントビルダーを格納することにします.

アプリケーションクラスが各アクティビティの詳細を知らなくてもいいように, アクティビティに直接関わるコンポーネントを抽象化したActivityComponentインタフェースとモジュールを抽象化したActivityModuleインタフェースを定義します. これらはまだマーカーインタフェース扱いですが後々メソッドを定義していきます.

public interface ActivityComponent {
}

public interface ActivityModule {
}

// Activityと関連するコンポーネントとモジュールはこれを実装する
@Subcomponent(modules = MainComponent.MainModule.class)
public interface MainComponent extends ActivityComponent<MainActivity> {
  ...
  @Module
  class MainModule extends ActivityModule {
    ...
  }
}

コンポーネントビルダーも抽象化します.
上記で定義したインタフェースActivityModuleをパラメータとして受け取り, ActivityComponentを構築して返すビルダーパターンのインタフェースです.

public interface ActivityComponentBuilder<M extends ActivityModule, C extends ActivityComponent> {
  ActivityComponentBuilder<M, C> activityModule(M activityModule);

  C build();
}

// Activityと関連するサブコンポーネントビルダはこれを実装する
@Subcomponent.Builder
interface Builder extends ActivityComponentBuilder<MainModule, MainComponent> {
}

アクティビティがアクティビティコンポーネントを取得したい場合の手順は次の通りです.

  1. ActivityComponentBuilderの実装クラスにあたるビルダーインスタンスを取得
  2. 必要なActivityModuleをビルダーに指定する
  3. buildメソッドでActivityComponentを構築してコンポーネントインスタンスを取得

今時点ではまだコンポーネントビルダーを取得するために親コンポーネントは具体的なクラス名を指定する必要がある状態です.

@Component(modules = { AppModule.class, ActivityBindingModule.class })
public interface AppComponent {
  MainComponent.Builder mainComponentBuilder();
  SettingComponent.Builder settingComponentBuilder();

アクティビティがコンポーネントを取得する時にはAppComponent経由で必要なコンポーネントビルダーを取得することになります.
現状でもまだ親コンポーネントからサブコンポーネントビルダーへの依存を分離できていません.
次はいよいよ, 用意したActivityComponent, ActivityModule, ActivityComponentBuilderとマルチバインディング機能を使って依存関係を取り除いていきます.

マルチバインディングでマップに格納するオブジェクトを提供するにはキーにする型をはっきりさせる必要があります.
今回は, アクティビティのクラス情報をキーとするので専用のアノテーションを作成します.
キーとしてClassを受け取り, その型をActivityのサブクラスに制限するジェネリクスを指定しておくのがポイントです.
これで, マルチバインディングが行われるマップのキーにはActivityのサブクラスだけが許可されるようになります.

@MapKey
public @interface ActivityMapKey {
  Class<? extends Activity> value();
}

それでは, マルチバインディングの対象となるマップを定義しましょう.
マップを提供する方法は他のオブジェクトと同じくモジュールのメソッドとして定義します.
今回はサブコンポーネントを柔軟に追加できるように改良することが目的なので, これを定義するモジュールは@Module(subcomponents=...)を宣言しているActivityBindingModuleが妥当でしょう.

ActivityBindingModuleにマルチバインディングされるマップの定義を追加したものが下記です.

@Module(subcomponents = {MainComponent.class, SubComponent.class})
public abstract class ActivityBindingModule {
  @Provides @IntoMap @ActivityMapKey(MainActivity.class)
  public ActivityComponentBuilder mainComponentBuilder(
      MainComponent.Builder builder) {
    return builder;
  }

  @Provides @IntoMap @ActivityMapKey(SettingActivity.class)
  public ActivityComponentBuilder settingComponentBuilder(
      SettingComponent.Builder builder) {
    return builder;
  }
}

ここでさらに, Dagger2.4から導入された@Bindsを使えば, こういったボイラープレートなプロバイダーメソッドの定義を簡略化できます.
Dagger2.5からはマルチバインディングに対しても使えるようになったので, これを使ってシンプルに定義したものが下記です.

@Module(subcomponents = {MainComponent.class, SettingComponent.class})
public abstract class ActivityBindingModule {
  @Binds @IntoMap @ActivityMapKey(MainActivity.class)
  public abstract ActivityComponentBuilder mainComponentBuilder(
      MainComponent.Builder builder);

  @Binds @IntoMap @ActivityMapKey(SettingActivity.class)
  public abstract ActivityComponentBuilder settingComponentBuilder(
      SettingComponent.Builder builder);
}

さて, これでClass<? extends Activity>型をキーに持ち, 値にはActivityComponentBuilder型が格納されるMapを自動生成できるようになりました.
これを提供するプロバイダーメソッドは親コンポーネントにあたるAppComponentに定義します.

@Component(modules = { AppComponent.AppModule.class, ActivityBindingModule.class })
public interface AppComponent {
  Map<Class<? extends Activity>, ActivityComponentBuilder> activityComponentBuilders();

これによって, Daggerがマルチバインディングによってコンポーネントビルダーのマップを構築し, それをactivityComponentBuilders()メソッド経由で提供するようになりました.
マルチバインディング適用前と異なる点は, もはやAppComponentがサブコンポーネント(MainComponentBuilder etc.)について知らなくてよくなったという点です.
今やAppComponentは抽象化されたビルダーActivityComponentBuilderのことだけ知っていればよいのです.
これでついに親コンポーネントがサブコンポーネントから独立しました Yay!
アクティビティとサブコンポーネントが追加されてもアプリケーションクラスを変更する理由はもはやありません.
アクティビティに関係するサブコンポーネントに追加・変更がある場合の修正はActivityBindingModuleに閉じています.

plus one

これで一通りの実装は完了ですが, もう一歩進めましょう.

アクティビティのコンポーネントにはinjectメソッドを定義することがよくあります.
なのでActivityComponentにこれを定義します.

public interface ActivityComponent<T extends Activity> extends MembersInjector<T> {
}

もうひとつ, ActivityModuleにはアクティビティインスタンスを保持させることがよくあるので, その定義を追加しておきます.

@Module
public abstract class ActivityModule<T extends Activity> {
  protected final T activity;

  public ActivityModule(T activity) {
    this.activity = activity;
  }

  @Provides public T provideActivity() {
    return activity;
  }
}

アクティビティコンポーネントを取得するときは, 下記の手順です.

  1. ActivityComponentBuilderの実装クラスにあたるビルダーインスタンスを取得
  2. 必要なActivityModuleをビルダーに指定する
  3. buildメソッドでActivityComponentを構築してコンポーネントインスタンスを取得

ActivityComponentBuilderを取得するメソッドは以前と同様, 親コンポーネントにあたるAppComponentに定義されているので, 下記の要領で取得します.
あとはビルダーに必要なモジュールを指定してコンポーネントを構築します.

// コンポーネントビルダーのマップを取得
Map<Class<? extends Activity>, ActivityComponentBuilder> map =
    ((AppComponent) context.getApplicationContext().activityComponentBuilders();

// コンポーネントビルダーのインスタンスを取得
MainComponent.Builder builder = (MainComponent.Builder)map.get(MainActivity.class);

// コンポーネントビルダーでコンポーネントを構築
MainComponent component = builder.activityModule(new MainModule(activity)).build();

いい感じですね. コンポーネントビルダーが取得できればコンポーネントを構築すれば目的のコンポーネントが取得できます.
コンポーネントが取得できれば, injectMembers(activity)で依存性を注入できます.

それでは最後に主要なクラス達を整理しておきます.

// アクティビティのコンポーネントを表現するインタフェース
public interface ActivityComponent<T extends Activity> extends MembersInjector<T> {...}

// アクティビティのモジュールを表現する基底クラス
public abstract class ActivityModule<T extends Activity> {...}

// 具体的なアクティビティコンポーネントとそのビルダとモジュール
@Subcomponent(modules = MainComponent.MainModule.class)
public interface MainComponent extends ActivityComponent<MainActivity> {
  @Subcomponent.Builder
  interface Builder extends ActivityComponentBuilder<MainModule, MainComponent> {
  }

  @Module
  class MainModule extends ActivityModule<MainActivity> {
    ...
  }
}

// アクティビティモジュールのバインドを定義するクラス
@Module(subcomponents = { MainComponent.class })
public abstract class ActivityBindingModule {
  @Singleton @Binds @IntoMap @ActivityMapKey(MainActivity.class)
  public abstract ActivityComponentBuilder mainActivityComponentBuilder(
      MainComponent.Builder builder);
}

// 親コンポーネントにあたるアプリケーションコンポーネント
@Component(modules = { AppModule.class, ActivityBindingModule.class })
public interface AppComponent {
  Map<Class<? extends Activity>, ActivityComponentBuilder> activityComponentBuilders();
  @Module
  class AppModule { ... }
}

// アクティビティコンポーネントを取得するコード
Map<Class<? extends Activity>, ActivityComponentBuilder> map =
    ((AppComponent) context.getApplicationContext().activityComponentBuilders();
MainComponent.Builder builder = (MainComponent.Builder)map.get(MainActivity.class);
MainComponent component = builder.activityModule(new MainModule(activity)).build();

// 依存性を注入
component.injectMembers(activity);

Daggerの新しいAPIを使ってアクティビティコンポーネントを取得することができました :)
さらに, もう一歩進めてDaggerによってスコープ制御されているコンポーネントをMortarライブラリで更に使いやすくする方法がありますが, これは次の機会にします.
今回紹介した内容+αと動くソースコードをGitHubにもアップしています. そちらもあわせてご覧ください :)

goto GitHub pages

以上です.

2016/11/07

Android: ExoPlayer - Demo application

翻訳ページ:
https://google.github.io/ExoPlayer/demo-application.html

Demo application

The ExoPlayer demo app serves two primary purposes:

  • To provide a relatively simple yet fully featured example of ExoPlayer usage. The demo app can be used as a convenient starting point from which to develop your own application.
  • To make it easy to try ExoPlayer. The demo app can be used to test playback of your own content in addition to the included samples.

This page describes how to get, compile and run the demo app. It also describes how to use it to play your own media.

ExoPlayerのデモアプリは次の2つの大きな理由のために用意してあります:

  • シンプルでありながら全てのExoPlayer機能の使い方を例示していること. デモアプリがアプリ開発を始める際の開始点としてとして便利であること.
  • ExoPlayerをお手軽に試すことができること. デモアプリにオリジナルコンテンツを追加して再生のテストにも使えること.

このページではどうやってデモアプリを取得し, コンパイルし, 実行するのかを説明します.
また, あなたが用意したメディアをどうやって再生するのかについても説明します.

Getting the code

The source code for the demo app can be found in the demo folder of our GitHub project. If you haven’t already done so, clone the project into a local directory:

git clone https://github.com/google/ExoPlayer.git

Next, open the project in Android Studio. You should see the following in the Android Project view (the relevant folders of the demo app have been expanded):

デモアプリのソースコードはGitHubプロジェクトから取得できます. まだ取得していないのであればローカルディレクトリにcloneしてください.

git clone https://github.com/google/ExoPlayer.git

次に, AndroidStudioでAndroid Projectビューを開くと次の表示を確認できます.

Figure 1. The project in Android Studio

Compiling and running

To compile and run the demo app, select and run the demo configuration in Android Studio. The demo app will install and run on a connected Android device. We recommend using a physical device if possible. If you wish to use an emulator instead, please read FAQ - Does ExoPlayer support emulators? and ensure that your Virtual Device uses a system image with an API level of at least 23.

AndroidStudioのConfigurationからdemoを選択し, デモアプリをコンパイル, 実行してください. 接続されているデバイスにデモアプリがインストールされて実行されます. 可能であればエミュレータではなく物理的なデバイスの使用を推奨します.

もし代わりにエミュレータを使用したいのであればFAQ - Does ExoPlayer support emulators?を読んでください. そうすればAPI lv23以降の仮想デバイスで使うことができます.

Figure 2. SampleChooserActivity and PlayerActivity

The demo app presents of a list of samples (SampleChooserActivity). Selecting a sample will open a second activity (PlayerActivity) for playback. The demo features playback controls and track selection functionality. It also has an EventLogger class that outputs useful debug information to the system log. This logging can be viewed (along with error level logging for other tags) with the command:

デモアプリのSampleChooserActivityにサンプルのリストがあります. リスト項目を選択すればそれを再生するためのPlayerActivityが起動します. デモは再生コントロールとトラック選択の機能を持っています. また, システムログに有用なデバッグ情報を出力するEventLoggerクラスを持っています. 次のコマンドでEventLoggerの有効にして, ほかのログはエラー情報のみ表示することができます.

adb logcat EventLogger:V *:E

Including extension decoders

ExoPlayer has a number of extensions that allow use of bundled software decoders, including VP9, Opus, FLAC and FFMPEG (audio only). The demo app can be built to include and use these extensions as follows:

  1. Build each of the extensions that you want to include. Note that this is a manual process. Refer to the README.md file in each extension for instructions.
  2. In Android Studio’s Build Variants view, change the build variant for the demo module from demoDebug to demo_extDebug, as shown in Figure 3.
  3. Compile, install and run the demo configuration as normal.

ExoPlayerはいくつかのエクステンションがありVP9, Opus, FLAC and FFMPEG (audio only)といったソフトウェアデコーダを使うことができます. デモアプリでこれらを使うための手順は次の通りです:

  1. 取り込みたいExtensionをビルドします. これは手作業での変更になります. それぞれのExtensionにあるReadmeには手順が示されています.
  2. Figure 3を参考に, AndroidStudioのビルドバリアントビューでデモモジュールのビルドバリアントをdemoDebugからdemo_extDebugに切り替えます.
  3. いつもの通りデモをコンパイル, インストール, 実行します.


Figure 3. Selecting the demo_extDebug build variant

By default an extension decoder will be used only if a suitable platform decoder does not exist. It is possible to indicate that extension decoders should be preferred, as described in the sections below.

デフォルトのデコーダ拡張は適切なプラットフォームデコーダが存在しない時だけ使われる。これはのそセクションで拡張デコーダが望ましいことを示すことができる。

Playing your own content

There are multiple ways to play your own content in the demo app.

デモアプリで手持ちのコンテンツを再生するにはいくつかの方法があります.

1. Editing assets/media.exolist.json

The samples listed in the demo app are loaded from assets/media.exolist.json. By editing this JSON file it’s possible to add and remove samples from the demo app. The schema is as follows, where [O] indicates an optional attribute.

デモアプリではasset/media.exolist.jsonから読み込まれたサンプルコンテンツがリストされています. このJSONファイルを編集することでこれらのサンプルに追加したり削除したりすることができます. JSONの形式は次の通り. [O]はオプションの属性です.

[
  {
    "name": "Name of heading",
    "samples": [
      {
        "name": "Name of sample",
        "uri": "The URI/URL of the sample",
        "extension": "[O] Sample type hint. Values: mpd, ism, m3u8",
        "prefer_extension_decoders": "[O] Boolean to prefer extension decoders",
        "drm_scheme": "[O] Drm scheme if protected. Values: widevine, playready",
        "drm_license_url": "[O] URL of the license server if protected",
        "drm_key_request_properties": "[O] Key request headers if protected"
      },
      ...etc
    ]
  },
  ...etc
]

Playlists of samples can be specified using the schema:

サンプルのプレイリストはスキーマを使って特定できる.

[
  {
    "name": "Name of heading",
    "samples": [
      {
        "name": "Name of playlist sample",
        "prefer_extension_decoders": "[O] Boolean to prefer extension decoders",
        "drm_scheme": "[O] Drm scheme if protected. Values: widevine, playready",
        "drm_license_url": "[O] URL of the license server if protected",
        "drm_key_request_properties": "[O] Key request headers if protected"
        "playlist": [
          {
            "uri": "The URI/URL of the first sample in the playlist",
            "extension": "[O] Sample type hint. Values: mpd, ism, m3u8"
          },
          {
            "uri": "The URI/URL of the first sample in the playlist",
            "extension": "[O] Sample type hint. Values: mpd, ism, m3u8"
          },
          ...etc
        ]
      },
      ...etc
    ]
  },
  ...etc
]

If required, key request headers are specified as an object containing a string attribute for each header:

もし必要なら, それぞれのヘッダに文字列を含むオブジェクトのキーリクエストヘッダを指定できます.

"drm_key_request_properties": {
  "name1": "value1",
  "name2": "value2",
  ...etc
}

2. Loading an external exolist.json file

The demo app can load external JSON files using the schema above and named according to the *.exolist.json convention. For example if you host such a file at https://yourdomain.com/samples.exolist.json, you can open it in the demo app using:

デモアプリは上記のスキーマを使った*.exolist.jsonにマッチするJSONを読み込むことができます. もしファイルをhttps://yourdomain.com/samples.exolist.jsonでホストしているならデモアプリを使って次のコマンドで開くことができます.

adb shell am start -d https://yourdomain.com/samples.exolist.json

Clicking a *.exolist.json link (e.g. in the browser or an email client) on a device with the demo app installed will also open it in the demo app. Hence hosting a *.exolist.json JSON file provides a simple way of distributing content for others to try in the demo app.

ブラウザやemailクライアントで*.exolist.jsonのリンクをクリックするとデモアプリがインストールされていれば起動することができます. デモアプリで試すには*.exolist.jsonのJSONファイルをホストするのがコンテンツを他へ配信するのに簡単な方法です.

3. Firing an intent

Intents can be used to bypass the list of samples and launch directly into playback. To play a single sample set the intent’s action to com.google.android.exoplayer.demo.action.VIEW and its data URI to that of the sample to play. Such an intent can be fired from the terminal using:

Intentはサンプルリストをバイパスするのと直接再生することに使えます. 1つのサンプルセットを再生するにはアクションcom.google.android.exoplayer.demo.action.VIEWとデータのURIが設定されたIntentを使います. そうしたIntentをターミナルから発行するには次のコマンドを使います.

adb shell am start -a com.google.android.exoplayer.demo.action.VIEW \
    -d https://yourdomain.com/sample.mp4

Supported optional extras for a single sample intent are:

  • extension [String] Sample type hint. Valid values: mpd, ism, m3u8
  • prefer_extension_decoders [Boolean] Whether extension decoders are preferred to platform ones
  • drm_scheme_uuid [String] Drm scheme UUID if protected
  • drm_license_url [String] Url of the license server if protected
  • drm_key_request_properties [String array] Key request headers packed as name1, value1, name2, value2 etc. if protected

IntentがサポートしているオプショナルなExtraは次の通り:

  • extension [String] サンプルの種類. 使える値は mpd ism m3u8
  • prefer_extension_decoders [Boolean] extensionデコーダーがプラットフォームに適しているかどうか
  • drm_scheme_uuid [String] 保護されている場合のDrm形式のUUID
  • drm_license_url [String] 保護されている場合のライセンスサーバーURL
  • drm_key_request_properties [String array] 保護されている場合, name1, value1, name2, value2の形でパッケージされたキーリクエストヘッダ

When using adb shell am start to fire an intent, an optional string extra can be set with --es (e.g. --es extension mpd). An optional boolean extra can be set with --ez (e.g. --ez prefer_extension_decoders TRUE). An optional string array extra can be set with --esa (e.g. --esa drm_key_request_properties name1,value1).

Intentの発行にadb shell am startを使うなら, Extraオプションの文字列は--es (e.g. --es extension mpd)で指定できる. booleanであれば--ez (e.g. --ez prefer_extension_decoders TRUE). String配列であれば--esa (e.g. --esa drm_key_request_properties name1,value1)を使って設定することができる.

To play a playlist of samples set the intent’s action to com.google.android.exoplayer.demo.action.VIEW_LIST and use a uri_list string array extra instead of a data URI. For example:

サンプルのプレイリストを再生するにはIntentのアクションcom.google.android.exoplayer.demo.action.VIEW_LISTと文字配列uri_listにデータURIを指定します. 例えば,

adb shell am start -a com.google.android.exoplayer.demo.action.VIEW_LIST \
    --esa uri_list https://a.com/sample1.mp4,https://b.com/sample2.mp4

Supported optional extras for a playlist intent are:

プレイリストのIntentでサポートされているオプションは次の通り:

  • extension_list [String array] Sample type hints. Entries may be empty or one of: mpd, ism, m3u8
  • prefer_extension_decoders, drm_scheme_uuid, drm_license_url and drm_key_request_properties, all as described above
  • extension_list [String array] サンプルタイプのヒント. 内容は空かmpd, ism, m3u8のうちどれかです
  • prefer_extension_decoders, drm_scheme_uuid, drm_license_urldrm_key_request_propertiesそれぞれは上記で説明されています.