真面目なブログはこっち 👉 blog.s64.jp

ライブラリ寿命と付き合う


どこまでいったってポエムなんだけど、じゃあ結局どういう風に考えようかっていう頭の中のことのdumpです。まとまってないけどごめんね。

モバイルアプリケーション開発におけるRxへの依存箇所でいうと、

  • 複雑な非同期的状態変化によるイベント処理を隠蔽する
  • ライフサイクルとの同期を容易にする

という目的で記述された箇所とそこから続く処理全体へ続いている場合が多いと思う。あとはMVVMアーキテクチャ採用でデータバインディングやる都合上とかか。
Rxは非同期的なイベントの繋ぎ込みをめちゃくちゃ容易にできる反面、上手くハンドルしないとそれを行うためのコード全体に影響を与えてしまいうる。
こうしてプロジェクトの至るところで利用されたRxライブラリはツールの置換を困難にする場合がある。

その意味で影響範囲の広いライブラリを用いることは悪と言えるかもしれないのだけど、じゃあRxに相当するライブラリを現実問題として外せるんだっけ? というとかなり難しいケースがある。
現代のアプリケーションはたくさんの状態を持っていて、たくさんの非同期的なイベントを処理しなきゃいけない。さらにモバイルアプリケーションの事情になると、限られたリソースで最大限のパフォーマンスを出すべくライフサイクルなんて言い方をされる制約とも戦う必要がある。
要はモバイルアプリの世界は究極のステートフルで、人間には早すぎる場合すらある。

とするとRxに相当するものとは (新たな実用的パラダイムが提唱されない限り) 付き合い続けることになる。要はRxを使わざるを得ないコードベースにおいては、Rxはライブラリではなく既にパラダイムであり、言語の一部にすら相当しうるのである。そうやすやすとRxへの依存度を下げようなどと考えないほうがよい。

じゃあRx以外使いようがないんだっけ? というとそんなこともない。むしろここには再考の余地が大いにある。
Androidアプリケーション開発の文脈で言うと、同様の機能を提供するものは複数ある。

  • Rx (RxJava)
  • LiveData
  • Kotlin Coroutine

まずLiveDataはGoogleが提供しているAndroid向けのデータホルダライブラリだ。これはRxをモデルにしており、密にAndroidのライフサイクルと結合している。Androidアプリ開発に限れば最適な選択肢になりうる。

そしてKotlin CoroutineはKotlin言語機能のひとつとして提供される非同期処理ライブラリで、特定スレッドに依存しない部分や中断可能である部分などを利用し、Androidのライフサイクルに同期させることが容易である。

これらはRxに依存した既存コードが起こしている問題すべてを解決させることができるか? 答えはNOだ。LiveDataはRxよりもライトではあっても、同様にイベントの伝播を行うために複数モジュール間 〜 アプリケーション全体で依存しうるし、Kotlin Coroutineはさらにさらにライトではあっても結局複数モジュールごとに個別でlaunchを行うことになりうる。

しかし思い返してみれば、Rxは既にそのアプリにおいてはひとつの言語に相当する役割を担っているはずだ。だとすれば、言語選択と同じように考えればアドバンテージが見つかるのではないか。

アプリケーション開発における言語選択というのは面白くて、あくまでツールでしかないにもかかわらず「そのアプリの生き方」を決める。
言語機能として何が提供されるか? それが生産性の向上に繋がるか? というのはライブラリやツールの選定基準と何も変わらないが、このツールはコードの書き方を決定する。コードはビジネスそのものを作る。資産を作っているのである。
もしその言語の実行環境が未来永劫に失われたとしたら、ビジネス全体を別の言語へ移植するという大規模な作業をすることになる。
そのようなことが起こらないよう、十分に情報量があるか、メンテナンスできるだけのリソースが確保できるか、将来に渡っての品質が担保されているか、などを慎重に調査したはずだ。

言語もツールであり、言語に相当する役割を担ったライブラリがあるならば、それは同じ選定基準を持って選択すればよい。すなわち、一緒に生きられるかだ。今メンテナンスしているコードもいつかは寿命が尽きる。その寿命に耐えうるかで言語選定をしたのと同様に選べば良い。

さきほど挙げた3つはどれもOSSだ。すなわち自分自身がメンテナンスをする限り寿命は永遠だ。しかし現実問題としてそれは難しい。

