Jetpack ComposeをServiceで使おうと思ったら、いくつかエラーが発生してうまく行かなかったので、メモがてらエラーの内容と対応を記載したいと思います。
対応前のコード
下記のコードがエラーが発生してしまう対応前のコードです。
エラーに対処しつつ、Jetpack Composeが使えるところまで行きたいと思います。
class MainService : Service() {
private val params by lazy {
WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT,
)
}
private val windowManager by lazy {
getSystemService(Context.WINDOW_SERVICE) as WindowManager
}
private val composeView by lazy {
ComposeView(this).apply {
setContent {
Text("Hello Compose")
}
}
}
override fun onCreate() {
super.onCreate()
windowManager.addView(composeView, params)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
return START_NOT_STICKY
}
override fun onDestroy() {
windowManager.removeView(composeView)
super.onDestroy()
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
}
java.lang.IllegalStateException: ViewTreeLifecycleOwner not found from androidx.compose.ui.platform.ComposeView
↓ throw箇所
ComposeView
は LifecycleOwner
が設定されてないと動かないようです。
そのため、 ViewTreeLifecycleOwner.set
を使って、 LifecycleOwner
を設定してあげます。
LifecycleService という LifecycleOwner
を実装した Service
があるので、親クラスをそれに変更し、 ViewTreeLifecycleOwner.set(composeView, this)
を onCreate
で呼ぶようにします。
(onBind
の実装が必須ではなくなるので削除もしました。)
変更後のコードはここをクリック
class MainService : LifecycleService() { // LifecycleServiceに変更
private val params by lazy {
WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT,
)
}
private val windowManager by lazy {
getSystemService(Context.WINDOW_SERVICE) as WindowManager
}
private val composeView by lazy {
ComposeView(this).apply {
setContent {
Text("Hello Compose")
}
}
}
override fun onCreate() {
super.onCreate()
ViewTreeLifecycleOwner.set(composeView, this) // 追加
windowManager.addView(composeView, params)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
return START_NOT_STICKY
}
override fun onDestroy() {
windowManager.removeView(composeView)
super.onDestroy()
}
// onBindを削除
}
これで大丈夫かと思いきや、他のエラーが発生しました。
java.lang.IllegalStateException: Composed into the View which doesn't propagateViewTreeSavedStateRegistryOwner!
↓ throw箇所
ComposeView
ではSavedStateRegistryOwner
も必要なようです。
ViewTreeSavedStateRegistryOwner.set
を使って、 SavedStateRegistryOwner を設定してあげます。
MainService
に SavedStateRegistryOwner
を実装させます。
SavedStateRegistry
が必要になるので、 SavedStateRegistryController
をプロパティとして定義し、そこから SavedStateRegistry
を返すようにしました。
変更後のコードはここをクリック
class MainService : LifecycleService(), SavedStateRegistryOwner {
// 追加
private val savedStateRegistryController = SavedStateRegistryController.create(this)
private val params by lazy {
WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT,
)
}
private val windowManager by lazy {
getSystemService(Context.WINDOW_SERVICE) as WindowManager
}
private val composeView by lazy {
ComposeView(this).apply {
setContent {
Text("Hello Compose")
}
}
}
override fun onCreate() {
super.onCreate()
ViewTreeLifecycleOwner.set(composeView, this)
ViewTreeSavedStateRegistryOwner.set(composeView, this) // 追加
windowManager.addView(composeView, params)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
return START_NOT_STICKY
}
override fun onDestroy() {
windowManager.removeView(composeView)
super.onDestroy()
}
// 追加
override fun getSavedStateRegistry(): SavedStateRegistry {
return savedStateRegistryController.savedStateRegistry
}
}
今度こそ、と思いきやまだ駄目なようでした。
java.lang.IllegalStateException: You can consumeRestoredStateForKey only after super.onCreate of corresponding component
↓ throw箇所
mRestored
が false
になっているとエラーになるようなので、 performRestore を呼んで mRestored
が true
になるようにします。
これでひとまずエラーが発生せずに、動くようになりました
対応後のコード
Hello Compose
と表示されているのが、Service上に表示されているJetpack ComposeのTextです。
最終的なコードは下記の通りです。
class MainService : LifecycleService(), SavedStateRegistryOwner {
private val savedStateRegistryController = SavedStateRegistryController.create(this)
private val params by lazy {
WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
} else {
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
},
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT,
)
}
private val windowManager by lazy {
getSystemService(Context.WINDOW_SERVICE) as WindowManager
}
private val composeView by lazy {
ComposeView(this).apply {
setContent {
Text("Hello Compose")
}
}
}
override fun onCreate() {
super.onCreate()
ViewTreeLifecycleOwner.set(composeView, this)
ViewTreeSavedStateRegistryOwner.set(composeView, this)
savedStateRegistryController.performRestore(null)
windowManager.addView(composeView, params)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
return START_NOT_STICKY
}
override fun onDestroy() {
windowManager.removeView(composeView)
super.onDestroy()
}
override fun getSavedStateRegistry(): SavedStateRegistry {
return savedStateRegistryController.savedStateRegistry
}
}
#参考
-
https://gist.github.com/handstandsam/6ecff2f39da72c0b38c07aa80bbb5a2f
- 同じようにServiceにJetpack Composeを表示させているGistです