2023年8月11日金曜日

Android 裏にいるActivityもすべて終了するfinishAffinityメソッドを紹介

どうも。どっことです。今回はスタックに積まれているActivityをすべて終了させるメソッドを紹介します。

裏のActivityもすべて終了するfinishAffinity

Activity AからActivity BstartActivity()でスタックを積んだとき、Activity Aが明示的に自身をfinish()させなければ、Activity Bの裏にActivity Aはスタックとして存在します。

そしてこのときActivity Bfinish()を呼び、自身を終了させても、スタックに残っているActivity Aが表示され、アプリを終了することができません。

今回は、Activity BからスタックにいるすべてのActivityを終了させるメソッドを紹介したいと思います。

Activity#finishAffinity()

すべてのActivityを終了させるにはfinishAffinity() というメソッドを使います。単純なケースであれば、このメソッドを呼び出すことで裏にいるActivityをすべて終了することができます。

単純なケースと書いたのは、Activityには親和性(affinity)という概念があるためです。これが厳格に管理されているActivityですと、今回のケースは期待した動作をしない可能性があります。

ただし基本的に同一のAndroidアプリという領域のActivityであるためaffinityが厳格に管理されているケースは非常に稀で、まずはやりたいことが実現できるかをこのメソッドを呼んで試してみるのが解決への一番の近道になるかと思います。

参考

2023年8月10日木曜日

Android 通知チャンネルの設定画面を表示する実装方法について解説

どうも。どっことです。今回は、通知チャンネルの設定画面を表示するための実装方法について解説します。

通知チャンネルの設定画面を表示する

Android O から追加された通知チャンネルですが、アプリからは追加・削除以外は通知チャンネルの設定値を修正・変更することができません。その代わりに端末の設定画面に遷移させるための導線を具備することで、ユーザに通知チャンネルまで簡単にアクセスできるようにしてあげましょう。

実装方法

アプリが追加したチャンネルの設定画面に遷移する方法は以下となります。

val intent = Intent(Settings.ACTION_CHANNEL_NOTIFICATION_SETTINGS)
intent.putExtra(Settings.EXTRA_CHANNEL_ID, channelId)
startActivity(intent)

通常のstartActivityと同様に、必要なActionやパラメーターを設定することで通知チャンネルの設定画面に遷移することができます。

まとめ

今回は、通知チャンネルの設定画面を表示するための実装方法について解説しました。このブログでは、アプリで通知を表示するために必要な実装として他にも投稿しているので、もしよろしければそちらも参考にしていただけると幸いです。

参考

2023年8月9日水曜日

Android AndroidPでhttp通信に失敗する問題

targetSDKVersionをAndroidP以上に設定すると、セキュリティの問題でアプリでのhttp通信が失敗する。AndroidManifest.xml のapplicationタグに以下の属性を追加する。

<application
    ....
    android:usesCleartextTraffic="true">
</application>

開発段階ではローカル環境にサーバ立ててそこにスタブデータを置いて動作確認することが多いが、本番環境では回避すべき設定なので、debug/releaseでAndroidManifest.xmlを分けるなど、きちんと切り分けること。

2023年8月8日火曜日

HTML/CSS マウスオーバーしたときの表示をカスタマイズする

マウスオーバーしたときに表示を切り替えたいときは、CSSに疑似クラスを指定する。

a:hover {
  マウスオーバー中に表示したい内容
}

この指定を複数の要素に指定することで、例えばテキストやイメージの表示切替などもできる。

a:hover {
  マウスオーバー中に表示したい内容
}
.a-covered:hover {
  マウスオーバー中に表示したい内容
}

マウスオーバー以外にも疑似クラスの指定により表示を変えられるものは以下。

  • link:御訪問リンク
  • visited:訪問済みリンク
  • hover : マウスオーバー
  • active : リンククリック中

参考

2023年8月7日月曜日

Android Parcelableを実装する方法を解説

どうも。どっことです。今回はParcelableの実装方法について解説します。

Parcelableを実装する

ActivityFragmentなどの画面間におけるデータ受け渡しはアプリ全体の動きを考える上でなくてはならない処理ですが、自前で用意したデータクラスはそのままだとBundleに乗せることができません。

それではどうするかというと、そのデータクラスにParcelableSerializableを実装します。取り出すときにキャストが必要にはなるが、これでBundleに乗せることができるようになり画面間のデータ受け渡しができるようになります。

今回はその呪文の如きParcelableのインターフェース群の実装について、説明します。

実装

