この記事は Chet Haase による Android Developers - Medium の記事 "Now in Android #29" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。
Android 開発の最新ニュースやトピックをご紹介する Now in Android。今回は連載シリーズ MAD Skills の「App Bundle」、App Bundle、AndroidX 安定版リリース、連載シリーズ Kotlin Vocabulary に関する新しい記事と動画、最近公開されたブログ記事・動画・関連ドキュメント、ポッドキャスト エピソードなどをご紹介します。
最先端の Android 開発に関する技術コンテンツをミニシリーズごとにテーマを決めて扱う連載シリーズ MAD Skills。前回のミニシリーズ「ナビゲーション」につづいて、App Bundle を取り上げます。Wojtek Kaliciński と Ben Weiss が Google Play のアプリ署名、初めての App Bundle のビルド、Play Feature Delivery についてのコンテンツを公開しました。詳しくは、以下の動画と記事をご覧ください。
Android App Bundle を取り上げるミニシリーズですから、アプリの署名についてお話しするのは当然です。Google Play はユーザーのデバイスにダウンロードする APK を生成しますが、その APK をインストールするには署名が必要だからです。この動画では、Google で鍵を生成するオプションや自分の鍵をアップロードするオプションなど、Google Play Console でアプリの署名を有効化する方法について順を追って説明しています。
こちらの関連記事も忘れずにご確認ください。Play のアプリ署名についてのよくある質問を取り上げています。
こちらの動画では、Ben が App Bundle の作成手順をご説明しています。App Bundle は Android Studio またはコマンドラインで作成し、Play Console にアップロードします。さらに、Play Console でツールを使い、アップロードしたバンドルの情報を確認する方法もお話しています。
このエピソードでは、Android Studio を使ってアプリをモジュール化する方法のほか、インストール時(インストールするかどうかを決定するオプションの条件も指定可能)や、オンデマンドでダウンロードするモジュールを選択する方法をお話しています。さらに Ben は、API を使ってオンデマンドでモジュールのインストールをリクエストする方法も詳しく説明しています。
本エピソードは、ブログ記事(英語)でもご覧いただけます。
App Bundle を取り上げるミニシリーズ最後のエピソードでは、Wojtek がローカルでテストを行う bundletool や、Play Console を使ってアップロードしたものをテストする方法など、利用可能なツールを使ってバンドルと生成された APK をテストする方法を説明しています。
この動画でお話している内容は、いくつかの記事やドキュメントとも関連しています。ぜひ、詳しい情報をご確認ください。
App Bundle を取り上げるミニシーズ最後のコンテンツとして、2020 年 11 月 19 日(日本時間 11 月 20 日)に YouTube でリアルタイム Q&A を開催します。こちらのプレイリストに YouTube ライブへのリンクが表示されますのでご確認ください。さらに、質問を募集するツイートも行います(編集部注:セッションはすべて英語で、この日本語記事投稿時には実施済み)。
本シリーズのコンテンツアーカイブは、YouTube の MAD Skills プレイリスト、Medium の記事、またはすべてのリンクが掲載されているランディング ページからご覧いただけます。来週は次のミニシリーズが始まります。お楽しみに!
多くのアルファ版、ベータ版、RC 版の AndroidX ライブラリがリリースされました。その中には、いくつかの重要な安定版リリースもありました。最近リリースされた以下のライブラリにご注目ください。
前回の Now in Android #28 以降、Kotlin についていくつかの新しい記事と動画が公開されています。
Florina Muntenescu が、連載シリーズ Kotlin Vocabulary に新しいエピソードを追加しました。今回のテーマは、Kotlin のデータクラスです。データクラスを使うと、ボイラープレート コードをほとんど使わずに、データを保持する構造を簡単に作成できます。しかも、equals() 関数と hashCode() 関数は、Kotlin が自動的に適切なものを生成してくれます。また、何もしなくてもクラスのプロパティの分割代入や copy() を利用できます。いつもの Kotlin Vocabulary エピソードのように、Florina がデータクラスを逆コンパイルしたバイトコードを確認し、内部の仕組みを細かくご説明しています。
Murat Yener が、以前に掲載した Kotlin の委譲機能についての記事の続編を投稿しました(英語)。今回は、Kotlin 標準ライブラリが提供するデリゲートである lazy、observable、vetoable、notNull についてご説明しています。
Florina がこちらの記事を投稿(英語)し、Kotlin の教育や開発に力を入れることに関して、デベロッパーの皆さんからよく寄せられるいくつかの質問にご回答しています。さらに、重要な学習関連情報へのリンクも掲載しています。
この記事では、Kotlin アプリのほうが Kotlin 以外で書かれたアプリよりもエラーが起こりにくい理由について、Florina がご説明しています。その証拠となる具体的なアプリやユースケースを取り上げますが、それだけでなく、null 可能性や hashCode() != equals() など、この言語を使ったコードが安定して動作する理由についても解説しています。詳しくは、こちらのブログ記事(日本語)をご覧ください。
前回の Now in Android 以降、Android Developers Backstage に新しいエピソードが投稿されています。以下のリンクまたはお気に入りのポッドキャスト クライアントでご確認ください。
Romain Guy と Tor Norbye、そして私が、Instacart の Colin White 氏から、オープンソースのイメージ読み込みライブラリである Coil について話を聞きました。イメージの読み込み、パフォーマンス、オープンソース、そしてこの Kotlin ファーストなライブラリを Kotlin やコルーチンを使ってどう作るのかについて語り合います。
今回は以上です。次回も Android デベロッパーの世界の最新アップデートをお届けします。お楽しみに。
Reviewed by Yuichi Araki - Developer Relations Team and Hidenori Fujii - Google Play Developer Marketing APAC
この記事は Murat Yener による Android Developers Blog の記事 "Meet Google Play Billing Library Version 3" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。
※2021年 8 月 2 日より新規リリースのアプリでは最新の Billing Library バージョン 3 への対応が必要となり、11 月 1 日までにすべてのアプリにその対象が広がるため、再度掲載します。
Google Play は、ユーザーに愛される高品質のアプリを開発するための、健全なエコシステムの実現に取り組んでいます。多くのデベロッパーは Google Play の 1 回限りの購入と定期購入 サービスによって、そのようなアプリの開発が実現しています。この 10 年間、キャリア決済を利用した支払い(現在対応キャリア数 180)などの機能や、費用の割り当てと定期購入の管理を簡単に行うツールにより、Android ユーザーの購入体験を改善してきました。
こういった取り組みをさらに進めたのが Billing Library バージョン 3 です。今回提供する新バージョンでは、新しい支払い方法への対応、定期購入のプロモーション機能の追加、ゲーム購入のアトリビューション、購入時の信頼性とセキュリティの向上などが行われました。2021 年 8 月 2 日以降、新しいアプリはすべて Billing Library バージョン 3 以降を使用する必要があり、2021 年 11 月 1 日までに、すべてのアプリのアップデートで Billing Library バージョン 3 以降を使用する必要があります。
Google は世界中のユーザーが 都度購入1 回限りの購入と定期購入の支払いを簡潔かつ便利にできるように継続して取り組んでいます。
現金は現在も世界で最も広く使われている支払い方法であり、2018 年時点では商品とサービス全体で 2.7 兆件の取引に使われています(出典: ユーロモニター)。Google は昨年、コンビニエンス ストアの現金払いのように、取引がデバイス外で完結する新しい支払い方法をプレビューしました。世界銀行によると、全世界で 20 億人が銀行口座を保有していません。そうした状況で、この新しい支払い方法は、特に現金払いが一般的な新興市場で新規の購入者を開拓するきっかけになり得ます。
今回のリリースにより、インドネシアとマレーシアでは、セブン-イレブンや Alfamart などの有名小売店を含む 50,000 を超える店舗で、現金による 都度購入1 回限りの購入が可能になります。この新しい支払方法は、まもなくすべてのデベロッパーにご利用いただけるようになります。
Billing Library バージョン 3 では、ユーザーがアプリ以外(Play ストア全体など)で商品を見つけて購入できるようになります。1 つの例として、新しくなったスムーズなプロモーション コードの利用があります。例えば、定期購入の無料試用のプロモーション コードを提供すると、ユーザーは簡単に Play ストアでの購入に利用することができます。しかも、これはインストール前であっても使うことができます。この機能により、に、シンプルな購入、登録、インストールの体験が実現され、ユーザーの負担を減らすことが可能になります。
ゲームやアプリではアプリ内の購入がゲーム内のどのキャラクターやアバター、プロフィールにアトリビュートされる(アトリビュートできる)ようにする必要があります。Billing Library を利用することで、購入フローの立ち上げ時にそうした情報を特定することができます。購入の完了時に、情報が取得されその購入を正しくアトリビュートすることができます。それにより、非推奨の AIDL デベロッパー ペイロードを利用した独自のソリューションを構築する必要がなくなります。
Google Play のターゲット SDK 要件と同じように、新バージョンの Billing Library ですべてのユーザーがセキュリティ、パフォーマンス、ユーザー体験の改善を体感できることが重要です。2019 年の Google I/O において、Google は Billing Library バージョン 2 をリリースし、各メジャー リリースに 2 年のサポート期間を設けるなどの変更をアナウンスしました。
そのため、2021 年 8 月 2 日以降、新しいアプリでは Billing Library バージョン 3 以降を使用する必要があり、2021 年 11 月 1 日までに、すべてのアプリのアップデートで Billing Library バージョン 3 以降を使用する必要があります。
上記の日以降、古いバージョンの AIDL、Billing Library バージョン 1、Billing Library バージョン 2 との統合を利用したアプリは公開できなくなります。すでに Play ストアにあるアプリは引き続きダウンロードが可能で、アプリ内の購入を処理できます。ただしその後のアプリのアップグレードには、Billing Library バージョン 3 以降が必要です。
Billing Library バージョン 3 は現在(2020年6月11日以降)、Java と Kotlin で開発されているすべてのゲームとアプリの デベロッパーが利用できます。Unity を利用するゲーム デベロッパー向けに、Billing Library バージョン 3 ベースの Unity IAP プラグインもリリースしており、はこれにより、Unityを利用したアプリ開発においてもBilling Library バージョン要件を満たし、Play Billingの機能すべてにアクセスできるようになります。
請求関連の SDK とライブラリを Billing Library バージョン 3 に対応したバージョンにアップグレードしてください。SDK またはライブラリが利用できない場合は所有者に問い合わせてください。Google は主要なプロバイダとともに、Billing Library バージョン 3 との互換性について取り組みを進めています。
Google は年 1 回のアップグレードをおすすめしていますが、各メジャー リリースに対しては 2 年間のサポートを提供します。デベロッパーは、2020 年 6 月 11 日(日本時間 6 月 12 日)からすべての新規アプリに Billing Library バージョン 3 を利用し、2021 年の期限より前に可能な限り早期に既存の請求の統合を移行することをおすすめします。
Billing Library に移行していないデベロッパーにとって、既存のアプリを AIDL から移行することは簡単ではありませんが、できるだけスムーズに移行できるようサポートいたします。現在 AIDL を使用しているアプリ向けには、移行ガイドと動画チュートリアルが用意されています。
また、購入のアトリビューション、プロモーション コードの利用、不正対策に関するガイドとなる、ドキュメントもアップデートされています。実装に関する問題については、こちらまでご連絡ください。
Play コマース プラットフォームに関する改善全体についての詳細は、動画 What's new in Play Commerce をご覧ください。
皆様のアプリとゲームへ素晴らしい購入体験を実現するために、今後ともご協力をお願いいたします。
Reviewed by Tetsuo Midorikawa - Google Play Product Operations
ユーザーは皆さんのアプリにシームレスな体験を期待しています。アプリがクラッシュすれば、低評価のレビューやアンインストールが増え、ブランドにダメージを与えてしまうでしょう。その一方で、コミュニティの皆さんとの対話の中で、Kotlin を採用する主な理由の 1 つはコードの高い安全性であるということをよく耳にします。実際に何社かのパートナーは、Kotlin を使ってコードの安定性を改善しています。この投稿では、その方法をいくつか紹介するとともに、Google Play ストアの統計結果にも注目し、Kotlin とクラッシュ数との間に相関関係があるかどうかを確認してみたいと思います。
アプリの品質が影響するのは、ユーザー エクスペリエンスだけではありません。クラッシュ多いと、他のいくつかの要素にも悪影響が生じます。
Kotlin で構築したアプリはクラッシュする可能性が 20% 低い
Google Play のトップ 1,000 アプリを調査してみたところ、Kotlin を使っているアプリは、それ以外のアプリよりもユーザー 1 人あたりのクラッシュ発生率が 20% 低いことがわかりました。
その具体例として、コードの 74% が Kotlin である Swiggy のエンジニアリング チームは、新機能の開発を Kotlin に移行して以来、クラッシュを 50% 減らしました。こういった結果を Kotlin はどのように実現しているのか見ていきましょう。
Google Play でのクラッシュの原因ナンバーワンは NullPointerException です。Google Home チームは、2018 年よりすべての新機能を Kotlin で書いています。その結果、1 年前と比べて null ポインタによるクラッシュが 33% 減少しました。
Google Home が NullPointerException を 33% 削減
NullPointerException を避けるには、メソッドを呼び出したりメンバーにアクセスしたりする前に、扱っているオブジェクトの参照が null でないことを確認しなければなりません。Kotlin では、null 可能性が型システムの一部として組み込まれています。たとえば、変数は最初から null 可能か null 不可能かを宣言する必要があります。null 可能性を型システムの一部として組み込むことで、コードベースについての自分の記憶や知識、またコンパイル時警告(フィールドやパラメータに @Nullable アノテーションを付けた場合)に頼る必要はなくなります。null 可能性を強制することで、単なる警告ではなく、コンパイル時にエラーが発生します。null 可能性を扱う方法については、こちらのページをご覧ください。
NullPointerException
@Nullable
私たちデベロッパーは、気づかないうちにアプリにたくさんの問題を紛れ込ませています。そのほとんどはあまりに軽微で、調査するのは難しいかもしれません。そのような問題のうち、Kotlin を使うことで回避できるものをいくつか紹介しましょう。
hashCode()
2 つのオブジェクトが等しい場合、それらのハッシュコードも同じである必要があります。しかし、どちらかのメソッドを実装し忘れたり、クラスに新しいプロパティを追加したときに更新し忘れてしまったりすることがよくあります。データを保持するためだけのクラスを扱う場合は、Kotlin のデータクラスを使うようにしましょう。データクラスを使うと、コンパイラが hashCode() と equals() を生成してくれるので、クラスのプロパティを変更すると自動的に更新されます。
equals()
2 つのオブジェクトは構造が等しい(中身が同じ)のでしょうか。それとも、参照が等しい(ポインタが同じ)のでしょうか。Java プログラミング言語では、プリミティブには常に == を使います。そのため、実際には構造が等しいかどうかを確認(equals() を呼び出してチェック)したいのに、オブジェクトにも ==(参照が等しい)を使ってしまうというのがよくある誤りです。まず、Kotlin にはプリミティブ型はなく、Int や String などのクラスを使います。つまり、すべてがオブジェクトなので、オブジェクトとプリミティブ型の区別を気にする必要はありません。次に、Kotlin では == は構造が等しい、=== は参照が等しいと定義されています。したがって、誤って参照が等しいかどうかを確認してしまうことはありません。
==
Int
String
===
enum を扱う場合、通常はすべての可能なケースを記述しなければなりません。そのため、switch や if else チェーンを使うことになります。enum を修正して新しい値を追加する場合、enum を使っている各コード スニペットを手動でチェックし、新しいケースに対応しているかどうかを確認する必要があります。しかし、これはエラーにつながりがちです。Kotlin では、when を式として使うと、この確認をコンパイラに任せることができます。すべての可能な分岐に対応していない場合、コンパイラ エラーが発生します。
switch
if else
ユーザーやブランドにとって、アプリの安定性は重要です。Kotlin を使い始めてクラッシュ率を減らし、ユーザーの満足度を高めてアプリの高評価を守り、ユーザーの維持や獲得につなげましょう。
詳しくは、Kotlin でより優れたアプリを作成するためにできることをお読みください。また、ケーススタディをご覧いただき、Kotlin のメリットをご確認ください。世界でデベロッパーに広く使われている言語の 1 つである Kotlin を使ってみたい方は、入門ページをご覧ください。
この記事は Chet Haase による Android Developers - Medium の記事 "Now in Android #28" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。
Android 開発の最新ニュースやトピックをご紹介する Now in Android。今回は新連載シリーズ MAD Skills の「ナビゲーション」、Android X ライブラリ、連載シリーズ Kotlin Vocabulary、最近公開されたブログ記事・動画・関連ドキュメント、ポッドキャスト エピソードなどをご紹介します。
新連載シリーズ MAD Skills は、Modern Android Development(最先端の Android 開発)テクノロジーの使い方をデベロッパーの皆さんにお伝えするコンテンツです。このテクノロジーを使えば、優れたアプリを簡単に作ることができます。このシリーズは、前回の Now in Android を投稿した時期に始まりました。それから数週間が経ち、Navigation コンポーネントを扱った最初のミニシリーズが完結し、リアルタイム Q&A もご用意しました。
MAD Skills の目的の 1 つは、最先端の Android 開発のさまざまな機能の使い方をお伝えするだけでなく、皆さんの問題や質問に耳を傾けて解決のお手伝いをすることでした。そのため、各ミニシリーズの終わりには、YouTube でリアルタイム Q&A を開催する予定です(編集部注:セッションはすべて英語で実施されます)。
それに向けて、Twitter で質問を募集しました。また、Twitter と YouTube でもライブで質問に回答し、さらに、プロダクトに関わっている何名かのエキスパートに、おすすめする内容について話を聞きました。
ナビゲーションについては、10 月 29 日木曜日の午前 10 時(日本時間 10 月 30 日午前 3 時)に私と Ian Lake がリアルタイム Q&A を開催し、現在はアーカイブを公開しています。寄せられたご質問は、こちらの Twitter スレッドと、#AskAndroid がついた投稿から確認できます。
ナビゲーションのエピソードを見逃してしまった方は、動画や記事で確認できます。
続きのコンテンツは、YouTube の MAD Skills プレイリスト、Medium の記事、またはすべてのリンクが掲載されているランディング ページからご覧いただけます。今後の MAD コンテンツにもご期待ください。11 月 1 日 からは次のシリーズが始まっています!
まず、AndroidX は、 GitHub を通して多くのライブラリを提供しています。
多くのデベロッパーの皆さんは、Android でずっと使われている AOSP+Gerrit システムよりも、使い慣れた GitHub を好まれていることは認識しています。
これはすばらしいアイデアです。ただししかし、私たちが利用しているインフラストラクチャを考慮すると、そのアイデアの実現はそう簡単ではありません。それでも、皆さんの意見を踏まえて模索し始め、数か月前から Paging、Room、WorkManager という積極的に開発が行われている一部のライブラリを GitHub で利用できるようにしました。先日、Activity、Fragment、Navigation の各ライブラリも追加しましたので、ぜひご確認ください。またご自分でも貢献したいという方は、CONTRIBUTING ドキュメントに記載されている詳細をご覧ください。
いつものように、たくさんのアルファ版、ベータ版、RC 版とマイナー安定版リリースがありました。主な安定版リリースについて紹介します。
MediaRouter 1.2.0: これは注目に値するバージョンです。というのも、Android 11 の新しいメディア機能のいくつかと同期する機能が追加されているからです。
Android 11 には、メディア プレーヤー用のたくさんの細かな UI が追加され、メディア コントロールが通知パネルの新しい専用の領域にまとまっているため、他の通知に紛れることなく 1 か所から簡単にメディアを制御できます。これは、新しいデベロッパー機能というより、皆さんが既に作っているかもしれない通知を別の形で表現したものです。Lollipop リリース以降で利用できる MediaSession や MediaStyle API はそのまま使うことができます。
ただし、Android 11 には、「シームレスなメディア移行」を目的としていくつかの新機能が追加されています。これにより、ユーザーは出力スイッチャー(下記)で再生デバイスを変更できます。今回の MediaRouter の新リリースを使うと、この新しいプラットフォーム機能を操作できます。
この MediaRouter の新リリースを使うと、出力スイッチャーにどの再生デバイスを表示するかを制御できるので、ユーザーはシームレスにオーディオを移動できます。
Android 11 のメディアに関する変更点を確認したい方は、Don Turner の動画 What's new in Media(メディアの新機能)をご覧ください。
いくつかのアルファ版リリースも紹介したいと思います。アルファ版はアルファ版であり、チームの開発が進むとともに変更が繰り返されるので、通常はアルファ版に注目することはありません。しかし、Paging と Navigation のアルファ版のリリースは、近未来のアプリ開発という点で注目に値するでしょう。
Jetpack Compose のアルファ版の開発が進んでいますが、「Jetpack Compose の世界で、[ここにお気に入りの Jetpack ライブラリを挿入] は今後どうなりますか?」と、質問を受けることがあります。
お答えします。アーキテクチャ コンポーネントの多くは、個々のビューや既存の UI ツールキットを扱うものではありません。そのため、それぞれのライブラリは、Jetpack Compose の新しい世界でどれも等しく必要かつ有用です。それだけでなく、現在私たちは、異なるコンポーネントを簡単に連携させることができるように、統合機能の開発を進めています。Compose は既に ViewModels および LiveData と組み合わせて使うことができますが、本日からは Paging と Navigation の両方も Jetpack Compose のサポート対象になりました。
先日、Kotlin の言語機能に関するシリーズで 記事を 3 件公開しました。(英語記事一覧・日本語記事一覧)
Florina Muntenescu が Kotlin の分割代入機能についての新しい記事(英語)と動画を投稿しました。分割代入は、オブジェクトの異なるフィールドの値を複数の変数に割り当てる際に便利です。たとえば、次のデータクラスがあったとします。
data class Donut( dough: String, topping: String)
すると、変数を Donut インスタンスのフィールドにすばやく割り当てることができます。
val (dough, topping) = someDonut
データクラスでは何もしなくても分割代入が動作しますが、他のクラスの関数に分割代入機能を持たせることもできます。
Meghan Mehta が Kotlin 言語機能のエクステンションについて説明した新しい記事(英語)を公開しました。エクステンションとは、既存のクラスに新しいメソッドやプロパティを追加できるような機能です。実際には、エクステンションが既存のクラスに何かを挿入することはありません。しかし、メソッドの呼び出し元からはそのように見えます。なお、
内部的には、クラスのインスタンスを受け取る静的メソッドとして実装されています。
この Kotlin の機能は、私もよく使うことになりそうです。API デベロッパーとして、後から API を改善できるという考え方が気に入っています。プラットフォームやライブラリのコアの外側に API を追加するにもかかわらず、コードから使うときは、まるでそこにあるように見えるのです。たとえば、String クラスに String.isAGoodDonutName() というエクステンション メソッドを作ったとしましょう。すると、呼び出し元はこのエクステンション メソッドを使い、“Sprinkle”.isAGoodDonutName() というように String からこのメソッドを直接呼び出すことができます。他の言語のアプローチのように、他のパッケージやクラスを使って Utils.StringMethods.isAGoodDonutName(“Sprinkle”) などと記述する必要はありません。
String.isAGoodDonutName()
“Sprinkle”.isAGoodDonutName()
Utils.StringMethods.isAGoodDonutName(“Sprinkle”)
最後に、Manuel Vivo が新しい動画「コルーチンの ABC」を公開し、CoroutineScope、CoroutineContext、Dispatcher、Job などのトピックについて説明しています。コルーチンの ABC というより、CDJ のように見えるかもしれません。
CoroutineScope
CoroutineContext
Dispatcher
Job
アプリを公開する際に厄介なことの 1 つは、そのアプリを確実にストアのガイドラインに準拠させることです。このガイドラインは、ユーザーにとって優れたアプリのエコシステムを実現するために存在しています。しかし、正しい対処方法を厳密に伝えるのは難しい場合もあります。そこで、問題につながる可能性があるいくつかのよくある違反と、それを避ける方法についてより理解を深めていただくため、この記事を公開しました。
よくあるポリシー違反の中には、アプリの UI から Play Store へのリンク、アプリの明確な概要よりも検索キーワードを意識した説明、ウェブサイトのコンテンツを WebView で見せるだけのアプリなどがあります。
多くのカンファレンスがオンラインでの開催を強いられているこの時期に、Droidcon は興味深いアプローチをとりました。たくさんのイベントを組み合わせて、広いタイムゾーンをカバーする大きなイベントを実施したのです。10 月に行われたこのカンファレンスのヨーロッパ、中東、アフリカ バージョンと、そのすべての動画がオンラインで公開されています(私たちのチームが作ったものもあれば、広大なコミュニティのとてもたくさんのデベロッパーの皆さんが作ったものもあります)。
スクリーンキャストの MotionTags シリーズには、前回から 2 つのエピソードが投稿されました。
MotionTags プレイリストの他のシリーズと合わせてご覧ください。
先日、Udacity Android Kotlin Developer Nanodegree が新しくリリースされました。このコースは、ベスト プラクティスに従って Kotlin で Android アプリを構築する方法を学びたい方が対象です。前提条件や詳しい内容は、プログラムの概要をご覧ください。
なお、この Nanodegree は有償プログラムで、フィードバック付きのプロジェクト、テクニカル メンター サポート、キャリア サービスが Udacity から提供されます。ただし、ベースとなるコースのコンテンツは Google が Udacity と共同で作成したもので、誰でも無料でアクセスできます。このコンテンツは、Kotlin による Android アプリ開発や Kotlin による高度な Android で確認できます。
Romain Guy と私が、新しい 3.0 バージョンの Paging ライブラリ(「Paging 3」)について、Android ツールキット チームの Dustin Lam と Chris Craik に話を聞きました。
このバージョン(現在アルファ版)は、コルーチンと Flow を使って Kotlin で完全に書き直されています。それにはさまざまな理由がありますが、このポッドキャストではその点に迫っています。ぜひお聴きください。
この記事は Andrew Ahn による Android Developers Blog の記事 "Developer tips and guides: Common policy violations and how you can avoid them" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。
Google Play では、安全でエンゲージメントが高く、便利で楽しいアプリのエコシステムを培いたいと考えています。そのようなアプリが、世界中の数十億人の Android ユーザーに愛され、使われることを期待しています。そのため、Google Play のデベロッパー ポリシーとデベロッパー販売/配布契約を定期的に改定し、アプリのコンテンツや機能の境界について詳しく記述したり、デベロッパーの皆さんがアプリの宣伝や収益化を行うための最新ガイドを提供しています。
Google Play Store で公開されているアプリがデベロッパーポリシーに準拠しているかどうかを分析したところ、デベロッパーの皆さんが犯しがちないくつかの誤りや違反があることがわかりました。この記事では、デベロッパー コミュニティの皆さんにそれらの点をお伝えするとともに、ポリシー違反によってアプリやデベロッパー アカウントが停止されるリスクを軽減できるように、違反を避けるためのヒントとガイドをご紹介します。
よくある誤りの 1 つは、Play Store にリンクしているボタンやメニューに多く見受けられます。リンク先は同じデベロッパーによるアプリだったり、連携している他社のアプリだったりしますが、それが広告や宣伝のリンクであるかを明らかにしていないことがよくあります。その場合、詐欺や偽装広告と見做されてしまう可能性があります。このような誤りを避ける方法の 1 つは、ボタンやリンクを明示的な名前にすることです。たとえば、「その他のアプリ」「その他のゲーム」「試してみる」「他のアプリをチェック」などです。
よく見られるもう 1 つの誤りは、あるキーワードやフレーズのランクを上げてアプリを見つかりやすくしようと、アプリの説明にキーワードを詰め込むことです。キーワードの繰り返しや、関係のないキーワードや参照を含むテキスト ブロックやリストは、ストアの掲載情報とプロモーションのポリシーに違反します。この違反を避ける最善の方法の 1 つは、ユーザーにとって読みやすくわかりやすいアプリの説明を書くことです。
不適切なストアの掲載情報を避ける方法や、人工的にアプリの視認性を上げる方法については、こちらの動画をご覧ください。
デベロッパーがかなり以前に公開し、既にメンテナンスされなくなったアプリも存在します。メンテナンスされていないアプリは、機能が動作しないなど、ユーザー エクスペリエンスの問題を生み出します。このようなアプリは、少ない星の数や否定的なユーザー レビューで評価されるリスクがあるだけでなく、最小限の機能を提供するというポリシーに違反しているとも認識される可能性があります。デベロッパーやアプリの評判が下がるのを防ぐためにも、そのようなアプリは Play Store で非公開にすることを検討してください。なお、非公開にしても、既にアプリをインストールしている既存ユーザーには影響しません。また、デベロッパーはいつでも問題を修正してアプリを再公開することができます。
また、既存ウェブサイトを Webview で見せているだけに過ぎないアプリもよく見かけます。こういったアプリのほとんどは、Android ユーザーにエンゲージメントの高いアプリのエクスペリエンスを提供するというよりは、単にトラフィックを上げるという目的で登録されています。このようなアプリは、Webview スパムと見なされ、Play から削除されます。アプリではウェブ以上の機能を提供することを検討し、ユーザー エクスペリエンスを高める関連機能を実装してください。
Play アカデミーの ‘Webview Spam’ コースを受講
ここで説明したのはよくある誤りの一部だけですが、ポリシーの最新情報については、Google Play デベロッパー ポリシー センターをご確認ください。また、最新のポリシー アップデートの詳細については、Comply with Google Play's Spam and Minimum Functionality policiesなどの Google Play アカデミーのポリシー トレーニングコースや Play PolicyBytes 動画をご覧ください。
Reviewed by Konosuke Ogura - Trust & Safety - Play & Android, Global Policy & Operations Lead and Hidenori Fujii - Google Play Developer Marketing APAC
この記事は Florina Muntenescu による Android Developers - Medium の記事 "Don’t argue with default arguments" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。
短くて使いやすいデフォルト引数を利用すると、ボイラープレートを書くことなく関数のオーバーロードを実現できます。多くの Kotlin の機能と同じように、この機能も魔法のように便利です。その秘密を知りたいと思いませんか?この記事では、デフォルト引数の内部の仕組みを紹介します。
関数のオーバーロードが必要な場合、同じ関数を複数回実装する代わりに、デフォルト引数を使うことができます。
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 -->// instead of:fun play(toy: Toy){ ... }fun play(){ play(SqueakyToy)} // use default arguments: fun play(toy: Toy = SqueakyToy)fun startPlaying() { play(toy = Stick) play() // toy = SqueakyToy}
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
// instead of:
fun play(toy: Toy){ ... }
fun play(){
play(SqueakyToy)
}
// use default arguments:
fun play(toy: Toy = SqueakyToy)
fun startPlaying() {
play(toy = Stick)
play() // toy = SqueakyToy
デフォルト引数はコンストラクタにも適用できます。
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 -->class Doggo( val name: String, val rating: Int = 11)val goodDoggo = Doggo(name = "Tofu")val veryGoodDoggo = Doggo(name = "Tofu", rating = 12)
class Doggo(
val name: String,
val rating: Int = 11
)
val goodDoggo = Doggo(name = "Tofu")
val veryGoodDoggo = Doggo(name = "Tofu", rating = 12)
デフォルトでは、Java はデフォルト値のオーバーロードを認識しません。
<!-- Copyright 2019 Google LLC. SPDX-License-Identifier: Apache-2.0 --> // kotlinfun play(toy: Toy = SqueakyToy) {... }// javaDoggoKt.play(DoggoKt.getSqueakyToy());DoggoKt.play(); // error: Cannot resolve method 'play()'
// kotlin
fun play(toy: Toy = SqueakyToy) {... }
// java
DoggoKt.play(DoggoKt.getSqueakyToy());
DoggoKt.play(); // error: Cannot resolve method 'play()'
コンパイラにオーバーロード メソッドを生成するよう指示するには、Kotlin 関数に @JvmOverloads アノテーションを追加します。
@JvmOverloads
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */@JvmOverloadsfun play(toy: Toy = SqueakyToy) {… }
/* Copyright 2020 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
fun play(toy: Toy = SqueakyToy) {… }
コンパイラが生成した内容を確認するため、逆コンパイルした Java コードを見てみましょう。[Tools] -> [Kotlin] -> [Show Kotlin Bytecode] を選択し、[Decompile] ボタンを押します。
[Tools] -> [Kotlin] -> [Show Kotlin Bytecode]
[Decompile]
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */fun play(toy: Toy = SqueakyToy)...fun startPlaying() { play(toy = Stick) play() // toy = SqueakyToy}// decompiled Java codepublic static final void play(@NotNull Toy toy) { Intrinsics.checkNotNullParameter(toy, "toy");}// $FF: synthetic methodpublic static void play$default(Toy var0, int var1, Object var2) { if ((var1 & 1) != 0) { var0 = SqueakyToy; } play(var0);}public static final void startPlaying() { play(Stick); play$default((Toy)null, 1, (Object)null);}
...
// decompiled Java code
public static final void play(@NotNull Toy toy) {
Intrinsics.checkNotNullParameter(toy, "toy");
// $FF: synthetic method
public static void play$default(Toy var0, int var1, Object var2) {
if ((var1 & 1) != 0) {
var0 = SqueakyToy;
play(var0);
public static final void startPlaying() {
play(Stick);
play$default((Toy)null, 1, (Object)null);
コンパイラが 2 つの関数を生成していることがわかります。
play
Toy
play$default
int
Object
null
play$default の int パラメータの値は、デフォルト引数が渡された引数の数と、そのインデックスに基づいて計算されます。Kotlin コンパイラは、どのパラメータを使って play 関数を呼び出すかを、このパラメータの値に基づいて判断します。
この例の play() の呼び出しでは、インデックス 0 の引数がデフォルト引数を使っています。そのため、int var1 = 2⁰ を使って play$default を呼び出します。
play()
int var1 = 2⁰
これで、play$default の実装は、var0 の値をデフォルト値で置き換えなければならないことを認識できます。
var0
この int パラメータの動作を確認するため、もう少し複雑な例を見てみましょう。play 関数を拡張し、この関数を呼び出す際に doggo と toy をデフォルト引数として使うようにします。
doggo
toy
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */ fun play(doggo: Doggo = goodDoggo, doggo2: Doggo = veryGoodDoggo, toy: Toy = SqueakyToy) {...}fun startPlaying() { play2(doggo2 = myDoggo)}
fun play(doggo: Doggo = goodDoggo, doggo2: Doggo = veryGoodDoggo, toy: Toy = SqueakyToy) {...}
play2(doggo2 = myDoggo)
逆コンパイルしたコードはどうなったでしょうか。
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */ public static final void play(@NotNull Doggo doggo, @NotNull Doggo doggo2, @NotNull Toy toy) {... }// $FF: synthetic methodpublic static void play$default(Doggo var0, Doggo var1, Toy var2, int var3, Object var4) { if ((var3 & 1) != 0) { var0 = goodDoggo; } if ((var3 & 2) != 0) { var1 = veryGoodDoggo; } if ((var3 & 4) != 0) { var2 = SqueakyToy; } play(var0, var1, var2);}public static final void startPlaying() { play2$default((Doggo)null, myDoggo, (Toy)null, 5, (Object)null); }
public static final void play(@NotNull Doggo doggo, @NotNull Doggo doggo2, @NotNull Toy toy) {
public static void play$default(Doggo var0, Doggo var1, Toy var2, int var3, Object var4) {
if ((var3 & 1) != 0) {
var0 = goodDoggo;
if ((var3 & 2) != 0) {
var1 = veryGoodDoggo;
if ((var3 & 4) != 0) {
var2 = SqueakyToy;
play(var0, var1, var2);
play2$default((Doggo)null, myDoggo, (Toy)null, 5, (Object)null);
int パラメータが 5 になりました。この計算の仕組みは次のようになります。0 番目と 2 番目のパラメータでデフォルト引数が使われているので、var3 = 2⁰ + 2² = 5 となります。パラメータは、ビット単位の & 演算を使って次のように評価されます。
var3 = 2⁰ + 2² = 5
var3 & 1 != 0
true
var0 = goodDoggo
var3 & 2 != 0
false
var1
var3 & 4 != 0
var2 = SqueakyToy
コンパイラは、var3 に適用されたビットマスクから、どのパラメータをデフォルト値と置き換えるかを計算できます。
var3
上の例で、Object パラメータの値が常に null になっていたことに気づいたかもしれません。実際に、play$default 関数ではこの値が使われることはありません。このパラメータは、オーバーライドする関数でデフォルト値をサポートするために使用されています。
デフォルト引数がある関数をオーバーライドすると、何が起きるでしょうか。
先ほどの例を次のように変更してみましょう。
Doggo
Open
PlayfulDoggo
PlayfulDoggo.play にデフォルト値を設定しようとしても、次のように表示され、許可されません。An overriding function is not allowed to specify default values for its parameters(オーバーライド関数では、パラメータへのデフォルト値の指定は許可されていません)
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */open class Doggo( val name: String, val rating: Int = 11) { open fun play(toy: Toy = SqueakyToy) {...}}class PlayfulDoggo(val playfulness: Int, name: String, rating: Int) : Doggo(name, rating) { // error: An overriding function is not allowed to specify default values for its parameters override fun play(toy: Toy = Stick) { }}
open class Doggo(
) {
open fun play(toy: Toy = SqueakyToy) {...}
class PlayfulDoggo(val playfulness: Int, name: String, rating: Int) : Doggo(name, rating) {
// error: An overriding function is not allowed to specify default values for its parameters
override fun play(toy: Toy = Stick) { }
override を削除して逆コンパイルしたコードを確認すると、PlayfulDoggo.play() は次のようになっています。
override
PlayfulDoggo.play()
public void play(@NotNull Toy toy) {... }// $FF: synthetic methodpublic static void play$default(Doggo var0, Toy var1, int var2, Object var3) { if (var3 != null) { throw new UnsupportedOperationException("Super calls with default arguments not supported in this target, function: play"); } else { if ((var2 & 1) != 0) { var1 = DoggoKt.getSqueakyToy(); } var0.play(var1); }}
public void play(@NotNull Toy toy) {... }
public static void play$default(Doggo var0, Toy var1, int var2, Object var3) {
if (var3 != null) {
throw new UnsupportedOperationException("Super calls with default arguments not supported in this target, function: play");
} else {
if ((var2 & 1) != 0) {
var1 = DoggoKt.getSqueakyToy();
var0.play(var1);
これは、デフォルト引数を使った super の呼び出しが将来的にサポートされるという意味なのでしょうか。この点については、成り行きを見守るしかありません。
コンストラクタでは、逆コンパイルした Java コードに 1 つだけ違う点があります。以下をご覧ください。
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */ // kotlin declarationclass Doggo( val name: String, val rating: Int = 11)// decompiled Java codepublic final class Doggo { ... public Doggo(@NotNull String name, int rating) { Intrinsics.checkNotNullParameter(name, "name"); super(); this.name = name; this.rating = rating; } // $FF: synthetic method public Doggo(String var1, int var2, int var3, DefaultConstructorMarker var4) { if ((var3 & 2) != 0) { var2 = 11; } this(var1, var2); }
// kotlin declaration
public final class Doggo {
public Doggo(@NotNull String name, int rating) {
Intrinsics.checkNotNullParameter(name, "name");
super();
this.name = name;
this.rating = rating;
public Doggo(String var1, int var2, int var3, DefaultConstructorMarker var4) {
var2 = 11;
this(var1, var2);
コンストラクタでも合成メソッドが作成されていますが、関数で使われていた Object ではなく、DefaultConstructorMarker が null で呼び出されています。
DefaultConstructorMarker
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */// kotlinval goodDoggo = Doggo("Tofu")// decompiled Java codeDoggo goodDoggo = new Doggo("Tofu", 0, 2, (DefaultConstructorMarker)null);
val goodDoggo = Doggo("Tofu")
Doggo goodDoggo = new Doggo("Tofu", 0, 2, (DefaultConstructorMarker)null);
デフォルト引数があるセカンダリ コンストラクタでも、プライマリ コンストラクタと同じように DefaultConstructorMarker を使った別の合成メソッドが生成されます。
/* Copyright 2020 Google LLC. SPDX-License-Identifier: Apache-2.0 */// kotlinclass Doggo( val name: String, val rating: Int = 11) { constructor(name: String, rating: Int, lazy: Boolean = true) }// decompiled Java codepublic final class Doggo { ... public Doggo(@NotNull String name, int rating) { ... } // $FF: synthetic method public Doggo(String var1, int var2, int var3, DefaultConstructorMarker var4) { if ((var3 & 2) != 0) { var2 = 11; } this(var1, var2); } public Doggo(@NotNull String name, int rating, boolean lazy) { ... } // $FF: synthetic method public Doggo(String var1, int var2, boolean var3, int var4, DefaultConstructorMarker var5) { if ((var4 & 4) != 0) { var3 = true; } this(var1, var2, var3); }}
constructor(name: String, rating: Int, lazy: Boolean = true)
public Doggo(@NotNull String name, int rating, boolean lazy) {
public Doggo(String var1, int var2, boolean var3, int var4, DefaultConstructorMarker var5) {
if ((var4 & 4) != 0) {
var3 = true;
this(var1, var2, var3);
シンプルで美しいデフォルト引数を利用すると、パラメータにデフォルト値を設定できるようになるので、メソッドをオーバーロードする際に書かなければならないボイラープレート コードの量が減ります。多くの Kotlin キーワードに言えることですが、生成されるコードを調べてみれば、そこでどのような魔法が使われているかを理解できます。詳しくは、他の Kotlin Vocabulary の投稿を確認してみてください。
仕事を完了する方法の 1 つは、その仕事を他者に委譲することです。皆さんの仕事を友だちに委譲することを言っているわけではありません。今回のテーマは、あるオブジェクトから別のオブジェクトに委譲することです。
ソフトウェアの世界では、委譲という考え方は新しいものではありません。委譲はデザイン パターンの 1 つで、あるオブジェクトが デリゲートと呼ばれるヘルパー オブジェクトに委譲することでリクエストを処理することを指します。デリゲートの役割は、元のオブジェクトに代わってリクエストを処理し、その結果を元のオブジェクトが利用できるようにすることです。
Kotlin はクラス委譲とプロパティ委譲をサポートしているので、委譲を簡単に扱えます。さらに、いくつかの独自の組み込みデリゲートも提供しています。
最後に削除された項目を復元できる ArrayList を使うとしましょう。基本的に、必要なのは同じ ArrayList の機能だけですが、最後に削除された項目への参照が必要です。
ArrayList
これを実現する方法の 1 つは、ArrayList クラスを拡張することです。この新しいクラスは、MutableList インターフェースの実装ではなく ArrayList の具象クラスを拡張したものなので、ArrayList の具象クラスの実装と強く結合されることになります。
MutableList
MutableList の実装で remove() 関数をオーバーライドし、削除した項目の参照を保持できるようにしたうえで、その他の空の実装を他のオブジェクトに委譲したいと思ったことはありませんか?Kotlin では、これを実現する方法が提供されています。具体的には、内部 ArrayList インスタンスに作業の大半を委譲し、その動作をカスタマイズできます。これを行うため、Kotlin には新しいキーワード by が導入されています。
remove()
by
では、クラス委譲の仕組みを確認してみましょう。by キーワードを使うと、Kotlin は innerList インスタンスをデリゲートとして使用するコードを自動的に生成します。
innerList
<!-- Copyright 2019 Google LLC.SPDX-License-Identifier: Apache-2.0 -->class ListWithTrash <T>( private val innerList: MutableList<T> = ArrayList<T>()) : MutableCollection<T> by innerList { var deletedItem : T? = null override fun remove(element: T): Boolean { deletedItem = element return innerList.remove(element) } fun recover(): T? { return deletedItem }}
class ListWithTrash <T>(
private val innerList: MutableList<T> = ArrayList<T>()
) : MutableCollection<T> by innerList {
var deletedItem : T? = null
override fun remove(element: T): Boolean {
deletedItem = element
return innerList.remove(element)
fun recover(): T? {
return deletedItem
by キーワードは、MutableList インターフェースの機能を innerList という名前の内部 ArrayList インスタンスに委譲するよう Kotlin に伝えます。内部 ArrayList オブジェクトに直接橋渡しするメソッドが提供されるので、ListWithTrash は MutableList インターフェースのすべての機能をサポートします。さらに、独自の動作を追加することもできるようになります。
ListWithTrash
動作の仕組みを確認してみましょう。ListWithTrash のバイトコードを逆コンパイルした Java コードを見ると、Kotlin コンパイラが実際にラッパー関数を作成していることを確認できます。このラッパー関数が、内部 ArrayList オブジェクトの対応する関数を呼び出していることもわかります。
public final class ListWithTrash implements Collection, KMutableCollection { @Nullable private Object deletedItem; private final List innerList; @Nullable public final Object getDeletedItem() { return this.deletedItem; } public final void setDeletedItem(@Nullable Object var1) { this.deletedItem = var1; } public boolean remove(Object element) { this.deletedItem = element; return this.innerList.remove(element); } @Nullable public final Object recover() { return this.deletedItem; } public ListWithTrash() { this((List)null, 1, (DefaultConstructorMarker)null); } public int getSize() { return this.innerList.size(); } // $FF: bridge method public final int size() { return this.getSize(); } //...and so on}
private Object deletedItem;
private final List innerList;
public final Object getDeletedItem() {
return this.deletedItem;
public final void setDeletedItem(@Nullable Object var1) {
this.deletedItem = var1;
public boolean remove(Object element) {
this.deletedItem = element;
return this.innerList.remove(element);
public final Object recover() {
public ListWithTrash() {
this((List)null, 1, (DefaultConstructorMarker)null);
public int getSize() {
return this.innerList.size();
// $FF: bridge method
public final int size() {
return this.getSize();
//...and so on
注: 生成されたコードで、Kotlin コンパイラは Decorator パターンという別のデザイン パターンを使ってクラス委譲をサポートしています。Decorator パターンでは、デコレータ クラスがデコレートされるクラスと同じインターフェースを共有します。デコレータ クラスは、ターゲット クラスの内部参照を保持し、そのインターフェースで提供されるすべてのパブリック メソッドをラップ(デコレート)します。
委譲は、特定のクラスを継承できない場合に特に便利です。クラス委譲を使うと、クラスが他のクラスの階層に含まれることはなくなります。その代わり、同じインターフェースを共有し、元の型の内部オブジェクトをデコレートします。つまり、パブリック API を維持したまま、実装を簡単に入れ替えることができます。
by キーワードを使うと、クラス委譲だけでなく、プロパティを委譲することもできます。プロパティ委譲では、デリゲートはプロパティの get 関数と set 関数の呼び出しを担当します。他のオブジェクトで getter/setter ロジックを再利用しなければならない場合、対応するフィールドだけでなく機能を簡単に拡張することができるので、この機能が非常に便利です。
get
set
次のような定義の Person クラスがあったとしましょう。
Person
class Person(var name:String, var lastname:String)
このクラスの name プロパティには、いくつかのフォーマット要件があります。name を設定するとき、先頭の文字が大文字、他の文字が小文字になるようにします。さらに、 name を更新する場合、updateCount プロパティを自動的にインクリメントします。
name
updateCount
この機能は、次のように実装してもいいかもしれません 。
<!-- Copyright 2019 Google LLC.SPDX-License-Identifier: Apache-2.0 -->class Person(name: String, var lastname: String) { var name: String = name set(value) { field = value.toLowerCase().capitalize() updateCount++ } var updateCount = 0}
class Person(name: String, var lastname: String) {
var name: String = name
set(value) {
field = value.toLowerCase().capitalize()
updateCount++
var updateCount = 0
これは動作しますが、要件が変わって lastname が変更されたときも updateCount をインクリメントすることになるとどうでしょうか。ロジックをコピーして貼り付け、カスタムの setter を書いてもいいかもしれませんが、両方のプロパティにまったく同じ setter を書いていることに気づくでしょう。
lastname
<!-- Copyright 2019 Google LLC.SPDX-License-Identifier: Apache-2.0 -->class Person(name: String, lastname: String) { var name: String = name set(value) { field = value.toLowerCase().capitalize() updateCount++ } var lastname: String = lastname set(value) { field = value.toLowerCase().capitalize() updateCount++ } var updateCount = 0}
class Person(name: String, lastname: String) {
var lastname: String = lastname
どちらの setter メソッドもほぼ同じということは、どちらかは不要ということです。プロパティ委譲を使うと、getter と setter をプロパティに委譲してコードを再利用できます。
クラス委譲と同じように、by を使ってプロパティを委譲します。すると、プロパティ構文を使ったときに、Kotlin はデリゲートを使うコードを生成します。
<!-- Copyright 2019 Google LLC.SPDX-License-Identifier: Apache-2.0 -->class Person(name: String, lastname: String) { var name: String by FormatDelegate() var lastname: String by FormatDelegate() var updateCount = 0}
var name: String by FormatDelegate()
var lastname: String by FormatDelegate()
この変更を行うと、name プロパティと lastname プロパティが FormatDelegate クラスに委譲されます。FormatDelegate のコードを確認してみましょう。デリゲート クラスは、getter だけを委譲する場合は ReadProperty<Any?, String> を、getter と setter の両方を委譲する場合は ReadWriteProperty<Any?, String> を実装する必要があります。この例の FormatDelegate は、setter が呼び出された場合にフォーマット処理を行うので、ReadWriteProperty<Any?, String> を実装しなければなりません。
FormatDelegate
ReadProperty<Any?, String>
ReadWriteProperty<Any?, String>
<!-- Copyright 2019 Google LLC.SPDX-License-Identifier: Apache-2.0 -->class FormatDelegate : ReadWriteProperty<Any?, String> { private var formattedString: String = "" override fun getValue( thisRef: Any?, property: KProperty<*> ): String { return formattedString } override fun setValue( thisRef: Any?, property: KProperty<*>, value: String ) { formattedString = value.toLowerCase().capitalize() }}
class FormatDelegate : ReadWriteProperty<Any?, String> {
private var formattedString: String = ""
override fun getValue(
thisRef: Any?,
property: KProperty<*>
): String {
return formattedString
override fun setValue(
property: KProperty<*>,
value: String
formattedString = value.toLowerCase().capitalize()
getter 関数と setter 関数に 2 つの追加パラメータがあることに気づいた方もいらっしゃるでしょう。最初のパラメータ thisRef は、プロパティを含むオブジェクトを表します。これを使うと、オブジェクト自体にアクセスし、他のプロパティを確認したり、他のクラス関数を呼び出したりできます。2 つ目のパラメータは KProperty<*> です。これは、委譲されたプロパティについてのメタデータにアクセスするために使うことができます。
thisRef
KProperty<*>
先ほどの要件を思い出してみてください。thisRef を使って updateCount プロパティにアクセスし、インクリメントしてみましょう。
<!-- Copyright 2019 Google LLC.SPDX-License-Identifier: Apache-2.0 -->override fun setValue( thisRef: Any?, property: KProperty<*>, value: String) { if (thisRef is Person) { thisRef.updateCount++ } formattedString = value.toLowerCase().capitalize()}
if (thisRef is Person) {
thisRef.updateCount++
この仕組みを理解するため、逆コンパイルした Java コードを見てみます。Kotlin コンパイラは、name プロパティと lastname プロパティについての FormatDelegate オブジェクトへのプライベートな参照を保持するためのコードと、追加したロジックを含む getter/setter の両方を生成します。
さらに、委譲されるプロパティを保持する KProperty[] も作成しています。name プロパティに対して生成された getter と setter を見てみると、インスタンスはインデックス 0 に保存されています。一方、lastname プロパティはインデックス 1 に保存されています。
KProperty[]
public final class Person { // $FF: synthetic field static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Person.class), "name", "getName()Ljava/lang/String;")), (KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Person.class), "lastname", "getlastname()Ljava/lang/String;"))}; @NotNull private final FormatDelegate name$delegate; @NotNull private final FormatDelegate lastname$delegate; private int updateCount; @NotNull public final String getName() { return this.name$delegate.getValue(this, $$delegatedProperties[0]); } public final void setName(@NotNull String var1) { Intrinsics.checkParameterIsNotNull(var1, "<set-?>"); this.name$delegate.setValue(this, $$delegatedProperties[0], var1); } //...}
// $FF: synthetic field
static final KProperty[] $$delegatedProperties = new KProperty[]{(KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Person.class), "name", "getName()Ljava/lang/String;")), (KProperty)Reflection.mutableProperty1(new MutablePropertyReference1Impl(Reflection.getOrCreateKotlinClass(Person.class), "lastname", "getlastname()Ljava/lang/String;"))};
@NotNull
private final FormatDelegate name$delegate;
private final FormatDelegate lastname$delegate;
private int updateCount;
public final String getName() {
return this.name$delegate.getValue(this, $$delegatedProperties[0]);
public final void setName(@NotNull String var1) {
Intrinsics.checkParameterIsNotNull(var1, "<set-?>");
this.name$delegate.setValue(this, $$delegatedProperties[0], var1);
//...
この仕組みによって、通常のプロパティ構文を使って任意の呼び出し元が委譲されるプロパティにアクセスできるようになっています。
person.lastname = “Smith” //
println(“Update count is $person.count”)
Kotlin は単に委譲をサポートしているだけではありません。Kotlin 標準ライブラリで組み込みの委譲も提供していますが、詳しくは別の記事で説明したいと思います。
委譲は他のオブジェクトにタスクを委譲する際に役立ち、コードの再利用性を高めます。Kotlin コンパイラは、委譲をシームレスに使えるようにコードを作成します。Kotlin は、by キーワードを使ったシンプルな構文でプロパティやクラスの委譲を行います。Kotlin コンパイラは、パブリック API を一切変更せず、委譲をサポートするために必要なすべてのコードを内部的に生成します。簡単に言えば、Kotlin は委譲に必要なボイラープレート コードをすべて生成して維持してくれます。つまり、委譲を Kotlin に委譲することができるのです。
Reviewed by Yuichi Araki - Developer Relations Team
この記事は Scott Swarthout による Android Developers Blog の記事 "Android studio 4.1" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。
2020 年 10 月 12 日(日本時間 10 月 13 日)、Android Studio 4.1 の安定版がリリースされました。編集、デバッグ、最適化の一般的なユースケースに対応する一連の機能が追加されています。今回のリリースの主なテーマは、Android Jetpack ライブラリを使う際の生産性向上でした。Android Jetpack とは、デベロッパーがベスト プラクティスに従って速くコードを書けるようにするための Android ライブラリ スイートです。皆さんからのフィードバックに基づき、コード編集操作にたくさんの改善を行ったほか、よく使われる Android ライブラリを IDE に統合しています。
Android Studio 4.1 で注目すべき機能には、アプリのデータベースを照会できる新しい Database Inspector、依存性注入に Dagger または Hilt を使うプロジェクトのナビゲーションのサポート、オンデバイス機械学習のサポート向上(Android プロジェクトでの TensorFlow Lite モデルのサポートを含む)などがあります。さらに、変更の適用を更新してデプロイを高速化しました。皆さんからのフィードバックに基づき、ゲーム デベロッパーに役立つ変更も行いました。新しいネイティブ メモリ プロファイラとスタンドアロン プロファイリング ツールを導入しています。
私たちは、Android Studio の品質を向上するため、バグやパフォーマンスの問題に懸命に対応してきました。多くのデベロッパーの皆さんから、パフォーマンスと信頼性の向上に主眼を置いていることを評価する声が届いています。今回のリリース サイクルでは、2,370 個のバグを修正し、公開されていた 275 個の問題をクローズしたことをご報告します。デベロッパーの皆さんの生産性にとって鍵となるのは、高い品質です。私たちはこれからも高い品質を維持することをお約束します。
プレビュー リリースで早くからフィードバックを寄せてくださった皆さん、ありがとうございました。皆さんからのフィードバックは Android Studio 4.1 の開発にあたって反復作業や機能改善に役立ちました。最新の安定版リリースを使う準備が整い、新たな生産性機能を使ってみたい方は、Android Studio 4.1 をこちらからダウンロードしてください。
続いて、主なデベロッパー フローごとに分類された、Android Studio 4.1 のすべての新機能をご紹介します。
新しいプロジェクトを作成する際のダイアログに表示される Android Studio のテンプレートが、マテリアル デザイン コンポーネント(MDC)を使ったものになりました。デフォルトで、テーマとスタイルの最新ガイドに準拠しています。この変更により、推奨のマテリアル スタイル パターンや、ダークテーマなどの最新の UI 機能を簡単に使えるようになります。
アップデートには、以下の内容が含まれています。
新しい Database Inspector では、アプリのデータベースを簡単に調査、照会、変更できるようにしたいと考えました。この機能を使ってみるには、API レベル 26 以降を実行しているデバイスにアプリをデプロイし、メニューバーから [View] > [Tool Windows] > [Database Inspector] を選択します。アプリで Jetpack Room ライブラリを使っている場合でも、Android プラットフォーム バージョンの SQLite を直接使っている場合でも、実行中のアプリのデータベースやテーブルを簡単に調査したり、カスタムクエリを実行したりできます。
Android Studio は、アプリを調査しているときもライブ接続を維持しているので、Database Inspector を使って値を変更し、実行中のアプリで変更内容を確認することもできます。Room 永続化ライブラリを使っている場合は、コードエディタの各クエリの隣にも実行ボタンが表示されるので、@Query アノテーションで定義したクエリをすばやく実行できます。詳細はこちらをご覧ください。
Android Studio の中で直接 Android Emulator を実行できるようになりました。この機能を使うと、画面スペースを節約したり、ホットキーでエミュレータとエディタのウィンドウ間をすばやく移動したり、1 つのアプリケーション ウィンドウの中で IDE とエミュレータのワークフローを整理したりできます。なお、スナップショットの管理や、回転やスクリーンショットなどの一般的なエミュレータ操作は Studio から行うことができますが、すべてのオプションにアクセスするには、安定版のエミュレータを実行する必要があります。この機能は、次の操作でオプトインできます。
[File] → [Settings] → [Tools] → [Emulator] → [Launch in Tool Window]
Android デベロッパーは、機械学習を使って革新的で便利な体験を生み出しています。TensorFlow Lite は、モバイル機械学習モデルを記述する際によく使われるライブラリです。私たちは、こういったモデルを Android アプリに簡単にインポートできるようにしたいと考えました。Android Studio は、ビューのバインディングと同じような使いやすいクラスを生成してくれます。そのため、少量の型安全なコードでモデルを実行できます。ML モデル バインディングの現在の実装では、メタデータで拡張されたイメージ分類とスタイル変換のモデルがサポートされています。
インポートしたモデルの詳細やアプリでモデルを使う手順は、プロジェクトで .tflite モデルファイルをダブルクリックし、モデルビューアのページを開くと確認できます。詳細はこちらをご覧ください。
Android エミュレータは、最近追加された 5G 携帯通信のテストに加え、折りたたみ式デバイスもサポートします。Android Emulator 30.0.26 以降では、さまざまなデザインや設定の折りたたみ式デバイスを設定できます。折りたたみ式デバイスを設定すると、エミュレータはヒンジ角度センサーのアップデートと姿勢の変化を報告するようになります。そのため、このフォーム ファクタに対してアプリがどのように応答するかをテストできます。詳しくは、ブログ投稿 Developing for Android 11 with the Android Emulatorをご覧ください。
ビルドが速くなれば、デベロッパーは短時間で簡単にアプリを変更できるようになります。アプリに対する反復作業の生産性を上げるため、Android 11 以降を実行しているデバイス向けに、変更の適用機能を強化しました。
私たちは反復作業にかかる時間の短縮に本格的に取り組み、アプリをインストールすることなく変更をデバイスにデプロイして永続化する方法を開発しました。一度 Android 11 デバイスにデプロイすれば、それ以降、コードの変更の適用 [Apply Code Changes] または変更を適用してアクティビティを再起動 [Apply Changes and Restart Activity] する場合のデプロイが大幅に速くなります。さらに、変更の適用でコードの変更のサポートが強化されています。メソッドを追加した場合でも、コードの変更の適用 [Apply Code Changes] または変更を適用してアクティビティを再起動 [Apply Changes and Restart Activity] のどちらかをクリックすることで、実行中のアプリに変更をデプロイできるようになっています。
Android Gradle プラグイン 4.0 には、AAR の依存関係の Prefab パッケージをインポートする機能が追加されています。この機能については、ネイティブ ライブラリの共有もサポートするように拡張したいと考えていました。AGP バージョン 4.1 を利用すると、Android ライブラリ プロジェクト用の AAR に格納されている外部ネイティブ ビルドからライブラリをエクスポートできます。ネイティブ ライブラリをエクスポートするには、ライブラリ プロジェクトの build.gradle ファイルの android ブロックに以下を追加します。
buildFeatures { prefabPublishing true } prefab { mylibrary { headers "src/main/cpp/mylibrary/include" } myotherlibrary { headers "src/main/cpp/myotherlibrary/include" } }
ネイティブ コードでクラッシュや ANR が発生した場合、システムはスタック トレースを生成します。これは、クラッシュした瞬間までにプログラムがネストして呼び出した一連の関数のスナップショットです。このスナップショットは、ソースの問題を特定して修正する際に役立つ可能性がありますが、マシンのアドレスを人間が読むことができる関数名に戻すため、まずシンボリケーションを行う必要があります。
C++ などのネイティブ コードを使ってアプリやゲームを開発する場合、アプリのバージョンごとにデバッグ シンボル ファイルを Play Console にアップロードできるようになりました。Play Console は、このデバッグ シンボル ファイルを使ってアプリのスタック トレースのシンボリケーションを行い、クラッシュや ANR を解析しやすくします。App Bundle にデバッグ シンボルを含めるには、プロジェクトの build.gradle ファイルに次の行を追加します。
android.buildTypes.release.ndk.debugSymbolLevel = 'SYMBOL_TABLE'
Android Studio 4.1 では、システム トレースを大幅に見直しました。システム トレースは、アプリがシステム リソースをどのくらい使っているかをリアルタイムで確認できる最適化ツールです。今回は、ボックス選択モードでトレースを簡単に選択できるようにし、新しい解析タブを追加し、アプリの UI のレンダリングに関する問題を調査できるように詳しいフレーム レンダリング データを追加しました。詳細はこちら(英語)をご覧ください。
ボックス選択: [Threads] セクションで、マウスをドラッグすると、四角形の領域をボックス選択できるようになりました。右上の [Zoom to Selection] ボタンをクリックする(または M キーボード ショートカットを使う)と、ズームできます。また、隣り合っている似たようなスレッドをドラッグ&ドロップすると、複数のスレッドをまたいで選択し、同時に調査できます。
Summary タブ: [Analysis] パネルに新しく [Summary] タブを追加しました。このタブには、以下の内容が表示されます。
データの表示: [Display] セクションに SurfaceFlinger と VSYNC の新しいタイムラインが追加されました。アプリの UI のレンダリング問題を調査する際に役立ちます。
Android Studio のメイン ウィンドウとは別のウィンドウで Android Studio のプロファイラにアクセスできるようになりました。この機能は、Unity や Visual Studio など、別のツールで構築した Android ゲームを最適化する場合に便利です。
スタンドアロン プロファイラを実行するには、以下の操作を行います。
<studio-installation-folder>\bin
<studio-installation-folder>/Contents/bin
profiler.exe
profiler.sh
Memory Profiler ウィンドウの上部にある [Record native allocations] をクリックすると、記録を開始します。
本資料は、Unity Technologies やその関連会社による提供または提携ではありません。“Unity” は、米国およびその他の場所における Unity Technologies またはその関連会社の商標または登録商標です。
特定の製品情報を探しやすくするために、2020 年 11 月以降、Android と Google Play の情報は Android Developers Japan Blog をメインのブログとして情報掲載を開始します。Android と Google Play の情報をお探しの方は、こちらのブログを今後ご覧ください。
日本で開催される Android と Google Play 関連のイベントやキャンペーンなどの日本のデベロッパー様向けの情報を随時発信していきます。
製品情報、新規機能やポリシーの変更などだけではなく、Android と Google Play をより活用するための事例や分析方法、マーケティングの情報なども順次掲載していきます。
Android Developers Japan Blog をぜひご覧ください。
Posted by Tamao Imura - Developer Marketing Manager, Platforms and Ecosystems