プログラマ英語学習日記

プログラミングと英語学習のまとめなど

Jetpack Compose をライブラリ化し動かす手順まとめ

Jetpack Composeとは

GoogleIOで発表された、平たくいうとReact/FlutterみたいなコードでAndroid-Viewを作れるやつです。(見た目はともかく構造としてはReactHooksが近いかもしれません)

しかし、まだリリースは遠く試すにはJetpackのソースからビルドするしか方法しかない模様。ちょっと試すだけならJetpack内のデモを編集でもいいのですが、いかんせん巨大なので時間がかかります。

そこで一度ライブラリ化し、独立したProjectから読み込めるようにするまでの手順をまとめました。

※記事は 2019/05/16 10:00 ごろのソースを用いました。前日はなぜかビルド不能だったので、日付により大きく変わる可能性があるので注意してください。また他のAndroidStudioがあるせいかわかりませんが、別マシンでは一切ビルドできませんでした。

流れ

  1. Jetpack一式取得
  2. サンプル実行
  3. ライブラリビルド
  4. プロジェクト生成
  5. 実行
  6. コード例他

(1)Jetpack

まずJetpackをソースごと一式引っ張ってくる必要があります。

ComposeサイトおよびJetpackサイトを参考に構築します。

一通り必要そうなコマンドを列挙します。数GBあり、公式に「コーヒー飲んでてね!:coffee:」と書かれているぐらいなので気長にまちましょう。

mkdir ~/bin
PATH=~/bin:$PATH
curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
chmod a+x ~/bin/repo
git config --global user.name “Your Name”
git config --global user.email “you@example.com”
mkdir WORKING_DIR
cd WORKING_DIR
repo init -u https://android.googlesource.com/platform/manifest -b androidx-master-dev
repo sync -j8 -c

(2)サンプル実行

実行するには、Jetpack組込の専用AndroidStudioを利用します。

cd WORKING_DIR/frameworks/support/ui
chmod a+x studiow
./studiow

途中で規約同意がでるのでYで進みます。 が、一回目はうまくProjectを読み込んでくれませんでした。

もし起動後にプロジェクトが自動で開かない場合、一度閉じてやりなおしてください。それで自分の場合はうまくいきました。

また巨大すぎるのでメモリが足りないと不安定な模様。 Help -> EditCustomVMOptions で変更します。 自分は最大8GBを割り当てました。ここはマシンと相談してください。

-Xms4g
-Xmx8g
-XX:MaxPermSize=512m

もちろん巨大なプロジェクトなので起動しても時間を食います。起動後も気長に待ってください。諸々終わると、 ui-demos ui-material-studies の2つが実行可能になります。

どちらでもいいので実行し、エミュレータ(など)で動作すれば成功です。

「新しいKotlinあるよ!アップデートしよう!」と出ると思いますが、専用の 1.3.30-compose-20190503 という特殊KotlinVersionなのでやらないほうがベターかと思います。


サンプルとしてはui-material-demosのモジュールが分かりやすいのではないかと。ちょっと実験する程度なら、ここのコードに直接変更を加えながら試すだけ十分かと思います。

以降はこのProjectをライブラリ化し、他Projectから楽に,早く読み込めるようにするための手順になります。直接サンプルを編集する場合は不要です。

(3)ライブラリ作成

やることはたいしたことなく、 ui/

./gradlew createArchive

するだけ....なのですがハマりポイントがありました。textがExportされず、出力ライブラリからアプリをビルドできません。

そこでui-android-textのgradleを開き修正します。

androidx {
  ...
  publish = true   //ここがfalseだった!
  ...
}

変更したら createArchive しましょう。(自分はついでに、一個上のフォルダでも createArchiveしておきました)

追記:だめだったらsudoで実行してみてください。これで別マシンでもStudioからはビルド不能でしたが、Archiveは作成できました。

(4)プロジェクト作成

いよいよProject作成に入りますが、意外と面倒でした。

まず 例の専用Studioからプロジェクトを新規作成します(もちろんKotlinを選択)。 好みですがBlankActivityで十分かと思います。

生成後、ガッツリProjectの設定に入ります。

local.properties

Jetpackをコピーしたディレクトリを環境変数に指定しておきます。local.properties

#他
androidx.home=WORKING_DIR

などなど環境に合わせてで。面倒ならGradleにフルパス直接でもOKです。

Gradle

次にProjectのGradleを編集します。上のほうだけざっくり