Androidアプリであれば、Androidプラットフォームの寿命が尽きたと同時にそのアプリの寿命は尽きる。それほどまでにAndroid向けアプリはAndroidプラットフォームに依存したコードベースとなっている。だとすればLiveDataはAndroidプラットフォームのためだけにGoogleが開発しているという点で、アプリの寿命に耐えうるかもしれない。

そのAndroidアプリがKotlinで書かれているとしたら、Kotlin言語の寿命が尽きたと同時にそのアプリの既存コードの寿命は尽きる。だとすればKotlin Coroutineはコードの寿命に耐えうるかもしれない。

もちろん期待してはいけない。プラットフォームのAPIから独立したライブラリは容易にクローズできるし、言語機能の一部が非推奨になることもよくある話だ。

しかしもしあなたがRxに依存したコードと別れるべきだと捉えるのだとしたら、そのアプリはRxと一生を共にできなかったのかもしれないし、次のパートナー選びは慎重になるべきだ。一生付き合うことになるかもしれないのだから。

もちろん、リアクティブプログラミングに相当するパラダイムから離れるという選択肢も忘れてはいけない。たとえばFlux、Reduxというアーキテクチャはこれに取って代わるものかもしれない。それにRxの台頭以前はそれがなくともなんとかやってきたはずだ。

さらに言えば、リアクティブプログラミングとの付き合い方を見直すという方法もある。各モジュール内で発行し、サブスクリプションを管理し、他のモジュールへ当該パラダイムを漏れ出させないという方法だ。Kotlin Coroutineなら使い方次第ではこうなるかもしれない。
しかしそれは本当にやりたかったことだろうか。結局のところリアクティブプログラミングのパラダイムによって実現したかったイベント伝播の容易性は失われ、本来のboilerplateを書き続けることになるのではないだろうか。

もし既存のコードがこれまでRxによるイベント伝播にベッタリだとしたら、それらを剥がすコストを払えるだろうか?これは言語を変えるのに相当する労力が必要かもしれない。
もし最初に言語選択を誤ったのだとしたら、はたして他言語への移植を本当にすべきなのか、そのコストは効果に見合うものなのか、よく考えたほうがいい。

How to print your gradle's property in shell

邦題を付けるとしたら、「シェルでGradleのプロパティを取得する方法」とかかな。


Write this:

// root build.gradle
task printMySomething {
    doLast {
        println rootProject.ext.mySomething // set variable to you want
    }
}

And do this:

./gradlew --console=plain --quiet :printMySomething

That's all!

Dynamic Deliveryで `resource integer/google_play_services_version not found.` が出る時の対処

AndroidのDynamic Feature Moduleで開発をする際、下記のようなエラーが出る時がある:

Android resource linking failed
/Users/***/***/app/build/intermediates/merged_manifests/debug/AndroidManifest.xml:***: error: resource integer/google_play_services_version (aka ***:integer/google_play_services_version) not found.
error: failed processing manifest.

詳細を開くと、下記のようになっている:

org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':app:processDebugResources'.
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:95)
    at org.gradle.api.internal.tasks.execution.ResolveTaskOutputCachingStateExecuter.execute(ResolveTaskOutputCachingStateExecuter.java:91)
    at org.gradle.api.internal.tasks.execution.ValidatingTaskExecuter.execute(ValidatingTaskExecuter.java:57)
    at org.gradle.api.internal.tasks.execution.SkipEmptySourceFilesTaskExecuter.execute(SkipEmptySourceFilesTaskExecuter.java:119)
    at org.gradle.api.internal.tasks.execution.ResolvePreviousStateExecuter.execute(ResolvePreviousStateExecuter.java:43)
    at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:93)
    at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:45)
    at org.gradle.api.internal.tasks.execution.ResolveTaskArtifactStateTaskExecuter.execute(ResolveTaskArtifactStateTaskExecuter.java:94)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:56)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:67)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:49)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:315)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:305)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:175)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:101)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:49)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:43)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:355)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:343)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:336)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:322)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:134)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker$1.execute(DefaultPlanExecutor.java:129)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:202)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:193)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:129)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
    at java.lang.Thread.run(Thread.java:745)
