20211130のAndroidに関する記事は4件です。

なぜ、Couchbaseなのか?〜モバイル/エッジコンピューティングプラットフォームとしての観点から

はじめに Couchbaseは、NoSQLドキュメント指向データベースです。この表現は間違ってはいませんが、若干正確性を欠いてもいます。つまり、Couchbaseという名前を持つデータベースには、Couchbase ServerとCouchbase Liteというふたつが存在します。 ここではCouchbaseについて、そのモバイル/エッジコンピューティングプラットフォームとしての側面について解説します。この側面においては、Couchbase ServerとCouchbase Liteの両方が重要な役割を持ちます。 以下、上述の本題に進む前にいくつか基本的な部分を整理します。 NoSQLドキュメント指向データベースについて NoSQLドキュメント指向データベースについては、パブリッククラウドが提供する独自のテクノロジーを除くと、代表的なものとして以下の3つをあげることができます。 MongoDB Apache CouchDB Couchbase Server Couchbase Serverについて Couchbase Serverについて、2021年11月に、以下の書籍を上梓しました。 Couchbase Serverに関しては、上記書籍に日本語で読める最新の情報が整理されています。 オンデマンド版のページの試し読みでは、ふたりの登場人物の会話形式によるプロローグを読むことができます。また、Kindle版のサンプルを入手いただくと、「第1章 Couchbase Serverとは何か」を全てお読みいただくことが可能です。 MongoDBとの違いや、Apache CouchDBとの関係などは、こちらをご参照ください。 モバイル/エッジコンピューティングプラットフォームとしてのCouchbase モバイル(組込)データベース モバイルアプリケーションで利用することのできるデータベースとしては、SQLiteが筆頭に上げられるのではないかと思います。あるいは、iOS開発者であれば、Core Dataを思い出すかもしれません。また、オブジェクト指向モバイルデータベースとして、Realmが知られています。 Couchbase Liteについて Couchbase Liteは、NoSQLドキュメント指向モバイル(組込)データベースです。KotlinやSwiftなどの開発言語を利用して、AndroidやiOSアプリケーションで利用することができるだけでなく、JVM環境でも動かすことができます。また、3.0.0ベータでは、C/C++環境でも利用することができるようになりました。これにより、エッジデバイスでの利用の幅が格段に広がっています。 Couchbase Mobileとは何か? ここで、新しい言葉が登場します。「Couchbase Mobile」とは、単体のデータベースないし何らかのテクノロジーを指す言葉ではなく、Couchbase LiteとSync Gatewayを含むカテゴリーとして用いられています。 Sync Gatewayとは、Couchbase LiteとCouchbase Serverとのデータ同期を行う技術です(具体的には、Couchbase Serverともに用いられる、Couchbase LiteとCouchbase Serverとの間でデータ同期を仲介するサーバーソフトウェアです)。 モバイルデータベースの重要性 スマートフォン上で利用されるモバイルアプリケーションの多くは、サーバーとデータを通信しながら稼働します。モバイルデータベースといえども、その中に格納されるデータは、モバイル端末で作成・保存・利用されれば十分なものばかりとは限らず、サーバーとの間で、データのやりとり〜サーバーからのデータの取得、サーバーへのデータの登録・更新〜が行われるデータを含むことがあります。サーバー(データベース)との間で、データ通信が行われる場合に、あえてローカルデータベースを持つ理由・利点としては、以下があります。 ネットワーク依存の分離 通信ロジックの分離 それぞれについて、以下解説します。 ネットワーク依存の分離 オフラインファーストアプリケーション ローカルデータベースを持つことによって、ネットワーク通信が行えない環境でもデータを利用しつつ、ネットワークが回復した場合にデータの同期を行う、という設計を指して「オフラインファーストアプリケーション」という言葉が使われます。 航空会社の従業員用のアプリケーションというユースケースもありますが、「ネットワーク通信が行えない環境」と言われても、非常に特殊なケースという印象を持つ方も多いかもしれません。その意味では、「オフラインファーストアプリケーション」は、「ユーザーエクスペリエンス向上」の中の一形態と考えた方が良いかもしれません。 ユーザーエクスペリエンス向上 モバイルデータベースを持たないアプリケーションが、サーバーからデータを取得して表示する場合、ユーザーがアプリケーションを立ち上げてから、データが画面が表示されるまでの期間は、ユーザー体験として、空白期間(単なる待ち時間)になります。これはウェブアプリケーションに典型的に見られる状況ということがいえます。 モバイルデータベースの存在は、「ネットワーク依存」をアプリケーション操作から分離するために役立たせることができます。当然ながら、モバイルデータベースを導入するだけで自動的に解決するようなものではなく、開発者が適切にユーザー体験をデザインする必要がありますが、ローカルデータベースとアプリケーションの関係をこうした観点から整理することができます。 通信ロジックの分離 ここまで、モバイルデータベースの意義について、ユーザーの利便性の観点から見てきました。「オフラインファーストアプリケーション」にしろ「ユーザーエクスペリエンス向上」にしろ、必要なのは、データの扱いをデザインすることであり、モバイルデータベースの導入は、その手段でしかないといえます。 そもそもデータベースは、アプリケーションアーキテクチャーにおける、データとロジックの分離を可能にするために存在しています。つまり、汎用的な処理(データマネジメント)を信頼性のある既存のテクノロジーに任せることで、開発者は利益を得ています。ローカルデータベースとリモートデータベースの同期を、データベースプロダクト(データプラットフォーム)に任せることにより、開発者はさらに、通信ロジックについても、信頼性のある既存のテクノロジーに任せることが可能になります。 これには、次のような副産物が伴います。 プレビルトデータベース アプリケーションが利用するデータを、アプリケーションインストール時に、データベースとして同梱することにより、初期データへのアクセスと、それ以外のデータへのアクセス手法を区別する必要がなくなります。 マルチプラットフォーム/チャネル 同じサービスをモバイルアプリケーションとウェブアプリケーションの両方の携帯で提供することは珍しくありません。このような場合に、モバイルアプリケーションではローカルデータベースを利用し、ウェブアプリケーションでは、ローカルデータベースと同期されたデータベースを利用することが考えられます。結果として、全体として、統一されたテクノロジーを採用することができます。 最後に 本稿では、データプラットフォームとして、Couchbaseを採用する意義について、モバイル/エッジコンピューティングプラットフォームとしての観点から概念的に整理しました。 テクノロジーを理解し、選定する際には、全体像、そして個々の要素について、具体的に把握することが欠かせません。 Couchbase Mobileについての記事を以下の投稿で整理していますので、ご関心に応じて、参照してみてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

