たくさんの自由帳
Androidのお話
たくさんの自由帳
投稿日 : | 0 日前
文字数(だいたい) : 1306
どうもこんばんわ。
Chromeくんがたまによく固まるんだけどどうした?
Android 12からローカルIPアドレスを取得する際にWifiManager
を利用するほうが非推奨になり、
ConnectivityManager
を使うのが正規ルートになったみたいなのでメモ
https://developer.android.com/reference/kotlin/android/net/wifi/WifiManager#getConnectionInfo()
AndroidManifest.xml
にandroid.permission.ACCESS_NETWORK_STATE
を追加します。
DANGERレベルじゃないので自動で付与されます。
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
適当にTextViewを置いておけばいいと思います(idはactivity_main_text_view
)
一回だけ取得する際はどんな難しくないです。ただしネットワークの変更があった際に対応できません。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView = findViewById<TextView>(R.id.activity_main_text_view)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
textView.text = getIpAddress(this)
}
}
/**
* IPアドレスを取得する関数
*
* @param context Context
* @return IPv4のIPアドレス
* */
@RequiresApi(Build.VERSION_CODES.M)
fun getIpAddress(context: Context): String? {
val connectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetwork = connectivityManager.activeNetwork
val linkProperties = connectivityManager.getLinkProperties(activeNetwork)
println(linkProperties?.linkAddresses)
val localIpAddress = linkProperties?.linkAddresses
?.find { it.address?.toString()?.contains("192") == true }
?.address
?.hostAddress
return localIpAddress
}
}
どうやらこれ、IPv6アドレス(16進数の:
区切りのやつ)とIPv4アドレス(10進数で.
区切りのやつ)が一緒の配列に入ってるみたいなので、
配列の中からIPアドレスに192
が含まれているものを探してます。
該当のコード
val localIpAddress = linkProperties?.linkAddresses
?.find { it.address?.toString()?.contains("192") == true }
?.address
?.hostAddress
コードが複雑になる代償にネットワーク変更の検知に対応できます。
app/build.gradle
にコルーチンとライフサイクルのライブラリを入れます。
dependencies {
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2"
// 省略
Kotlin
のコルーチンにある、Flow
の一種、callbackFlow
がいい感じに扱ってくれます。
これを使うと、コールバック形式のコードをFlowに変換してくれます。さらに、コルーチンが終了した際の後始末まで一つの関数で完結するのでマジ便利。
callbackFlow {
// BroadcastReceiver など
awaitClose {
// Context#unregisterReceiver() で登録解除
}
}
lifecycleScope
を利用することで、Activityのライフサイクルに合わせてコルーチンを終了してくれます。
コルーチンが終了すると、callbackFlow
内のawaitClose
へ進み後始末をしてくれます。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val textView = findViewById<TextView>(R.id.activity_main_text_view)
lifecycleScope.launch {
collectIpAddress(this@MainActivity).collect {
textView.text = it
}
}
}
/**
* IPアドレスをFlowで受け取る
*
* @param context Context
* @return IPv4のIPアドレス
* */
@OptIn(ExperimentalCoroutinesApi::class)
fun collectIpAddress(context: Context) = callbackFlow {
val connectivityManager = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
val request = NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
.build()
// コールバック
val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
super.onCapabilitiesChanged(network, networkCapabilities)
val linkProperties = connectivityManager.getLinkProperties(network)
// IPv4アドレスを探す
val address = linkProperties?.linkAddresses
?.find { it.address?.toString()?.contains("192") == true }
?.address?.hostAddress
if (address != null) {
trySend(address)
}
}
}
connectivityManager.registerNetworkCallback(request, networkCallback)
awaitClose {
connectivityManager.unregisterNetworkCallback(networkCallback)
}
}
}
以上です。
お疲れさまでした
ソースコード置いておきます