🦔
android AWS S3からImageUrlを発行してPush通知に画像を乗せるとクラッシュした話
発生内容
タイトルままなのですが
AWS S3のImageUrlを使ってPush通知に乗せると
UnknownHostExceptionが発生してクラッシュします。
Caused by java.net.UnknownHostException: Unable to resolve host "hogehoge.jp": No address associated with hostname
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:156)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:103)
at java.net.InetAddress.getAllByName(InetAddress.java:1152)
at com.android.okhttp.Dns$1.lookup(Dns.java:41)
at com.android.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:178)
at com.android.okhttp.internal.http.RouteSelector.nextProxy(RouteSelector.java:144)
at com.android.okhttp.internal.http.RouteSelector.next(RouteSelector.java:86)
at com.android.okhttp.internal.http.StreamAllocation.findConnection(StreamAllocation.java:192)
at com.android.okhttp.internal.http.StreamAllocation.findHealthyConnection(StreamAllocation.java:144)
at com.android.okhttp.internal.http.StreamAllocation.newStream(StreamAllocation.java:106)
at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:400)
at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:333)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:483)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:429)
at com.android.okhttp.internal.huc.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:560)
at com.android.okhttp.internal.huc.DelegatingHttpsURLConnection.getResponseCode(DelegatingHttpsURLConnection.java:106)
at com.android.okhttp.internal.huc.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:30)
at com.google.firebase.perf.network.InstrURLConnectionBase.getInputStream(InstrURLConnectionBase.java:7)
at com.google.firebase.perf.network.InstrHttpsURLConnection.getInputStream(InstrHttpsURLConnection.java:2)
at com.google.firebase.perf.network.FirebasePerfUrlConnection.openStream(FirebasePerfUrlConnection.java:26)
at com.google.firebase.perf.network.FirebasePerfUrlConnection.openStream(FirebasePerfUrlConnection.java:14)
at jp.arsaga.app.notification.PushNotificationService.getBitmapFromUrl(PushNotificationService.java:20)
at jp.arsaga.app.notification.PushNotificationService.notificationCompatBuilder(PushNotificationService.java:39)
at jp.arsaga.app.notification.PushNotificationService.receiveOnBackGround(PushNotificationService.java:312)
at jp.co.arsaga.extensions.gateway.PushNotification$AbstractService.handleIntent(PushNotification.java:11)
at com.google.firebase.messaging.EnhancedIntentService.lambda$processIntent$0(EnhancedIntentService.java:1)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at com.google.android.gms.common.util.concurrent.zza.run(zza.java:6)
at java.lang.Thread.run(Thread.java:919)
原因
正直なところよくわからないのですが
恐らくandroid内部で使われているokhttpの例外に入ってクラッシュしてるっぽいです
この方は内部のokhttpにパッチを当てて解決していますが
正直内部のコードは触りたくないのであまりこの方法はやりたくないです.......
- そもそもこの方法でいいのかも謎
- 発生するのもIPv6に限らずIPv4で発生することも確認
- ただ確実にクラッシュするわけでもなく上手くいく時もある
- しかしクラッシュはかなりの高頻度で発生するため何かしらの対策はするべき
解決方法
これで解決できているかは正直怪しいのですが
画像セットのタイミングを直接NotificationCompat.Builderに渡すのではなく
先にimageUrlからBitmapを作成し
それをNotificationCompat.Builderに渡すようにしたところ
クラッシュすることは無くなりました(恐らく)
サンプルコード
val image = getBitmapFromUrl(bundle.getString(PushNotificationKeyType.PinpointNotificationImageUrl.keyName))
//通知の実施
notificationManager.notify(
senderId,
notificationCompatBuilder(
this,
channelId,
bundle.getString(PushNotificationKeyType.PinpointNotificationTitle.keyName),
bundle.getString(PushNotificationKeyType.PinpointNotificationBody.keyName),
pendingIntent,
image
).build()
)
private fun notificationCompatBuilder(
context: Context,
channelId: String,
title: String?,
message: String?,
pendingIntent: PendingIntent?,
image: Bitmap?
) = NotificationCompat.Builder(context, channelId)
.setContentTitle(title)
.setContentText(message)
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.setSmallIcon(R.drawable.hogehoge)
.setStyle(
NotificationCompat.BigPictureStyle()
.bigPicture(image)
)
private fun getBitmapFromUrl(url: String?): Bitmap? =
if (!url.isNullOrBlank()) URL(url).openStream().use {
try {
BitmapFactory.decodeStream(it)
} catch (e: Exception) {
return@use null
}
} else null
元のPush実装はこちらを参照してください
結論
NotificationCompat.Builderの中でimageURlにアクセスしてあれこれするとバグるっぽいです
なので先にBitmap化してから渡そうねということですね(恐らく)
参考記事
Discussion