必要な実装ですが、データクラスに以下のクラス・メソッドを実装します。

  • Parcelableを実装する
  • describeContentsを実装する
  • 引数がParcelのコンストラクタを実装する
  • writeToParcel()を実装する
  • CREATOR : Parcelable.Creatorを実装する

サンプルとしてItemクラスにParcelableを実装する場合は以下となります。

data class Item(
    var title: String,
    var description: String,
) : Parcelable {
    constructor(parcel: Parcel) : this(
        parcel.readString()!!,
        parcel.readString()!!
    )

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(title)
        parcel.writeString(description)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<Item> {
        override fun createFromParcel(parcel: Parcel): Item {
            return Item(parcel)
        }

        override fun newArray(size: Int): Array<Item?> {
            return arrayOfNulls(size)
        }
    }
}

あとは実装したデータクラスのオブジェクトをBundle#putParcelable()Bundleに詰め

val bundle = Bundle().apply {
    putSerializable("適当なキー", item)
}

Bundle#getParcelable()Bundleから取り出すことで

val item = bundle.getParcelable("適当なキー", Item::class.java)

画面間でデータクラスをやり取りすることができます。

ここまで書いててですが…

Percelableをいい感じに実装してくれるアノテーションがあると公式サイトが謳っていました。

Parcelable 実装生成ツール

アノテーション付けるだけで上記の面倒臭い実装をしなくてよくなるなんて、なんて素晴らしいんだ!!ありがとうGoogle様!!!

まとめ

今回はParcelableの実装方法について解説しました。ボイラープレートなParcelableの実装ですが、すでに自動生成してくれるツールがあるので、そちらを使う方が無難でしょう。今回の紹介した内容がほぼ無用なものになってしまいますが、必要なものが簡単に実装できることの方が重要だと思うので、まあ仕方ないでしょう笑

2023年8月6日日曜日

VBA 今日の日付を取得する

単純な日付だけであれば Dateをって取得する。

Dim date As Date: date = Date()

時刻まで必要であれば Now を使って取得する。

Dim now As Date: now = Now()

参考

2023年8月5日土曜日

VBA 変数宣言時に初期値を設定する

Dimによる変数宣言と、値の設定は一行で書ける。

Dim numberValue As Integer: numberValue = 30000
Dim text As String: text = "sample text"

2023年8月4日金曜日

SQL 複数のキーを主キーに設定するPRIMARY KEY節を紹介

どうも。どっことです。今回は複数のキーをPRIMARY KEYに設定するSQLを紹介します。

複数のキーをPRIMARY KEYに設定する

まず、前提として主キーが1つだけの場合です。

テーブルを生成する際、1つのキーだけを主キーに設定する場合は以下のようになります。

create table user(id int primary key,  name text);

しかし複数のキーを主キーに設定する場合、上記と同じような形にするとエラーが発生します。

複数のキーを設定する場合は、最後の方にPRIMARY KEY節を追加します。例えばid,subIdを主キーに設定する場合は以下となります。

create table user(id int, subId int, value text, ..., PRIMARY KEY(id, subId));

今回は簡単ですが以上です。

2023年8月3日木曜日

iOS TextFieldのキーボードを閉じる実装方法を解説

どうも。どっことです。今回はiOSでのキーボードを閉じる実装について解説します。

解説

iOSではアプリからキーボードを閉じるのは非常に簡単で、UITextField#endEditing(true)を呼び出すだけでOKです。

@IBOutlet weak var textField: UITextField!

func closeKeyboard() {
    // キーボードを閉じる
    textField.endEditing(true)
}

AndroidではInputManagerにアクセスしたり必要な情報を渡したりで面倒臭いのに、iOSはとにかくシンプルで羨ましいですね。

参考

2023年8月2日水曜日

Android Koin の使い方を調べたので解説

どうも。どっことです。今回はKoinの使い方について解説します。

Koinを使ったDIの実装

Koinってなんだよ、というあなたはこちらを見てくださると大変嬉しいです。ちなみに、Daggerというライブラリを使ってDIを実現するパターンもありましたが、実装難易度の観点から今回はKoinを使う方針としました。

それでは実装方法について解説します。

ライブラリの組み込み

まずはライブラリを組み込む必要があるのでapp/build.gradle.ktdependenciesに以下を記述します。

dependencies {
    // 他にもあるけど説明のためこれだけ。
    implementation("io.insert-koin:koin-android:3.4.2")
}

Applicationクラスの実装

続いて、Applicationクラスに必要な処理を追加していきます。Applicationってどうすんだっけ、というあなたはこちらを見てくださると大変嬉しいです。