Caused by: com.android.builder.internal.aapt.v2.Aapt2Exception: Android resource linking failed
/Users/***/***/app/build/intermediates/merged_manifests/debug/AndroidManifest.xml:***: AAPT: error: resource integer/google_play_services_version (aka ***:integer/google_play_services_version) not found.
    
error: failed processing manifest.
    at com.android.builder.internal.aapt.v2.Aapt2Exception$Companion.create(Aapt2Exception.kt:45)
    at com.android.builder.internal.aapt.v2.Aapt2Exception$Companion.create$default(Aapt2Exception.kt:39)
    at com.android.build.gradle.internal.res.Aapt2ErrorUtils.rewriteException(Aapt2ErrorUtils.kt:97)
    at com.android.build.gradle.internal.res.Aapt2ErrorUtils.rewriteLinkException(Aapt2ErrorUtils.kt:73)
    at com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask$AaptSplitInvoker.invokeAaptForSplit(LinkApplicationAndroidResourcesTask.kt:808)
    at com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask$AaptSplitInvoker.run(LinkApplicationAndroidResourcesTask.kt:669)
    at com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask.doFullTaskAction(LinkApplicationAndroidResourcesTask.kt:262)
    at com.android.build.gradle.internal.tasks.IncrementalTask.taskAction(IncrementalTask.java:106)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:73)
    at org.gradle.api.internal.project.taskfactory.IncrementalTaskAction.doExecute(IncrementalTaskAction.java:47)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:41)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:28)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$2.run(ExecuteActionsTaskExecuter.java:284)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:301)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:293)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:175)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:91)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:273)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:258)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$200(ExecuteActionsTaskExecuter.java:67)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:145)
    at org.gradle.internal.execution.impl.steps.ExecuteStep.execute(ExecuteStep.java:49)
    at org.gradle.internal.execution.impl.steps.CancelExecutionStep.execute(CancelExecutionStep.java:34)
    at org.gradle.internal.execution.impl.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:69)
    at org.gradle.internal.execution.impl.steps.TimeoutStep.execute(TimeoutStep.java:49)
    at org.gradle.internal.execution.impl.steps.CatchExceptionStep.execute(CatchExceptionStep.java:33)
    at org.gradle.internal.execution.impl.steps.CreateOutputsStep.execute(CreateOutputsStep.java:50)
    at org.gradle.internal.execution.impl.steps.SnapshotOutputStep.execute(SnapshotOutputStep.java:43)
    at org.gradle.internal.execution.impl.steps.SnapshotOutputStep.execute(SnapshotOutputStep.java:29)
    at org.gradle.internal.execution.impl.steps.CacheStep.executeWithoutCache(CacheStep.java:134)
    at org.gradle.internal.execution.impl.steps.CacheStep.lambda$execute$3(CacheStep.java:83)
    at java.util.Optional.orElseGet(Optional.java:267)
    at org.gradle.internal.execution.impl.steps.CacheStep.execute(CacheStep.java:82)
    at org.gradle.internal.execution.impl.steps.CacheStep.execute(CacheStep.java:36)
    at org.gradle.internal.execution.impl.steps.PrepareCachingStep.execute(PrepareCachingStep.java:33)
    at org.gradle.internal.execution.impl.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:38)
    at org.gradle.internal.execution.impl.steps.StoreSnapshotsStep.execute(StoreSnapshotsStep.java:23)
    at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:96)
    at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.lambda$execute$1(SkipUpToDateStep.java:91)
    at java.util.Optional.orElseGet(Optional.java:267)
    at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:91)
    at org.gradle.internal.execution.impl.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:36)
    at org.gradle.internal.execution.impl.DefaultWorkExecutor.execute(DefaultWorkExecutor.java:34)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:91)
    ... 35 more
Caused by: com.android.builder.internal.aapt.v2.Aapt2Exception: Android resource linking failed
/Users/***/***/app/build/intermediates/merged_manifests/debug/AndroidManifest.xml:***: error: resource integer/google_play_services_version (aka ***:integer/google_play_services_version) not found.
error: failed processing manifest.

    at com.android.builder.internal.aapt.v2.Aapt2Exception$Companion.create(Aapt2Exception.kt:45)
    at com.android.builder.internal.aapt.v2.Aapt2Exception$Companion.create$default(Aapt2Exception.kt:39)
    at com.android.builder.internal.aapt.v2.Aapt2DaemonImpl.doLink(Aapt2DaemonImpl.kt:191)
    at com.android.builder.internal.aapt.v2.Aapt2Daemon.link(Aapt2Daemon.kt:103)
    at com.android.builder.internal.aapt.v2.Aapt2DaemonManager$LeasedAaptDaemon.link(Aapt2DaemonManager.kt:176)
    at com.android.builder.core.AndroidBuilder.processResources(AndroidBuilder.java:858)
    at com.android.build.gradle.internal.res.LinkApplicationAndroidResourcesTask$AaptSplitInvoker.invokeAaptForSplit(LinkApplicationAndroidResourcesTask.kt:797)
    ... 79 more

