kokoakumaのメモ (original) (raw)

概要

組立時の情報を残していなかったので、メモ

2021/10/01購入&組立

メモ

ちょうどグラボが高騰してた。WIFIが機能せず、USBに挿すタイプで代用した。

KSP2における変更

android-developers.googleblog.com

上のAndroid Developersのブログ記事にて様々な変更点が紹介されています。その中でも特にスタンドアローンに実行できる点が気になったため試してみました。

エントリーポイントのあるライブラリとして実装されているため、デバッグやテストが容易になっているらしいです。

テストを書いてみる

試しにコード生成の単体テストを書いてみます。

依存関係を追加する

まずは依存関係を追加します。

implementation("com.google.devtools.ksp:symbol-processing-aa-embeddable:")

// もし足りない場合は以下の依存関係も追加する implementation("com.google.devtools.ksp:symbol-processing-api:") implementation("com.google.devtools.ksp:symbol-processing-common-deps:")

実行したいモジュールに任意のバージョンのsymbol-processing-aa-embeddableを追加します(執筆時点の最新は2.0.0-RC2-1.0.20)。 筆者の手元の環境では、KSPLoggerとKSPJvmConfigの情報が足りなかったため、symbol-processing-apiとsymbol-processing-common-depsも追加しています。

テストを書く

Processorを書いている前提で簡単なテストコードを書いてみます。

以下が最終的なテストコードです。

class SampleTest { // 生成ファイル用のDirの生成 @JvmField @Rule val kotlinOutputDirFolder = TemporaryFolder() // その他のファイル用のDir生成 @JvmField @Rule val dummyFolder = TemporaryFolder()

@Test
fun sample_test() {
    // 生成ファイル用のFileインスタンス生成
    val annotationFolder = kotlinOutputDirFolder.newFolder(
        "com", "sample", "annotation"
    )
    val generatedFile = File(annotationFolder, "GeneratedFile.kt")

    // Processorが処理するファイルの準備
    val projectFile = File(dummyFolder.root, "Project.kt")
    projectFile.writeText(
        """
        package com.sample.ksp

        @TargetAnnotation
        Interface Hoge

        annotation class TargetAnnotation()
        """.trimIndent(),
    )

    // Configの生成
    val kspConfig = KSPJvmConfig.Builder().apply {
        kotlinOutputDir = kotlinOutputDirFolder.root
        javaOutputDir = dummyFolder.newFolder("java")
        outputBaseDir = dummyFolder.newFolder("base")
        resourceOutputDir = dummyFolder.newFolder("resource")
        cachesDir = dummyFolder.newFolder("cache")
        classOutputDir =  dummyFolder.newFolder("class")
        jvmTarget = JvmTarget.DEFAULT.description
        moduleName = "<module_name>"
        sourceRoots = listOf(projectFile)
        javaSourceRoots = listOf()
        commonSourceRoots = listOf()
        projectBaseDir = dummyFolder.root
        languageVersion = "<version>"
        apiVersion = "<version>"
    }.build()
    val exitCode = KotlinSymbolProcessing(
        kspConfig, 
        listOf(HogeProcessorProvider()),
        TestKSPLogger()
    ).execute()

    assertTrue(
        exitCode == KotlinSymbolProcessing.ExitCode.OK 
            && generatedFile.exists()
    )
}

}

class TestKSPLogger: KSPLogger { override fun error(message: String, symbol: KSNode?) { // operation } override fun exception(e: Throwable) { // operation } override fun info(message: String, symbol: KSNode?) { // operation } override fun logging(message: String, symbol: KSNode?) { // operation } override fun warn(message: String, symbol: KSNode?) { // operation } }

順番に見ていきます。

フォルダを用意する

Configを作成する際、ファイルの読み込み先と出力先となるディレクトリを渡す必要があるようです。今回はTemporaryFolderを使用してフォルダを生成しました。

@JvmField @Rule val dummyFolder = TemporaryFolder()

テストメソッド毎にフォルダを再作成できるように、Ruleアノテーションを付けておきます。 また、Kotlinで使用する場合はJvmFieldアノテーションも必要みたいです。

読み込むファイルを用意する

SymbolProcessorの処理対象となるファイルを用意します。

val projectFile = File(dummyFolder.root, "Project.kt") projectFile.writeText( """ package com.example.ksp

@TargetAnnotation
Interface Hoge

annotation class TargetAnnotation()
""".trimIndent(),

)

Configを用意する

諸々の設定情報を渡すためにKSPJvmConfigを用意する必要があります。

val kspConfig = KSPJvmConfig.Builder().apply { kotlinOutputDir = kotlinOutputDirFolder.root javaOutputDir = dummyFolder.newFolder("java") outputBaseDir = dummyFolder.newFolder("base") resourceOutputDir = dummyFolder.newFolder("resource") cachesDir = dummyFolder.newFolder("cache") classOutputDir = dummyFolder.newFolder("class") jvmTarget = JvmTarget.DEFAULT.description moduleName = "" sourceRoots = listOf(projectFile) javaSourceRoots = listOf() commonSourceRoots = listOf() projectBaseDir = dummyFolder.root languageVersion = "" apiVersion = "" }.build()

先ほど作成した出力先のフォルダをkotlinOutputDirに、読み込むファイルをsourceRootsに設定します。残りはよしなに埋めておきます。

Loggerを用意する

KotlinSymbolProcessingにKSPLoggerを実装したクラスを渡す必要があります。

class TestKSPLogger: KSPLogger { override fun error(message: String, symbol: KSNode?) { // operation } override fun exception(e: Throwable) { // operation } override fun info(message: String, symbol: KSNode?) { // operation } override fun logging(message: String, symbol: KSNode?) { // operation } override fun warn(message: String, symbol: KSNode?) { // operation } }

KSPの処理を実行する

作成しておいたConfigとSymbolProcessorProvider、Loggerを渡して、executeメソッドを呼びます。今回は、返り値のExitCodeと生成ファイルを用いてassertしておきます。

val exitCode = KotlinSymbolProcessing( kspConfig, listOf(HogeProcessorProvider()), TestKSPLogger() ).execute() assertTrue( exitCode == KotlinSymbolProcessing.ExitCode.OK && generatedFile.exists() )

参考リンク

Is there any guidance or example project for how to use KSP2 kotlinlang #ksp

Prepare for K2 by ZacSweers · Pull Request #196 · ZacSweers/kotlin-compile-testing · GitHub

概要

https://developer.android.com/design-for-safety/privacy-sandbox/introduction

ユーザープライバシー保護を目的としていて、主に以下二つの解決策が盛り込まれているものです。 (あくまでプロポーザルの段階)

SDK Runtime

https://developer.android.com/design-for-safety/privacy-sandbox/sdk-runtime SDK Runtimeは、サードパーティSDKを専用のランタイム環境で実行できるようにするもの (アプリのサンドボックス環境と似たもの) アプリはSDKのインタフェースを介して、SDKランタイム内のSDKにアクセスするらしいです。 このSDKとの分離はSDKの配布方法とも関連していて、以下のようなメカニズムが紹介されていました。