buildscript {
   Properties properties = new Properties()
   properties.load(project.rootProject.file('local.properties').newDataInputStream())

   ext.kotlin_version = '1.3.30-compose-20190503'
   ext.androidx_home = properties.getProperty("androidx.home")
   repositories {
       maven { url "$androidx_home/out/ui/build/support_repo/" }
       maven { url "$androidx_home/prebuilts/androidx/external/" }
       //supportを作ってないなら次の行は不要
       maven { url "$androidx_home/out/support/build/support_repo/" } 
       mavenLocal()
       google()
       jcenter()
   }
   //以下略

prebuiltsがプラグイン他のようです。ここも指定しないとビルドできませんでした。 その後モジュールのGradleに依存を記述します。

//他の部分は省略
repositories {
    maven { url "$androidx_home/out/support/build/support_repo/" }
    maven { url "$androidx_home/out/ui/build/support_repo/" }
    maven { url "$androidx_home/prebuilts/androidx/external/" }
    google()
    jcenter()
}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    //このへんをもろもろ追加
    implementation "androidx.appcompat:appcompat:1.0.2"
    implementation "androidx.core:core-ktx:1.0.2"
    implementation "androidx.compose:compose-runtime:1.0.0-alpha01"
    implementation "androidx.ui:ui-framework:1.0.0-alpha01"
    implementation "androidx.ui:ui-material:1.0.0-alpha01"
    implementation "androidx.ui:ui-layout:1.0.0-alpha01"
    //以上
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

これでGradleSyncができれば次のフェイズにすすみます。(※この時点で自分の場合はsyncしたが、ビルドは通らなかった)

(5)実行

プロジェクト生成時に自動作成されたSimpleComposable.kt がエラーだったので修正します。短いので全部書いてしまいます。

package YOUR_PACKAGE
import androidx.compose.*
import androidx.ui.core.CraneWrapper
import androidx.ui.core.Text
@Composable
fun SimpleComposable() {
    CraneWrapper {
        Text(text = "Hello World!")
    }
}

上記コードが数ヶ月後には動かなくなる可能性はかなり大きいので注意してください。 またMainActivityがandroidxでなく古いsupport-libだったら置き換えてください。

ここまでくれば実行可能なはず!エミュレータなり実機で実行し、HelloWorldとでれば成功です。

(6)コード例他

どうもCraneWrapperで囲む必要があるようです。大半の場合Materialも必要になると思うので、次がテンプレコードになりそうです。

//Activity等
fun onCreate(...) {
   //略
   setContent {
     CraneWrapper {
       MaterialTheme {
          //your view
       }}}
}

React/Flutterと同様に、コードでViewを生成するという特性を持っているので、XML-Layoutより柔軟にViewを構築できます。forやifが使い放題です。

@Composable
fun TextListView(texts: List<String>) {
    Column {
        texts.map {
            if (it.isEmpty()) Text(text = "EMPTY")
            else Text(text = it)
        }
    }
}

またGoogleIOの動画ではあまり触れられてなかったように見えたのですが、Hooksのようなコードが書けます。実際に次のようなカウンタが動作しました。

@Composable
fun CounterView() {
    val counter = +state<Int> { 0 } //useState相当と思われる
    Column {
        Text(text="${counter.value}")
        Button(
            text="Count Up",
            onClick = { counter.value++ }
        )
    }
}

通信非同期系も作ってみたのですが、うまくListが更新されずに詰み。厳密にはデータ自体は正常更新なのですがうまくViewが出ませんでした(一瞬でて消える不審な挙動)

ListView的なものが見つからなかったので(現在は縦ScrollViewで強引に処理)、見つけたらリトライしてみたいです。


将来的には、既存ViewのようにXML埋め込みも視野にいれている模様。既存アプリを徐々に移行させることもできそうです。

//まだありません。注意
@Composable
@GenerateView
fun Greeting(name: String) {
}
//xmlのほう
<GreetingView app:name=”引数”>

最後に

一通り現状でJetpackComposeを楽しむための手順をまとめてみました。Viewの組み方が大幅に変わるので正式版が楽しみです。

ようやくライブラリからのビルドに成功した程度でまだ手探りですが、今までにない組み方が面白いです。

が、まだ不具合が多いのか思ったとおりにViewが出ないことも... エラーもなく終了することが多いので、現状では出なかったら諦めて修正を待つぐらいがちょうどいいと思います。

※全ソースはgithubにおきましたので、うまくビルドできない場合などは参考にしてください。