原因

これはAndroidアプリのManifestに本来設定されるべきGoogle Play Servicesのバージョン番号メタデータが付与されていないことによるエラーである。
シングルモジュールでの一般的なAndroidアプリ開発では、必要とされるシーンでは自動的に設定される値である。というのも、play-services-basement内に含まれるManifestの内容がManifest Mergerによって自動的に設定されるからだ。

対してDynamic Feature Moduleを用いたマルチモジュール構成では Base APK (多くの場合 :app モジュール) がいわば単一のアプリのように振る舞うため、ビルド時点で必要な値であるにも関わらず アプリに値が含まれないことになってしまう。

たとえば今回 広告系のSDKを新たに導入しようとして発生したのなら、下記のような構成にしているのではないだろうか。

/app/build.gradle:

dependencies {
    ...
    api "com.google.android.play:core:1.6.1"
    ...
}

/feature/build.gradle:

dependencies {
    ...
    implementation 'com.google.android.gms:play-services-ads:18.1.1'
    ...
}

対処

当該の値を追加するモジュールのみ、Base Moduleに含んでしまえばよい。

/app/build.gradle:

dependencies {
    ...
    api "com.google.android.play:core:1.6.1"
    api 'com.google.android.gms:play-services-basement:17.1.0'
    ...
}

/feature/build.gradle:

dependencies {
    ...
    implementation 'com.google.android.gms:play-services-ads:18.1.1'
    ...
}

PIXELAスマートテレビ製品でリモコンが反応しづらいorペアリングできない時

PIXELAのスマートテレビ製品 ( PIX-SMB400, PIX-43VP100, PIX-55VP100 ) において、まれにリモコンが反応しづらかったり、何度試してもリモコンのペアリングができない場合がある。

具体的にどういった現象が起こるかというと、

  • 音声入力を認識しない(リモコンのランプが点灯しない)
  • 再ペアリングを試みるも、テレビ本体や他Bluetooth製品から見えない

などが発生する。この時のリモコンとは、製品付属の製品や別売りのPIX-RM047-BN1などを指す。

この記事執筆時点では、表示されるリモコンの電池残量は 30% 近く残っていた。

原因

まだかなり電池残量があるが、電池残量不足が原因の模様

対処

電池を交換した後に再度試みたところ、正常にリモコンを使用できた。
交換前はリモコンの再ペアリングを試みた際にすぐランプが消灯してしまっていたが、交換後は再ペアリングの際にはランプがちゃんと点灯してくれていた。

VSCodeの C# エクステンション (OmniSharp) が .NET Coreプロジェクトに対しInvalidProjectFileExceptionを吐いて上手くいかない時の対処

Visual Studio Code向けC#エクステンションには、OmniSharpが含まれる。
.NET Core SDKのプロジェクトを上記環境で利用しようとした際、以下のようなエラーが表示され上手く利用できない場合がある:

...
[info]: OmniSharp.MSBuild.Discovery.MSBuildLocator
        Located 2 MSBuild instance(s)
            1: Visual Studio Build Tools 2017 15.9.28307.718 - "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin"
            2: StandAlone 15.0 - "C:\Users\shuma\.vscode\extensions\ms-vscode.csharp-1.20.0\.omnisharp\1.32.20\.msbuild\Current\Bin"
[info]: OmniSharp.MSBuild.Discovery.MSBuildLocator
        Registered MSBuild instance: Visual Studio Build Tools 2017 15.9.28307.718 - "C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin"
...
[warn]: OmniSharp.MSBuild.ProjectManager
        Failed to load project file 'c:\Users\shuma\Documents\my-dotnet-app\MyDotnetApp.csproj'.