  1. SDK提供者は直接プレイストアにSDKをアップロードする
  2. アプリ開発者はアプリとSDKのバージョン指定情報をプレイストアにアップロードする
  3. プレイストア上で両者を組み合わせる

プライバシー保護API

プライバシー保護APIは、アプリ間の識別子に依存せずに広告のパーソナライズと測定をサポートするもの

Topic

ユーザーの過去のアプリ使用内容に基づいて大まかな広告のトピックを推測するもの これによって、個々のユーザーをアプリ間で追跡することなく、興味・関心に基づく広告(IBA)のユースケースをサポートできるらしいです。

Protected Audience

(よくわからない)

Attribution Report

広告IDのようなクロスパーティなユーザー ID への依存を排除することでユーザーのプライバシーを向上しつつ、アプリとウェブ全体でのアトリビューションとコンバージョン測定の主要なユースケースをサポートできるらしいです。

影響ありそうなサードパーティ

インストールと遷移元の広告の紐付け

例)Adjust、AppFlyer

紐づける際の根拠として広告IDを使用しているため、影響を受ける可能性大です。https://help.adjust.com/ja/article/attribution-methodsAdjustは既にブログを出しており、該当部分の内容は以下のようになっていました。

https://www.adjust.com/ja/blog/what-google-privacy-sandbox-for-android-s-beta-launch-means-for-app-marketers/ https://www.adjust.com/ja/blog/privacy-sandbox-on-android-mobile-attribution/ https://developer.android.com/design-for-safety/privacy-sandbox/attribution?hl=ja#cross-network-attrib-without-redirects

Firebase

デフォルトだと広告IDを取得しているようですが、どう影響するかは追えていないです。https://firebase.google.com/docs/analytics/configure-data-collection?hl=ja&platform=android#disable_advertising_id_collection

iOSのAnalyticsWithoutAdIdSupportの質問↓https://stackoverflow.com/questions/70166513/what-are-the-downsides-of-using-firebase-analytics-without-ad-id-analyticswitho

マーケ運用ツール

例)KARTE

KARTEでは広告IDを渡すメソッドが用意されているため、広告IDに基づいた分類や連携を行なっているか確認していただくと良さそうです。https://support.karte.io/post/13v6pgNyNKtrxyIvYZcnAs

広告

例)AdMob、Facebook広告SDK

Topicやattributionについて、案内が出始めています。https://developers.google.com/admob/android/privacy/sandbox https://developer.android.com/design-for-safety/privacy-sandbox/attribution#extended-debugging-reports

初見では分かりづらいエラーだったため、備忘録として残しています。

error while writing ...$1.class (Permission denied...)と怒られた

Androidのテストケース名を日本語で書いていた時、ローカルでは問題なく通るものの、GitHub ActionsのCI上でerror while writing ...$1.class (Permission denied...)と怒られてしまいました。

他の日本語で書かれているテストは問題なく成功しているのに、これだけ落ちるのはどういうことなんだ、、、と途方に暮れていましたが、メンバーの方から「文字数が多いとそのエラーが出るよ」と教えてもらいました。

android-guidelines/unit_test_guideline/unit_test_guideline.md at master · Trendyol/android-guidelines · GitHub

実際にテストケース名を短くしてみたところ、上記エラーは出なくなりました(初見で絶対分からん)。

ただ、これで終わりにするのも気持ち悪いのでもう少し調べておきます。

エラーの発生原因

stackoverflow.com

このページのコメント欄を見ると、Linuxのファイル名が最大255bytesまでであり、ファイル名またはファイル名 + パスがその上限に引っかかっているのではないかというやりとりがされています。

実際に自分が最初に書いたケース名を確認してみると、

HogeTest$$1.class

で260bytesとなっており、上記の話に当てはまりそうでした。

ライブラリが提供しているモジュールをインジェクトする時と同様に、 provideメソッドを書いてあげれば大丈夫。

@Module @InstallIn(SingletonComponent::class) class RepositoryModule {

    @Provides     @Singleton     fun provideHogeRepository(         httpClient: HttpClient,         @IODispatcher         dispatcher: CoroutineDispatcher     ): HogeRepository {         return HogeRepositoryImpl(             httpClient,             dispatcher         )     } }

interface HogeRepository //

class HogeRepositoryImpl //