✴️ 배경
안드로이드 스튜디오에서 Kotlin Multiplatform 앱에 사용할 앱 아이콘을 바꾸려고 할 때 발생한 오류를 정리했어요.
✅ 개요
1. Kotlin Multiplatform Wizard에서 Android와 iOS(swiftUI) 두 플랫폼만 선택해서 새로운 프로젝트를 만들었어요.
Kotlin Multiplatform Wizard | JetBrains
kmp.jetbrains.com
2. 안드로이드 문서대로 composeApp > src > androidMain으로 이동했어요.
앱 아이콘 변경 | Android Developers
Affirmations 앱의 앱 아이콘을 변경합니다.
developer.android.com
3. res 폴더를 우클릭해서 New > Image Asset을 선택했어요.
4. 원래대로라면 Image Asset을 설정할 수 있는 화면이 나와야 하지만 아래와 같이 IDE 에러가 발생했어요. 자세한 오류 코드는 아래 접힌글을 참고해주세요.
java.lang.NullPointerException
at java.base/java.util.Objects.requireNonNull(Unknown Source)
at com.android.tools.idea.npw.assetstudio.wizard.GenerateImageAssetPanel.<init>(GenerateImageAssetPanel.java:213)
at com.android.tools.idea.npw.assetstudio.wizard.NewImageAssetStep.<init>(NewImageAssetStep.java:38)
at com.android.tools.idea.npw.actions.NewImageAssetAction.createWizard(NewImageAssetAction.kt:35)
at com.android.tools.idea.npw.actions.AndroidAssetStudioAction.actionPerformed(AndroidAssetStudioAction.java:136)
at com.intellij.openapi.actionSystem.ex.ActionUtil.doPerformActionOrShowPopup(ActionUtil.kt:345)
at com.intellij.openapi.actionSystem.ex.ActionUtil.performActionDumbAwareWithCallbacks$lambda$4(ActionUtil.kt:316)
at com.intellij.openapi.actionSystem.impl.ActionManagerImpl.performWithActionCallbacks(ActionManagerImpl.kt:1168)
at com.intellij.openapi.actionSystem.ex.ActionUtil.performActionDumbAwareWithCallbacks(ActionUtil.kt:315)
at com.intellij.openapi.actionSystem.impl.ActionMenuItem.performAction$lambda$5(ActionMenuItem.kt:273)
at com.intellij.openapi.wm.impl.FocusManagerImpl.runOnOwnContext(FocusManagerImpl.java:231)
at com.intellij.openapi.actionSystem.impl.ActionMenuItem.performAction(ActionMenuItem.kt:264)
at com.intellij.openapi.actionSystem.impl.ActionMenuItem._init_$lambda$0(ActionMenuItem.kt:71)
at java.desktop/javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
at com.intellij.openapi.actionSystem.impl.ActionMenuItem.fireActionPerformed$lambda$4(ActionMenuItem.kt:102)
at com.intellij.openapi.application.TransactionGuardImpl.performActivity(TransactionGuardImpl.java:106)
at com.intellij.openapi.application.TransactionGuardImpl.performUserActivity(TransactionGuardImpl.java:95)
at com.intellij.openapi.actionSystem.impl.ActionMenuItem.fireActionPerformed(ActionMenuItem.kt:101)
at com.intellij.ui.plaf.beg.BegMenuItemUI.doClick(BegMenuItemUI.java:518)
at com.intellij.ui.plaf.beg.BegMenuItemUI$MyMouseInputHandler.mouseReleased(BegMenuItemUI.java:551)
at java.desktop/java.awt.Component.processMouseEvent(Unknown Source)
at java.desktop/javax.swing.JComponent.processMouseEvent(Unknown Source)
at java.desktop/java.awt.Component.processEvent(Unknown Source)
at java.desktop/java.awt.Container.processEvent(Unknown Source)
at java.desktop/java.awt.Component.dispatchEventImpl(Unknown Source)
at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
at java.desktop/java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
at java.desktop/java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
at java.desktop/java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
at java.desktop/java.awt.Container.dispatchEventImpl(Unknown Source)
at java.desktop/java.awt.Window.dispatchEventImpl(Unknown Source)
at java.desktop/java.awt.Component.dispatchEvent(Unknown Source)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(Unknown Source)
at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
at java.desktop/java.awt.EventQueue$4.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
at java.desktop/java.awt.EventQueue$5.run(Unknown Source)
at java.base/java.security.AccessController.doPrivileged(Unknown Source)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(Unknown Source)
at java.desktop/java.awt.EventQueue.dispatchEvent(Unknown Source)
at com.intellij.ide.IdeEventQueue.defaultDispatchEvent(IdeEventQueue.kt:696)
at com.intellij.ide.IdeEventQueue.dispatchMouseEvent(IdeEventQueue.kt:635)
at com.intellij.ide.IdeEventQueue._dispatchEvent$lambda$14(IdeEventQueue.kt:581)
at com.intellij.openapi.application.impl.AnyThreadWriteThreadingSupport.runWriteIntentReadAction(AnyThreadWriteThreadingSupport.kt:84)
at com.intellij.ide.IdeEventQueue._dispatchEvent(IdeEventQueue.kt:581)
at com.intellij.ide.IdeEventQueue.access$_dispatchEvent(IdeEventQueue.kt:73)
at com.intellij.ide.IdeEventQueue$dispatchEvent$processEventRunnable$1$1$1$1.compute(IdeEventQueue.kt:357)
at com.intellij.ide.IdeEventQueue$dispatchEvent$processEventRunnable$1$1$1$1.compute(IdeEventQueue.kt:356)
at com.intellij.openapi.progress.impl.CoreProgressManager.computePrioritized(CoreProgressManager.java:843)
at com.intellij.ide.IdeEventQueue$dispatchEvent$processEventRunnable$1$1$1.invoke(IdeEventQueue.kt:356)
at com.intellij.ide.IdeEventQueue$dispatchEvent$processEventRunnable$1$1$1.invoke(IdeEventQueue.kt:351)
at com.intellij.ide.IdeEventQueueKt$performActivity$runnableWithWIL$1.invoke$lambda$0(IdeEventQueue.kt:1035)
at com.intellij.openapi.application.WriteIntentReadAction.lambda$run$0(WriteIntentReadAction.java:24)
at com.intellij.openapi.application.impl.AnyThreadWriteThreadingSupport.runWriteIntentReadAction(AnyThreadWriteThreadingSupport.kt:84)
at com.intellij.openapi.application.impl.ApplicationImpl.runWriteIntentReadAction(ApplicationImpl.java:910)
at com.intellij.openapi.application.WriteIntentReadAction.compute(WriteIntentReadAction.java:55)
at com.intellij.openapi.application.WriteIntentReadAction.run(WriteIntentReadAction.java:23)
at com.intellij.ide.IdeEventQueueKt$performActivity$runnableWithWIL$1.invoke(IdeEventQueue.kt:1035)
at com.intellij.ide.IdeEventQueueKt$performActivity$runnableWithWIL$1.invoke(IdeEventQueue.kt:1035)
at com.intellij.ide.IdeEventQueueKt.performActivity$lambda$1(IdeEventQueue.kt:1036)
at com.intellij.openapi.application.TransactionGuardImpl.performActivity(TransactionGuardImpl.java:114)
at com.intellij.ide.IdeEventQueueKt.performActivity(IdeEventQueue.kt:1036)
at com.intellij.ide.IdeEventQueue.dispatchEvent$lambda$10(IdeEventQueue.kt:351)
at com.intellij.ide.IdeEventQueue.dispatchEvent(IdeEventQueue.kt:397)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(Unknown Source)
at java.desktop/java.awt.EventDispatchThread.run(Unknown Source)
🩺 진단
KMP는 실행가능한 Application이 두 개 이상이어서(composeApp, shared, 그 외) 안드로이드 스튜디오에서 소스 폴더를 하나로 특정하지 못하고 에러가 난 것이라고 생각해요.
그래서 아래 방법들을 시도했고 해결 방법을 찾을 수 있었어요!
1️⃣ 첫 번째 시도 : composeApp을 따로 열어보기
Kotlin Multiplatform은 위와 같이 한 개의 앱 안에 또다른 프로젝트 폴더(composeApp, shared)가 있어요. src를 특정하지 못한 거라면 그중 특정한 하나만 열어서 수정하면 어떨까요?
composeApp을 선택하고 OK를 눌러 스튜디오를 열었습니다.
앗 그런데 시작도 하기 전에 gradle build이 실패했네요... gradle/wrapper 폴더가 바깥에 있어서 실행 자체가 실패하는 것 같아요. 아쉽지만 이 방법은 실패한 것 같네요.
2️⃣ 두 번째 시도 : 바깥에서 만들고 가져오기
Kotlin Multiplatform에서 안 된다면 다른 플랫폼에서 먼저 만들고 그대로 복붙하면 되지 않을까요?
File > New > New Project를 선택하고 Empty Activity를 임시로 만들어 볼께요. 이름만 바꾸고 나머지는 기본 설정 그대로 따라갔어요.
그런 다음 app > src > main에 들어가서 똑같이 res에 우클릭 후 Image Asset을 선택해줄께요.
그럼 성공적으로 설정창이 열리고 Path 파일을 넣어서 작업할 수 있어요! Next하고 Finish를 누르니 res 폴더에 파일이 많이 생겼네요. 이제 이 친구들이 메인 앱에 잘 적용할 수 있을지 살펴 봐야겠어요.
mipmap으로 시작하는 폴더는 그대로 복사 붙여넣기하고, drawable은 안의 ic_launcher_foreground.xml 파일을 따로 가져다 붙였어요.
저는 background layer를 단색으로 채워서 values안의 xml 파일을 메인의 values에 그대로 따로 집어 넣어주었어요. 경우에 따라 다를 수 있으니 그 위치 그대로 붙여넣어 주세요! src/main/ic_launcher-playstore.png도 마찬가지예요.
실행하면 잘 적용된 걸 확인할 수 있어요!