kakts-log

programming について調べたことを整理していきます

Android Studio で新規プロジェクト作成後にビルドするとエラーとなる

概要

Android Studioの2025年1月時点の最新版を利用して新規プロジェクトを作成し、ビルドすると下記のエラーが出てしまう問題があり、その解決法をまとめます。

Dependency 'androidx.core:core:1.15.0' requires libraries and applications that
      depend on it to compile against version 35 or later of the
      Android APIs.

      :app is currently compiled against android-34.

      Recommended action: Update this project to use a newer compileSdk
      of at least 35, for example 35.

前提

原因と対処法

解決方法でいうと、 私の環境ではエラー文言にある通り、Gradleのbuild.gradle.kts ファイルで指定しているcompileSdK の値が34になっており、これを35に変更してビルドしたらエラーが解消しました。

android {
    namespace = "com.hoge.sample"
    compileSdk = 35 # ここを34から35にする

利用しているAndroid Studioで対応している最小のAPIバージョンの確認

Android Studioのバージョンによって、Android API レベルをサポートするツールの最小バージョンが異なっていて、 2024.2.1 というバージョンでは最小バージョンが35になっていて、新規プロジェクトで生成される初期設定だとcompileSdkが34で指定されており、最小バージョンの条件を満たしていないためエラーとなりました。

同様の問題が発生した場合、Android Studioの利用バージョンを確認した上で、下記のページに記載されている通り、対応する最小バージョンを指定してあげれば解決します。
developer.android.com

gcovによるテストカバレッジと生成されるgcovデータファイルについて

概要

gccのテストカバレッジツールであるgcovを使ったテストカバレッジ計測と、そのgcovを利用する際に生成される.gcno.gcda などのデータファイルについて簡単に整理します。

gcovとは

gcc.gnu.org

gcovとは、GCCと合わせて利用できる、テストカバレッジ計測ツールです。

gcov データファイルとは

gcovによるテストカバレッジ計測において、2種類のファイルがプロファイリングにおいて使われます。

カバレッジ計測対象の.cファイル対して、同じファイル名でサフィックス異なるファイルが生成されます。
例えば、list.c というファイルに対してのテストカバレッジ結果は、 list.gcno またはlist.gcda という2種類のファイルが生成され、これらは特定のプラットフォームに依存しない形式で保存されたカバレッジとプロファイルデータが含まれています。

.gcnoファイルはカバレッジ計測対象のファイルと同じディレクトリに生成されます。
.gcdaファイルは、デフォルトでは同様に同じディレクトリ配下に生成されますが、GCC-fprofile-dirオプションを指定すると生成先ディレクトリを変更できます。

.gcnoファイルについて

.gcno は notes file(no)を意味し 、ソースファイルが GCCftest-coverageオプションを指定してコンパイルすると生成されます。
このファイルは、基本のブロックグラフと、そのブロックに対するソースコード上の行番号を再構築するための情報を含みます。

.gcdaファイルについて

.gcda はcount data file(cda)を意味し、オブジェクトファイルを含んだソースファイルがGCC-fprofile-arcs オプションを指定してコンパイルされると生成されます。
このオプションによって、オブジェクトファイルごとに.gcdaファイルが生成されます。

このファイルは、 テスト実行における対象のコード内の分岐の遷移回数や、値のプロファイルカウント、さらにはカバレッジ結果のサマリ情報を含みます。

これらのファイルは直接アクセスすることは推奨されていません。 gcovのユーザはgcovツールが--json-formatオプションで提供される中間フォーマットを使うべきとされています。

参考情報

Valkeyのビルド、サーバ起動方法とvalkey-benchmarkを使ったValkeyとredisのベンチマーク実行方法

概要

昨年 redisからフォークされたvalkeyがリリースされました。

redisからvalkeyへは、互換性があるため比較的容易に切り替えができると思います。

ここでは、ローカル環境でvalkeyをビルドして利用する方法と、valkeyの公式で用意されているベンチマークツールであるvalkey-benchmark コマンドによるベンチマークについてまとめます。

調査環境

  • macOS 15.2
  • Valkey 8.0.2
  • redis 7.2.7

valkey-benchmark コマンド

valkey.io

Valkeyのビルド

Valkeyをビルドして、valkey-serverやclientなどのコマンドをローカルで使えるようにします。  

valkey-io/valkey で公式のリポジトリをクローン

github.com

上記のリポジトリをクローンします。 今回はValkey v8.0.2を使うため v8.0.2のタグをチェックアウトします

$ git checkout -b 8.0.2 refs/tags/8.0.2

$ git branch
* 8.0.2
  unstable

Valkeyリポジトリのビルド

ビルド作業自体は簡単で、Makeコマンドにより簡単にビルドできます

make

これにより、 リポジトリのsrcディレクトリ配下に関連するビルドの成果物として、valkey-serverなどのコマンドが存在することを確認できます。

valkey-benchmarkについて

次に、Valkeyのビルドにより生成されたvalkey-benchmarkコマンドを使って、valkeyとredisのベンチマークを実行していきます。

valkeyリポジトリsrcディレクトリ配下に、valkey-server, valkey-benchmarkが生成されているかを確認します。

valkey-benchmarkというコマンドのドキュメントは下記になります。 valkey.io コマンドのオプションとしてvalkeyにリクエストするクライアント数や、総リクエスト数などの値を設定し、 実行できます。

valkey-benchmark -r 10000 -n 10000 lpush mylist __rand_int__

valkey-benchmark -t set -n 1000000 -r 100000000

valkey-benchmark によるベンチマーク実行

ここで、valkey-benchmarkによるベンチマークの実行を行います。

ここで、Valkeyはredisとも互換性があるため、valkey, redisでそれぞれサーバを立てて、valkey-benchmarkによりValkey, Redisそれぞれで同じ条件でベンチマークが実行できます。

ここではそのベンチマーク結果についての説明はしませんが、実行方法は下記となります。

valkeyに対するベンチマーク

まず、valkey-serverコマンドを実行してvalkeyサーバを立ち上げます。
先ほど書いた通り、redisとは互換性があり、Valkeyもデフォルトポートが6379でredisと同じです。

./src/valkey-server
50692:M 11 Jan 2025 00:52:59.061 * oO0OoO0OoO0Oo Valkey is starting oO0OoO0OoO0Oo
50692:M 11 Jan 2025 00:52:59.061 * Valkey version=255.255.255, bits=64, commit=e60990e5, modified=0, pid=50692, just started
50692:M 11 Jan 2025 00:52:59.061 # Warning: no config file specified, using the default config. In order to specify a config file use ./src/valkey-server /path/to/valkey.conf
50692:M 11 Jan 2025 00:52:59.062 * Increased maximum number of open files to 10032 (it was originally set to 2560).
50692:M 11 Jan 2025 00:52:59.062 * monotonic clock: POSIX clock_gettime
                .+^+.                                                
            .+#########+.                                            
        .+########+########+.           Valkey 255.255.255 (e60990e5/0) 64 bit
    .+########+'     '+########+.                                    
 .########+'     .+.     '+########.    Running in standalone mode
 |####+'     .+#######+.     '+####|    Port: 6379
 |###|   .+###############+.   |###|    PID: 50692                     
 |###|   |#####*'' ''*#####|   |###|                                 
 |###|   |####'  .-.  '####|   |###|                                 
 |###|   |###(  (@@@)  )###|   |###|          https://valkey.io      
 |###|   |####.  '-'  .####|   |###|                                 
 |###|   |#####*.   .*#####|   |###|                                 
 |###|   '+#####|   |#####+'   |###|                                 
 |####+.     +##|   |#+'     .+####|                                 
 '#######+   |##|        .+########'                                 
    '+###|   |##|    .+########+'                                    
        '|   |####+########+'                                        
             +#########+'                                            
                '+v+'                                                

50692:M 11 Jan 2025 00:52:59.062 # WARNING: The TCP backlog setting of 511 cannot be enforced because kern.ipc.somaxconn is set to the lower value of 128.
50692:M 11 Jan 2025 00:52:59.063 * Server initialized
50692:M 11 Jan 2025 00:52:59.064 * Loading RDB produced by Redis version 7.2.7
50692:M 11 Jan 2025 00:52:59.064 * RDB age 4 seconds
50692:M 11 Jan 2025 00:52:59.064 * RDB memory usage when created 112.49 Mb
50692:M 11 Jan 2025 00:52:59.482 * Done loading RDB, keys loaded: 994882, keys expired: 0.
50692:M 11 Jan 2025 00:52:59.482 * DB loaded from disk: 0.419 seconds
50692:M 11 Jan 2025 00:52:59.482 * Ready to accept connections tcp

そして、valkey-benchmarkコマンドを実行してみます。

./src/valkey-benchmark -q -n 100000
PING_INLINE: 174825.17 requests per second, p50=0.151 msec                    
PING_MBULK: 187969.92 requests per second, p50=0.151 msec                    
SET: 182815.36 requests per second, p50=0.151 msec                    
GET: 185185.17 requests per second, p50=0.151 msec                    
INCR: 190114.06 requests per second, p50=0.151 msec                    
LPUSH: 168634.06 requests per second, p50=0.151 msec                    
RPUSH: 189035.92 requests per second, p50=0.151 msec                    
LPOP: 186219.73 requests per second, p50=0.151 msec                    
RPOP: 188679.25 requests per second, p50=0.151 msec                    
SADD: 185528.77 requests per second, p50=0.151 msec                    
HSET: 188679.25 requests per second, p50=0.151 msec                    
SPOP: 188323.91 requests per second, p50=0.151 msec                    
ZADD: 188679.25 requests per second, p50=0.151 msec                    
ZPOPMIN: 189035.92 requests per second, p50=0.151 msec                    
LPUSH (needed to benchmark LRANGE): 188679.25 requests per second, p50=0.151 msec                    
LRANGE_100 (first 100 elements): 103950.10 requests per second, p50=0.271 msec                    
LRANGE_300 (first 300 elements): 52521.01 requests per second, p50=0.503 msec                   
LRANGE_500 (first 500 elements): 36218.76 requests per second, p50=0.719 msec                   
LRANGE_600 (first 600 elements): 29316.92 requests per second, p50=0.863 msec                   
MSET (10 keys): 183150.19 requests per second, p50=0.215 msec                    
XADD: 186219.73 requests per second, p50=0.159 msec     

これで Valkeyに対するベンチマークを実行することができました。

redisに対するベンチマーク

続いてredisについてまとめます。 特に変わったことはせず、一度valkey-serverを止めた上でredis-serverを立ち上げ、また同じvalkey-benchmarkのコマンドを実行してあげるだけで完了です。 今回はvalkey redisともに同じデフォルトポートで立ち上げるのですが、使うポートを分けて、valkey-benchmarkで向き先ポートを分けて実行するのもありかと思います。

特に説明はしませんが、 mac OS環境でbrew install redisでredis v7をインストールした上でサーバを起動します。

redis-server

55389:C 11 Jan 2025 00:59:15.438 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
55389:C 11 Jan 2025 00:59:15.438 * Redis version=7.2.7, bits=64, commit=00000000, modified=0, pid=55389, just started
55389:C 11 Jan 2025 00:59:15.438 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
55389:M 11 Jan 2025 00:59:15.438 * Increased maximum number of open files to 10032 (it was originally set to 2560).
55389:M 11 Jan 2025 00:59:15.438 * monotonic clock: POSIX clock_gettime
                _._                                                  
           _.-``__ ''-._                                             
      _.-``    `.  `_.  ''-._           Redis 7.2.7 (00000000/0) 64 bit
  .-`` .-```.  ```\/    _.,_ ''-._                                  
 (    '      ,       .-`  | `,    )     Running in standalone mode
 |`-._`-...-` __...-.``-._|'` _.-'|     Port: 6379
 |    `-._   `._    /     _.-'    |     PID: 55389
  `-._    `-._  `-./  _.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |           https://redis.io       
  `-._    `-._`-.__.-'_.-'    _.-'                                   
 |`-._`-._    `-.__.-'    _.-'_.-'|                                  
 |    `-._`-._        _.-'_.-'    |                                  
  `-._    `-._`-.__.-'_.-'    _.-'                                   
      `-._    `-.__.-'    _.-'                                       
          `-._        _.-'                                           
              `-.__.-'                                               

55389:M 11 Jan 2025 00:59:15.439 # WARNING: The TCP backlog setting of 511 cannot be enforced because kern.ipc.somaxconn is set to the lower value of 128.
55389:M 11 Jan 2025 00:59:15.440 * Server initialized
55389:M 11 Jan 2025 00:59:15.440 * RDB age 4 seconds
55389:M 11 Jan 2025 00:59:15.440 * RDB memory usage when created 79.60 Mb
55389:M 11 Jan 2025 00:59:15.806 * Done loading RDB, keys loaded: 994882, keys expired: 0.
55389:M 11 Jan 2025 00:59:15.806 * DB loaded from disk: 0.367 seconds
55389:M 11 Jan 2025 00:59:15.806 * Ready to accept connections tcp

あとは先ほどのvalkey-benchmarkコマンドを実行してみます。

./src/valkey-benchmark -q -n 100000
PING_INLINE: 181488.20 requests per second, p50=0.151 msec                    
PING_MBULK: 188679.25 requests per second, p50=0.151 msec                    
SET: 187265.92 requests per second, p50=0.151 msec                    
GET: 183150.19 requests per second, p50=0.151 msec                    
INCR: 188323.91 requests per second, p50=0.151 msec                    
LPUSH: 185528.77 requests per second, p50=0.151 msec                    
RPUSH: 184501.84 requests per second, p50=0.151 msec                    
LPOP: 182481.77 requests per second, p50=0.151 msec                    
RPOP: 187969.92 requests per second, p50=0.151 msec                    
SADD: 189753.31 requests per second, p50=0.151 msec                    
HSET: 185873.61 requests per second, p50=0.151 msec                    
SPOP: 187617.27 requests per second, p50=0.151 msec                    
ZADD: 189035.92 requests per second, p50=0.151 msec                    
ZPOPMIN: 189753.31 requests per second, p50=0.151 msec                    
LPUSH (needed to benchmark LRANGE): 187617.27 requests per second, p50=0.151 msec                    
LRANGE_100 (first 100 elements): 108108.11 requests per second, p50=0.279 msec                    
LRANGE_300 (first 300 elements): 52493.44 requests per second, p50=0.575 msec                   
LRANGE_500 (first 500 elements): 33300.03 requests per second, p50=1.023 msec                   
LRANGE_600 (first 600 elements): 28034.76 requests per second, p50=1.167 msec                   
MSET (10 keys): 192678.23 requests per second, p50=0.175 msec                    
XADD: 186219.73 requests per second, p50=0.159 msec  

これでredisに対してもvalkey-benchmarkによるベンチマークが実行できました。

ざっと比較してみるとややValkeyの方がスループットが高いですが、大差はなく、もっと複雑な状況下、大きい負荷をかけた時に違いが出てくるのだと思います。
valkey-benchmarkによるベンチマークが実行できたので、これにより色々な条件でValkey, redisのベンチマークを比較することができます。

rust 非同期I/Oライブラリのtokio でasync fnを使う際にrt-multi-threadを有効にする

概要

rustで 非同期I/Oライブラリのtokioを使って、async fnを利用する際、実行時に下記エラーとなった際の対処法をまとめます。

error: The default runtime flavor is multi_thread, but the rt-multi-thread feature is disabled.

今回は簡単なweb apiを実装する際を例に挙げます。

コード

Cargo.toml

[package]
name = "web-api"
version = "0.1.0"
edition = "2021"

[dependencies]
axum = "0.8.0"
tokio = "1.42.0"

axumというwebフレームワークと、tokioという非同期I/Oライブラリを使って、3000番ポートでリクエストを受け付ける簡単なサーバを実装します。

src/main.rs

use axum::{
    routing::get,
    Router,
};

#[tokio::main]
async fn main() {
    let app = Router::new().route("/", get(|| async { "Hello, World!" }));

    // port 3000でリクエストを待ち受ける
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
    axum::serve(listener, app).await.unwrap();
}

この際、main()関数は asyncキーワードと #[tokio::main] 属性を記述しています。

このコードを起動時に、先ほど説明した通り、下記エラーになりました。

error: The default runtime flavor is `multi_thread`, but the `rt-multi-thread` feature is disabled.
 --> src/main.rs:6:1
  |
6 | #[tokio::main]
  | ^^^^^^^^^^^^^^
  |
  = note: this error originates in the attribute macro `tokio::main` (in Nightly builds, run with -Z macro-backtrace for more info)

エラーを読んでみると、 tokioを利用する際にはランタイムフレーバーを選ぶことができますが、デフォルトだとmulti_thread というフレーバーになっているようです。
このフレーバーを使う際は、 tokiort-multi-threadというフィーチャーフラグを有効にする必要がありますが、上述したコード・tomlの設定だとそれが無効になっているようでした。

tokioのフィーチャーフラグで rt-multi-thread 機能を有効にする

Cargo.tomlのtokiofeatures項目で tokioのフィーチャーフラグの有効・無効の設定ができます。

docs.rs

設定例

tokio = { version = "1", features = ["rt", "net"] }

tokiort-multi-thread 機能を有効にするには、 rt-multi-threadというフィーチャーフラグを有効にする必要があります。

rt-multi-thread: Enables the heavier, multi-threaded, work-stealing scheduler.

他にも複数の機能があるのですが、それらをまとめて有効にする際には fullを指定してあげれば他の機能も全て有効にできるようです。

full: Enables all features listed below except test-util and tracing.

そのため、今回発生したエラーに関しては、 Cargo.tomlでfeaturesにfullrt-multi-thread を指定してあげることで マルチスレッドの機能が利用でき、解決できます。

tokio = { version = "1.42.0", features = ["full"]}

もしくは

tokio = { version = "1.42.0", features = ["rt-multi-thread"]}

となります。 実運用においては、必要な機能だけ使えるようにする方が良いので、rt-multi-threadの指定がベターかと思います。

github.com

新刊「ソフトウェアアーキテクトのための意思決定術」の内容レビューに関わらせていただきました。

先日

2024年12月11日に発売予定の「スタッフエンジニアの道 - 優れた技術専門職になるためのガイド」の翻訳内容のレビューに関わらせていただき、先日 書籍をご恵贈いただきました。


f:id:kakts:20241231230819j:image

book.impress.co.jp

 

 

ソフトウェアアーキテクトとして、サービスを持続的に成長させるためのアーキテクチャ設計の手法と、それを進めるための意思決定と組織に浸透させるための考え方について丁寧にまとめられており、私自身のテックリードとしての経験を振り返ってみて、翻訳レビューに参加させていただきながら学べる点が多く、非常に内容の濃い書籍だと思っております。

 

翻訳者の島田 浩二さんの記事でも紹介されています。

snoozer05.hatenablog.jp

 

島田さん翻訳の書籍については、先日関わらせていただいた「スタッフエンジニアの道」の翻訳レビューに引き続きとなります。

シニアエンジニアにとって指針となる書籍の制作に関わらせていただき、大変光栄です。

立て続けに翻訳レビューに関わらせていただき、自分自身としても将来1つの目標としている技術書の執筆・翻訳についてもいい経験を積むことができ、また今後のエンジニアとしての仕事において有意義な知識を得ることができました。  

 

翻訳者の島田 浩二さん、版元の株式会社インプレス さんにはこのような機会を頂けたことに大変感謝しております。ありがとうございました。  

 

シニアエンジニア、ソフトウェアの設計の経験がそこまでない方など、幅広く学びになるポイントがある書籍ですので、ぜひ手にとってみてください。

 

 

kakts.dev

「Linuxプログラミングインタフェース」を通読してみて

この記事は、Linux Advent Calendar 2024 第24日目の記事です。
Linux - Qiita Advent Calendar 2024 - Qiita

https://www.oreilly.co.jp/books/9784873115856/

OSまわりのインプットをしたいと思い、先日1年以上かけてLinuxプログラミングインタフェースという 約1600ページもある本を読み終わりました。

その中で、個人的に特に興味深く読めた項目について、かいつまんで取り上げます。

Linuxプログラミングインタフェース」で興味深かった項目について

第2章 基礎概念

OSのカーネルや、ファイルI/O、プロセス、シグナル、スレッドなど、基本的な機能についての概略がまとめられています。

第3章 システムプログラミング

システムコールについて、そしてシステムコールを扱って実装されているライブラリ関数や標準のCライブラリであるglibcなどについての説明があります。

アプリケーションがCのラッパー関数経由でシステムコールを実行するにあたり、 そのラッパー関数がシステムコールを呼び出し、それによりユーザモードからカーネルモードへ移行し、指定したシステムコールに対応する処理を実行する流れについての説明が詳しく書かれています。

第4〜5章 ファイルI/O

ファイル操作の基本となるファイルI/Oについての説明があり open(), read() write()などのシステムコールを使ってファイルの読み書き、クローズの基本的な操作について説明されています。

また、ファイル操作において重要なファイルディスクリプタについても丁寧二節笑みしてあったり、ファイルに対するフラグやファイルディスクリプタの複製、ノンブロッキングI/Oについての説明もとても面白く読めました。

第6章 プロセス

Linuxにおけるプロセスについての説明や、プロセスID、メモリレイアウトや、メモリ管理において重要なスタック・スタックフレームも扱っています。

第7章 メモリ割り当て

malloc()、free()など、ヒープ上のメモリ割り当て、解放についてサンプルコードを動かしつつ理解ができました。

第13章 I/Oのバッファリング

システムコールや、stdioなどの標準ライブラリ関数におけるデータのバッファリングについての説明がありました。 ユーザプロセスとカーネルのやりとりににおける速度や効率を改善するために、データを毎回やり取りせず、メモリ上でバッファして、指定したサイズごとにデータをやり取りするという考え方と、それに伴うstdio関数での注意点についてが非常に勉強になりました。

また、バッファサイズによるI/O性能の影響についても興味深かったです。

第20〜22章 シグナルについて

カーネル->プロセス、あるプロセスから別のプロセスにおけるシグナル送信と、その送信方法について詳しく説明されています。 また、プロセスが特定のシグナルを受け取った際に行う処理を定義するシグナルハンドラや、シグナルをブロックするシグナルマスクについても概要を理解することができました。

第24〜28章 プロセスについて

プロセスの作成、終了の基本的な操作や、親プロセスが作成した子プロセスに対する監視と、ゾンビプロセスについて、プロセスに関する基礎知識がまとまっています。

また、プロセス作成において利用できるfork()とexecve()の違いについてコードを書きながら理解することができました。

第29章〜33章 スレッドについて

Pthreads APIを利用したスレッドの作成や終了、そしてスレッドとプロセスの違いなどスレッドの基礎知識についての説明がありました。 また、mutexを使った同期や、スレッドセーフ(リエントラント)についての考え方、注意点が大変勉強になりました。

第37章 デーモン

以前の章で説明したプロセスの作成方法などを踏まえて、バックグラウンドで動作し、特定の制御端末を持たないデーモンについての詳細な説明と、デーモンプロセスの作り方について学ぶことができました。

第56〜61章 ソケット

同一ホストやネットワークを介した異なるホスト間で通信するためのソケットについて基本的な概要から、UNIXドメインソケットやTCP/IPの基礎、インターネットドメインについてもかなり詳しくまとめられていました。 特にwebサービスなどを開発するエンジニアにとっては繰り返し読んで理解しておきたい項目でした。 また、socket()によるソケット作成、bind()によるソケットアドレスのバインドやlistenを行い、サーバとクライアントを実装して手を動かしてソケット通信を理解できました。

書籍内のコードのハンズオンについて

システムコールを扱っている項目では、そのシステムコールを使ったサンプルコードが提示されており、実際にコードを書いて手を動かして確認できるようになっています。
ただ、Linuxの環境がなかったため、ローカルPCでubuntuのDockerコンテナを立ち上げ、そのコンテナ内でプログラムを実行する方法で進めました。

この本の公式のサイトでソースコードがまとめられているのですが、実際にビルドして実行する環境までは整っていなかったため、github上でこの本のコードをあげている方のリポジトリを参考にしながら、ソースコードのビルドしてプログラムの実行もできる環境を整えました。 man7.org

下記のリポジトリにまとめているので、もしコードを書きながら読み進めたい方は参考にしてみてください。
github.com

Linuxプログラミングインタフェース」を読んでみてどうだったか

長期間少しずつ読み進めるなかで、Linuxの各機能についての歴史的な経緯の説明があったり、ファイル操作、プロセス、スレッド、ソケットなどを扱うためのシステムコールの利用例をコードを書きながら学ぶことができ、Linuxを扱う上での基礎を身につけることができ、とても学びの多い本でした。

1600ページもあるため、全部の項目についての知識定着はできていないですが、重要な項目にきついてはコードを書き、Dockerコンテナ上で動作確認を進めることができ理解を深めることができました。

OS周りの知識が一通り取り上げられているので、今後業務や個人で扱う際に、各機能について頭の中でインデックスを構築することができ、通読できてよかったと思っております。 量は多いですが、1回通読して終わるのは勿体無いくらい、体型的に知識がまとまっていて、個人的にソケットやファイル操作、プロセス、スレッドなどの章はたびたび繰り返し読み返して知識を定着させたいと思いました。

各章は意外と丁寧に優しく書いてあるので、ある程度詳しい方だけでなくLinux周りの経験が浅い人にもおすすめしたいです。

VPN接続時にAndroid Studioで「loading devices」の表示のままデバイス選択ができなくなる

概要

Android Studioにおいて、Androidアプリの開発時の動作確認にVirtual Deviceを使う場合、デバイス選択項目の箇所に「loading devices」と表示が出たままでDeviceが選択できなくなる場合があります。

原因は複数あるかと思いますが、私の環境で直面した原因の1つについて整理します。

前提条件

  • Android Debug: Bridge version 1.0.41
  • Android SDK: Version 35.0.2-12147458
  • Local OS: Running on Darwin 24.1.0 (arm64)

まとめ

VPN接続下でADB(Android Debug Bridge)のサーバが起動しないのが原因のようでした。

  • 今回はAndroid Virtual Deviceを利用し、Android Studio上でVirtual Deviceでアプリの動作確認をしようとしていた。
  • Virtual Deviceを利用する際にデバイスと接続するためのadb (Android Debug Bridge) コマンドでのadbサーバ を起動時にエラーが発生していた。
  • Android Studio のNotificationsタブでログを確認すると、Android sdk内のplatform-tools内に含まれているadbの起動が失敗するエラーが出ていた。
'/Users/hoge/Library/Android/sdk/platform-tools/adb start-server' failed -- run manually if necessary
  • adbのstart-server実行時にudp socketへのbindを試みている処理でエラーが出ている
./adb start-server
* daemon not running; starting now at tcp:5037
ADB server didn't ACK
Full server startup log: /var/folders/t1/cm12lwy17mgbt4nkxr4_csj40000gn/T//adb.501.log
Server had pid: 9223
--- adb starting (pid 9223) ---
12-19 23:42:11.709  9223 300100 I adb     : main.cpp:63 Android Debug Bridge version 1.0.41
12-19 23:42:11.709  9223 300100 I adb     : main.cpp:63 Version 35.0.2-12147458
....
12-19 23:42:11.709  9223 300100 I adb     : main.cpp:63 Running on Darwin 24.1.0 (arm64)
12-19 23:42:11.709  9223 300100 I adb     : main.cpp:63 
12-19 23:42:11.713  9223 300100 I adb     : auth.cpp:416 adb_auth_init...
12-19 23:42:11.713  9223 300100 I adb     : auth.cpp:152 loaded new key from '/Users/hoge/.android/adbkey' with fingerprint ...
12-19 23:42:11.714  9223 300100 I adb     : udp_socket.cpp:170 AdbUdpSocket fd=9
12-19 23:42:11.714  9223 300100 I adb     : udp_socket.cpp:274 SetMulticastOutboundInterface for index=12
12-19 23:42:11.714  9223 300100 I adb     : udp_socket.cpp:533 bind endpoint=0.0.0.0:5353
12-19 23:42:11.714  9223 300100 I adb     : udp_socket.cpp:170 AdbUdpSocket fd=11
12-19 23:42:11.714  9223 300100 I adb     : udp_socket.cpp:274 SetMulticastOutboundInterface for index=14
12-19 23:42:11.714  9223 300100 I adb     : udp_socket.cpp:558 bind endpoint=[0000:0000:0000:0000:0000:0000:0000:0000]:5353 scope_id=0
12-19 23:42:11.714  9223 300100 I adb     : udp_socket.cpp:170 AdbUdpSocket fd=12
12-19 23:42:11.714  9223 300100 I adb     : udp_socket.cpp:274 SetMulticastOutboundInterface for index=16
12-19 23:42:11.714  9223 300100 I adb     : udp_socket.cpp:558 bind endpoint=[0000:0000:0000:0000:0000:0000:0000:0000]:5353 scope_id=0
12-19 23:42:11.738  9223 300100 F adb     : udp_socket.cpp:447 Check failed: static_cast<size_t>(num_bytes_sent) == length (static_cast<size_t>(num_bytes_sent)=12, length=33) 

upd socketに対して何かしらを送信している箇所でのチェックに失敗している。

今回の原因

  • 私のローカルPCでVPNに接続しており、VPN接続している際に上記のチェックに失敗してしまっていた。
    • VPNを有効にしている際に、ローカル環境でのネットワークの設定変更が悪さをしているようです。
    • VPNを使わないようにし、Androis Studio再起動でadb start-serverが成功しました。
* daemon started successfully

別の対処方法

  • VPNを接続した状態でもadb serverを起動できるようです。
ADB_MDNS_OPENSCREEN=0 adb start-server
  • Android SDK 35.0.2では ローカル環境でのデバイスを見つける際に使われているmDNSの設定に変更があり mDNSがデフォルトで有効になっているようです。 (確認し切れてはいないですが、おそらくこの辺りの変更が原因となっていそうです。) developer.android.com `ADB_MDNS_OPENSCREENという環境変数を0にすることで無効化し、VPN接続下でもadb serverを起動できるようです。
ADB_MDNS_OPENSCREEN=0 adb start-server
- 不完全なクエリでサーバーが停止する、openscreen mDNS バックエンドのバグ(問題 #294120933)を修正。
- macOS で openscreen mDNS バックエンドを動作可能に
- すべてのプラットフォームで openscreen mDNS バックエンドをデフォルトにする。
- 診断目的で USB SuperSpeed+(現在の速度とネゴシエートされた速度)を検出するサポート。
- 正常なシャットダウン: シャットダウン時にすべての USB インターフェースを解放します(すべての OS)。

ローカルでのmDNSデーモンが稼働しているかのチェック方法

adb mdns check を実行するとローカル環境でのmDNSのデーモンが稼働しているかをチェックできます。

試してみると、VPN接続時にmDNSデーモンの稼働チェックに失敗しており、デーモンが稼働しておらず、VPN接続を解除によりデーモンが稼働しているのを確認できました。

---- VPN接続中 -------
./adb mdns check
* daemon not running; starting now at tcp:5037
ADB server didn't ACK
Full server startup log: /var/folders/t1/cm12lwy17mgbt4nkxr4_csj40000gn/T//adb.501.log
Server had pid: 12024
--- adb starting (pid 12024) ---
12-20 00:31:51.337 12024 533719 I adb     : main.cpp:63 Android Debug Bridge version 1.0.41
12-20 00:31:51.337 12024 533719 I adb     : main.cpp:63 Version 35.0.2-12147458
12-20 00:31:51.337 12024 533719 I adb     : main.cpp:63 Installed as 

....
12-20 00:31:51.342 12024 533719 I adb     : udp_socket.cpp:170 AdbUdpSocket fd=11
12-20 00:31:51.342 12024 533719 I adb     : udp_socket.cpp:274 SetMulticastOutboundInterface for index=12
12-20 00:31:51.342 12024 533719 I adb     : udp_socket.cpp:533 bind endpoint=0.0.0.0:5353
12-20 00:31:51.342 12024 533719 I adb     : udp_socket.cpp:170 AdbUdpSocket fd=12
12-20 00:31:51.342 12024 533719 I adb     : udp_socket.cpp:274 SetMulticastOutboundInterface for index=14
12-20 00:31:51.342 12024 533719 I adb     : udp_socket.cpp:558 bind endpoint=[0000:0000:0000:0000:0000:0000:0000:0000]:5353 scope_id=0
12-20 00:31:51.342 12024 533719 I adb     : udp_socket.cpp:170 AdbUdpSocket fd=13
12-20 00:31:51.342 12024 533719 I adb     : udp_socket.cpp:274 SetMulticastOutboundInterface for index=16
12-20 00:31:51.342 12024 533719 I adb     : udp_socket.cpp:558 bind endpoint=[0000:0000:0000:0000:0000:0000:0000:0000]:5353 scope_id=0
12-20 00:31:51.342 12024 533729 I adb     : transport.cpp:313 emulator-5554: write thread spawning
12-20 00:31:51.342 12024 533728 I adb     : transport.cpp:341 emulator-5554: read thread spawning
12-20 00:31:51.346 12024 533719 I adb     : transport.cpp:1710 fetching keys for transport emulator-5554
12-20 00:31:51.346 12024 533719 I adb     : auth.cpp:468 Calling send_auth_response
12-20 00:31:51.350 12024 533719 I adb     : adb.cpp:181 emulator-5554: offline
12-20 00:31:51.378 12024 533719 F adb     : udp_socket.cpp:447 Check failed: static_cast<size_t>(num_bytes_sent) == length (static_cast<size_t>(num_bytes_sent)=13, length=45) 

* failed to start daemon
adb: failed to check server version: cannot connect to daemon

----- ここで VPNの接続を解除する ---------

./adb mdns check
mdns daemon version [Openscreen discovery 0.0.0]

ADB (Android Debug Bridge)について

ADBについての情報は下記のページで詳細を確認できます。 developer.android.com