withLockで楽しい排他制御ライフを!

初めて遭遇し焦ったものの、使い方自体はさほど難しくないwithLockについてまとめました。 Javaのパッケージが関連しているためJavaで調べると欲しい情報が見つけやすいこと、早めに気づきたかったです。 仕様 与えられた処理をロック下で実行するための関数です。 引数にロック下で行いたい処理 actionをとり、actionの戻り値を返します。 action実行前に自動でロックされた後actionを実行し、その後自動でロックが解除されます。 inline fun <T> Lock.withLock(action: () -> T): T ロック下とは、排他制御下であるという意味です。 排他制御とは、同じ資源を同時に利用すると不都合が起きる場合に、同時アクセスを禁止して整合性を保つ仕組みを指します。 例えば、共同で使用している銀行口座から同時に2人がお金をおろしてしまうと残高が合わなくなる、そのようなことを防ぐために使われている仕組みです。 withLockは、Kotlinの標準ライブラリkotlin-stdlibに含まれる並列プログラミングのためのユーティリティ関数パッケージkotlin.concurrentにて提供されています。 ちなみに、並列プログラミングとは同時に複数の処理を実行することで、高いパフォーマンスを実現するためのプログラミングの手法です。 Androidでよく目にする「マルチスレッド」も並列プログラミングの考え方の1つであり、実装で利用されるthread{ }もこのパッケージで提供されています。 Kotlin: withLock 使用方法 withLockを使用する際には、withLockを使えるように環境を整え、ブロック内に行いたい処理を記述します。 具体的には、以下の手順で行います。 ①build.gradleに依存関係を追加 ②kotlin.concurrent.withLockをインポート ③java.util.concurrent.locks.ReentrantLock()を呼び出す ④withLockを記述し、ブロック内にロック下に行いたいアクションを記述 build.gradle dependencies { // ①kotlin-stdlibライブラリを追加 implementation "org.jetbrains.kotlin:kotlin-stdlib:1.5.20" } import kotlin.concurrent.withLock // ②withLockをインポート private val lock = java.util.concurrent.locks.ReentrantLock() //③ 毎回呼ぶには長いためlockという定数に代入 class Main(){ fun main(){ lock.withLock { // ④withLockを実装し、行いたい処理を記述 } } } ちなみにReentrantLock()を使う別の方法としてlock()とunlock()を使う方法もありますが、こちらはどちらかを記述し忘れてしまうとエラーが発生します。 その点withLockでは、処理の実行前/実行後に自動でロック/ロック解除される仕様のため、抜け漏れの心配をせず安心して使うことができます。 実装例 使い方は難しくありませんが、初心者には具体的にどのような場合に使えば良いが分かりにくいのがwithLockです。 今回は以下の2種類の場合について、withLockを使う場合/使わない場合で結果がどのように変換するかを確認します。 引き算を行う例 順番通りに出力する例 1. 引き算を行う例 1つ目は、排他制御の説明で紹介した、銀行残高からの引き落としとその結果を出力する処理です。 この処理では元々1万円あった残高をAさん〜Eさんの5人がそれぞれ300〜1500円ずつ使った場合に、残高が正しく計算されていることを確認します。 withLockを使わない場合 private fun main() { val name = listOf<String>("A", "B", "C", "D", "E") // 名前のリスト for(i in 1..5){ // for文で5本のスレッドを作成 thread { withdraw(name[i - 1], 300 * i) // Aさん〜Eさんの使用金額とその時の残高を計算 Thread.sleep(50) // 出力待ちなどで50ミリ秒の間このスレッドが一時中断することを再現 } } } private var balance: Int = 10000 private fun withdraw(name: String, used: Int){ balance -= used // 残高から使用金額を引く Log.d("結果", "${name}さんが${used}円利用。残:${balance}円") } // 結果 Cさんが900円利用。残:9100円 Eさんが1500円利用。残:6400円 Dさんが1200円利用。残:6400円 Bさんが600円利用。残:5500円 Aさんが300円利用。残:6100円 withLockを使う場合 private fun main() { val lock = java.util.concurrent.locks.ReentrantLock() val name = listOf<String>("A", "B", "C", "D", "E") for(i in 1..5){ thread { lock.withLock { // withLockで排他制御をかける withdraw(name[i - 1], 300 * i) Thread.sleep(50) } } } } // withdrawメソッドはwithLockを使わない場合と同じため省略 // 結果 Aさんが300円利用。残:9700円 Bさんが600円利用。残:9100円 Cさんが900円利用。残:8200円 Dさんが1200円利用。残:7000円 Eさんが1500円利用。残:5500円 withLockを使わない場合は計算結果が毎回異なり、残高の計算が意図しない結果になりました。 一方で、withLockを使う場合は排他制御が行われ、利用額と毎回の残高が正確に計算されていることが分かります。 2. 順番通りに出力する例 2つ目の例は、1-1, 1-2, … , 5-2を出力する処理です。 一時中断される処理がある場合でも、意図した順序で実行できるかを確認します。 withLockを使わない場合 private fun main() { for(i in 1..5){ thread { print("${i}-1 ") Thread.sleep(50) // 50ミリ秒の間このスレッドを一時中断する print("${i}-2\n") } } } // 結果 I/System.out: 2-1 1-1 4-1 5-1 3-1 2-2 I/System.out: 1-2 I/System.out: 4-2 I/System.out: 5-2 I/System.out: 3-2 withLockを使う場合 private fun main() { val lock = java.util.concurrent.locks.ReentrantLock() for(i in 1..5){ thread { lock.withLock { // withLockで排他制御をかける print("${i}-1 ") Thread.sleep(50) // 50ミリ秒の間このスレッドを一時中断する print("${i}-2\n") } } } } // 結果 I/System.out: 1-1 1-2 I/System.out: 2-1 2-2 I/System.out: 3-1 3-2 I/System.out: 4-1 4-2 I/System.out: 5-1 5-2 withLockを使わない場合は、一時中断される直前までの処理が次々と実行されています。 一方でwithLockを使う場合は、排他制御が行われるためスレッドが実行されている間の割り込みができず、スレッド内の処理がひとまとまりで実行されていることが分かります。 以上より、withLockを使うことで、 ①他のスレッド情報の影響を受けず ②割り込みの心配をせずにひとまとまりで 処理を実行できることが分かりました。 withLockを使いこなして、①や②を満たす楽しい排他制御ライフを送りましょう! 参考 ・Kotlin: withLock ・KotlinのConcurrencyライブラリを使う - 算譜王におれはなる!!!! ・ReentrantLockで順序性を保証するロック ・ラムダ式の基本 (lambda expression) | まくまくKotlinノート ・Javaのsleep機能 Tread sleepメソッドとは?基本情報をご紹介Javaコラム
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Navigation SDK v2 for Androidを試してみる (いじってみる編)

はじめに 前回サンプルの中身について詳しく見ました。今回はこれを踏まえてナビゲーションアプリとしての使い勝手を向上させたいと思います。 注意事項 この記事では2021/11/25現在で最新のv2.0.2を使用します。 Pricingにご注意ください。特にMAUは100まで無料ですが、ここに記載されているようにアプリケーションの削除・インストールでカウントアップします。デバッグ中に何度も削除・インストールを繰り返すと無料枠を超える可能性があります。 変更点 以下の点について変更を行います。 GPS 日本語(多言語)サポート カメラの変更 バナー色の変更 音声案内の編集 Map Styleの変更 通過済み経路の削除 GPS 前回見たように、サンプルではReplayLocationEngineが使用されています。デバッグには便利ですが、実用上不便なので(というか使えないので) デフォルトのLocationEngineを使用するように変更します。 パーミッションの設定 マニフェストに以下のandroid.permission.ACCESS_FINE_LOCATIONを設定してください。 app/src/main/AndroidManifest.xml 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="com.example.navigationsdksample"> 4 5 <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> 6 7 <application ... LocationEngineの変更 実はNavigationOptionsで何も指定しなければデフォルトのLocationEngineが使用されるので、前回のNavigation Coreの初期化の以下の部分をコメントアウトすればほとんど完了です。 TurnByTurnExperienceActivity.kt 430 // initialize Mapbox Navigation 431 mapboxNavigation = if (MapboxNavigationProvider.isCreated()) { 432 MapboxNavigationProvider.retrieve() 433 } else { 434 MapboxNavigationProvider.create( 435 NavigationOptions.Builder(this.applicationContext) 436 .accessToken(getString(R.string.mapbox_access_token)) 437 // comment out the location engine setting block to disable simulation 438 .locationEngine(replayLocationEngine) // ここ 439 .build() 440 ) 441 } あとは不要になったコード(mapboxReplayer, replayLocationEngine, replayProgressObserver等)をガシガシ消します。 パーミッションの取得 手動でパーミッションを与えても動きますが、ダイアログを表示たほうがユーザーフレンドリーでしょう。好きな方法で実装してOKですが、私はNavigation SDKの中にあるLocationPermissionsHelperを拝借してきました。 GitHub 変更点はこちらです。 日本語(多言語)サポート 音声案内および方向指示はDirections APIで生成されます。そこで、言語や単位の設定はRouteOptionsで行います。具体的には以下の場所にオプションを追加します。これらのオプションはDirections APIのlanguageとvoice_unitsに相当します。 TurnByTurnExperienceActivity.kt 615 mapboxNavigation.requestRoutes( 616 RouteOptions.builder() 617 .applyDefaultNavigationOptions() 618 .applyLanguageAndVoiceUnitOptions(this) 619 .language("ja-JP") //ここ 620 .voiceUnits(DirectionsCriteria.METRIC) //ここ 音声案内 MapboxSpeechApiとMapboxVoiceInstructionsPlayerの言語設定も変更します。MapboxSpeechApiはDirections APIで得られた音声案内の文字列(SSML)から音声ファイル(MP3)に変換する際のAmazon Pollyの設定に使用されます。MapboxVoiceInstructionsPlayerはデバイスのText to Speechを使用する際の設定です。 TurnByTurnExperienceActivity.kt 493 // initialize voice instructions api and the voice instruction player 494 speechApi = MapboxSpeechApi( 495 this, 496 getString(R.string.mapbox_access_token), 497 Locale.JAPAN.language 498 ) 499 voiceInstructionsPlayer = MapboxVoiceInstructionsPlayer( 500 this, 501 getString(R.string.mapbox_access_token), 502 Locale.JAPAN.language 503 ) 方向指示 バナー表示はデフォルトでシステムロケールにしたがいます。なので、もしデバイスがUSになっていると残りの距離がft単位になります。この挙動はDistanceFormatterOptionsで定義されています。そこで、以下のようにNavigationOptionsでDistanceFormatterOptionsの設定を変更すれば画面表示もm単位に切り替わります。 TurnByTurnExperienceActivity.kt 427 MapboxNavigationProvider.create( 428 NavigationOptions.Builder(this.applicationContext) 429 .accessToken(getString(R.string.mapbox_access_token)) 430 .distanceFormatterOptions(DistanceFormatterOptions.Builder(this.applicationContext).locale(Locale.JAPAN).build()) 431 .build() 432 ) 多言語サポート 上記のように強制的に日本語設定にするのもアプリケーション次第ではアリですが、一般的にはシステムロケールに合わせたほうがユーザーフレンドリーです。 そこでバナー表示で追加したDistanceFormatterOptionsは削除し、他の部分は以下のように設定しました。 TurnByTurnExperienceActivity.kt 620 mapboxNavigation.requestRoutes( 621 RouteOptions.builder() 622 .applyDefaultNavigationOptions() 623 .applyLanguageAndVoiceUnitOptions(this) 624 .language(application.inferDeviceLanguage()) 625 .voiceUnits(if(application.inferDeviceLocale().getUnitTypeForLocale() == UnitType.METRIC) DirectionsCriteria.METRIC else DirectionsCriteria.IMPERIAL) TurnByTurnExperienceActivity.kt 498 // initialize voice instructions api and the voice instruction player 499 speechApi = MapboxSpeechApi( 500 this, 501 getString(R.string.mapbox_access_token), 502 applicationContext.inferDeviceLanguage() 503 ) 504 voiceInstructionsPlayer = MapboxVoiceInstructionsPlayer( 505 this, 506 getString(R.string.mapbox_access_token), 507 applicationContext.inferDeviceLanguage() 508 ) GitHub 変更点はこちらです。 カメラの設定 経路設定後、ナビゲーション開始時にFollowing(Puck後方上空からの視点)に切り替わってほしいです。navigationCamera.requestNavigationCameraToOverview()を以下のように変えるだけでOKです。 TurnByTurnExperienceActivity.kt 661 private fun setRouteAndStartNavigation(routes: List<DirectionsRoute>) { ... 671 // move the camera to following when new route is available 672 navigationCamera.requestNavigationCameraToFollowing() 673 } また、街中で使うにはズームが少し小さい気がします。もう少しズームレベルを上げましょう。ズーム等、カメラのパラメータはViewportDataSourceが生成しているため、そのオプションで決められています。 具体的には以下のように変更します。 TurnByTurnExperienceActivity.kt 438 // initialize Navigation Camera 439 viewportDataSource = MapboxNavigationViewportDataSource(mapboxMap) 440 viewportDataSource.options.followingFrameOptions.minZoom = 16.0 //ここ 441 viewportDataSource.options.followingFrameOptions.maxZoom = 18.0 //ここ 442 navigationCamera = NavigationCamera( 443 mapboxMap, 444 binding.mapView.camera, 445 viewportDataSource 446 ) GitHub 変更点はこちらです。 バナー色の変更 ボトムバナー(MapboxTripProgressView)の背景色を変えてみます。SDKでリソースで定義されているものは、リソースを再定義することで見た目を変更できます。MapboxTripProgressViewの場合、mapbox_trip_progress_layout.xmlで定義されているので、このファイルをコピーして、自分のアプリのres以下にペースト、修正すればOKです。 GitHub 変更点はこちらです。 オリジナルのファイルに対し、7行目を追加しました。 1 <androidx.constraintlayout.widget.ConstraintLayout 2 xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 xmlns:tools="http://schemas.android.com/tools" 5 android:layout_width="match_parent" 6 android:layout_height="wrap_content" 7 android:background="#F5DEB3" 8 > 音声案内の編集 Directions APIから受け取ったSSMLテキストをSDKがAmazon Pollyに送ることで音声案内が生成されます。VoiceInstructionsObserverはAmazon Pollyに送る直前に呼び出されるため、このタイミングで音声案内を編集すると任意の案内を行うことができます。特殊読みでどうしても正しく発話されない・・・等で使えるのではないでしょうか。 今回は以下のように変更してみました。語尾が「デステニー。」になります。ちなみに、元ネタはがんばれ!おでんくんの神様です。 TurnByTurnExperienceActivity.kt 243 /** 244 * Observes when a new voice instruction should be played. 245 */ 246 private val voiceInstructionsObserver = VoiceInstructionsObserver { voiceInstructions -> 247 val announcement = voiceInstructions.announcement()?.replace(Regex("です。"), "デステニー。") 248 val ssmlAnnouncement = voiceInstructions.ssmlAnnouncement()?.replace(Regex("です。"), "デステニー。") 249 val customVoiceInstructions = VoiceInstructions.builder() 250 .announcement(announcement) 251 .ssmlAnnouncement(ssmlAnnouncement) 252 .distanceAlongGeometry(voiceInstructions.distanceAlongGeometry()) 253 .build(); 254 255 speechApi.generate(customVoiceInstructions, speechCallback) 256 } GitHub 変更点はこちらです。 Map Styleの変更 サンプルではStreetsが使用されていますが、よりナビゲーション向きのmapbox://styles/mapbox/traffic-day-v2に変更します。Style.MAPBOX_STREETSを以下のように変更するだけで完了です。 TurnByTurnExperienceActivity.kt 535 // load map style 536 mapboxMap.loadStyleUri( 537 Style.TRAFFIC_DAY //ここ 538 ) { ... ただし、Style内で使用されているLayer名が異なるので以下の部分も変更が必要です。 TurnByTurnExperienceActivity.kt 525 val mapboxRouteLineOptions = MapboxRouteLineOptions.Builder(this) 526 .withRouteLineBelowLayerId("road-label-motorway-trunk") //ここ 527 .build() GitHub 変更点はこちらです。 通過済み経路の削除 すでに通った経路は表示を隠したほうがわかりやすいです。Navigation SDKではVanishingという機能を使用することで実現できます。 まずは機能をONします。 TurnByTurnExperienceActivity.kt 535 val mapboxRouteLineOptions = MapboxRouteLineOptions.Builder(this) 536 .withRouteLineBelowLayerId("road-label-motorway-trunk") 537 .withVanishingRouteLineEnabled(true) //ここ 538 .build() onPositionChangedListenerを定義し、 TurnByTurnExperienceActivity.kt 338 private val onPositionChangedListener = OnIndicatorPositionChangedListener { point -> 339 val result = routeLineApi.updateTraveledRouteLine(point) 340 mapboxMap.getStyle()?.apply { 341 routeLineView.renderRouteLineUpdate(this, result) 342 } 343 } turn-by-turnナビゲーション開始時にリスナに登録します。 TurnByTurnExperienceActivity.kt 684 private fun setRouteAndStartNavigation(routes: List<DirectionsRoute>) { 685 binding.mapView.location.addOnIndicatorPositionChangedListener(onPositionChangedListener) これにより、Puckが動くたびに経路の通過地点が更新されます。また、処理の途中に現在のナビゲーションのステータス(Trackingなのか、終わったのか)を参照する箇所があります。このステータスをアップデートするために以下のコードを追加します。 TurnByTurnExperienceActivity.kt 348 private val routeProgressObserver = RouteProgressObserver { routeProgress -> ... 381 routeLineApi.updateWithRouteProgress(routeProgress,{}) 382 } GitHub 変更点はこちらです。 成果物 まとめ Navigation SDKは非常にカスタマイズ性が高く、様々なユースケースにマッチします。ぜひ、色々と試してみてください!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Remote Configurationの使い方

Remote Configurationとは パラメータをリモート(サーバー)に持たせることで、リモート側の変更のみで、新しいアプリをリリースせずに、アプリの見た目や動作を変更します。 Remote Configurationの使い方 前準備 (1) Huaweu Developerを登録します。 (2) AppGallery Connectでアプリのプロジェクトを登録します。 (3) keytoolで生成したSHA256をAppGallery Connectのアプリプロジェクトに登録します。 (4) agconnect-services.jsonをダウンロードし、appフォルダに配置します。 (5) "Remote Configuration"を開き、"Enable now"をクリックし、Remote Configurationを有効にします。 HMS SDKを導入 (1) プロジェクトのbuild.gradleを開き、mavenとclasspathを追加します。 build.gradle // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { ext.kotlin_version = "1.3.72" repositories { google() jcenter() // こちらの行を追加 maven {url 'http://developer.huawei.com/repo/'} } dependencies { classpath "com.android.tools.build:gradle:4.0.1" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files // こちらの行を追加 classpath 'com.huawei.agconnect:agcp:1.3.1.300' } } allprojects { repositories { google() jcenter() // こちらの行を追加 maven { url 'http://developer.huawei.com/repo/' } } } (2) モジュールのbuild.gradleを開き、次のようにを追加します。 build.gradle plugins { id 'com.android.application' id 'kotlin-android' // こちらの行を追加 id 'com.huawei.agconnect' } android { // signingConfigsを追加 signingConfigs { debug { storePassword 'My password' keyAlias 'My keyAlias' keyPassword 'My password' storeFile file('My keystore file.jks') v1SigningEnabled true v2SigningEnabled true } release { storePassword 'My password' keyAlias 'My keyAlias' keyPassword 'My password' storeFile file('My keystore file.jks') v1SigningEnabled true v2SigningEnabled true } } buildTypes { release { // signingConfigを追加 signingConfig signingConfigs.release minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } debug { // signingConfigを追加 signingConfig signingConfigs.debug debuggable true } } buildFeatures { dataBinding true } } dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.appcompat:appcompat:1.3.1' implementation 'com.google.android.material:material:1.4.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.0' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.3' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' // Remote Configurationのライブラリを追加 implementation 'com.huawei.agconnect:agconnect-remoteconfig:1.6.1.300' // Analytics Kitのライブラリを追加 implementation 'com.huawei.hms:hianalytics:6.3.0.303' } コーディング デフォルトのパラメータをXMLファイルに定義します。 パラメータの種類は主にboolean、double、long、stringがあります。 remote_config.xml <remoteconfig> <value key="boolean">true</value> <value key="double">1234567890.12</value> <value key="long">123456789012</value> <value key="string">Text</value> ... <value key="key5">value5</value> <value key="key6">value6</value> <value key="key7">value7</value> <value key="key8">value8</value> <value key="key9">value9</value> </remoteconfig> // 更新頻度 private val FETCH_INTERVAL = 60 * 5L // Remote Configurationのオブジェクト private val config = AGConnectConfig.getInstance() override fun onCreate(savedInstanceState: Bundle?) { ... // XMLに入れたデフォルト値をロードします。 config.applyDefault(R.xml.remote_config) } // リモートからパラメータをダウンロードし、適用します private fun fetch() { config.fetch(FETCH_INTERVAL).addOnSuccessListener { config.apply(it) applyConfigValue() }.addOnFailureListener { it.printStackTrace() } } // 前回リモートからダウンロードした値を適用します private fun loadConfig() { config.apply(config.loadLastFetched()) applyConfigValue() } // パラメータを適用します private fun applyConfigValue() { viewModel.boolean.value = config.getValueAsBoolean("boolean") viewModel.double.value = config.getValueAsDouble("double") viewModel.long.value = config.getValueAsLong("long") viewModel.string.value = config.getValueAsString("string") } 注意:更新頻度の制限は端末ごとに1時間に20回までです。 リモート側の新パラメータの追加 (1) "Remote Configuration"に入り、"New parameter"をクリックします。 (2) キーを"Parameter"に入れ、値を"Default value"に入れます。 リモート側のパラメータの編集 対象パラメータの右上のメニューを開き、編集を行います。 GitHub https://github.com/Rei2020GitHub/MyPublicProject/tree/master/RemoteConfigDemo 参考 HMS:https://developer.huawei.com/consumer/jp/ Remote Configurationの紹介:https://developer.huawei.com/consumer/jp/agconnect/remote-configuration Remote Configurationのドキュメント:https://developer.huawei.com/consumer/jp/doc/development/AppGallery-connect-Guides/agc-remoteconfig-introduction-0000001055149778 Huawei Developers:https://forums.developer.huawei.com/forumPortal/en/home Facebook Huawei Developersグループ:https://www.facebook.com/Huaweidevs/
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む