どうも。どっことです。
タイトルの通りですが、諸事情により GitHubPage に移行予定です。
この備忘録に記載の内容を転記しつつ、今後はこちらのページを更新していく予定です。
アウトプットしないとインプットできない私が Androidアプリ開発をメインとした実装方法の解説や備忘録を載せています。
どうも。どっことです。今回はgradle.properties
を使った設定値の管理方法について紹介します。
gradle.properties
を用意し定数値感覚で設定すれば、アプリのバージョンやtargetVersion
/minSDKVersion
などの値を一つのファイルで管理させることができます。
例えば以下のbuild.gradleにあるマジックナンバーを定数化しgradle.properties
に移して参照しましょう。
android {
namespace = "com.mkt120.sampleapplication"
compileSdk = 35
defaultConfig {
applicationId = "com.mkt120.sampleapplication"
minSdk = 26
targetSdk = 35
versionCode = 100
versionName = "1.0.0"
...
}
...
gradle.properties
に外出しする定数を記述します。
ANDROID_SDK_VERSION=35
ANDROID_MIN_SDK_VERSION=26
ANDROID_VERSION_NAME="1.0.0"
ANDROID_VERSION_CODE=100
上で配置した定数をbuild.gradle
で参照します。
android {
namespace = "com.mkt120.sampleapplication"
compileSdk = Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)
defaultConfig {
applicationId = "com.mkt120.sampleapplication"
minSdk = Integer.parseInt(project.ANDROID_MIN_SDK_VERSION)
targetSdk = Integer.parseInt(project.ANDROID_BUILD_SDK_VERSION)
versionCode = Integer.parseInt(project.ANDROID_VERSION_CODE)
versionName = project.ANDROID_VERSION_NAME
...
}
...
gradle.properties
に記述した定数はproject.XXXXX
の形式で参照することができます。
今回はgradle.properties
を使った設定値の管理方法について紹介しました。
どうも。どっことです。今回はKotlinCoroutineのテーマです。
Kotlin Coroutine
は非同期処理などのコールバック地獄を解消してくれたり、可読性の高いコードを実装できるようになる便利な機能ですが、「実際のところ、どう使うのが良いの?」と考えました。今回はそんな使うと便利だけど実際どう使うかイマイチ分からない人向けに、ベストプラクティスのページの内容を簡単に紹介したいと思います。
ViewModel
を実装してviewModelScope.launch
/View側ではlifecycleScope.launch
を使うのがよさそうです。
Google開発者サイトでコルーチンに関するベストプラクティスが紹介されています。コルーチンにフォーカスしたものだと、以下が挙げられています。
Dispatcher
を外から注入できるようにするViewModel
でコルーチンを作成するGlobalScope
は使わない
withContext
などで引数に設定するDispatcher(どのスレッドで処理するかの指定)は、ハードコードせず外から注入できる形にした方が良いです。テストコードを実装するときにテストが容易にできるようになります。
// OK:処理するDispatcherを外から指定する
class SampleRepository(private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO) {
suspend fun veryHeavyTask() = withContext(ioDispatcher) {
// very heavy task
}
}
class SampleRepository() {
// NG: Dispatcherをハードコード(直接指定)しない
suspend fun veryHeavyTask() = withContext(Dispatchers.IO) {
// very heavy task
}
}
ViewModel
を作成し、そこでコルーチンを作成→非同期処理を実装するのがよいとのことです。
そして画面側に非同期処理が必要なことを意識させることがないよう、suspend関数として公開しないよう実装するのが良いとのことです。
class SampleViewModel(): ViewModel() {
private val repository = SomeRepository()
// NG:UI側にsuspend関数(=非同期処理)として公開しない
suspend fun badSample() = repository.veryHeavyTask()
// OK:ViewModel内でコルーチンを生成し非同期処理を実行する
fun sample() {
viewModelScope.launch {
repository.veryHeavyTask()
}
}
}
テストコードの実装しにくさ、GlobalScope
をハードコードしていることによりDispatcher
もハードコードされやすくなる点などから、GlobalScope
を利用しないことを推奨しています。
class SampleRepository() {
fun veryHeavyTask() {
// NG:GlobalScopeをハードコードしない
GlobalScope.launch {
// very heavy task
}
}
}
GlobalScope
の利用が正当であるケースはかなり稀で、アプリが生存期間中にアクティブな状態を維持しなければいけない状態とのことです。また、現在のスコープより長い生存期間が必要な処理であれば、CoroutineScope
を外部から指定するような構成とすることを検討すべきとのことです。
コルーチンはキャンセル可能ですが、実際コルーチン内でキャンセルされたか確認したり、停止したりするまではコルーチンはキャンセルされないとのことです。ensureActive
を使用することでキャンセルの確認ができます。
class SampleViewModel(): ViewModel() {
private val repository = SampleRepository()
fun sample(value: Int) {
viewModelScope.launch {
val result = repository.validateValue(value)
// キャンセルできるタイミングでアクティブか確認
ensureActive()
if (result) {
repository.submit(value)
}
}
}
}
今回はKotlinCoroutineのベストプラクティスについて紹介しました。より詳しい内容は参考に記載したGoogleのベストプラクティスの記事からさまざまなページにアクセスできるので、ぜひ参考にしてください。
どうも。どっことです。今回はAndroid StudioにあるVCS Operations Popup
にカスタマイズ方法ついて解説します。
VCS Operations Popup
はAndroid
Studioでバージョン管理ツールの操作するときのポップアップです。control
+v
のショートカットで表示することができます。メニューから操作する手間が省けるので、多用している人もいるかと思いますが、今回はこのポップアップをカスタマイズする方法を解説します。
カスタマイズですが、実はAndroid Studioがカスタマイズするための設定項目を用意してくれています。なので謎の設定ファイルの書き換えや、ターミナルなどでの謎のコマンド操作は不要です。(地味に嬉しい)
Android StudioのSettings | Appearance & Behavior | Menus and Toolbars
にアクセスしてください。プロジェクトにGitを利用している場合は以下のようなポップアップが表示されると思います。
この画面で項目をカスタマイズすることができます。一覧の中にあるVCS Operations PopupからVCS.Operations.Popup.VcsAware
を選択している状態で+
ボタンをクリックしましょう。
ここから項目を追加することができます。たとえば、Update project
をリストに追加してみましょう。一覧からUpdate project
を探してOK
をクリックしてください。それで完了です。
それではVCS Operations Popup
を表示してみましょう。control
+v
を入力すると...
追加したUpdate project
がポップアップに追加されていることを確認できました!これでいちいちVCSメニューまでマウス移動させる手間が省けますね!
今回はAndroid StudioにあるVCS Operations Popupのカスタマイズ方法について解説しました。Android Studioや、そのベースとなっているIntellij IDEAでは、このポップアップに限らず、さまざまな項目をカスタマイズする設定画面を提供しているので、作業効率がより良くなるようどんどんカスタマイズしていきたいですね。
どうも。どっことです。今回はGitHub Packagesを使って自作ライブラリを簡単に公開する方法を解説したいと思います。
プライベートで資格試験などに向けた問題集アプリを不定期に作るのですが、毎回データクラスや画面を一から作っているので「そろそろ標準化しよう。。。」と思いました。ただ標準化したはいいものの、いい感じに使い回すためにはどうしたらいいか、というところで調べた結果、GitHub Packagesのサービスに行き着いたので今回はこちらを紹介し、実際に公開する方法を解説したいと思います。
GitHubPackagesはGitHubのサービスの一つで、まさに今回のようなライブラリや、パッケージなどを共有するためのものだそうです。mavenやnpm、NuGetなどの形式で公開することができます。しかも嬉しいことに公開範囲も指定できるようで、一般公開からプライベートやTeam限定など、制御することができるとのこと。とても便利。
今回はJava向けのライブラリを公開するために、Gradleプラグインを使った手順を紹介したいと思います。
以下の手順で進めます。
順番に解説していきます。
app/build.gradle
に以下を追加します。
plugins {
...
id 'maven-publish'
}
publishing {
...
repositories {
maven {
name = "GitHubPackages"
url = "https://maven.pkg.github.com/mkt120/sample_repository"
credentials {
username = System.getenv("USER_NAME")
password = System.getenv("ACCESS_TOKEN")
}
}
}
}
plugin
にmaven-publish
を追加。ライブラリ公開を簡単に実行してくれるプラグインを組み込む。url
はGitHubのリポジトリURL。username
、password
はログイン情報。secret and variables
に設定しておいて、それを参照するようにしておくのが無難。
前項の設定を追加し、sync gradle
を実行すると以下のタスクが追加されます。
publishGprPublicationToGitHubPackagesRepository
これをコマンドラインやGradleペインから実行することで、 GithubPackagesへの登録が行われます。
./gradlew publishGprPublicationToGitHubPackagesRepository
(GithubPackageによると)以下のタスクはGithubPackagesへの登録だけでなく、その前段階のライブラリのビルドなども一緒に実行してくれるようなので、こちらを利用するのが良さそうです。
./gradlew publish
ここまでだけでGitHubPackagesにパッケージを登録することができますが、GitHubにコミットをプッシュしたことを契機に、GitHubPackagesに更新を入れるようGitHub Actionで自動登録できるようにしておきます。
#masterブランチにプッシュされたら実行
on:
push:
branches: [ "master" ]
...
jobs:
build:
...
# name. githubAction のコンソール上で実行内容がわかりやすいように名前付けしておく
- name: publish library
run: ./gradlew publish
env:
USER_NAME: ${{ secrets.USER_NAME }}
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
今回はGitHubPackagesにライブラリを公開する方法を解説しました。似たようなデータ形式・UIのアプリケーションをたくさん開発している方は、こういった機能を活用することで開発作業の効率化を図りたいですね。また、ライブラリ化したはいいけど、maven repository に登録するほどのものではないんだよなぁ...という方にも、GitHubPackagesという公開範囲を管理できる領域に登録することで適切にスケーリングすることもできるのではないかと思います。
どうも。どっことです。今回は、テストコードの実装について解説したいと思います。
テストコードは実機やエミュレータに開発段階のアプリをインストールせずとも、プログラムとしてテストを実行できるようにするものです。テストコードを実装せずとも、世の中に公開するアプリを開発することができるので、より難しい機能の実装や複雑なドメインロジックをプログラムに落とし込むなどのスキルを優先して獲得するというロードマップも考えとしてはあると思います。
ただ、昨今のCI/CDの重要性の高まりを鑑みると、テストコードを実装できることもエンジニアとして必要なスキルとして需要が上がってきているように見受けられます。そしてそんな需要が高まっている一方で、テストコードを実装できるエンジニアはまだまだ多くありません。ぜひテストコードを実装できるようになり、より広い知見をもつエンジニアへと一歩を進めましょう。
Androidの開発においては、大きく2種類のテストコードがあります。
開発PC上で実行されるテストコードです。Androidの機能に依存しないビジネスロジックやアプリケーションロジックに対するテストコードを実装する際のものとなります。開発PC上で実行されるため、後述のインストルメントテストよりも高速に実行・実施ができます。
実機やエミュレーター上で実行されるテストコードです。こちらは、Androidの機能に依存するUIまわりのテストや、外部機能などとの連携に対するテストコードとなります。より現実に近い環境でのテストとなるため、ユーザーストーリーを意識したテストに向いています。一方で、インストールなどのステップがビルド時に含まれるため、実行時間が比較的遅めになります。
今回は、ユニットテストの実装方法について解説します。
ユニットテストの実装方法について、以下の流れで説明します。
ユニットテストのテストコードを実装するためには以下をbuild.gradle
に追加が必要です。プロジェクトを新規作成する際にAndroid
Studioが自動で追加している場合がありますので、追加が必要か確認してください。
dependencies {
testImplementation("junit:junit:4.13.2")
}
それでは実装していきましょう。
テスト対象は以下のクラス・メソッドとします。
class Sample {
fun test(input: Int?): Boolean {
if (input == null) {
throw IllegalArgumentException()
}
return input > 0
}
}
テスト対象となるメソッドは、以下の観点でテストコードを実装することができます。
true
が返される確認テストfalse
が返される確認テスト上をベースに確認するテストコードを追加していきましょう。
テストを実装するため、テストクラスを実装します。テスト対象のクラスで
option
+ Enter
からCreate Test
を選択すればテストクラスを作成できます。SampleTest
という名前にすると、test
ディレクトリに以下のようなクラスが作成できると思います。
class SampleTest {
}
まずはtrue
が返されることのテストです。
@Test
fun testTrue() {
val sample = Sample()
val value = sample.test(1)
Assert.assertEquals(value, true)
}
以降の実装も同様ですが、テストメソッドを実装し@Test
アノテーションを付与します。
そして、実際にテストしたい内容を実装します。今回は、入力値に1
以上を指定した場合はtrue
が返却されるので、それを確認するためのテストを実装します。
次にfalse
が返されることのテストです。
@Test
fun testFalse() {
val sample = Sample()
val value = sample.test(0)
Assert.assertEquals(value, false)
}
同様に、false
が返却されるケースを実装します。入力値に0
以下を指定した場合はfalse
が返却されるので、それを確認するためのテストを実装します。
そして、例外が投げられることのテストです。
@Test(expected = IllegalArgumentException::class)
fun testException() {
val sample = Sample()
sample.test(null)
Assert.fail()
}
例外が発生するテストは@Testアノテーションのパラメータexpected
を指定します。そのパラメータには発生する例外のクラスを指定します。
また、例外が発生しなかった場合がテストがNGとなるように、例外がthrowされる直後のタイミングでAssert.fail()
を実装しておきます。
これにて、該当クラスメソッドのテストが実装できました。あとはテストを実行すれば期待した結果になるかを確認する処理が実行されます。
ここまでテストコードの実装について解説しましたが、実は1点問題があります。ユニットテストは開発PC上で実行されますが、その都合により、AndroidSDKに含まれる機能を利用することができません。これが結構ネックになり、例えばよくある実装の一つであるFragment
のインスタンス生成メソッドは、Fragment
やそれに渡すBundle
がAndroidSDKに含まれるものなので、テストコードを実装することができません。
ではどうするかというと、一つはインストルメントテストで実装することです。実機やエミュレーター上でテストすることで、テストコードを実行することができます。
もう一つはRobolectric
を利用することです。Robolectric
はユニットテストでもAndroidSDKに含まれる機能を利用することができます。
今回は説明を省略しますが、公式サイトを紹介します。またRobolectric
の利用方法についても、後日解説を予定しています。
今回はテストコード、特にユニットテストの実装方法について解説しました。テストコードやその実装の重要性は今後も高いものと予想されるので、今後もこれをテーマとした投稿をしていこうと考えていますので、よろしくお願いします。
どうも。どっことです。今回は、通知を実際に表示する実装方法を解説します。
通知チャンネルも実装できて、いよいよ通知を表示する方法まで来ました。さっそく実装方法を解説していきます。
AndroidManifest.xml
にパーミッションの追加
まずはAndroidManifest.xml
に今回の通知表示に必要となるpermission.POST_NOTIFICATIONS
の権限を追加します。このpermission.POST_NOTIFICATIONS
が実装時に忘れやすいので、実装時にはぜひ備忘録としてご確認いただけると嬉しいです。
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
権限許諾ですが、実装方法は通常のパーミッションチェックと同様です。つまりregisterForActivityResult
を使ってlauncher
を生成し、Manifest.permission.POST_NOTIFICATIONS
の許諾確認をします。このManifest.permission.POST_NOTIFICATIONS
も実装時に忘れやすいので、ぜひ参考にしてください。
private val launcher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) {
/** 許諾ダイアログが閉じられた。何かやりたいことがあればここに実装。*/
}
...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
TODO("あとで追加実装")
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
権限を確認したにもかかわらず拒否されてしまった場合ですが、一般的には次回などで「この権限がXXXという理由で必要なんだ」という権限を必要としている理由を説明した上で再度権限を確認する流れとなります。前項のTODO("あとで追加実装")
に追記していきます。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
// 権限を許諾してもらっている。後続処理に流さないでOK
return
}
val shouldShow = shouldShowRequestPermissionRationale(
Manifest.permission.POST_NOTIFICATIONS
)
if (shouldShow) {
// 過去に許諾確認したけど、許可してくれなかった人。
// ダイアログなどで権限を必要としている理由を説明した上で再度依頼する。
} else {
// それ以外。初回のケース、または「もう表示しない」設定としたケース。
launcher.launch(Manifest.permission.POST_NOTIFICATIONS)
}
}
そしてManifest.permission.POST_NOTIFICATIONS
の許諾を確認をした上で、問題なければNotification
を表示するようNotificationManager
に依頼します
if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
val notification = Notification.Builder(this, "id")
.setContentTitle("this is title")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentText("this is text")
.build()
NotificationManagerCompat.from(this)
.notify(null, 0, notification)
}
実際にうまく動作していれば、上記の場合以下のような通知が通知領域に表示されます。
今回は通知領域に通知を表示する方法を解説しました。実装はそんなに複雑ではないものの、参照すべき定数を毎回忘れてしまいがちなので、ぜひ参考にしていただけると嬉しいです。
どうも。どっことです。今回は通知チャンネルを作成するための実装方法について解説します。
Android 8(Oreo)から 通知領域に通知を表示するためには、その通知に対応する通知チャンネルをあらかじめ作成しておかなければならなくなりました。
以前の記事で「作成した通知チャンネルの設定画面に遷移する」実装方法を解説しましたが、そもそも通知チャンネルの追加方法を解説していなかったので、今回はこちらをテーマに解説していこうと思います。
通知チャンネルは以下の手順で作成します。
NotificationManager
のインスタンスを取得NotificationManager
に通知チャンネルの作成を依頼順番に解説していきます。
まずはNotificationManager
のインスタンスを取得します。androidxではNotifcationManagerCompat
というクラスが提供されており、今まではAndroidのバージョンがO未満かどうかで制御をする必要がありましたが、このクラスが提供されたことによりAndroidのバージョンを気にせず制御することができるようになりました。
呼び出し方も単純で、以下でOKです。
val manager = NotificationManagerCompat.from(this)
次に実際に作成する通知チャンネルのインスタンスを生成します。最小の構成では、ID、Importanceとタイトルさえあれば作成できるはずです。
val channel = NotificationChannelCompat.Builder("id", NotificationManagerCompat.IMPORTANCE_DEFAULT)
.setName("ChannelName")
.setDescription("ChannelDescription")
.build()
実装するとき一番よく忘れるのが、Importance
です。定数はNotificationManagerCompat
で定義されていますので、そちらを参照してください。以下が定義されています。
NotificationManagerCompat.IMPORTANCE_MAX
NotificationManagerCompat.IMPORTANCE_HIGH
NotificationManagerCompat.IMPORTANCE_DEFAULT
NotificationManagerCompat.IMPORTANCE_LOW
NotificationManagerCompat.IMPORTANCE_MIN
NotificationManagerCompat.IMPORTANCE_NONE
NotificationManagerCompat.IMPORTANCE_UNSPECIFIED
最後に生成した通知チャンネルのインスタンスをNotificationManager#createNotificationChannel()
で渡して、通知チャンネルの作成を依頼します。
manager.createNotificationChannel(channel)
今回は通知チャンネルの作成手順を解説しました。Androidの通知に関する処理は、最新のバージョンになるに連れて必要な実装が増えていくのが辛いところですが、素敵なアプリには必要不可欠な要素の一つだと思いますので、是非ともマスターしたいところですね。
作るのと同じで、NotificationManager#deleteNotificationChannel(id)
を呼ぶことで、通知チャンネルの削除依頼することができます。
manager.deleteNotificationChannel(channel)
どうも。どっことです。今回はMacを使っていると時々やってしまう間違えたパスワードの解消方法について解説します。
最近はPCがID/パスワードをいい感じに記憶してくれるので、とても便利になりました。ただ、そんなPCが間違えたID/パスワードを記憶してしまうと、それを使ってログインやアクセスを自動で試みて、結局失敗してエラーが出て終了。「何かよくわからないけど勝手にアクセスして、勝手に失敗して、エラー吐いて、終わった。だったら最初から聞いてくれ...」状態になります。
エンジニアの方で特によくあるのはSourceTree
だと思います。SourceTree
はGithub
などで管理しているGitリポジトリにアクセスするためのクライアントソフトですが、悲しいことに一度パスワードを記憶するとSourceTree
側からパスワードを再入力する導線が表示されなくなります。そのためパスワードを間違えて記憶してしまうと「パスワード間違えたのはわかっているから解消させてくれ...」ということになります。ということで今回は、SourceTree
を例とした上記問題の解消方法について解説していきたいと思います。
手順は以下の通りです。
SourceTree
で認証情報を検索する。SourceTree
で再度認証情報を入力する。順番に解説します。
キーチェーンアクセス
を起動します。 Command
+
Space
でspotlight検索を表示し、キーチェーンアクセス
を表示します。
キーチェーンアクセス
ではそのマシンの認証情報をすべて管理しています。検索窓にSourceTree
を入力し、該当の認証情報を探しましょう。
認証情報が見つかったら、右クリックして認証情報を削除しましょう。
ここまでの手順により、マシンからSourceTree
で入力された認証情報が削除されるので、再度SourceTree
から認証情報を入力すれば、正しい認証情報を記憶させることができます。
今回はSourceTree
をはじめとした誤った認証情報の解消方法について解説しました。ID/パスワードだけでなく、例えばアクセストークンといった認証情報などもキーチェーンアクセスで管理されているので、今回の内容を参考に解消していただければ嬉しいです。
どうも。どっことです。今回は、ディスプレイスリープを無効にする実装方法を解説します。
動画の再生中やドキュメントの表示中、タイマー表示中などの画面を常に表示しておきたいケースでディスプレイがスリープしてしまうのは避けたいと思います。アプリ側でディスプレイがスリープしてしまうことを無効にする設定があるので、今回はこちらを解説したいと思います。ただし、スリープを無効にすることは電力消費の観点から必要最低限の範囲で設定することを心がけましょう。
スリープを無効にするためにはActivity
から参照できるwindow
インスタンスに対して、FLAG_KEEP_SCREEN_ON
のフラグを設定します。
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
スリープ無効設定をクリアするためには、window
インスタンスに対して、FLAG_KEEP_SCREEN_ON
のフラグをクリアしてあげます。
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
今回はディスプレイがスリープされる設定を無効にする実装方法を解説しました。使い所が限られる機能ですが、動画再生などディスプレイをスリープさせたくないような利用方法をアプリに提供する場合、必要な実装になりますので参考にしていただけると幸いです。
どうも。どっことです。 タイトルの通りですが、諸事情により GitHubPage に移行予定です。 https://mkt120.github.io/ この備忘録に記載の内容を転記しつつ、今後はこちらのページを更新していく予定です。