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

【Android】AnimatedVectorDrawableを作成する

概要 作成方法が少しややこしかったので、自分用にメモ。 AnimatedVectorDrawableにより画像をアニメーション化する方法について説明。 ここでは、Shape Shifterを使用して作成する方法を記述。 作成方法 1. SVG画像を2個用意 アニメーション開始時の画像と終了時の画像をsvg形式で用意。 2. Shape Shifterにimport Shape Shifterサイト内の左側のImport→SVGをクリックし、用意した2つの画像を選択し、importする。 3. アニメーション追加 3-1. 左下のフォルダツリーから終了時の画像を選択し、右上にあるpathDataをコピー。 3-2. 左下のフォルダツリーの開始時の画像の右にある時計アイコン→pathDataを選択。 3-3. pathDataのtoValueを消して、3-1でコピーした値をペースト。Auto Fixするために、下に出てくる赤色のアイコンをクリック。自動でいい感じにアニメーションをしてくれる。 3-4. 左下のフォルダツリーから終了時の画像を選択し、デリートキーを押す。これはもういらないので削除する。 4. 保存 左下のExport→Animated Vector Drawableを選択。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

WorkManagerを使ってバックグラウンドでエンドポイントを呼び出す

はじめに WorkManagerとは WorkManagerは重たい処理や永続的な処理などをバックグラウンドで実行するために提供されているAndroidのフレームワークです。例えば重いデータのアップロード処理や定期的なタスクをバックグラウンドで実行したいといったユースケースで推奨されるフレームワークです。 やりたいこと 今回はViewのライフサイクルに依存せずにエンドポイントを呼びたいケースがあり、WorkManagerを使ったやり方でシンプルに実現できたため、その説明したいと思います。簡単に説明するとボタンを押下後に、エンドポイントを呼び出す必要があったのですが呼び出すViewが破棄されてしまうため通常のやり方だと非同期処理がキャンセルしてしまうのため、WorkManagerを使ってライフサイクルに依存せずにエンドポイントを呼び出したいというのがモチベーションです。 アクセス方法 Workerには2種類のアクセス方法があり、 OneTimeWorkRequest PeriodicWorkRequest が実装されており、OneTimeWorkRequestはその名の通り1回のみの処理のスケジュール設定をし、PeriodicWorkRequestは一定間隔で繰り返すようなスケジュール設定をする場合に適してます。今回に関してはエンドポイントの呼び出しということでOneTimeWorkRequestを採用しました。その他、今回では特に設定はしておりませんが、WiFiに接続したときやバッテリーが十分なときに処理を実行する制約も指定できるようです。 実際にWorkerにデータを渡す 実際のWorkerとのデータのやり取りはDataで行います。このクラスはデータをMapで保持してます。基本的なやりとりとしてはOneTimeWorkRequestBuilderで用意されているsetInputData()にデータを渡します。workDataOfはBuilder処理をラップした拡張関数です。そして実行時にWorkerのinputDataからrequest時に渡した引数を取得します。 fun createRequest( data: String, ): OneTimeWorkRequest { return OneTimeWorkRequestBuilder<MyWorker>() .setInputData(workDataOf( IN_KEY_DATA to data, )) .setBackoffCriteria( BackoffPolicy.LINEAR, OneTimeWorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS) .build() } override suspend fun doWork(): Result = withContext(Dispatchers.Default) { launch { val data = inputData.getString(IN_KEY_DATA) ?: error("invalid data") //~~ 省略 ~~// } return@withContext Result.success() } 基本的なやりとりに関しては以上ですが、例えば、Workerで以下のようなパラメータをエンドポイントに渡したいといったケースがあったとします。 data class Parameter( val hoge: String, val fugaId: Int, val payload: Payload ) Payloadはsealed interfaceとしてpayloadは以下のようなパラメータを渡すとします。 data class ItemPayload( val item1: Item1, val item2: Item2, ) : Payload { data class Item1( val id: Long, val itemName: String, ) data class Item2( val id: Long, ) } 上記のようなデータ型を上の例で示したのと同じくリクエスト時にsetInputData(workDataOf(...))で渡します。workDataOfは引数がPair<String, Any?>なのでなんでも受け付けてくれるようにみえますが、workDataOfは実装の中身をみると実はプリミティブ型以外の型は受け付けていないことがわかります。 androidx.work.Data.kt public inline fun workDataOf(vararg pairs: Pair<String, Any?>): Data { val dataBuilder = Data.Builder() for (pair in pairs) { dataBuilder.put(pair.first, pair.second) } return dataBuilder.build() } androidx.work.Data.kt public Builder put(@NonNull String key, @Nullable Object value) { if (value == null) { mValues.put(key, null); } else { Class<?> valueType = value.getClass(); if (valueType == Boolean.class || valueType == Byte.class || valueType == Integer.class || valueType == Long.class || valueType == Float.class || valueType == Double.class || valueType == String.class || valueType == Boolean[].class || valueType == Byte[].class || valueType == Integer[].class || valueType == Long[].class || valueType == Float[].class || valueType == Double[].class || valueType == String[].class) { mValues.put(key, value); } else if (valueType == boolean[].class) { mValues.put(key, convertPrimitiveBooleanArray((boolean[]) value)); } else if (valueType == byte[].class) { mValues.put(key, convertPrimitiveByteArray((byte[]) value)); } else if (valueType == int[].class) { mValues.put(key, convertPrimitiveIntArray((int[]) value)); } else if (valueType == long[].class) { mValues.put(key, convertPrimitiveLongArray((long[]) value)); } else if (valueType == float[].class) { mValues.put(key, convertPrimitiveFloatArray((float[]) value)); } else if (valueType == double[].class) { mValues.put(key, convertPrimitiveDoubleArray((double[]) value)); } else { throw new IllegalArgumentException( String.format("Key %s has invalid type %s", key, valueType)); } } return this; } ここでエンドポイントの引数であるデータ型をどうWorkerに渡すのかですが、JSON文字列に変換し受け渡すことで解決しました。Worker実行時にはItemPayloadクラスに再度戻すことでデータ型への対応も問題なくエンドポイントのパラメータとして渡すことができます。 fun createRequest( hoge: String, fugaId: Int, payload: Payload, ): OneTimeWorkRequest { val jsonStr: String = gson.toJson(payload) //JSON文字列へ return OneTimeWorkRequestBuilder<MyWorker>() .setInputData(workDataOf( IN_KEY_HOGE to hoge, IN_KEY_FUGA_ID to fugaId, IN_KEY_PAYLOAD to payload, )) .setBackoffCriteria( BackoffPolicy.LINEAR, OneTimeWorkRequest.MIN_BACKOFF_MILLIS, TimeUnit.MILLISECONDS) .build() } override suspend fun doWork(): Result = withContext(Dispatchers.Default) { launch { val hoge = inputData.getString(IN_KEY_HOGE) ?: error("invalid hoge") val fugaId = inputData.getInteger(IN_KEY_FUGA_ID) ?: error("invalid fugaId") val jsonStr = inputData.getString(IN_KEY_PAYLOAD) ?: error("invalid jsonStr") val payload = gson.fromJson(jsonStr, ItemPayload::class.java) postAPI(hoge, fugaId, payload) } return@withContext Result.success() } また、もしエンドポイントの結果をハンドリングしたい場合は、処理結果をworkDataOfで処理結果をData型で返してあげれば良さそうです。 result = postAPI(hoge, fugaId, payload) val outputData = workDataOf(OUT_KEY_JSON_STRING to gson.toJson(result)) return@withContext Result.success(outputData) 以上です。ライフサイクルに依存しない通信処理ですがWorkManagerを利用すると簡単でかなりシンプルに実装ができる印象でした。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DroidKaigi2021

Index 1.プロダクトレベルで必要になるJetpackComposeテクニック 2.Master Of Lifecycle 3.長く生きるコードベースの「品質」問題に向き合う 1.プロダクトレベルで必要になるJetpackComposeテクニック Android Jetpackとは Android Jetpackとは、Android公式開発者ページにある通り、 ベストプラクティスを集めたライブラリである。ボイラープレートコード(ほぼ固定化されたよく出てくるコード)を減らし Androidバージョンやデバイスが変わっても一貫して機能するコードの作成を助け、 コーディングの負担を軽減する。 Android Jetpackは、4つのカテゴリに分類される。 JetpackComposeとは Jetpackに含まれる、ネイティブUIをビルドするためのAndroid最新ツールキット。 AndroidのUI開発を簡素化し、加速する。 少ないコード、パワフルなツール、直感的なKotlinAPIを使用して アプリをすぐに動かすことができる。 JetpackComposeのサンプルを用いて、画面遷移の実装を行う JetpackComposeサンプル Activity生成数 画面遷移 Crane 複数Activity Activity遷移、Fragment差し替え Jetchat、Jetsurvey 1Activity Fragment差し替え JetNews、JetCaster、Rally、Owl 1Activity Compose 補足) Activity・・・一つの枠を表示するのに向いている Fragment・・・タブやマルチメイン画面など、複数の枠を表示するのに向いている            Activity上で動く Compose・・・状態ごとに画面を持つという形式 JetpackComposeにおける、必要な機能の検索手順 ①リファレンスをみる ・androidx.compose.*の Top-level functions summary ・androids.compose.materialのComponents 必要な機能の検索は、リファレンスをみて探すのが一番早い 必要なパッケージは大体、androidx.compose.foundationか、androidx.compose.materialにある URL) ・androidx.compose.foundation https://developer.android.com/reference/kotlin/androidx/compose/foundation/package-summary?hl=ja ・androidx.compose.material https://developer.android.com/reference/kotlin/androidx/compose/material/package-summary?hl=ja ②accompanistにないか調べる URL) https://github.com/google/accompanist ・JetPack Composeの便利ライブラリ集  ・Swipe to Refresh  ・Pager  ・… もともとはGoogle開発社個人のリポジトリにあったものだが、Jetpackの方に移動されてきた 本体にない時はここをチェックする 時々みると新しいものが増えていたりする ③Kotlin Slackのcomposeチャンネルで検索する URL) https://kotlinlang.slack.com/ ※https://kotlinlang.org/community/ から招待をリクエストできる どうもそれらしいものがない、または、ありそうだけどどれか良くわからない場合に調べる ④JetpackComposeのサンプルコードを読む URL) ・Android Coad Search https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ JetpackComposeの機能はサンプルが用意されていることが多い Kotlin slackやstack overflowで見つけた機能のサンプルがないか Android Code Searchで調べる それでも見つからない場合はsampleでありそうな名前(materialdesignなど)で調べる accompanistで見つけた名前などで検索すると引っかかることもある ⑤JetpackCompose Roadmapをチェックする URL) https://developer.android.com/jetpack/androidx/compose-roadmap 今後の実装予定が公開されている ・自分で実装するか、用意されるまで待つかの判断材料になる それでも見つからない場合は、リリース予定のロードマップをみる それでも見つからない場合は自分で実装するか、3rdParty製ライブラリを利用する UIパーツはライブラリを探すよりかは自分で実装した方が速い それ以外は大体は本体かaccompanistかcoilで対応している まとめ JetpackやJetpackに含まれるJetpackComposeを使うことで開発スピードを上げられる。 2.Master Of Lifecycle ライフサイクルとは 例えばActivityにはライフサイクルというものがあり Activityの生成から破棄までの周期(状態遷移)を表したものである。 Activityのライフサイクル ライフサイクルを理解した方が良い理由 ライフサイクルを理解しなくても、「動く」アプリを作ることができる しかし、「より良いアプリ」を作るためには理解しておいた方が良い ・不要に端末リソースを消費しない ・無駄なネットワーク通信をしない ・クラッシュしない 不要な端末リソース消費として、メモリリークがある メモリリークとは確保メモリの解放漏れのことであり、例えば以下により起こる ・生存期間が長いインスタンスAが、生存期間が短いインスタンスBを強参照する ・非同期処理のキャンセル忘れ メモリリークを起こさないために ・不必要になったタイミングでインスタンスの解放、処理を終了する必要がある ・Androidでは、ライフサイクルを利用することで、適切なタイミングで解放することができる Activityのライフサイクルを適用する ・Activityにはライフサイクル実装を行うために、コールバックメソッドが定義されている ライフサイクルの表にもある、以下メソッド onCreate()、onStart()、onRestart()、onResume()、onPause()、onStop()、onDestroy() ・アクティブ・非アクティブの状態変化を受け取ることができ、適切に処理を実行することができる 例)ライフサイクルに従った、UIの更新 UIの更新でやりたいこと ・UIの構成に必要なデータを、非同期に取得し、取得したデータをUIに反映したい ・常に最新のデータを保つ ・画面が非アクティブの時はUIを更新しない ・画面回転など、Activity再生成時にデータを保持する ViewModel ・UIの構成に必要なデータを保持するクラス ・Activityが再生成されても、データを保持することができる ・ViewModelを使うことで、Activityの再生成、プロセスデスを乗り越えてデータを保持できる ・次に、そのデータのUIへの通知方法について考える LiveData ・値が変更されたら、ライフサイクルの状態に応じてUIに通知する  ・アクティブ時に通知する  ・非アクティブ時には通知されない LiveDataビルダー ・必要なデータをコルーチン(非同期処理)を使い、取得することができる ・指定した時間非アクティブになったら、処理を終了する LiveDataのまとめ ・LiveDataを使うことで、ライフサイクルに合わせたデータ更新が可能になる ・ViewModelと組み合わせると、Activity再生成時に、効率良く画面復帰ができる ・LiveDataビルダーも使うと、処理のキャンセルもできる まとめ ・ライフサイクルは「良いアプリを作る」ために必要 ・アプリをライフサイクルと協調して動かすことで、端末のリソースを効率良く使うことができる ・ViewModel、LiveData、LiveDataビルダーを使うことで、UIの更新を効率良くできる 3.長く生きるコードベースの「品質」問題に向き合う はじめに 現場において大規模開発チームで何チームもある場合、こういうことがある ・他のチームが書いているコードの内容がよく分からない ここでは、LineのAndroidクライアントチームが行っていることを紹介する 話の前提 ・スクラム開発 ・バージョン管理にGitを利用 ↓TEAM Aの中でやっていることはわかるがTEAM Bの人にはわからない ↓新しいルールやライブラリを作っても特定のチームは反映できていない状況が起こる これらを解消するために、コンテキスト(歴史や背景)を記録していく。 これは、世間一般で行っていることだが LineAndroidクライアントチームでは、加えて ・ソースコードについてコンテキストなしで読めるようにする  取り組みを行っている ・チームの垣根を壊して、チーム間での知識共有、技術共有、  開発文化の共有を行っている 具体的に落とし込んだのが次の5つになる ・2フェーズレビュー ・構造ドキュメント ・コード作成のベストプラクティス作成 ・技術コンサルタント ・レビュー委員会 チーム内のレビューでは、以下のような問題がある ・依頼者とレビュー者が固定化されてしまう ・チームをまたがったレビューが行いにくくなる これを解決するためにレビューを2段階に分ける ①まずチーム内で知識のある人が1stレビューを行う ②次に、プルリクエストを検知するbotを通してランダムで  slack通知して選出された他チームの何も知らない人に  2ndレビューをしてもらう  →背景なしにコードが読めるかどうかをチェックすることができる 2フェーズレビューの利点として、 ①レビュアー同士でフィードバックができる  →1stレビュアーで何か見落としがあった場合に、   2ndレビュアーが1stレビュアーにフィードバックすることができる ②チームの垣根を超えたレビューをすることになるので  チームの垣根を超えた知識共有ができる ・2フェーズレビューを使うことによって、プルリクエストの範囲で コンテキストなしにコードが読めるかチェックできる だが、プルリクエストを複数合わせた「構造」で見たときに分からない →個々ではわかるけど、まとまると理解できない  それを解決するのが、次の構造ドキュメント 構造ドキュメント 構造ドキュメントは実装を始める前にドキュメントを書く これにより技術的な問題点を明確にできる ウォーターフォールの詳細設計との大きな違い →フォーマットがないこと、好きなものを好きなだけ書く  もう一つの大きな違いとして、実装して問題がある場合、  構造ドキュメントに立ち戻って修正する 構造ドキュメントに書くこと ・やることとゴール ・背景 ・クラス図(抽象図、具象図)やモジュール図 ・Modelクラスの定義  →開発の規模に応じて省略 ・タスクで、セキュリティ・プライバシー・パフォーマンスについて懸念点がある場合は、  その旨をドキュメントに残す LINE Androidクライアントチームが構造ドキュメントを実際に導入して得られた知見 ①構造ドキュメントを必須とするべきはない  1、2日で終わるようなタスクでは必須にしてしまうと  かえって生産性が落ちてしまう  →例えば2週間以上とか中・大規模のタスクで構造ドキュメントを書くようにするといい ②よく話し合ってから書く  話し合ってから書くようにするとより手戻りが少なくなる ③構造ドキュメント自体が背景などを示すコンテキストになる  プルリクエストのdiscriptionなどにリンクとして残しておくと、あとあと役に立つ 2フェーズレビューや構造ドキュメントは開発プロセスを改善するのに役立つ だが、実際のコード改善はこれではできない →LineのAndroidクライアント開発においては、発表が8hにも  及ぶコード作成のベストプラクティスが作成されている  ベストプラクティスを用いて実装することにより、  後々のリファクタリングの効率が上がる 例)以下、Account TypeによってUIで表示するBackgroundとAccount Iconを切り分けたい 関数を作成する方法として、Account typeで分ける方法とUIで分ける方法がある 結論を先に言うと、UIによって分けるべきであり、Account typeで分けてはならない なぜそうした方がいいのか →読みやすさ、外的要因に左右されない、  将来のリファクタリングのしやすさ 以下のCase1はAccount Typeで関数分けした →関数名を見ても何がしたいか分からない 以下のCase2はUIで関数分けした →関数名を見ると何をするかが分かる 2フェーズレビュー、構造ドキュメント、ベストプラクティス作成 これらは全てのチームに採用してほしいが 全てのチームが全てのルールを更新するのは難しい →これを解決するのが技術コンサルタント 技術コンサルタント シニアエンジニアがチーム内でコードレビューを行うが その際、コードだけではなく、知識共有やプロセスの改善点も フィードバックする レビューを受けた人は、構造ドキュメントやソース構造を修正する →コードだけでなくプロセスや構造を覚えスキルアップする レビュー時の観点 ①プロジェクトルール 関係者の集まり方が正しいか、プラットフォームの使い方が正しいか ②タスクのマネジメント方法 話し合いの仕方、構造ドキュメントの書き方、スケジュールの改善の仕方 ③アーキテクチャデザイン レイヤーの切り方が正しいか、依存関係の貼り方が正しいか ④コード詳細 名前の付け方、ロジックのフロー 技術コンサルタントで影響するのは特定のチームでしかない より効率的にチーム間で知識を共有するためにレビュー委員会を行う レビュー委員会 やること ・チームに属さないシニアエンジニア数名を配置し  すでにマージ済みのプルリクエストを各チームから送る  それをレビューしてフィードバックする ・シニアエンジニアは1週間に1回これをレポートとしてまとめ  全チームに共有する まとめ ・2フェーズレビュー チームの垣根を越えて知識を共有できる コンテキストなしでもコードが読めるようになる ・構造ドキュメント コンテキスト(歴史や背景)を残す ・コード作成のベストプラクティス作成 のちのちのリファクタリングがしやすくなる ・技術コンサルタント チーム内でシニアエンジニアの知識を共有することにより、 エンジニアのスキルアップ ・レビュー委員会 全チームにシニアエンジニアの知識が行き渡る 結果として、大規模開発でのコード品質が長い間に渡って保たれていく
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

