- 投稿日:2020-10-18T20:19:20+09:00
AndroidStudio4.1でViewModelを使う
なんか前のやり方でできなくなってたので
環境
- Android Studio 4.1
- Kotlin
- Mac
結論
ライブラリ
build.gradle(app)plugins { id 'kotlin-android-extensions' } dependencies { // ViewModel implementation "androidx.activity:activity-ktx:1.1.0" // Activityから使う時 implementation "androidx.fragment:fragment-ktx:1.2.5" // Fragmentから使う時 }バージョンは
Activity - AndroidDeveloper
Fragment - AndroidDeveloper
から安定版を使用
extensions
はsynthetic
でfindViewByIdの代わりにする時のやつ
Kotlin Android Extensionsを試してみた - Qiita
今まではデフォルトで入ってたのに4.1で消えたActivity
MainActivity.ktval viewModel: SampleViewModel by viewModels()Activityから呼ぶ時は
by viewModels()
Fragment
SampleFragment.ktval viewModel: SampleViewModel by activityViewModels() // Activityと同じViewModel使う時はこっち val viewModel: SampleViewModel by viewModels() // こっちだと新しいインスタンスを作成っぽいFragmentから呼ぶ時は二通り、好きな方を使う
Activityと同じインスタンスを使いたければby activityViewModels()
ViewModel
SampleViewModel.ktclass SampleViewModel: ViewModel() { // 中身 }ViewModelは変わらず、ViewModel()を継承するだけ
コード全文
とにかく簡単にViewModelまとめリスペクトで、ActivityとViewModelそれぞれの変数からtextViewに表示してボタンを押したら値を+1するサンプル
ViewModelって何?の説明もリスペクト元の記事に短くまとまってるのでどうぞ
Githubactivity_main.xml<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/textActivity" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" app:layout_constraintBottom_toTopOf="@+id/textViewModel" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/textViewModel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textViewModel" /> </androidx.constraintlayout.widget.ConstraintLayout>MainActivity.ktimport androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.activity.viewModels // by viewModels()用 import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val viewModel: SampleViewModel by viewModels() // ViewModelのインスタンスを作成 var activityVariable: Int = 0 // Activityに保存する変数 // 画面作成時に表示 textActivity.text = activityVariable.toString() // Activity textViewModel.text = viewModel.viewModelVariable.toString() // ViewModel // ボタンを押したら+1する button.setOnClickListener { // 変数の値を+1 activityVariable++ viewModel.viewModelVariable++ // 表示 textActivity.text = activityVariable.toString() textViewModel.text = viewModel.viewModelVariable.toString() } } }SampleViewModel.ktimport androidx.lifecycle.ViewModel class SampleViewModel: ViewModel() { var viewModelVariable: Int = 0 // ViewModelに保存する変数 }参考にした記事
Android Jetpack(AndroidXライブラリ)の最近の更新
[Android]lifecycleライブラリ2.2.0からViewModelProviders.ofが非推奨になっちゃった件
- 投稿日:2020-10-18T20:19:20+09:00
[Android/Kotlin]AndroidStudio4.1でViewModelを使う
なんか前のやり方でできなくなってたので
環境
- Android Studio 4.1
- Kotlin
- Mac
結論
ライブラリ
build.gradle(app)plugins { id 'kotlin-android-extensions' } dependencies { // ViewModel implementation "androidx.activity:activity-ktx:1.1.0" // Activityから使う時 implementation "androidx.fragment:fragment-ktx:1.2.5" // Fragmentから使う時 }バージョンは
Activity - AndroidDeveloper
Fragment - AndroidDeveloper
から安定版を使用
いやなんでこれデフォルトで入ってないんだよ
extensions
はsynthetic
でfindViewByIdの代わりにする時のやつ
Kotlin Android Extensionsを試してみた - Qiita
今まではデフォルトで入ってたのに4.1で消えたActivity
MainActivity.ktval viewModel: SampleViewModel by viewModels()Activityから呼ぶ時は
by viewModels()
Fragment
SampleFragment.ktval viewModel: SampleViewModel by activityViewModels() // Activityと同じViewModel使う時はこっち val viewModel: SampleViewModel by viewModels() // こっちだと新しいインスタンスを作成っぽいFragmentから呼ぶ時は二通り、好きな方を使う
Activityと同じインスタンスを使いたければby activityViewModels()
ViewModel
SampleViewModel.ktclass SampleViewModel: ViewModel() { // 中身 }ViewModelは変わらず、ViewModel()を継承するだけ
コード全文
とにかく簡単にViewModelまとめリスペクトで、ActivityとViewModelそれぞれの変数からtextViewに表示してボタンを押したら値を+1するサンプル
ViewModelって何?の説明もリスペクト元の記事に短くまとまってるのでどうぞ
Githubactivity_main.xml<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/textActivity" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" app:layout_constraintBottom_toTopOf="@+id/textViewModel" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/textViewModel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/textViewModel" /> </androidx.constraintlayout.widget.ConstraintLayout>MainActivity.ktimport androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.activity.viewModels // by viewModels()用 import kotlinx.android.synthetic.main.activity_main.* class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val viewModel: SampleViewModel by viewModels() // ViewModelのインスタンスを作成 var activityVariable: Int = 0 // Activityに保存する変数 // 画面作成時に表示 textActivity.text = activityVariable.toString() // Activityの変数を表示 textViewModel.text = viewModel.viewModelVariable.toString() // ViewModelの変数を表示 // ボタンを押したら+1する button.setOnClickListener { // 変数の値を+1 activityVariable++ viewModel.viewModelVariable++ // 表示 textActivity.text = activityVariable.toString() textViewModel.text = viewModel.viewModelVariable.toString() } } }SampleViewModel.ktimport androidx.lifecycle.ViewModel class SampleViewModel: ViewModel() { var viewModelVariable: Int = 0 // ViewModelに保存する変数 }参考にした記事
Android Jetpack(AndroidXライブラリ)の最近の更新
[Android]lifecycleライブラリ2.2.0からViewModelProviders.ofが非推奨になっちゃった件
- 投稿日:2020-10-18T16:15:13+09:00
Android Studioでエディタの縦線を消す
- 投稿日:2020-10-18T14:22:51+09:00
#9 Kotlin Koans Introduction/Extension functions 解説
1 はじめに
Kotlin公式リファレンスのKotlin Koans/Extension functionsの解説記事です。
Kotlin Koansを通してKotlinを学習される人の参考になれば幸いです。
ただし、リファレンスを自力で読む力を養いたい方は、
すぐにこの記事に目を通さないで下さい!一度各自で挑戦してから、お目通し頂ければと思います
2 拡張関数
拡張関数とは、継承を用いずに特定のクラスに対して追加した関数のことです。
拡張関数はオリジナルのクラスに定義された関数と同様に参照することができます。
以下が拡張関数の定義と参照です。
拡張関数の定義 fun MutableList<Int>.swap(index1: Int, index2: Int) { val tmp = this[index1] this[index1] = this[index2] this[index2] = tmp } 拡張関数の参照 val list = mutableListOf(1, 2, 3) list.swap(0, 2)上記の
MutableList<Int>
をレシーバー型と呼び、このクラスに関数を追加します(今回の場合はswap()関数
を追加します。)。拡張関数内の
this
はレシーバーオブジェクトを意味しています。なので、thisはswap()関数を呼び出している変数list(MutableListオブジェクトが代入されている。)自身ということになります。
3 Introduction/Extension functionsの解説
Kotlin Koans Introduction/Extension functionsの解説です。
随時本サイトの内容を引用させていただきます。右側の本文を見てみましょう。
Read about extension functions. Then implement extension functions Int.r() and Pair.r() and make them convert Int and Pair to RationalNumber.
extension functionsについて読みなさい。拡張関数Int.r()とPair.r()を定義し、IntとPairをRationalNumberに変換するようにしなさい。
左側の実装するコードです。
fun Int.r(): RationalNumber = TODO() fun Pair<Int, Int>.r(): RationalNumber = TODO() data class RationalNumber(val numerator: Int, val denominator: Int)つまり、Int型のオブジェクトとPair型のオブジェクトがそれぞれ拡張関数r()を呼び出したときに、RationalNumber型に変換すれば良いです。
Int型のオブジェクトが拡張関数r()を呼び出すことを考えてみましょう。
Int型のオブジェクトは任意の整数を意味するので、これをRationalNumber型に変換するにはTODO()の部分を
RationalNumber(this,1)と定義すればよいです。
(※)2個目の引数が1なのは、RationalNumberクラスが有理数を生成するクラスを意図しており、引数名もdenominator(分母)とあるからだと推測しています。
Pair型のオブジェクトが拡張関数r()を呼び出すことを考えてみましょう。
Pairクラスはfirstとsecondというプロパティを持っています。
それぞれの型はPairの右横の<,>内に記されています。今回の場合、firstもsecondもInt型ということになります。なので、
RationalNumber(first,second)と定義すればよいです。
4 最後に
- 投稿日:2020-10-18T14:19:13+09:00
#8 Kotlin Koans Introduction/Smart casts 解説
1 はじめに
Kotlin公式リファレンスのKotlin Koans/Smart castsの解説記事です。
Kotlin Koansを通してKotlinを学習される人の参考になれば幸いです。
ただし、リファレンスを自力で読む力を養いたい方は、
すぐにこの記事に目を通さないで下さい!一度各自で挑戦してから、お目通し頂ければと思います
2-1 when構文
when構文は、if構文の仲間のようなもので、条件ごとに結果が分岐します。
以下がwhen構文の例です。when (x) { 1 -> print("x == 1") 2 -> print("x == 2") else -> { print("x is neither 1 nor 2") } }変数xが1のとき
x == 1
が表示され、2のときはx == 2
が表示され、それ以外の場合x is neither 1 nor 2
が表示されます。2-2 Smart cast
cast(キャスト)とは変数(インスタンス)の型を変換させる仕組みのことを言います。
Javaではキャストを実行するには、変数名の前に()でクラス名を囲む必要があります。
一方で、Kotlinではコードの文脈を理解し自動的にキャストしてくれます。
これをスマートキャストと言います。
3 Introduction/Smart castsの解説
Kotlin Koans Introduction/Smart castsの解説です。
随時本サイトの内容を引用させていただきます。右側の本文を見てみましょう。
Rewrite the following Java code using smart casts and when expression:
public int eval(Expr expr) { if (expr instanceof Num) { return ((Num) expr).getValue(); } if (expr instanceof Sum) { Sum sum = (Sum) expr; return eval(sum.getLeft()) + eval(sum.getRight()); } throw new IllegalArgumentException("Unknown expression"); }eval()関数について考えてみましょう。
Javaのコード内の
instanceof
は右側の型と左側の型が同じならtrueをとることを表現します。eval()関数が引数exprを受ってNum型かSum型かを判断し、型に応じて戻り値が変化します。
exprがNum型のときは、
(Num) expr
としてNum型に変換してgetValue()関数を呼び出します(これがキャストを用いた型の変換です。)。exprがSum型のときは、
Sum sum = (Sum) expr
としてSum型に変換して変数に代入し、getleft()関数とgetRight()関数を呼び出します。このJavaのコードを左側のKotlinのコードに書き換えましょう。
fun eval(expr: Expr): Int = when (expr) { is Num -> TODO() is Sum -> TODO() else -> throw IllegalArgumentException("Unknown expression") } interface Expr class Num(val value: Int) : Expr class Sum(val left: Expr, val right: Expr) : ExprNumクラスではコンストラクタ内でプロパティvalueが、Sumクラス内ではleftとrightが定義されています。
また、条件に応じた結果の分岐をwhen式を用いて表現しています。
exprがNum型である場合、JavaのコードではgetValue()関数を用いて値を取得していましたが、
kotlinのコードではプロパティの値をそのまま利用すればよいので、1つ目のTODO()は
is Num -> expr.valueとすればよいです。
このとき、exprは条件式でNum型であることが明らかなのでキャストが省略されています(これがスマートキャストによる型の変換です。)。
exprがSum型である場合、変数leftとrightをeval()関数に引数として渡せばよいので、
is sum -> eval(expr.left) + eval(expr.right)とすればよいです。
4 最後に
- 投稿日:2020-10-18T12:40:35+09:00
FlutterアプリをGitHub Actionsを使ってwebアプリとandroid apkを同時にbuildし公開するまで
はじめに
私は普段休みの日の朝を中心に、普段家庭内の生活に使ったり、子供と遊ぶための便利なアプリなどを作ってます。
個人開発なので、あまり手間をかけずにサクッと色んなものを作りたいと思っていたときに
Flutterに出会いました。
Flutterはほぼローコード開発ツールやノーコードツールと同じくらいと言っていいくらい
あまり手間がかからずそれなりのアプリをサクッと開発できます。
はじめは、手元でbuildし、いちいちサーバにdeployするといった作業をしていましたが、貴重な土日の朝の時間
少しでも手間を減らしたいと思っていました。
手元ではコードを書いてテストを走らせ、かんたんにエミュレータで確認すると言った作業だけに集中し、
buildやdeployはGitHub Actionsに任せようと思い色々調査をした結果をメモしています。flutter web を使ってwebアプリを gh-pagesに公開する
前提
この記事では前提として下記を想定しています。
- Flutterでアプリ開発をしている
- GitHub上にレポジトリが配置されている
- レポジトリ上にgh-pagesというbranchが作成されているgh-pagesの設定
まずはweb版をdeployするための下地としてgh-pagesを設定しましょう
https://github.com/[あなたのユーザID]/[対象のレポジトリ]/settings
に行き gh-pagesの設定を下記のようにします
GitHub Actionsの設定
/path/to/[flutterアプリのルート]/.github/workflows/flutter.yaml
を作成しましょうname: Flutter CI 'on': push: branches: - main pull_request: branches: - main jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: Checkout ?️ uses: actions/checkout@v2 - name: Flutter setting uses: subosito/flutter-action@v1 with: channel: beta - name: "Install and Build ?" run: | flutter config --enable-web flutter pub get flutter test flutter build web - name: "Deploy ?" uses: JamesIves/github-pages-deploy-action@releases/v3 with: GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' BRANCH: gh-pages FOLDER: build/webこれをmainにpushしましょう
web appがdeployされていることを確認しましょう
pushすると自動的にGitHub Actions Flutter CIが走ると思います
こちらが終わったら(自分のレポジトリの場合は3分位かかります^^;)
https://[あなたのgithubカウント].github.io/[レポジトリ名]/
に行ってあなたのFlutterアプリが動くかどうか確認してくださいapkをGitHub Actionsを使ってsignする
なぜsignが必要か
自分の場合これを調査したきっかけは、firebase_authを使った場合、
ではkeystoreのSHA-1 fingerprintをfirebase consoleに設定しないと
google_sign_inがAndroid app版では動かないとう症状に悩まされたことと、
基本的にはbuildはGitHub Actionsを使ってやりたいなと思っていたたため、これを同時に出来る方法が無いか調査していました。
またセキュリティ上の理由から、keystoreファイルや生のパスワードをgithub repositryにpushすることは流石に嫌ですよね。
もしこれをやってしまうと悪意を持った人がリポジトリにアクセスし、
Google Play にアプリをあなたになりすまして公開ができるようになってしまいます。
以下の章ではSigning Flutter Android apps for release in GitHub Actionsを参考にさせていただき
GitHub Actionsを使ってflutter build apkを使って安全にAndroidをリリース署名を行う方法を解説します。keystoreを作成する
Signing the app にある方法を使って
keystoreを作成してください
例えばWindowsであれば$ keytool -genkey -v -keystore c:\Users\USER_NAME\key.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias [ALIAS]このようにして作成します。
また作成時に利用したKEY_STORE_PASSWORD, KEY_PASSWORD, ALIASは忘れないで覚えるかメモしてくださいSIGNING_KEYを取得する
opensslコマンドを使えば SIGNING_KEYを取得できます
$ openssl.exe base64 -A -in c:\Users\USER_NAME\key.jks /xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.....xxxxxxxxxxxxxx=出力されたながーい文字列をコピペしてメモしておきましょう
(option) SHA-1 fingerprintをfirebase consoleのAndroidアプリに設定しましょう
自分がもともとハマっていたのはここで、先程つくったkeystoreを下記のコマンドを使ってSHA-1 fingerprintを得て
それをメモしておきましょう$ keytool.exe -list -v --alias [ALIAS] -keystore c:\Users\USER_NAME\key.jks Enter keystore password: [KEY_STORE_PASSWORD] Alias name: [ALIAS] Creation date: YYYY/MM/DD Entry type: PrivateKeyEntry Certificate chain length: 1 Certificate[1]: Owner: C=US, O=Android, CN=Android Debug Issuer: C=US, O=Android, CN=Android Debug Serial number: 1 Valid from: Fri Sep 25 20:27:37 JST 2020 until: Sun Sep 18 20:27:37 JST 2050 Certificate fingerprints: MD5: XX:XX:XX.. SHA1: XX:XX:XX.. SHA256: XX:XX:XX.. Signature algorithm name: SHA1withRSA Subject Public Key Algorithm: 2048-bit RSA key Version: 1上記に表示されるSHA1 XX:XX:XXとSHA256XX:XX:XXをコピーして
Firebase consoleに行き設定しましょう
手元でbuildする準備をする
key.propertiesを作成
/path/to/[flutterアプリのルート]/android/key.properties
を作成しますstorePassword=[先程メモったKEY_STORE_PASSWORD] keyPassword=[先程メモったKEY_PASSWORD] keyAlias=[先程メモったALIAS] storeFile=[c:\Users\USER_NAME\key.jks など]key.propertiesを.gitignoreに忘れずに入れておきましょう
key.propertiesを公開してしまったら元も子もないので
ちゃんと.gitignoreに入れておきましょう$ echo android/key.properties >> .gitignorebuild.gradleの編集
/path/to/[flutterアプリのルート]/android/app/build.gradle
を編集します
編集する箇所は2つ
まずは key.propertiesがあったら
先程設定した key.propertiesからKEY_STORE_PASSWORD, KEY_PASSWORD, ALIASとKEY_PATHを読み込むように編集します
GitHub Actions上では環境変数から読み込むのでelseの中身はGitHub Actions用ですdef keystoreProperties = new Properties() def keystorePropertiesFile = rootProject.file('key.properties') if (keystorePropertiesFile.exists()) { keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) } else { keystoreProperties.setProperty('storePassword', System.getenv('KEY_STORE_PASSWORD')); keystoreProperties.setProperty('keyPassword', System.getenv('KEY_PASSWORD')); keystoreProperties.setProperty('keyAlias', System.getenv('ALIAS')); keystoreProperties.setProperty('storeFile', System.getenv('KEY_PATH')); }次にsignできるようにbuildTypesの上にsiningConfigsを追加し、
更にbuildTypesもsigningConfigs.releaseに変えましょうsigningConfigs { release { keyAlias keystoreProperties['keyAlias'] keyPassword keystoreProperties['keyPassword'] storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null storePassword keystoreProperties['storePassword'] } } buildTypes { release { signingConfig signingConfigs.release } } /* buildTypes { release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig signingConfigs.debug } }*/手元でbuildしてみましょう
$ flutter build apk You are building a fat APK that includes binaries for android-arm, android-arm64, android-x64. If you are deploying the app to the Play Store, it's recommended to use app bundles or split the APK to reduce the APK size. To generate an app bundle, run: flutter build appbundle --target-platform android-arm,android-arm64,android-x64 Learn more: https://developer.android.com/guide/app-bundle To split the APKs per ABI, run: flutter build apk --target-platform android-arm,android-arm64,android-x64 --split-per-abi Learn more: https://developer.android.com/studio/build/configure-apk-splits#configure-abi-split Running Gradle task 'assembleRelease'... 19.0s √ Built build\app\outputs\flutter-apk\app-release.apk (7.6MB).このようにbuildできたら成功です
GitHubのレポジトリのsecretsにこれらを設定していきます
https://github.com/[あなたのユーザID]/[対象のレポジトリ]/settings/secrets
に行って
右上のNew secretを押下してメモしたKEY_STORE_PASSWORD, KEY_PASSWORD, ALIASとSIGNING_KEY
を設定しましょう.github/workflows/flutter.yamlを編集
さあ 後少しで終わりです
/path/to/[flutterアプリのルート]/.github/workflows/flutter.yaml
を下記のように書き換えましょうname: Flutter CI 'on': push: branches: - main pull_request: branches: - main jobs: build-and-deploy: runs-on: ubuntu-latest steps: - name: Checkout ?️ uses: actions/checkout@v2 - name: Flutter setting uses: subosito/flutter-action@v1 with: channel: beta - name: "Install and Build ?" run: | echo $SIGNING_KEY | base64 -d > android/app/key.jks flutter config --enable-web flutter pub get flutter test flutter build apk flutter build web mkdir build/web/apks cp build/app/outputs/flutter-apk/*.apk build/web/apks/ env: SIGNING_KEY: ${{ secrets.SIGNING_KEY }} KEY_STORE_PASSWORD: ${{ secrets.KEY_STORE_PASSWORD }} KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }} ALIAS: ${{ secrets.ALIAS }} KEY_PATH: key.jks - name: "Deploy apk ?" uses: actions/upload-artifact@v1 with: name: release-apk path: build/app/outputs/flutter-apk/app-release.apk - name: "Deploy to gh-pages ?" uses: JamesIves/github-pages-deploy-action@releases/v3 with: GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}' BRANCH: gh-pages FOLDER: build/webこれをまたmainにpushしましょう
成功すると
https://[あなたのgithubカウント].github.io/[レポジトリ名]/apks/app-release.apk にアプリが配置されますデモアプリはこちら
おわりに
ちょっと長かったですが、なんとかこれで
web appとAndroid apk(野良アプリ)を gh-pages上に同時公開することができました。
すごく極端な話、Android Studioがなくても GitHub上でかんたんなbug fixとか編集してしまえば
自動的にGitHub Actionsが走り、ビルドされ、Deployまでできます
- 投稿日:2020-10-18T10:11:59+09:00
Chromebookで開発するメモ
Chromebookを開発マシンとして使おうと数日設定したときのメモ。
結論 (ChromeOS 86の時点)
結論としてはLinux開発マシンとして十分使える。
特にWebアプリ開発や外部サーバーにSSHでつなげて作業する場合は便利。しかし、課題はLinux側の日本語入力。Mozcを入れてLinux側でも日本語の入力が可能だが非常に不安定。日本語入力が必要ならChromeOS側のAndroidアプリやLinux側でエディタサーバーを起動してChrome側へアクセスしてやると良い。
どのLinuxを使うか
たくさん選択肢がある。多いことは良いことだがどれにしてよいか悩む。
- ChromeOS標準のLinuxコンテナのcrostini --- Debian
- AndroidアプリのTermux --- Androidとの親和性高し
- AndroidアプリのUserLand --- 好きなOSを選べる
- ChromeOSを開発者モードにして使うcrouton --- 開発者モードが悪
手軽に始められてDebianが使えるのがcrostini。ChromeOSの設定で、Linux(ベータ)をオンにするだけ。ChromeOS/Androidとの親和性が高いのはTermux。Termuxは別途Termux-APIをインストールすると、クリップボードの操作やいろいろコマンドラインからAndroidの機能が操作できる。ChromeOSのクリップボードを自由に読み書きできるのでちょっとしたツールを実行するのに便利。
Termuxもcrostiniも別個のアプリとしてインストールされるので、ディスクスペースに余裕があるなら、両方入れて良いところどりすると良いかも。個人的には、crostiniとTermuxの二刀流を選択。
ただし、ChromeOSでは、Termuxアプリのコマンドライン上では日本語が一切入力できないというデメリットがある。(sshサーバーを実行してSecure Shellと組み合わせることで回避可能。)
crostiniのメモ
Debian、基本的に便利。ただし、GUIアプリの日本語サポートが微妙。PythonのIDLEなど、Tkベースのコンポーネントでは問題なくMozcで日本語入力できた。しかし、geditなどインストールしてみたが、全く日本語入力ができなかった。原因は不明。
crostiniの端末画面では、マウスで文字を選択するとクリップボードにコピーされる。[ctrl]+[shift]+[v]で文字を端末に貼り付けできる。
Termuxの設定メモ
Termuxの設定については以下に詳しく手順を書いた。
Termuxの端末画面では、マウスで文字を選択するとクリップボードにコピーされる。[ctrl]+[alt]+[v]で文字を端末に貼り付けできる。
ChromeOSとLinux側の連携
Termuxを使えば、クリップボード(termux-clipboard-set, termux-clipboard-get)で読み書きできる。
ネットワークは安全性を考慮され隔離されているので、linux側でサーバーを起動しても、ChromeOSのブラウザ(chrome)側からアクセスはできないようになっている。
# crostiniの場合 sudo ifconfig | grep inet # Termuxの場合 ifconfig | grep inet筆者のIdeaPad Duetでは、
100.115.92.xxx
が表示された。Chromeブラウザでこのアドレスにアクセスすると正しくアクセスできた。例えば、php7のサーバー機能を使う場合、以下のようにしてポートを指定してサーバーを起動。
php -S 0.0.0.0:8888crostiniではChromeOSのLinux設定でポート転送を指定する必要がある。Termuxでは指定不要。
ブラウザで、
http://100.115.92.xxx
にアクセスすると、正しくPHPのプログラムを実行できた。拙作のKonaWiki3 で確認した。クラウドに保存するときは、Googleドキュメントでも良いが、WIKIを動かせばローカルにいろいろメモできて便利。
VSCodeを快適に使う方法
crostiniではVisual Studio Codeをインストールして使うことも可能。しかし、まだ不安定で使いづらいとのこと。そこで、サーバー版のVSCodeをインストールして、Chrome側からアクセスすると快適に使える。
残念ながらTermuxではうまくいかなかったが、crostiniで下記コマンドでサーバー版のVSCodeがインストール可能。
curl -fsSL https://code-server.dev/install.sh | shそして、ChromeOSのLinuxの設定でポート転送を有効にする。
そして、以下のようにコードサーバーを起動する。
code-server --bind-addr 0.0.0.0:8080そして、上記で紹介したようにブラウザ側でLinux側のIPアドレス:ポート番号にアクセス。
VSCode、ブラウザの中で動いてとても便利。
Permission Deniedの罠
Termuxではtermux-setup-storageを実行すると、ChromeOS側のダウンロード(download)やギャラリー(dcim)にアクセスできるようになる。
しかし、ChromeOSではシステム領域とユーザーが使うストレージ領域が明確に分離されており、ストレージ領域ではスクリプトの実行も拒否される設計となっている。
Go言語など、ChromeOS側でも共通のファイルを編集しようとdownload内にGOPATHを設定していたのだが、download内で作成したバイナリはTermux内にコピーしても実行できなかった。ストレージ領域ではシェルスクリプトも実行できない。
ただし、ストレージ領域でスクリプトファイルを作成し、システム領域にあるコマンドを実行することはできる。例えば、download内でa.pyというファイルを作成し、
python a.py
とすればa.pyを実行することはできる。ただし、a.pyのいち行目に#!のシェバンを書いて実行権限を与えても実行はできない。参考
- 投稿日:2020-10-18T06:07:27+09:00
【Kotlin】WebViewからFirebase 向けGoogle アナリティクスにイベントを送りDebugViewで検証する
AndroidアプリのWebView内から発生させたイベントをネイティブ側のFirebase向けGoogleアナリティクス(あるいはApp+Webプロパティ、GA4)にて計測するための方法。
Googleの公式ヘルプガイドにandroid向けにはJavaのサンプルコードが記載されていたもののKoltlin版が無かったので作成しました。
1. Android プロジェクトに Firebase を追加
以下リンクの「オプション1」の手順通りにやればらくらく。
https://firebase.google.com/docs/android/setup
最後の手順の「アプリを実行してインストールを確認」が何度リトライしても上手く行きませんが大抵スキップして翌日チェックしたら成功してます。
2. JavaScript ハンドラを実装
以下Googleヘルプガイド記載のJavaScriptコードをWebViewで開かれる可能性があるすべてのWebページに実装することで、 logEvent() とsetUserProperty()をJavaScriptから使えるようになります。
また、アプリ側でネイティブインターフェースが実装されていない場合はconsole.log("No native APIs found.");
が実行されるため、User Agentによるアプリ判定が無い場合でもエラーにはなりません。JavaScript コード
Googleヘルプガイドのスクリプトをそのまま利用
→ https://firebase.google.com/docs/analytics/webview?platform=android#implement-javascript-handlerlogEvent() 実装例
FirebaseアナリティクスのJavaScriptハンドラを実装、更にアプリ側に後述のネイティブインターフェースを実装の上、
あるボタンのタップ数をFirebaseで計測したい場合、以下のようなコードで実行可能。<button type="button" onclick="logEvent('tapButton',{'param_foo':'key_bar','param_baz':'key_qux'})">logEvent Test Button</button>3. ネイティブ インターフェースを実装する
ヘルプガイドにKotlin版が無かったため、Javaのコードを変換して作成しています。
AnalyticsWebInterfaceクラスを実装
Kotlin コード
/** Instantiate the interface and set the context */ class AnalyticsWebInterface (private val mContext: Context) { companion object{ const val TAG = "AnalyticsWebInterface" } private val firebaseAnalytics: FirebaseAnalytics = FirebaseAnalytics.getInstance(mContext) @JavascriptInterface open fun logEvent(name: String, jsonParams: String) { LOGD("logEvent:$name") firebaseAnalytics.logEvent(name, bundleFromJson(jsonParams)) } @JavascriptInterface fun setUserProperty(name: String, value: String?) { LOGD("setUserProperty:$name") firebaseAnalytics.setUserProperty(name, value) } private fun LOGD(message: String) { // Only log on debug builds, for privacy if (BuildConfig.DEBUG) { Log.d(TAG, message) } } private fun bundleFromJson(json: String): Bundle? { if (TextUtils.isEmpty(json)) { return Bundle() } val result = Bundle() try { val jsonObject = JSONObject(json) val keys = jsonObject.keys() while (keys.hasNext()) { val key = keys.next() val value = jsonObject[key] when(value){ is String -> result.putString(key, value) is Int -> result.putInt(key, value) is Double -> result.putDouble(key, value) else -> Log.w(TAG, "Value for key $key not one of [String, Integer, Double]") } } } catch (e: JSONException) { Log.w(TAG, "Failed to parse JSON, returning empty Bundle.", e) return Bundle() } return result } }必要なimport文は赤字部分にマウスオーバーして Alt+Enter で適宜追加
ネイティブインターフェースをWebViewに紐づけ
先ほど作成したネイティブインターフェースをWebViewリソースに紐づけ
Kotlin コード
コード内の「
webview
」の文字を目的のリソースIDに変更の上でご利用ください。if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { webview.addJavascriptInterface(AnalyticsWebInterface(this), AnalyticsWebInterface.TAG); } else { Log.w(AnalyticsWebInterface.TAG, "Not adding JavaScriptInterface, API Version: " + Build.VERSION.SDK_INT); }DebugViewによるイベント計測状況の確認
手順
- PC にadbコマンドを導入する → ADBコマンド導入の方法 - Qiita
- 検証用の Android 端末にデバッグ対象のアプリをインストール
- Android 端末のデバッグモードを有効化
- Android 端末をPCにUSBデバッグモードで接続
- PC でコマンドプロンプトを起動し、コマンド
adb devices
を実行。以下のように端末名とdevice
の文字が表示されれば次の手順へ。unauthorize
と出た場合は端末側でUSBデバッグの認証が通っていないので端末を操作して許可させる。- コマンド
adb shell setprop debug.firebase.analytics.app ●●●
を実行(黒丸部分はデバッグ対象のパッケージ名に置換えた上で実行)- Android 端末を PC から外してアプリを操作する
- PC のブラウザで Firebase Console を開き、目的の Firebase プロジェクト画面の左側ナビゲーション内の 分析 > DebugView を選択して開く
- 「デバッグに使用するデバイス」に先ほど自分が設定した Android 端末があることを確認して選択
- Android 端末で操作した内容が反映されるか確認する(15秒程度の遅延あり)
イベントが計測されている様子
以下のようにイベントが計測されれば実装および検証が成功。お疲れ様でした。
DebugViewで目的のイベントが表示されることを確認
表示されたイベントをクリックしてパラメータも正常に計測できていることを確認
以上。「もっとこうした方がいいよ」などありましたらアドバイス頂けますと幸いです。
参考
WebView でアナリティクスを使用する | Firebase
- Android(Java) : https://developers.google.com/analytics/devguides/collection/firebase/android/webview
- iOS(Swift, Objective-C) : https://firebase.google.com/docs/analytics/webview?platform=iOS
- 投稿日:2020-10-18T01:11:12+09:00
futterのインストールからサンプルプログラムを動かすまで
目的
- モバイルアプリの開発を1つのコードで実装することを目的とし、そのためのツールとしてFlutterを使用することとした
- Flutterのインストールを行い、インストール後の確認としてサンプルプログラムを作成してiOSとAndroidの各シミューレタで動かす
環境
- OS:macOS Mojave(10.14.6)
- iOS向けのコマンドラインツール等はインストール済み(iOSの開発をした端末を使用)
- 動作確認はVSCodeを使用(インストール済み)
flutterのインストール
gitからclone
# 開発用フォルダへ移動 cd /Users/xxxx/develop # gitから取得 git clone https://github.com/flutter/flutter.gitflutterへパスを通す
# bash_profileへ設定追加 export PATH="\$PATH:`pwd`/flutter/bin" >> ~/.bash_profile # 設定を即時反映させる source ~/.bash_profile開発者ツールをダウンロード(任意)
flutter precache環境構築状況の確認
flutter doctorflutter doctorの結果に応じて必要なツールをインストール
- 表示されたメッセージの内容に合わせて設定していけばOK
- 解決しない場合は個別に調査
私の環境で足りなかった設定の追加
- android studio のセットアップ
- android studio のページからインストーラをダウンロードして実行
cocoapods のインストール
- インストールコマンドを実行
sudo gem install cocoapodsERROR: While executing gem ... (NoMethodError) undefined method `invoke_with_build_args' for nil:NilClass
- エラーになったので、エラーメッセージを調べてた結果rubyを入れ直し
rbenv uninstall 2.6.1 rbenv install 2.6.1
- インストールコマンドを再実行して問題なく終了することを確認
flutter doctor
を実行 → android studio のtool chainのエラーが消えない[!] Android toolchain - develop for Android devices: is partially installed; more components are available. (Android SDK version 30.0.2)
- android studio からコマンドラインツールを追加インストール
- 次の
flutter doctor
の実行でandroidのライセンス確認?のエラーが出ていたので、メッセージに合わせてコマンドを実行flutter doctor --android-licenses
デバイスが見つからない
[!] Connected device ! No devices available
- android studio から仮想端末を起動
- android studio を起動
- 「Configure → AVD manager」
- 設定済みの端末があったので、そのまま再生ボタンのアイコンをクリックして実行
flutter doctor
ですべての問題が解決していることを確認Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel master, 1.23.0-19.0.pre.83, on Mac OS X 10.14.6 18G6020 darwin-x64, locale ja-JP) [✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2) [✓] Xcode - develop for iOS and macOS (Xcode 11.2.1) [✓] Android Studio (version 4.1) [✓] VS Code (version 1.48.0) [✓] Connected device (1 available)サンプルプロジェクトの作成、シミュレータでの動作確認
- 必要に応じてVSCodeの拡張機能を入れる、これらは入れておきましょう
- Flutter
- Dart
プロジェクトを作成
flutter create {project name}VSCodeで
lib/main.dart
を開くiOSでの動作確認
- F5もしくはrun→start debuggingで実行
- シミュレータの選択肢が表示されるのでOSを選択
- iOSシミュレータ起動後、しばらく(数分程度)待つと起動します
- iOSとして動作することを確認
androidでの動作確認
- コマンドパレットから「Flutter: Select Device」を選択、androidのエミュレータを選択
- 選択したエミュレータ起動後、androidとして動作することを確認
参考
- 公式サイトのflutterのインストール手順(macOS)(https://flutter.dev/docs/get-started/install/macos)
- github(https://github.com/flutter/flutter)
- FlutterでHello worldを動かすまでの環境構築手順(iOS, Android)(https://qiita.com/unsoluble_sugar/items/deaa129fb289922c8634)