c:\Users\shuma\Documents\my-dotnet-app\MyDotnetApp.csproj(1,1)
Microsoft.Build.Exceptions.InvalidProjectFileException: SDK 競合回避モジュールを読み込めませんでした。マニフェスト ファイルは存在しますが、SDK 競合回避モジュール DLL ファイルへのパスが見つかりませんでした。マニフェスト ファイル パス 'C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\MSBuild\15.0\Bin\SdkResolvers\Microsoft.Build.NuGetSdkResolver\Microsoft.Build.NuGetSdkResolver.xml'。SDK 競合回避モジュール パス: C:\Program Files (x86)\Microsoft Visual Studio\2017\BuildTools\Common7\IDE\CommonExtensions\Microsoft\NuGet\Microsoft.Build.NuGetSdkResolver.dll  c:\Users\shuma\Documents\my-dotnet-app\MyDotnetApp.csproj
   場所 Microsoft.Build.Shared.ProjectFileErrorUtilities.VerifyThrowInvalidProjectFile(Boolean condition, String errorSubCategoryResourceName, BuildEventFileInfo projectFile, Exception innerException, String resourceName, Object[] args)
   場所 Microsoft.Build.BackEnd.SdkResolution.SdkResolverLoader.TryAddAssemblyFromManifest(String pathToManifest, String manifestFolder, List`1 assembliesList, ElementLocation location)
   場所 Microsoft.Build.BackEnd.SdkResolution.SdkResolverLoader.FindPotentialSdkResolvers(String rootFolder, ElementLocation location)
   場所 Microsoft.Build.BackEnd.SdkResolution.SdkResolverLoader.LoadResolvers(LoggingContext loggingContext, ElementLocation location)
   場所 Microsoft.Build.BackEnd.SdkResolution.SdkResolverService.Initialize(LoggingContext loggingContext, ElementLocation location)
   場所 Microsoft.Build.BackEnd.SdkResolution.SdkResolverService.ResolveSdk(Int32 submissionId, SdkReference sdk, LoggingContext loggingContext, ElementLocation sdkReferenceLocation, String solutionPath, String projectPath, Boolean interactive)
   場所 Microsoft.Build.BackEnd.SdkResolution.CachingSdkResolverService.<>c__DisplayClass3_0.<ResolveSdk>b__0(String key)
   場所 System.Collections.Concurrent.ConcurrentDictionary`2.GetOrAdd(TKey key, Func`2 valueFactory)
   場所 Microsoft.Build.BackEnd.SdkResolution.CachingSdkResolverService.ResolveSdk(Int32 submissionId, SdkReference sdk, LoggingContext loggingContext, ElementLocation sdkReferenceLocation, String solutionPath, String projectPath, Boolean interactive)
   場所 Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImportsFromUnescapedImportExpressionConditioned(String directoryOfImportingFile, ProjectImportElement importElement, List`1& projects, SdkResult& sdkResult, Boolean throwOnFileNotExistsError)
   場所 Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImports(String directoryOfImportingFile, ProjectImportElement importElement, SdkResult& sdkResult)
   場所 Microsoft.Build.Evaluation.Evaluator`4.EvaluateImportElement(String directoryOfImportingFile, ProjectImportElement importElement)
   場所 Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport)
   場所 Microsoft.Build.Evaluation.Evaluator`4.Evaluate(ILoggingService loggingService, BuildEventContext buildEventContext)
   場所 Microsoft.Build.Evaluation.Project.Reevaluate(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext)
   場所 Microsoft.Build.Evaluation.Project.ReevaluateIfNecessary(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext)
   場所 Microsoft.Build.Evaluation.Project.Initialize(IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext)
   場所 Microsoft.Build.Evaluation.Project..ctor(String projectFile, IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings, EvaluationContext evaluationContext)
   場所 Microsoft.Build.Evaluation.ProjectCollection.LoadProject(String fileName, IDictionary`2 globalProperties, String toolsVersion)
   場所 OmniSharp.MSBuild.ProjectLoader.EvaluateProjectFileCore(String filePath) 場所 D:\a\1\s\src\OmniSharp.MSBuild\ProjectLoader.cs:行 129
   場所 OmniSharp.MSBuild.ProjectLoader.BuildProject(String filePath) 場所 D:\a\1\s\src\OmniSharp.MSBuild\ProjectLoader.cs:行 72
   場所 OmniSharp.MSBuild.ProjectFile.ProjectFileInfo.Load(String filePath, ProjectIdInfo projectIdInfo, ProjectLoader loader) 場所 D:\a\1\s\src\OmniSharp.MSBuild\ProjectFile\ProjectFileInfo.cs:行 94
   場所 OmniSharp.MSBuild.ProjectManager.LoadOrReloadProject(String projectFilePath, Func`1 loader) 場所 D:\a\1\s\src\OmniSharp.MSBuild\ProjectManager.cs:行 304

[fail]: OmniSharp.MSBuild.ProjectManager
        Attempted to update project that is not loaded: c:\Users\shuma\Documents\my-dotnet-app\MyDotnetApp.csproj

あるいは、以下のようなエラーである場合もある:

...
[info]: OmniSharp.MSBuild.Discovery.MSBuildLocator
        Located 2 MSBuild instance(s)
            1: Visual Studio Community 2017 15.4.27004.2002 - "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin"
            2: StandAlone 15.0 - "C:\Users\shuma\.vscode\extensions\ms-vscode.csharp-1.20.0\.omnisharp\1.32.20\.msbuild\Current\Bin"
[info]: OmniSharp.MSBuild.Discovery.MSBuildLocator
        Registered MSBuild instance: Visual Studio Community 2017 15.4.27004.2002 - "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin"
...
[warn]: OmniSharp.MSBuild.ProjectManager
        Failed to load project file 'c:\Users\shuma\Documents\my-dotnet-app\my-dotnet-app.csproj'.
c:\Users\shuma\Documents\my-dotnet-app\my-dotnet-app.csproj(1,1)
Microsoft.Build.Exceptions.InvalidProjectFileException: 指定された SDK 'Microsoft.NET.Sdk' は見つかりませんでした。  c:\Users\shuma\Documents\my-dotnet-app\my-dotnet-app.csproj
   場所 Microsoft.Build.Shared.ProjectErrorUtilities.ThrowInvalidProject(String errorSubCategoryResourceName, IElementLocation elementLocation, String resourceName, Object[] args)
   場所 Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImportsFromUnescapedImportExpressionConditioned(String directoryOfImportingFile, ProjectImportElement importElement, List`1& projects, Boolean throwOnFileNotExistsError)
   場所 Microsoft.Build.Evaluation.Evaluator`4.ExpandAndLoadImports(String directoryOfImportingFile, ProjectImportElement importElement)
   場所 Microsoft.Build.Evaluation.Evaluator`4.EvaluateImportElement(String directoryOfImportingFile, ProjectImportElement importElement)
   場所 Microsoft.Build.Evaluation.Evaluator`4.PerformDepthFirstPass(ProjectRootElement currentProjectOrImport)
   場所 Microsoft.Build.Evaluation.Evaluator`4.Evaluate(ILoggingService loggingService, BuildEventContext buildEventContext)
   場所 Microsoft.Build.Evaluation.Project.Reevaluate(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings)
   場所 Microsoft.Build.Evaluation.Project.ReevaluateIfNecessary(ILoggingService loggingServiceForEvaluation, ProjectLoadSettings loadSettings)
   場所 Microsoft.Build.Evaluation.Project.Initialize(IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectLoadSettings loadSettings)
   場所 Microsoft.Build.Evaluation.Project..ctor(String projectFile, IDictionary`2 globalProperties, String toolsVersion, String subToolsetVersion, ProjectCollection projectCollection, ProjectLoadSettings loadSettings)
   場所 Microsoft.Build.Evaluation.ProjectCollection.LoadProject(String fileName, IDictionary`2 globalProperties, String toolsVersion)
   場所 OmniSharp.MSBuild.ProjectLoader.EvaluateProjectFileCore(String filePath) 場所 D:\a\1\s\src\OmniSharp.MSBuild\ProjectLoader.cs:行 129
   場所 OmniSharp.MSBuild.ProjectLoader.BuildProject(String filePath) 場所 D:\a\1\s\src\OmniSharp.MSBuild\ProjectLoader.cs:行 72
   場所 OmniSharp.MSBuild.ProjectFile.ProjectFileInfo.Load(String filePath, ProjectIdInfo projectIdInfo, ProjectLoader loader) 場所 D:\a\1\s\src\OmniSharp.MSBuild\ProjectFile\ProjectFileInfo.cs:行 94
   場所 OmniSharp.MSBuild.ProjectManager.<>c__DisplayClass30_0.<LoadProject>b__0() 場所 D:\a\1\s\src\OmniSharp.MSBuild\ProjectManager.cs:行 293
   場所 OmniSharp.MSBuild.ProjectManager.LoadOrReloadProject(String projectFilePath, Func`1 loader) 場所 D:\a\1\s\src\OmniSharp.MSBuild\ProjectManager.cs:行 304

[fail]: OmniSharp.MSBuild.ProjectManager
        Attempted to update project that is not loaded: c:\Users\shuma\Documents\my-dotnet-app\my-dotnet-app.csproj

対処

Visual Studio Build Tools 2017 (Build Tools for Visual Studio 2017) をアンインストールすれば上手く動くことを確認した。
具体的には、スタートメニュー内に Visual Studio Installer という名前のアプリケーションが存在するので、これを起動し Visual Studio Build Tools 2017 という項目の記載を探す。当該項目の 詳細▼ から アンインストール、OKと進めばよい。

アンインストールが完了したら、一旦VSCodeを開いていれば終了させてから再度プロジェクトを開き直すとよい。この状態で既に正常動作するようになっているはず。
もしBuildやRunが上手くいかないようなら、プロジェクト内の.vscodeディレクトリを削除して、再度プロジェクトを開き直してみる、というのを試してほしい。

広告まわりのドメイン知識メモ(1)

アドネットワーク

複数の媒体(の広告枠)につながっている広告事業者。
ここへ入稿すると、アドネットワークのロジックによりどこかのメディアへ掲載される。

最近はDSP経由で取ってきたものを表示したりもするらしい。

ADNW

アドネットワークの略称。

アドエクスチェンジ

複数のアドネットワークの広告枠へ出稿できる市場。必ず入札型のimp課金だけになるのが特徴。
セカンドプライスビッディングになる。

DoubleClick Ad Exchange

アドエクスチェンジ。Googleの認定パートナー企業が媒体ないしSSPとして枠を売れるようになる。
現在はGoogle Ad Managerに名称が変わっている。

AdX

DoubleClick Ad Exchange(ないし、現在のGoogle Ad Manager)の略称。

DSP

Demand Side Platform。複数のアドネットワークやSSPがつないでくる。まれに媒体(の広告枠)につながっている場合もある。

SSPからbidを受けると、入札額を返す。落札に成功した旨をSSPから受けると、広告を返す。
bid時にはユーザのターゲティング情報を貰えるので、それを元に入札額を考える。

アドジェネ / Adgene

Ad Geenrationの略称。

Ad Generation

DSP。Supershipが提供する。

SSP

Supply Side Platform。媒体(の広告枠)につながっている。複数のDSPへつなぎにいく。

媒体からimpが発生すると、DSPにユーザのターゲティング情報と一緒にbidを投げる。いちばん高い金額を提示してくれたDSPがいたら、落札する旨をDSPに投げて、広告を貰う。貰った広告は媒体で表示する。

fluct

SSP事業者。

Google AdSense

アドネットワーク。

YDN

Yahoo! ディスプレイアドネットワークのこと。

Yahoo! ディスプレイアドネットワーク

アドネットワーク。

i-mobile

アドネットワーク。

Googleディスプレイネットワーク

アドネットワーク。

アドサーバー

純広告, 自社広告, ADNW, SSP, アドエクスチェンジ とかを入稿しておいて、組んでおいた座組みに応じて表示できるツール。通常は媒体が自前で導入する。
たとえば「純広告 -> 自社広告 -> SSP 」のようにウォーターフォールを組んでおいて、かつ「フロアプライス○○円」とかしておくと、上からフロアプライスを満たすか順々にチェックして、当てはまったタイミングでそれを返す、みたいなことができる。

DFP

DoubleClick for Publisher の略称。現在はGoogle Ad Managerに名称が変わっている。

GAM

Google Ad Manager のこと。

Google Ad Manager

旧DoubleClick for Publishersと、DoubleClick Ad Exchangeの両者を吸収したブランド。
すなわち、アドサーバー および アドエクスチェンジ。

GMA

Google Mobile Ads のこと。

Header Bidding

Google Ad Manager のロジックより前に、他の各SSPへ平行でbidしておき より収益性の高い案件を出す手法。

GAMを用いたウォーターフォールの場合、上流でフロアプライスを満たせた時点でそれが配信されることに決定してしまう。結果、もしより後ろに配置してあったSSPのほうが収益性の高い案件を出せたとしても気付くことができなくなってしまう。

また、GAMのウォーターフォールはシリアルにチェックされるため、実際の広告表示まで遅延が発生する可能性がある。

そこで、GAMのロジックが走り出すよりも前に 各SSPへパラレルでbidし、最も高かったモノをGAMへ通知し表示させることにより、高い収益性と遅延の解消を実現するもの。

またAdXは動的にeCPMを決定する有利な仕組みにあるため、GAM内でAdXを利用している場合には、他SSPとAdXの競売をさせることができるようにもなる。AdXがフロアプライスを満たしウォーターフォール上流でせき止めてしまう状況が続いていた場合、より価格の適正化を図ることができる。

<head/>内で先にbidさせることから、header biddingと呼ばれている模様。

Header Biddingソリューション / Bidderソリューション / Header Bidding ライブラリ

Header Biddingを実現するためのツール(ないし、それを提供する事業者)。
事業者の場合、DSPにつながっている。

Prebid.js

Header Biddingライブラリ。JavaScriptを用い自前でADNWへbidできる。

APS

Amazon Publisher Services のこと。

Amazon Publisher Services

Amazonの提供する媒体向けサービス郡。AWSのようなもの。

TAM

Transparent Ad Marketplace の略称。

Transparent Ad Marketplace

Header Biddingソリューション。複数のDSPに接続されている。 Amazonが提供する Amazon Publisher Services のひとつ。

Client Header Bidding

<head/>内に<script/>を置いて、クライアントサイドで広告biddingから表示まで行う、Header Biddingの方式。

S2S

Server-to-Server。広告ドメインでは、Header Biddingの方式である Server-to-Server Header Bidding のことを指す。

Server-to-Server Header Bidding

バックエンド側でheader biddingを済ませてしまってからクライアントへレスポンスを返す、Header Biddingの方式。

PubMatic OpenWrap

Webサイト向けHeader Biddingソリューション。

PubMatic OpenBid

モバイルのネイティブアプリ向けHeader Biddingソリューション。

IAB

Interactive Advertising Bureauの略称。

Interactive Advertising Bureau

オンライン広告に関するAPIなどの標準化団体(業界団体)。

OpenRTB

IABの策定する、Real Time Biddingに関するAPI規格。ディスプレイ広告だけでなく、動画広告やHeader Biddingなどのリアルタイムbiddingを要するあらゆるAPI仕様も内包する。

MoPub

モバイル専業のアドエクスチェンジおよびSSP事業者。多くの場合はここが提供するSDKのことを指す。
MoPubに入稿されている在庫でRTBした後、広告SSP各社のSDKメディエーションを行う。
基本的に1st lookを取り、メディエートされる側には原則入らない方針の模様。

TAP

タッチスクリーンにおいて画面を軽く押すこと、言い換えればクリックのこと。
広告ドメインでは、Twitter Audience Networkのことを指す場合が多い。

Twitter Audience Network

アドネットワーク。MoPub買い付けも行う。

Androidで "cocos2d: warning: Uniform not found: depth" と出たら

以下のようなログが出力され続け、一向にアプリが立ち上がらない時がある:

2019-06-06 16:01:32.528 29778-29890/***.*****.******** D/cocos2d-x debug info: OpenGL error 0x0502 in /Users/***/***/cocos2d/cocos/./renderer/CCRenderer.cpp saveRenderState 158
2019-06-06 16:01:32.529 29778-29890/***.*****.******** D/cocos2d-x debug info: OpenGL error 0x0502 in /Users/***/***/cocos2d/cocos/./renderer/CCRenderer.cpp restoreRenderState 188
2019-06-06 16:01:32.542 29778-29890/***.*****.******** D/cocos2d-x debug info: cocos2d: warning: Uniform not found: depth

Androidエミュレータの右側 … と表示されたボタンを押して、Settings -> Advanced -> OpenGL ES renderer (requires restart) と書かれた項目を SwiftShader に変えた上、エミュレータを再起動してアプリを走らせ直せば、動く。