スマホアプリ クロスプラットフォーム開発 in Flutter

Index 1.Flutterフレームワーク 2.AndroidStudioで開発できる 3.UIの記載はネストを多用する 4.UIとビジネスロジックを分けて書く 5.KotlinとSwiftによる書き分けが必要な場合がある 6.Dartプログラムのエントリーポイント 7.使用パッケージの宣言 8.ホットリロードで一回一回ビルドしなくてもいい 9.動作確認 10.アプリ公開 1.Flutterフレームワーク 2018年12月にGoogleからリリースされた、モバイルアプリ開発用のフレームワーク。 Dart言語 従来ならば、AndroidならKotlin iOSであればSwift という、それぞれの言語を覚えなければいけないが Flutterの場合は、DartというJavaライクな言語を覚えるだけで 両方の開発ができ、学習コストを抑えることができる。 2.AndroidStudioで開発できる AndroidStudioのインストール Flutterのインストール 3.UIの記載はネストを多用する 画面を構成するコンポーネントはWidgetというクラスが基底クラスにあり 各コンポーネントはWidgetを継承する。 Widgetを並べることで画面を作っていく。 コード const Text.rich( TextSpan( text: 'Hello', // default text style children: <TextSpan>[ TextSpan(text: ' beautiful ', style: TextStyle(fontStyle: FontStyle.italic)), TextSpan(text: 'world', style: TextStyle(fontWeight: FontWeight.bold)), ], ), ) 4.UIとビジネスロジックを分けて書く BLoC(BusinessLogicComponent)というアーキテクチャを用いることで、UIとビジネスロジックを分けて書くことができるようになる BLoCパターンを用いることにより、次のことができるようになる ・ソースファイルごとの責務を明確化し、ソースの見通しがよくなる ・イベントハンドラ(StreamBuilder)を使って、状態管理をできる 参考URL)長めだけどたぶんわかりやすいBLoCパターンの解説 https://qiita.com/kabochapo/items/8738223894fb74f952d3 5.KotlinとSwiftによる書き分けが必要な場合がある UI部分、HTTP通信、内部ストレージへの保存などは一つのソースで書くことができるが 担当したプロダクトにおいてFirebaseとAzureNotificationHubsを利用したプッシュ通知をすることがあり、 Firebaseに端末を登録をするところまではDart記載で共通だが、 プッシュ通知の送信先を指示する部分であるAzureNotificationHubsへの端末識別情報の登録部分は、 Dartが対応していない部分であり、KotlinとSwiftで書き分ける必要がある。 6.Dartプログラムのエントリーポイント 以下のように、C言語などと同じで、main()から始まる // Copyright 2018 The Flutter team. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. import 'package:flutter/material.dart'; void main() { runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Welcome to Flutter', home: Scaffold( appBar: AppBar( title: const Text('Welcome to Flutter'), ), body: const Center( child: Text('Hello World'), ), ), ); } } 参考URL) https://docs.flutter.dev/get-started/codelab 7.依存パッケージの宣言 pubspec.yamlという設定ファイルで、HTTP通信や内部ストレージアクセスなど、使用するパッケージを宣言する。 また、「version」でアプリバージョンを設定する name: <project name> description: A new Flutter project. publish_to: 'none' version: 1.0.0+1 environment: sdk: ">=2.7.0 <3.0.0" dependencies: flutter: # Required for every Flutter project sdk: flutter # Required for every Flutter project http: '>=0.12.0 <0.13.0' cupertino_icons: ^1.0.2 # Only required if you use Cupertino (iOS style) icons dev_dependencies: flutter_test: sdk: flutter # Required for a Flutter project that includes tests flutter: uses-material-design: true # Required if you use the Material icon font assets: # Lists assets, such as image files - images/a_dot_burr.jpeg - images/a_dot_ham.jpeg fonts: # Required if your app uses custom fonts - family: Schyler fonts: - asset: fonts/Schyler-Regular.ttf - asset: fonts/Schyler-Italic.ttf style: italic - family: Trajan Pro fonts: - asset: fonts/TrajanPro.ttf - asset: fonts/TrajanPro_Bold.ttf weight: 700 参考URL) https://docs.flutter.dev/development/tools/pubspec 宣言したパッケージはビルド時にダウンロードされる Flutterルートに.pubspecという隠しフォルダができ そこにパッケージがダウンロードされてビルドに使用される 8.ホットリロードで一回一回ビルドしなくてもいい Flutterにはホットリロードという機能があり UIを修正した際に修正が即反映される main関数を再度実行することなく、イベントハンドラの部分のみが処理される ①メインカラーをblueに指定 ②タイトルやボタンの色がblue ③メインカラーをyellowに指定してソースを保存 ④タイトルやボタンの色がyellow 9.動作確認 Androidのエミュレータ、実機 iPhoneのシミュレータ、実機 で確認を行うことができます。 Androidエミュレータ起動 iPhoneシミュレータ起動 10.アプリ公開 テスト完了後、アプリ審査に提出 AndroidであればGooglePlayConsole、 iOSであればAppleDeveloperから 作成したアプリを審査に提出する アプリの作成ルールに違反していないか審査され、問題がなければ公開の流れとなる
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む