Flutter|UDID を端末から取得する手順
スマレジのテックファーム(SES 部門)で Web 系エンジニアとして働いている やまて(@r_yamate) と申します。
2023 年 4 月からは、スマレジの関連アプリの開発業務を担当しています。触ったことのなかった Flutter での開発を担当することになり、日々奮闘中です。めっちゃ楽しい。
はじめに
今回は、 Flutter を用いて Android や iOS の端末から UDID(Unique Device Identifier)を取得する手順についてまとめます。
なお、一般的にはネイティブアプリ開発で UDID を利用するケースは、あまりないのではないかとは思いますが、使用を検討する機会があり、調べたことをまとめておきたい目的で記事にしています。
目次
環境
私が試したFlutterの環境は以下のとおりです。
- MacBook Pro(Intel)
- macOS Monterey 12.6.5
- Flutter 3.7.1
- Dart 2.19.1
- flutter_udid ^2.0.1
UDID とは
まずは UDID とは、どのようなものかについて確認します。
UDID(Unique Device Identifier)の定義
UDID とは、端末ごとに異なる一意の識別子です。製造元や OS によって生成され、ソフトウェアやサービスが端末を識別する際に用いられます。
flutter_udid パッケージ
flutter_udid パッケージを使用して、 UDID を取得し表示します。
UDID の使用の注意
UDID は、プライバシーの観点などから使用には注意が必要です。ユーザー追跡やデバイス特定、広告のパーソナライゼーションなどには、他の一意の識別子を使用するのがベストプラクティスとされます。
そのため、一般的には UDID を利用する場面は、あまりないのではないかとは思います。
参考: 一意の識別子に関するベスト プラクティス
なお、一般的なデバイス情報の取得に使われる device_info_plus パッケージでは、 UDID の取得はサポートされていません。
step1. 依存パッケージの追加
pubspec.yaml への依存パッケージ追加
プロジェクトの pubspec.yaml ファイルに以下のようにflutter_udid
を追加します。
dependencies: flutter_udid: ^2.0.1
その後、ターミナルで flutter pub get
コマンドを実行して依存パッケージを取得します。
step2. コードの実装
パッケージのインポート
まず、 flutter_udid パッケージをインポートします。
import 'package:flutter_udid/flutter_udid.dart';
UDID 取得の関数を作成
次に、以下のような UDID 取得の関数を作成します。
/// ログインする券売機端末のUDIDを取得する。 /// /// 非同期で実行され、UDIDとして文字列を返す。 Future<String> getUdid() async { final udid = await FlutterUdid.udid; return udid; }
実行結果
例えば Android で正常に実行すると、以下のような UDID 文字列が取得できます。
'8af8770a27cfd182'
パッケージのコードリーディング
FlutterUdid.udid
がどのように動作しているか、パッケージのコードを読んで確認します。
Dart とネイティブコード(Android と iOS)の間でデータを送受信するための MethodChannel が作成されているため、Dart と Kotlin (Android 側)のデータの送受信のコードを見てみます。
Dart のコード
確認:lib/flutter_udid.dart
import 'dart:async'; import 'package:crypto/crypto.dart'; import 'package:flutter/services.dart'; import 'dart:convert'; /// API to retrieve a unique device ID of the device the Flutter /// app is currently running on. class FlutterUdid { static const MethodChannel _channel = const MethodChannel('flutter_udid'); /// Returns the UDID in the platform-specific format. /// iOS: 7946DA4E-8429-423C-B405-B3FC77914E3E, /// Android: 8af8770a27cfd182 static Future<String> get udid async { final String udid = await _channel.invokeMethod('getUDID'); return udid; } /// Returns the UDID in a consistent format for all platforms. /// Example: 984725b6c4f55963cc52fca0f943f9a8060b1c71900d542c79669b6dc718a64b static Future<String> get consistentUdid async { final String udid = await _channel.invokeMethod('getUDID'); var bytes = utf8.encode(udid); var digest = sha256.convert(bytes); return digest.toString(); } }
FlutterUdid
クラス: UDID を取得するためのメインクラス。_channel
: MethodChannel を作成している。 Dart とネイティブコード(AndroidとiOS)の間でデータを送受信するためのチャンネル。udid
メソッド:getUDID
という名前のメソッドをネイティブ側に呼び出して、 UDID 取得。consistentUdid
メソッド:getUDID
で取得した UDID を SHA-256 でハッシュ化。
参考:MethodChannel の参考記事
Kotlin のコード
ネイティブコードは Android 用の Kotlin のコードを確認してみます。
確認:android/src/main/kotlin/de/gigadroid/flutter_udid/FlutterUdidPlugin.kt
package de.gigadroid.flutter_udid import androidx.annotation.NonNull import android.annotation.SuppressLint import android.provider.Settings import android.content.Context import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.PluginRegistry.Registrar import io.flutter.plugin.common.PluginRegistry import io.flutter.embedding.engine.plugins.FlutterPlugin import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.BinaryMessenger class FlutterUdidPlugin() : MethodCallHandler, FlutterPlugin { private lateinit var channel : MethodChannel private var applicationContext: Context? = null companion object { @JvmStatic fun registerWith(registrar: Registrar): Unit { val instance = FlutterUdidPlugin() instance.onAttachedToEngine(registrar.context(), registrar.messenger()) } } override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { onAttachedToEngine(flutterPluginBinding.getApplicationContext(), flutterPluginBinding.getBinaryMessenger()); } private fun onAttachedToEngine(applicationContext : Context, messenger: BinaryMessenger) { this.applicationContext = applicationContext; channel = MethodChannel(messenger, "flutter_udid") channel.setMethodCallHandler(this) } override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { if(call.method == "getUDID"){ val udid = getUDID() if(udid == null || udid == ""){ result.error("UNAVAILABLE", "UDID not available.", null) }else{ result.success(udid) } }else{ result.notImplemented() } } override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { applicationContext = null; channel.setMethodCallHandler(null) } private fun getUDID() : String{ return Settings.Secure.getString(applicationContext?.contentResolver, Settings.Secure.ANDROID_ID) } }
FlutterUdidPlugin
クラス: Flutter プラグインとしてのメインクラス。registerWith
メソッド: 旧式の Flutter プラグイン API に対応するためのメソッド。onAttachedToEngine
: Flutter エンジンにアタッチされたときに呼ばれるメソッド。ここでMethodChannel を設定している。onMethodCall
: Dart 側から呼ばれるメソッドをハンドリングする。getUDID
メソッドが呼ばれた場合、getUDID()
を呼び出して結果を返す。getUDID
メソッド: 実際に Android のSettings.Secure.ANDROID_ID
を使って UDID 取得。
おわりに
今回は、 UDID を Android などの端末から取得する手順についてまとめました。まとめてはみたものの、 UDID を利用するケースはあまりないのではないかとは思います。
ありがとうございました。
届いた!息子と開封タイム😁 pic.twitter.com/aNr9dO9YgS
— やまて|Web系エンジニア2年目 (@r_yamate) 2023年9月30日
ちょっとずつ開けてる。ポケモン初代を小学生の頃にやり込んだ人間としては 151 が最強。