shuhelohelo’s blog

Xamarin.Forms多めです.

Xamarin.Forms : アプリのターゲットをAndroid10にする

devblogs.microsoft.com

Android 10への対応を行わなければならないということ.そうだったのか...

developer.android.com

  • 2020/8/3以降 : 新規アプリはAndroid 10(API 29)以上を対象とする必要がある.
  • 2020/11/2以降 : すべてのアプリのアップデートはAndroid 10(API 29)以上を対象とする必要がある.

なるほど.

コンパイルターゲットを変更する

ソリューションエクスプローラAndroidプロジェクトで右クリックしてコンテキストメニューからプロパティを開いて,Applicationで以下のようにCompile using Android version: (Target Framework)Android 10.0 (Q)にする.

f:id:shuhelohelo:20200708094244p:plain

ターゲットAPIを変更する

引き続きAndroidプロジェクトのプロパティでAndroid ManifestTarget Android version:という項目があるので,それをAndroid 10.0 (API Level 29 - Qにする.

f:id:shuhelohelo:20200708094745p:plain

ビルドしたらエラーが出た

f:id:shuhelohelo:20200708122838p:plain

利用しているライブラリがXamarin.Android.Support.CompatXamarin.Android.v7.AppCompatを必要だけど見つからない,ということなのでインストールする.

(20200715追記)
どうやらこれはSyncfusionのライブラリがこれら必要としているため.

パッケージはAndroidプロジェクトだけにインストールすれば良いようだ.

Xamarin.Forms 4.5以降のアプリはここまででいいのかな?

Android Xに移行する

(注:Xamarin.Forms 4.7以降はAndroidXに対応しているので,特にすることはない)

docs.microsoft.com

devblogs.microsoft.com

James Montemagnoさんによると, Android10をターゲットにするならAndroidXライブラリへの移行も検討すべき,とのこと.

AndroidXライブラリは古いAndroidサポートライブラリを置き換えるもので,ターゲットをAndroid10にすると,Xamarin.FormsもXamarin.EssentialsもAndroidXを使うようになっている,とのこと.

何か作業する必要はあるのだろうか詳しくはこちら.

devblogs.microsoft.com

移行ツールを使う

Visual Studio 2019 Preview 16.5以上であること.

Visual StudioTools > Optionsを開く.

次にAndroid Settingsを検索する.

その中でEnable AndroidX Migrator(Experimental)とあるので,チェックをつけて有効にする.Okボタンを押す.

ソリューションエクスプローラAndroidプロジェクトを右クリックすると,コンテキストメニューMigrate to AndroidXという項目があるので,それをクリックする.

f:id:shuhelohelo:20200708100224p:plain

以下のような確認ダイアログが表示される.

f:id:shuhelohelo:20200708100333p:plain

「移行処理はキャンセルできないし,やり直しもできないけどいい?」

バックアップをとるなり,gitでコミットしておくなりして,対策してからYesを押す.

Google Play StoreにAndroidアプリを公開する メモ

このへんはこちらの記事がとても参考になりました.

kuneoresearch.com

Google PlayAndroidアプリを公開」を押します.

f:id:shuhelohelo:20200706182328p:plain

「アプリの作成」というダイアログが表示されるのでアプリ名などを入力していきます.

f:id:shuhelohelo:20200706182504p:plain

アプリの説明などを入力します.

f:id:shuhelohelo:20200706215810p:plain

アプリのスクリーンショットをアップロードする必要がありますが,画像のサイズやアスペクト比など条件があるのでちょっと手間です.

私はストア用のスクリーンショットをアップロード条件を満たすようにいい感じに調整してくれるサービスを使いました.

apptoolkit.io

次にフィーチャーグラフィックという画像を作らなければならないが,これが1024×500指定なので面倒...

適当に画像を用意して,ペイント3Dでサイズを変更しました. こんなの↓

f:id:shuhelohelo:20200706221816p:plain

最終的にアップロードしてこんな感じです.

f:id:shuhelohelo:20200706222036p:plain

あとはアプリのタイプやカテゴリを指定して完了です.

f:id:shuhelohelo:20200706222212p:plain

今気づきましたが,左側のメニューを観ると入力しなければならない項目にチェックマークがついてわかるようになっています.まだまだやることはたくさんあります.

f:id:shuhelohelo:20200706222502p:plain

アプリのリリース

AAB(Android App Bundle)やAPKをアップロードする必要があります.

App Centerで最新のビルドからAABをダウンロードします.

f:id:shuhelohelo:20200706224247p:plain

「Download Bundle」というやつですね.zipファイルのダウンロードが始まるので,完了したら解凍して.aabファイルがあることを確認します.

f:id:shuhelohelo:20200707001301p:plain

今度はGoogle Play Consoleに移動します.

Google Play Consoleでアプリのリリースを選択して,ベータ版や製品版などいくつかの種類のリリースがありますが,今回は製品版でいきます.

f:id:shuhelohelo:20200706235700p:plain

製品版トラックの管理を押します.

f:id:shuhelohelo:20200706235802p:plain

リリースを作成を押します.

次の画面はアプリの署名鍵についてです.

f:id:shuhelohelo:20200706235947p:plain

App Centerで自動ビルドを行う手順の中で署名鍵を生成したと思ったんだけれど,Google Play Storeの署名鍵はこれを自動的に置き換えてくれるのだろうか?

ともかく,このGoogleが用意する署名鍵を使えば,鍵の管理が不要になるというメリットはある.

このあたりは以前の記事を確認すること

shuhelohelo.hatenablog.com

さて,先程の.aabファイルをアップロードします.

f:id:shuhelohelo:20200707001437p:plain

ファイルを選択ボタンを押して先程の.aabファイルを指定します.

アップロードが完了すると,そのまま自動的にapkファイルの生成などが行われます.

f:id:shuhelohelo:20200707001614p:plain

後は保存して,ここでの手順は完了です.

レーティングの設定

年齢制限やなんかですね.

f:id:shuhelohelo:20200707002015p:plain

アプリのカテゴリとしては一番下のユーティリティなどなどを選びました.

次のステップでは,アプリについていくつかの質問項目があるので回答します.

f:id:shuhelohelo:20200707002253p:plain

プライバシーポリシーを用意するのが面倒だったので「13才未満」は対象に含まないことにした.

f:id:shuhelohelo:20200707002839p:plain

すべてのチェックマークが点灯した.

f:id:shuhelohelo:20200707003545p:plain

公開の準備完了になった.

f:id:shuhelohelo:20200707003620p:plain

上のリストからアプリを選択して,次のページで右下の「公開の準備完了」というボタンをクリックします.

f:id:shuhelohelo:20200707004106p:plain

すると以下のダイアログが開くので,「リリースを管理」をクリックします.

f:id:shuhelohelo:20200707004215p:plain

次に「アプリのリリース」内の「製品版トラック」をみると,「公開されていない製品版のリリースがあります」とあるので,リリースを編集ボタンを押します.

f:id:shuhelohelo:20200707004403p:plain

次のページで右下の「確認」を押します.

f:id:shuhelohelo:20200707004716p:plain

最後に「製品版として公開を開始」ボタンを押します.

f:id:shuhelohelo:20200707004755p:plain

以下のダイアログが表示されるので,もちろん「確認」ボタンを押します.

f:id:shuhelohelo:20200707004903p:plain

どうやら公開された?

f:id:shuhelohelo:20200707004954p:plain

「公開待ち」になっている.

f:id:shuhelohelo:20200707005431p:plain

Xamarin.FormsのAndroidアプリのアプリ名を変更する

20200714追記

「アプリ名を変更する」と書きましたが,この記事で変更するのはAndroidのホーム画面に表示されるアイコンのラベルでした.

この記事のとおりに作業してもアプリ名は変更されません.

アプリ名の変更は以下の記事を参考にしてください.

shuhelohelo.hatenablog.com

元の記事ここから

前回はXamarin.FormsのAndroidアプリのアイコンを変更しました.

shuhelohelo.hatenablog.com

しかし,アプリ名はプロジェクト名のまま(XFMyDecode2020)でした.

これをプロジェクト名ではなく,任意の名前にしたいと思います.

アプリ名を変更するにはどうしたらよいでしょうか.

ここでCovid19Radarの場合を見てみます.

これはAndroidプロジェクトのMainActivity.csです.

    [Activity(Label = "@string/app_name", Icon = "@mipmap/ic_launcher", Theme = "@style/MainTheme.Splash", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ScreenOrientation = ScreenOrientation.Portrait, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]

Label = "@string/app_name"と指定しています.

app_nameに割り当てられている文字列がここに入るようです.

ではこのapp_nameはどこで定義されているかというと,AndroidプロジェクトのResources > valuesフォルダ内のStrings.xmlというファイルに記述されています.

f:id:shuhelohelo:20200706163209p:plain

中を見てみると,アプリケーション名が定義されています.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">COVID-19 Contact App</string>
</resources>

同じようにしてみます.valuesフォルダ内にStrings.xmlという名前のファイルを作成します.

アプリ名をMy de:code 2020とします.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">My de:code 2020</string>
</resources>

次にMainActivity.csファイルのActivity属性を同じように以下のように@string/app_nameとしました.

    [Activity(Label = "@string/app_name", Icon = "@mipmap/ic_launcher", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]

これで実行してみます.

f:id:shuhelohelo:20200706164613p:plain

無事,アプリ名も変わりました.

Xamarin.FormsでAndroidアプリのアイコンを変更する

こちらを参考にした.

www.kurigohan.com

アイコンのPNG画像を用意する.

用意の仕方は色々あるだろうけれど,今回はこちらのサービスで簡易なアイコンを作成した.

romannurik.github.io

Launcher icon generatorをクリックする.

f:id:shuhelohelo:20200706150656p:plain

エディタが表示されるので,画像を使うか文字列を使うか,や,アイコンの形(四角,丸),背景色や文字色などいくつかのパラメータを設定する.

f:id:shuhelohelo:20200706151019p:plain

できたら,右上にあるダウンロードボタンを押してzipファイルをダウンロードする.

f:id:shuhelohelo:20200706151158p:plain

zipファイルの中にはmipmap-**hdpiという複数のフォルダがあり,その中に各サイズの画像が入っている.

f:id:shuhelohelo:20200706151541p:plain

これらをまるっとAndroidプロジェクトにコピーする.今回,アイコンのファイル名はic_launcher.pngとしています.

ここで一度ビルドする(重要).

次にAndroidプロジェクトのプロパティを開き,左側のメニューからAndroid Manifestを選択してApplication iconのドロップダウンリストで先程追加したアイコンを選択する.

f:id:shuhelohelo:20200706152434p:plain

最後にMainActivity.csファイルを開き,MainActivityクラスについているActivity属性で,以下のようにIcon=の部分に@mipmap/ic_launcherと指定する.拡張子の.pngは不要.

    [Activity(Label = "XFMyDecode2020.Android", Icon = "@mipmap/ic_launcher", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]

改めて実行する.

配置されたアプリのアイコンが指定したアイコンになっていることが確認できる.

f:id:shuhelohelo:20200706155952p:plain

App Center ビルドスクリプト

前回の記事でApp Centerを使ってPushのタイミングで自動ビルド(releaseビルド)まで行いました.

しかし,Syncfusionのライセンスキーをコード内に記述していないため,アプリ起動後に何も表示されずにアプリを利用できませんでした.

ソースコードに直接記述すれば動きはしますが,そのままGitHubなどにPushしてしまうとライセンスキーが公開されてしまうリスクが生じます.

ライセンスキーやDBへの接続文字列,APIキーなどの機密情報はソースコード内に記述したくはありません.

モバイルアプリ開発における解決方法の1つは,App Centerでビルドが実行される前にこれらの機密情報をソースコードに差し込む処理を自動で行われるようにする,というものです.

例えば,Covid19Radarであれば,settings.jsonファイルにこれらの情報をまとめて記述しておき,実行時に取り出すようになっていますが,ソースコード上では以下のように実際のキーは記述されていません.APP_VERSIONAPI_SECRET,ANDROID_SAFETYNETKEYといった部分がそれです.

{
  "appVersion": "APP_VERSION",
  "apiSecret": "API_SECRET",
  "apiUrlBase": "https://API_URL_BASE/api",
  "supportedRegions": "440",
  "blobStorageContainerName": "c19r",
  "androidSafetyNetApiKey": "ANDROID_SAFETYNETKEY",
  "cdnUrlBase": "https://CDN_URL_BASE/",
  "licenseUrl": "https://covid19radarjpnprod.z11.web.core.windows.net/license.html",
  "appStoreUrl": "https://itunes.apple.com/jp/app/id1516764458?mt=8",
  "googlePlayUrl": "https://play.google.com/store/apps/details?id=jp.go.mhlw.covid19radar",
  "supportEmail": "SUPPORT_EMAIL"
}

また,TurnipTracker(あつまれ動物の森のカブ情報をプレーヤー間で共有するためのアプリ)ではApp.xaml.csファイル内にまとめられています.

        public const string GetFriendsKey = "AC_GetFriendsKey";
        public const string GetFriendRequestsKey = "AC_GetFriendRequestsKey";
        public const string GetFriendRequestCountKey = "AC_GetFriendRequestCountKey";
        public const string PostRemoveFriendRequestKey = "AC_PostRemoveFriendRequestKey";
        public const string PostApproveFriendRequestKey = "AC_PostApproveFriendRequestKey";
        public const string DeleteRemoveFriendKey = "AC_DeleteRemoveFriendKey";
        public const string PostSubmitFriendRequestKey = "AC_PostSubmitFriendRequestKey";
        public const string PutUpdateProfileKey = "AC_PutUpdateProfileKey";
        public const string PutUpdateTurnipPricesKey = "AC_PutUpdateTurnipPricesKey";
        public const string PostCreateProfileKey = "AC_PostCreateProfileKey";

        public static bool IsStore => "AC_IsStore" == "true" ? true : false;

        const string AppCenteriOS = "AC_IOS";
        const string AppCenterAndroid = "AC_ANDROID";
        const string AppCenterUWP = "AC_UWP";
        const string SyncFusionKey = "AC_SYNC";

このように,ソースコードでは実際の機密情報を記述せずに,「置換の目印となる文字列」を記述しておき,App Centerでビルド前にこれらの文字列を機密情報に置換するという方法をとっています.

App Centerではこのようにビルド前(後)などに何らかの処理を行うスクリプトを実行することができます.

https://montemagno.com/vs-app-center-custom-build-scripts-for-production-apps/amp/?__twitter_impression=true

こちらの動画はTurnipTrackerの実装についてJames Montemagnoさんが紹介しているものですが,その46分39秒あたりでスクリプトを使った仕組みについて触れています.

youtu.be

長くなりましたが,今回はSyncfusionのライセンスキーをビルドスクリプトを使ってビルド時に差し込む方法についてです.

Syncfusionのライセンスキーの取得についてはSyncfusion公式サイトや以前の記事が参考になるかと思います.

JSONファイルの読み込み

やり方はなんでも良いですが,基本的には対象のJSONファイル(appsettings.jsonなど)をEmbedded resourceに設定して,Assemblyから文字列(もしくはstream)として読み込むのが簡単かと思います.

例えばCovid19Radarだと以下のように読み込んでいます.

そして読み込んだJSONをパースして必要な情報を取得していくといった感じです.

            var assembly = Assembly.GetExecutingAssembly();

            using var file = assembly.GetManifestResourceStream("Covid19Radar.settings.json");
            using var sr = new StreamReader(file);
            var json = sr.ReadToEnd();
            var j = JObject.Parse(json);

            AppVersion = j.Value<string>("appVersion");
            LicenseUrl = j.Value<string>("licenseUrl");
            AppStoreUrl = j.Value<string>("appStoreUrl");
            GooglePlayUrl = j.Value<string>("googlePlayUrl");
            ApiUrlBase = j.Value<string>("apiUrlBase");
            ApiSecret = j.Value<string>("apiSecret");
            CdnUrlBase = j.Value<string>("cdnUrlBase");
            BlobStorageContainerName = j.Value<string>("blobStorageContainerName");
            SupportedRegions = j.Value<string>("supportedRegions").ToUpperInvariant().Split(';', ',', ':');
            AndroidSafetyNetApiKey = j.Value<string>("androidSafetyNetApiKey");
            SupportEmail = j.Value<string>("supportEmail");

もしくはGeneric Hostを使ってASP.NET Coreのような設定,DIの仕組みを使う場合は以下のような方法でJSONの読み込みとデータの取得をすることもできます.

shuhelohelo.hatenablog.com

appsettings.jsonの編集

appsettings.jsonファイルを用意し,以下のようにします.

{
  "SyncfusionLicenceKey": "SYNCFUSION_LICENCE_KEY"
}

App Center側でこのSYNCFUSION_LICENCE_KEYという文字列を本物のライセンスキーに置換するようにします.

念の為,"SyncfusionLicenceKey"をキーにして値を取得できるか確認してみます.ここではIConfigurationを使った方法でやっています.

f:id:shuhelohelo:20200704220059p:plain

取得できています.

App Centerのビルドスクリプトの作成

次にApp Center側でビルド前に実行するスクリプトを作成します.

montemagno.com

docs.microsoft.com

ビルド前後の3つのタイミングで実行されるスクリプトをそれぞれ作成してリポジトリに含んでおくことで,App Centerでのビルド時にそれらが実行されます.

これらはbashスクリプトで名前が決まっています.

以下のタイミングでそれぞれ実行されます.

  • Post-clone(cloneの後) : appcenter-post-clone.sh -> リポジトリがcloneされた直後.

  • Pre-build(ビルド前) : appcenter-pre-build.sh -> ビルドの直前.

  • Post-build(ビルド後) : appcenter-post-build.sh ->ビルドの直後.

これらのスクリプトは用意されているビルトインの変数だけでなく,任意の環境変数を作成して利用することができたり,基本的には何でもできます.

これらのファイルをソリューション,プロジェクトのどこに置くのかというと,ソリューションファイル(sln)やプロジェクトファイル(csproj)と同じ階層ということです.

Place the scripts with the format specified below next to the project-level (.xcodeproj, .csproj, .sln, or package.json) file or module-level (build.gradle) file that you've selected in the build configuration and we'll run them as custom build steps.

Androidプロジェクトの直下にappcenter-post-clone.shを配置しました.

f:id:shuhelohelo:20200705102128p:plain

TurnipTrackerのビルドスクリプトを参考にして以下のように一箇所だけ置換するスクリプトを書きました.

#! /usr/bin/env bash

echo "Variables:"

APP_SETTINGS_FILE=$BUILD_REPOSITORY_LOCALPATH/XFMyDecode2020/XFMyDecode2020/appsettings.json

sed -i '' "s|SYNCFUSION_LICENCE_KEY|$SYNCFUSION_LICENCE_KEY|g" $APP_SETTINGS_FILE

# print out for reference
cat $APP_SETTINGS_FILE

$BUILD_REPOSITORY_LOCALPATHはビルトインの環境変数(?)なのでしょうか,どうやらこのリポジトリのルートフォルダのパスを表すようです.

sedコマンドで,指定したファイル(appsettings.json)内のSYNCFUSION_LICENCE_KEYという文字列を環境変数SYNCFUSION_LICENCE_KEYの値で置換する,という内容です.

あと,確認のために置換後にappsettings.jsonの内容を出力するようにしてあります.

App Centerに環境変数を追加する

App Centerを開き,以下のようにメニューのBuildを開きます.

f:id:shuhelohelo:20200705025607p:plain

右上に🔧のようなアイコンがあるので,これをクリックすると,ビルドの設定画面が開きます.

f:id:shuhelohelo:20200705030040p:plain

ここで,Environment varialesをOnにして,SYNCFUSION_LICENCE_KEYをキー,Syncfusionのライセンスキーを値とするペアを登録します.

f:id:shuhelohelo:20200705093632p:plain

Saveボタンを押して変更を保存します.

ちなみにこの時点でBuild scriptsNoneになっています.まだPushしてないので.

f:id:shuhelohelo:20200705030145p:plain

それではここまでの変更をGitHubにプッシュします.

f:id:shuhelohelo:20200705030323p:plain

プッシュの最中にビルドの設定画面を開くと,Build scriptsPost-cloneが表示されていました.ビルドスクリプトは認識されたようです.

f:id:shuhelohelo:20200705034314p:plain

ビルドは成功しましたが,ビルドスクリプトは実行されませんでした.

どうやら,

ということのようです.

このように,ビルドスクリプトを追加してpushした後,App Center側で設定を「保存」しなおす必要がありました.

なので,ビルドスクリプト追加後の1回目のビルドはすぐにキャンセルしてしまえばよいと思います.

Xamarin.FormsのAndroidアプリのAPKファイルの生成

ストアに上げるにしてもテスト用にデバイスに配置(デバッグではなく)するにしても,APKに対して署名する必要があるようだ?

docs.microsoft.com

こちらも参考に.

riptutorial.com

で,テスト用のAPKを生成する際は「Ad hoc」を選ぶようだ?

ちょっとやってみよう.

docs.microsoft.com

Androidプロジェクトのプロパティを開く.

これはソリューションエクスプローラAndroidプロジェクトを右クリックしてコンテキストメニューから「Properties」を選択する.

f:id:shuhelohelo:20200703170227p:plain

Android Manifest

左側のメニューからAndroid Manifestを選ぶ.

アプリケーションのアイコンを指定する

Application iconでpngファイルを指定する.

f:id:shuhelohelo:20200703171533p:plain

ここにはAndroidプロジェクトのResources > drawableResources > mipmap-**内に配置されたpngファイルが候補として表示される.

バージョンを指定する

バージョン番号

  • これはビルドのたびにインクリメントされていく.
  • ユーザーに見せるものではない.
  • AndroidManifest.xmlandroid:versionCodeとして記述される.

バージョン名

  • ユーザーに見せる「バージョン」情報.
  • ストアなどでも表示されるもの.
  • 内部的には使われない.
  • ユーザーが使っているバージョンの特定ができるのであれば,どんな文字列であってもよい.
  • AndroidManifest.xmlandroid:versionNameとして記述される.

Android Options

左側のメニューからAndroid Optionsを選択する.

この設定はビルド設定毎に異なるので,対象のビルド設定を選択していることを確認する.今回はReleaseを選択している.

f:id:shuhelohelo:20200703173450p:plain

Code shrinker

サイズを小さくしてくれるのか?

f:id:shuhelohelo:20200703180940p:plain

r8とかProGuardとかわかんない.

James Montemagnoさんのアプリの設定がr8だったので,とりあえずr8にしておく

Code Generation and Runtime

これもよくわかっていない.

JamesさんのアプリではEnable Startup TracingUse the concurrent garbage collectorの両方にチェックがついているので,そのようにした.

わかった.DotNetConf2020 Xamarin回のメモに書いてた.

shuhelohelo.hatenablog.com

事前コンパイラ(AOT)を有効にするオプションだった.

f:id:shuhelohelo:20200703194249p:plain

Linker

静的な解析によって,実行時に使用されるアセンブリ,型,メンバーなどを検出し,それ以外の使用されないものをパッケージから除外する.

デフォルトでSdk Assemblies Onlyが選択されているので,そのままにする. f:id:shuhelohelo:20200703173826p:plain

このあたりの設定は,James Montemagnoさんのアプリの設定を参考にするとしよう.これはストアにも公開している有料アプリで,そこで使われている設定なので大いに参考になるはず.

github.com

てか,ビルド設定の種類がたくさんあるなぁ. f:id:shuhelohelo:20200703175018p:plain

とりあえずこのへんで.

Archive化する

ソリューションエクスプローラで,Androidプロジェクトで右クリックしてコンテキストメニューからArchiveを選択する.

f:id:shuhelohelo:20200703201536p:plain

もしくはVisual StudioのメニューからTools > Archive Managerを選択する.

f:id:shuhelohelo:20200703201500p:plain

Archive Mangerタブが開く.

f:id:shuhelohelo:20200703201114p:plain

Archive化が実行され,以下のように表示されるので,完了するまで待つ.

f:id:shuhelohelo:20200703201733p:plain

成功したら,Distribute...ボタンを押す.

f:id:shuhelohelo:20200703202218p:plain

このようなダイアログ(Distribution Channel )が表示される.

f:id:shuhelohelo:20200703202307p:plain

ここでは2つの選択肢がある.

Androidバイスにインストール(サイドローディング)可能な署名されたAPKをローカルに保存する.

詳しくはこちら.

docs.microsoft.com

Google Playに署名されたAPKを発行する.

詳しくはこちら.

docs.microsoft.com

Ad Hoc

Ad Hocボタンを押すと次の画面(Signing Identity)が表示される.

f:id:shuhelohelo:20200703202851p:plain

APKを発行するためには署名キー(または証明書)で署名する必要がある.

既存の署名キーがある場合はImportボタンを押す. 新規作成する場合は+ボタンを押す.

f:id:shuhelohelo:20200703203437p:plain

ここでは新規作成する.

以下のダイアログが開く. Visual Studio opens dialog as shown in the next screenshot.とかdialog is displayedとか.

f:id:shuhelohelo:20200703203504p:plain

Androidアプリへの署名に必要な情報をここで入力する.

AliasとPasswordを設定し,後半の情報は少なくとも1つ入力すれば良いので,Organization(組織)だけ入力した.

f:id:shuhelohelo:20200703204507p:plain

Aliasはまあこの署名キーを区別するための名前だと思うので,適当に決めた.

Createボタンを押す.

すると以下のように,リストにアイテムが追加された.

harusoftという名前で署名キーが作成されたということか.

f:id:shuhelohelo:20200703204657p:plain

この署名キーの保存場所は,以下のとおりである.

C:\Users\{USERNAME}\AppData\Local\Xamarin\Mono for Android\Keystore\{ALIAS}\{ALIAS}.keystore

この{ALIAS}の部分が,先程設定したAliasの文字列になる.つまりさっきAliasにharusoftという文字列を指定したので,私の場合は以下のようになる.

C:\Users\hoge\AppData\Local\Xamarin\Mono for Android\Keystore\harusoft\harusoft.keystore

注意!

この署名キーをなくさないこと! ぐぬぬ.どこに保管しておくのが良いのだろうか. プライベートなリポジトリか?

続いて,その署名キーを選択した状態で,ダイアログ下部のSave asボタンを押す.

f:id:shuhelohelo:20200703204945p:plain

APKファイルの保存先を指定するダイアログが開くので,好きな場所を指定する.今回はデスクトップにした. Saveボタンを押す.

パスワードを訊かれるので,先程設定した署名キーのパスワードを入力する.

f:id:shuhelohelo:20200703205144p:plain

OKボタンを押すと,指定したディレクトリに以下のようにAPKファイルが作成される.

f:id:shuhelohelo:20200703205654p:plain

APKのサイドローディング

署名されたAPKファイルが無事生成できたので,これをAndroid端末にインストールする.

AndroidアプリのサイドローディングについてはGoogleの公式サイトを参考にする.

developer.android.com

このページの説明によると,サイドローディングとは、Google Play などのアプリストアを使用せずに手動でアプリをデバイスにインストールすることを指します,とのこと.

なるほど.

PCとAndroid端末をつないで,Android内のいずれかのフォルダにAPKファイルをコピーし,Android側でそのファイルを開けば,インストールが始まる.

色々警告されるが,自分で作ったものなので自信を持って進んでいけばよい.

dot net conf 2020 xamarin メモ

動画 www.youtube.com

サンプルコード github.com

Keynote : Xamarin All The Things

AndroidのProfiled AOT

Profiled AOTはStartup Speedが速く(AOTについで)でAPK SizeがAOTよりも小さい(Normalよりは大きい).

バランスがいい.

Type Startup Speed APK Size
Normal 2s914ms 16.1 MB
AOT 1s18ms 34.6 MB
Startup Tracing 1s518ms 20.1 MB

Visual StudioAndroidプロジェクトのProfileを開く.

左ペインからAndroid Optionsタブを選択し,Enable Startup Tracingという項目にチェックをつける.

f:id:shuhelohelo:20200408181155p:plain

試してみた.

  • チェックなし.8.3MB.

f:id:shuhelohelo:20200408195120p:plain

  • チェックあり.8.4MB.

f:id:shuhelohelo:20200408195909p:plain

Xamarin.Formsデモ

FlyMeアプリケーション.

All Things Xamarin.Forms Shell

Shellは

  • アプリケーションの構造を宣言的に記述できる
  • ナビゲーションをシンプルにする
  • カスタマイズ性に優れる
  • 作りたいアプリを素早く開発できる

Shellの見た目を設定する

細かく変更できる. f:id:shuhelohelo:20200411012517p:plain

ドロワーメニューの見た目を設定する

DataTemplateをResourceDictionary内に定義して,それを使う.

f:id:shuhelohelo:20200411012622p:plain

StyleClass (Xamarin.Forms 4.6以降)

Shellに限らない.すべての要素に有効な機能.

HTML,CSSのclassのようにスタイルを適用できる. 複数適用できるのが特徴.

docs.microsoft.com

追記

ShellのFlyoutItemに関するスタイルの指定については以下のShell Fly-out Stylingの項目を参照する.

devblogs.microsoft.com

Tabの見た目を設定する

f:id:shuhelohelo:20200411012938p:plain

Shellのナビゲーション(遷移)

  • 遷移先に名前をつける.
<Tab Title="Details" Route="Details"....
  • どこからでも遷移できる
await Shell.Current.GoToAsync("//Details");
  • ページとRoutingを登録しておける
Routing.RegisterRoute("ModalPage", typeof(ModalPage));
  • Deep link ナビゲーション
await GoToAsync("//Details/ModalPage");

文字列でナビゲーションできる.

アプリから離脱しても,その時のURLを記録してさえ於けば,復帰も簡単.

モーダルページ

Shell.PresentationMode="Modal"
await Shell.Current.GoToAsync("ModalPage");

docs.microsoft.com

Search Handler

これ使いたいな.

BackButtonBehavior

TextOverrideで戻るボタンのテキストを変更できる IconOverrideで戻るボタンのアイコンを変更できる

Shellの特徴

ContentPage,MasterDetailPage,NavigationPage,TabbedPageをまとめてShellで置き換えることができる.

Shell.TitleViewでTitleBarのUIを作成できる.

Shellの今後

  • 3rdパーティ製のMVVMサポート(Prism,FreshMVVM,RxUIなど)
  • TitleViewの修正
  • すべてをテンプレート化
  • デュアルスクリーンをサポート
  • パフォーマンスの向上

    QA

    GoToはAsyncだけれど,OnNavigatedなどはAsyncではない.

Spectacular Components for Xamarin Apps

MacOSもXamarin.Formsで?

  • Expressive
  • MVVM Atom
  • HttpTracer
  • AnyBind
  • TinyInsights
  • Shiny

Shinyの紹介

  • Background Task
  • Notification

Resizetizer

通常,様々なディスプレーサイズに合わせて,各画像ごとに複数のサイズを用意しなければならない.

このライブラリは画像を一つ用意しておけば,それらを自動的に生成してくれるもの?

Control Vender製のコントロールについて紹介

Communityによって提供されるコントロール

  • Magic Gradients
  • Color Picker Control
  • XamAnimation
  • Sheet Control
  • Xamlly
  • PancakeView

DebugRainbowsの紹介

開発のための機能がある - 各コントロールに色をつけて領域をわかりやすくする - 格子を表示して,コントロールの位置をわかりやすくできる <- Xamarin.Forms.DebugRainbowsだ.これ

github.com

PancakeViewの紹介

FrameやImage的なものの枠を三角形にしたり1つの角だけ丸めたり,色々できる.

f:id:shuhelohelo:20200410214748p:plain

SkiaSharp

SkiaSharpを使ったライブラリの紹介

  • Balloony
  • Magic Gradients
  • Microchart
  • Mapsui
  • Aurora Highlights
  • eliteKit
  • Draw2D

Sharpnado

  • CollectionViewのアイテムの入れ替えなどができる

Task Loaderとは?

Building Beautiful Apps with Xamarin.Forms

おしゃれなアプリとは?

  • とても主観的なもの
  • トレンドに左右される
  • 無意識に
  • 重要なものの不足を補えない
  • すごい開発者がすごいデザイナとはいえない

なんで評判がよくない?

  • シンプルなデータ入力アプリに向いているというマーケティングを行った
  • 過去の経験
  • 「Forms」という言葉の印象

おしゃれUIの要素

  • グラデーション
  • SkiaSharp
  • MagicGradients
  • PancakeView
  • Built-in Xamarin Forms Gradients(そのうち)
  • iOSでは好きなようにできる
  • Androidでは制限がある
  • フォント,アイコン
  • Google Fonts
  • FontAwesome などなど
  • 色(の組み合わせなど)を生成するツールを使う
  • 動き,アニメーション
  • Micro Animation
  • Lottie
  • Xamarin.Forms built-in animation
  • Xamanimation
  • Page Transitions
  • Xamarin.Plugin.SharedTransition

おしゃれさのためだけではない

Styleを使おう!

UIの再利用性を高めるためにStyleを使う.

Button,色,フォントなどなどを個別のファイルに分けてStyleを定義しておく.

f:id:shuhelohelo:20200409113917p:plain

練習の仕方

  • オンラインで色々探してインスピレーションを得る
  • 実際に試してみる
  • 後はやるだけ

具体的には

  • デザインをまとまりごとに分ける
    • どのように構成したらいいかを考える

サンプル

github.com/sthewissen/FocusOnXamarin

GridでRowDefinition

作れなければ,それっぽく見せればいい

If you can't make it, fake it

おすすめのサイト,パッケージやフォントなどのリソース

f:id:shuhelohelo:20200410213701p:plain

Xamarin Productivity to the Max

Code Build Deploy Iterate この繰り返しを迅速に行うには.

  • IntelliCode

Xamarin.Forms.Visual.Materialとは. iOSAndroidで同じMaterialDesignな見た目に統一することができる.

完全ではない.

docs.microsoft.com

Testing Your Xamarin Apps

テストの階層は上から順に以下のとおり.

  • UI Test エンドユーザーの視点でのテスト.Viewに対して行う.

  • Automated Service Test Serviceが期待どおりに動作して,正しいデータを返すかをテストする.

  • Automated Unit Test 機能の動作を確認するためのテスト.ViewModelに対して行う.

FIRSTの原則

  • Fast テストは遅いものだけれども,遅くならないように書かなければならない.

  • Independent 他のテストを呼び出してはいけない.

  • Repeatable アクセストークンのような定数を使ってはいけない.MockサーバーやMockサービスを使う

  • Self-validating Assertsを使う

  • Timely 複雑にならないようにする.そのためにはアクションを複数のテストに分ける必要があるかもしれない.

Unitテストの基本的な構成

AAAパターンで書く.

  • Arrange
  • Act
  • Assert

テスト対象

public int MySum(int a, int b)
{
    return a + b;
}

テストコード

[Test]
public void TestMySum()
{
    //Arrange
    int a = 5;
    int b = 7;

    //Act
    int result = MySum(a, b);

    //Assert
    Assert.AreEqual(12,result);
}

テスト条件を設定(Arrange)して,実行(Act)して,判定(Assert)する.

なぜテストをしなければならないのか

  • システムの機能をテストする
  • アプリケーションの各部分を一度にテストする
  • アーキテクチャをシンプルに保つ
  • バグを早期に発見する
  • リグレッションを避ける

テストしよう

Mock(Moq)サービスを使ってインターフェースのインスタンスを作成する.

ViewModelを使っている場合,テストするときにBinding,JSON parsers,PluginsやXamarin.Formsの機能などXamarin開発チームによってテストされているものについては考える必要はない.

自分のコマンドやロジックをViewModel越しにテストする.

選択したテスティングフレームワークに従ってテストを書くこと.

IntelliTestが役に立つよ!

すべてにInterfaceを

アプリに様々なプラグインやパッケージをインストールするよね.Xamarin.Essentialsとか.

例えばXamarin.Essentialsを使うなら,IXamarinEssentialsを作ってプロパティとメソッドだけを公開すると,柔軟性が高くなる.

Unit Test Demo

github.com

UI Test

BDDアプローチ

Behavior Driven Development

技術的な言葉を使わずに誰もがわかる言葉でテストを記述する.(例えば,Gherkinのようなドメイン記述言語など)

例示を用いて議論を行うことを通じて,どのようなソフトウェアを作るのかについて共通の理解を構築するためのアプローチ.

例えば:

As a [Role]
I want [Feature]
So that [Benefit]

Roleとして
Benefitを得るために
Featureがほしい

つまり,「誰が何のためにどんな機能がほしい」のかを明確にするということか.

これに従うと:

As a [Registered User]
I want [to be able to login]
so that [I can see the Application]

Scenario 1: User is able to log in

Given that I am a registered user,
when I enter the username 'Codrina'
and password 'ladybug',
then I should see the first screen of the app.

UI Test Demo

UIテストをどうするか.

Going Reactive with Reactive Extensions & UI

ReactiveUIについて.

よく見るこのイベントハンドラ.

SearchBar.TextChanged += (sender, args) => { };
textChangedEvents
    .Throttle(TimeSpan.FromMilliseconds(750),RxApp.TaskpoolScheduler)
    .Select(x => x.NewTextValue.Trim())
    .DistinctUntilChanged()
    .Where(x => !string.IsNullOrWhiteSpace(x))
    .ObserveOn(RxApp.MainThreadScheduler)
    .InvokeCommand(this, x => x.ViewModel.Search)
    .DisposeWith(ViewBindings);

ロジックのすべてをコンストラクタにまとめることができる.

テキスト

kent-boogaart.com

github.com/kentcb

イベントを普通に使うのであれば役に立つよ.

All Things Xamarin.Forms Shell

www.youtube.com

Developing Performant Xamarin Apps

Improve Xamrin.Forms App Performance という動画があるのでチェックするように.

Performance Optimizations

  • 起動時にすべてのServiceを開始/登録しないこと.可能な限りLazyロードする.
  • montemagnoさんのTurnip TrackerでLazy使ってる.
  • 起動時にすべてのデータをダウンロードしない.
  • User Experienceについて考える
  • Lottieつかったり
  • iOS: Launch Storyboard
  • Android: Splash Screen Activity

Androidの起動時について

  • AOT with startup tracingを使う
  • Custom Profiles with Startup Tracingを使う.

Async/Awaitのベストプラクティスを使う

  • 同時に実行できるところで,処理を順次行わないようにする.
  • すべてのTaskが完了することを不必要に待たないようにする
  • ループの中でTask.Runを使わない
  • Cancellation tokensを使うこと

順次処理と並行処理

  • 例1: f:id:shuhelohelo:20200420124905p:plain

Okay Codeではループの中でawaitを使って処理を一つずつ完了している.

Better CodeではWhenAllを使ってすべての処理を同時に処理している.

  • 例2: f:id:shuhelohelo:20200420124940p:plain

Okay CodeではWhenAllを使ってすべてのリクエストの結果が得られるまでawaitして,結果のリストからオブジェクトを取得している.

Better CodeではWhenAnyを使って,結果が得られたものから直ちにオブジェクトを取得.

こちらの方が効率が良いとのこと.

Cancellation tokens

Taskのキャンセルに使用する.

tokenを作って,それをTaskの実行時に渡す.そして,Cancel()メソッドを実行すると,Taskの実行を終了させることができる.

var cts = new CancellationTokenSource();
var token = cts.Token;

Task.Run(async() => await someLongRunningAction(),tokens);

cts.Cancel();

しかし,Cancelのハンドリングは以下のように並行処理の中にtokenを介してThrowIfCancellationRequested()を記述しておく必要がある.

Cancelされた後に,この記述の位置に来たときにTaskがキャンセルされる.

var cts = new CancellationTokenSource();
var token = cts.Token;

Task.Run(async() => await {
        for (int i = 0; i<3; i++){
            token.ThrowIfCancellationRequested();
        }
    },tokens);

cts.Cancel();

参考: f:id:shuhelohelo:20200420131746p:plain

Event Handlers

  • イベントハンドラーのDetachとObserverのDisposeを必ず行うこと.
    • モリーリークを防ぐために,イベントのUnsubscribeを行い,匿名デリゲートの使用を避けること.
public class Subscriber :IDisposable
{
    readonly Publisher publisher;
    EventHandler handler;

    public Subscriber(Publisher publish)
    {
        publisher = publish;
        handler=(sender,e)=>{
            Debug.Writeline("The publisher notified the subscriber of an event.");
        };
        publisher.MyEvent+=
    }

    public void Dispose()
    {
        publisher.MyEvent -=handler;
    }
}
public class Publisher
{
    public event EventHandler MyEvent;

    public void OnMyEventFires()
    {
        if(MyEvent != null){
            MyEvent(this,EventArgs.Empty);
        }
    }
}

public class Subscriber: IDisposable
{
    readonly Publisher;

    public Subscriber(Publisher publish)
    {
        publisher=publish;
        publisher.MyEvent+=OnMyEventFires;
    }

    void OnMyEventFires (object sender,EventArgs e)
    {
        Debug.Writeline("...");
    }

    public void Dispose ()
    {
        publisher.MyEvent -= OnMyEventFires;
    }
}

データのロード

  • ローカルデータをまず読み込む
  • Webとのデータのやり取りを最小限に抑える.
  • ただのラベルやタイトルといった静的なデータをバインドしないこと.

Profilerを活用しろ

  • 確認する点
  • モリーリーク,
  • 大きな画像
  • 大きなオブジェクトグラフ
  • 広いスコープ
  • 相互参照

  • 計測する点

  • 起動時間
  • 実行時間
  • メモリ消費
  • CPU利用状況
  • ネットワーク状況
  • I/O状況

Icon, Image, Asset

ディスプレイの解像度ごとに画像の解像度も最適化するのが良い.

でも,それを手動で行うのは手間なので,ResizetizerNTというNugetパッケージがある.

レイアウトについて

docs.microsoft.com

  • レイアウトを入れ子にしない
  • GridでAutoを使わない
  • ListViewやCollectionViewではDataTemplateを使うこと
  • AndroidではAppCompatを使うこと

フォントについて

  • フォントを
  • 画像を使わずに,アイコンフォントを使うこと
  • 非表示のタブページの扱いに気をつけること

参考: f:id:shuhelohelo:20200421022434p:plain