moduleインスタンスを作ってstartKoin を実行します。moduleインスタンスには DIするインターフェースとそれの実装クラスを登録します。以下はSampleRepositoryImpl(dao: Dao)をDIするサンプルです。

    private val appModule = module {
        single<SampleRepository> { SampleRepositoryImpl(get()) }
    }
    protected open val dao = module {
        single {
            Room.databaseBuilder(
                applicationContext,
                Database::class.java,
                DATABASE_NAME
            ).build().dao()
        }
    }
    override fun onCreate() {
        super.onCreate()
        startKoin {
            androidContext(this@SampleApplication)
            modules(appModule, dao)
        }
    }

メモ:single で登録しておくと、シングルトンパターンでオブジェクトを管理してくれる。

各機能にアクセスする

あとはDIしたい箇所で、by inject()を使ってインジェクトすると、簡単にアクセスすることができます。

private val repository : SampleRepository by inject()

まとめ

今回はDIをシンプルにアプリに導入できるライブラリであるKoinの使い方を解説しました。昨今のプログラムのレガシー化やCI/CDに向けたモジュールの疎結合を意識する上でDIは必要不可欠な機能の一つです。DIを実現するものは今回のKoin以外にもさまざまなものがありますので、比較・検討した上で導入しましょう。

参考

2023年8月1日火曜日

CI/CD Bitriseでaabを生成する

前回の続きです。今回はリリースファイルを作るための各種設定を備忘録として記載したいと思います。

リリースファイル(aab)を生成するための設定

とはいえ、基本は以下の手順通りに設定すればOK。日本語ページはなかなか残念なことになっているので、英語ページをベースに作業したほうが結果的にストレスが少なくて済む。

Generating and deploying Android app bundles

ただし、少しだけハマりポイントがあるので注意が必要。ハマったポイントは以下。

gradlewのパス

唯一納得いかなかったハマりポイント。gradlewのパスがデフォルトのままだとビルドが通らない。

上記ページの手順で追加した「Gradle Runner」のステップ内にあるInput variables>gradlew file pathの項目に以下を設定する。

./gradlew

最小構成のプロジェクトなら、これだけ手直しすればビルドが通ってaabができるはず。

(追記)最新の設定だと、「Gradle Runner」を追加せずともaabファイルを生成できる模様...何のために試行錯誤したのか。。。

google-services.jsonの準備

google-services.jsonはFirebaseの機能を使うためにアプリに組み込む設定ファイル。秘匿情報が含まれるため、原則Gitには登録しない。しかし、Gitに登録しないとBitriseでビルドする時にこの設定ファイルが無いせいでビルドに失敗する。

この矛盾を解消するために、Bitriseに用意されているテキスト生成のステップを利用する。ざっくり書くと、以下。

  1. ファイルの中身をまるっとコピーする。
  2. コピーしたテキストをBitriseの定数として保存。
    • 定数の名前は適当でOK。例えば直感的にGOOGLE_SERVICES_JSONとか。
  3. テキストファイル生成のステップ(Generate Text File)を追加。
    • 定数として定義したテキストを使って、google-services.jsonファイルとして生成する。

「テキストファイルを生成する」ステップが用意されているのが、直感的で非常にわかりやすい。

センシティブな情報の設定

おそらくAndroidアプリ開発者ならほとんどの人がハマるポイント。build.gradleBuildConfigFieldを定義する際、ダブルクォーテーションを定義に含めないと、生成されるBuildConfigでいい感じに定義できない問題。これがBitriseでも(というより環境変数として定義する場合でも)同様に発生する。

何も考えないならbuild.gradle内で

buildConfigField "String", "AD_UNIT_ID", "\"広告ID\""

って形で定義すれば問題ない。

しかし、これを(センシティブな情報だから、外部に設定+それを参照するような形とするために)環境変数などに設定しそれを参照する形で

buildConfigField "String", "AD_UNIT_ID", System.getEnv(定義したキー)

って書くと残念ながらビルドに失敗する。設定値側(環境変数)にダブルクォーテーションを含めて定義するか、参照時に文字テンプレートを使うことで解消できる。

AndroidManifest.xmlも参照する必要が出てくるはずなので、文字テンプレートを使った方が無難。つまり以下。

buildConfigField "String", "AD_UNIT_ID", "${System.getEnv(定義したキー)}"

参考

移行予定

どうも。どっことです。 タイトルの通りですが、諸事情により GitHubPage に移行予定です。 https://mkt120.github.io/ この備忘録に記載の内容を転記しつつ、今後はこちらのページを更新していく予定です。