kakao / kakao_flutter_sdk

Flutter SDK for Kakao Open API
Apache License 2.0
191 stars 70 forks source link

[Bug] 안드로이드 앱에서 ShareClient.instance.launchKakaoTalk() 사용 시 NullPointerException 발생 #181

Closed riflockle7 closed 11 months ago

riflockle7 commented 11 months ago

이슈 설명 (Issue description)

안녕하세요 ShareClient.instance.launchKakaoTalk() 을 사용했을 때 아래 오류가 발생하면서 공유가 안되고 있습니다.

자세한 로그, 버전 정보 등의 내용은 아래에 추가했습니다. 추가로 설명이 필요한 내용은 comment 부탁드립니다.

앱 ID (App ID)

회사 앱이어서 공유가 불가능합니다. 혹시 꼭 필요한 정보일까요?

플랫폼 (Platform)

Android 13

디바이스 (Device)

Samsung Galaxy Ultra 21

Version

1.3.0, 1.4.2

Flutter SDK

3.7.12

재현 방법 (Steps to reproduce)

  1. 아래 ShareClient.instance.launchKakaoTalk() 을 실행하면 오류가 발생합니다.

코드 샘플 (Code Sample)

...
await ShareClient.instance.launchKakaoTalk(uri);

Logs

PlatformException(error, null, null, java.lang.NullPointerException
    at com.kakao.sdk.flutter.KakaoFlutterSdkPlugin.getActivity(KakaoFlutterSdkPlugin.kt:35)
    at com.kakao.sdk.flutter.KakaoFlutterSdkPlugin.onMethodCall(KakaoFlutterSdkPlugin.kt:130)
    at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:258)
    at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
    at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:322)
    at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
    at android.os.Handler.handleCallback(Handler.java:942)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:226)
    at android.os.Looper.loop(Looper.java:313)
    at android.app.ActivityThread.main(ActivityThread.java:8757)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
)

activity 부분에서 null 이 되는 듯 합니다.


### Flutter Doctor

```shell
[!] Flutter (Channel stable, 3.7.12, on macOS 13.2.1 22D68 darwin-arm64 (Rosetta), locale ko-KR)
    • Flutter version 3.7.12 on channel stable at /Users/user/Develop_Document/flutter
    ! Warning: `dart` on your path resolves to /opt/homebrew/Cellar/dart/3.0.4/libexec/bin/dart, which is not inside your current Flutter SDK checkout at /Users/user/Develop_Document/flutter. Consider adding /Users/user/Develop_Document/flutter/bin to the front of your path.
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 4d9e56e694 (3 months ago), 2023-04-17 21:47:46 -0400
    • Engine revision 1a65d409c7
    • Dart version 2.19.6
    • DevTools version 2.20.1
    • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades.

[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.2)
    • Android SDK at /Users/user/Library/Android/sdk
    • Platform android-33, build-tools 33.0.2
    • ANDROID_HOME = /Users/user/Library/Android/sdk
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 14.3.1)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 14E300c
    • CocoaPods version 1.11.3

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[!] Android Studio
    • Android Studio at /Applications/Android Studio Preview.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    ✗ Unable to find bundled Java version.
    • Try updating or re-installing Android Studio.

[✓] Android Studio (version 2022.2)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694)

[✓] Connected device (3 available)
    • SM F711N (mobile) • R3CT50323MR • android-arm64  • Android 13 (API 33)
    • macOS (desktop)   • macos       • darwin-arm64   • macOS 13.2.1 22D68 darwin-arm64 (Rosetta)
    • Chrome (web)      • chrome      • web-javascript • Google Chrome 115.0.5790.114

[✓] HTTP Host Availability
    • All required HTTP hosts are available
mbkim95 commented 11 months ago

안녕하세요

말씀주신 것처럼 에러 로그를 확인했을 때는 KakaoFlutterSdkPlugin 클래스 내부의 activity가 null이 되어 에러가 발생하는 것으로 보입니다.

제 테스트 환경에서는 재현이 되지 않고, 일반적인 경우에는 activity가 null이 되지 않는 것으로 보여 정확한 원인 파악이 되지 않고 있습니다.

이슈 관련해서 몇 가지 궁금한 점이 있는데요, 확인해보시고 답변 부탁드립니다.

  1. 이슈가 다른 디바이스에서도 동일하게 발생하는지 궁금합니다
  2. 이슈가 발생하기까지의 동선이 어떻게 되는지 궁금합니다.
  3. 이슈가 항상 재현되는지 궁금합니다.
  4. SDK와 관련된 코드 모두 첨부 부탁드립니다
  5. 앱 id는 카카오 디벨로퍼스 사이트에서 확인할 수 있는 숫자 값으로, 서버 로그 확인을 위해 필요한 값입니다. 앱 id는 공개되어도 문제없는 값이기 때문에 앱 id도 첨부해주시면 감사하겠습니다.
riflockle7 commented 11 months ago

안녕하세요 확인해주셔서 감사합니다 🙇 말씀해주신 내용 관련 comment 남깁니다.

이슈가 다른 디바이스에서도 동일하게 발생하는지 궁금합니다 이슈가 항상 재현되는지 궁금합니다.

네 현재 모든 기기에서 100% 발현하고 있습니다.

이슈가 발생하기까지의 동선이 어떻게 되는지 궁금합니다.

현재 Add To App 을 적용한 앱인데 순서는 아래와 같습니다.

  1. Pigeon 을 통해 네이티브에서 flutter 로 URL (ex. www.daum.net) 을 넘겨줌
  2. dart 코드에서 await ShareClient.instance.launchKakaoTalk(uri); 실행
  3. NullPointerException 이 발생

SDK와 관련된 코드 모두 첨부 부탁드립니다 앱 id는 카카오 디벨로퍼스 사이트에서 확인할 수 있는 숫자 값으로, 서버 로그 확인을 위해 필요한 값입니다. 앱 id는 공개되어도 문제없는 값이기 때문에 앱 id도 첨부해주시면 감사하겠습니다.

제 권한 밖의 내용도 있어, 확인 후 가능하면 추가 comment 남기겠습니다.


추가적으로 확인된 내용 및 궁금한 점 남깁니다.

  1. 초기화 로직 및 Manifest 내용은 공식문서 내용대로 정상 설정되어 있습니다.
  2. 과거 1.1.1 을 사용했을 때는 문제가 없었지만, 1.3.0 을 적용하면서 문제가 발생했습니다.
  3. 공식 문서에서 아래 내용을 확인했는데, 1.3.0 으로 되면서 앱에서 대응할 내용이 있을까요? image
mbkim95 commented 11 months ago

Add to App 형태로 구현 중이라는 답변 확인하고 Add to App 형태로 샘플 프로젝트 만들어서 테스트를 진행해봤는데요, 문제없이 잘 동작하는 것 확인했습니다.

첨부해주신 에러 로그 상의 activity는 구현 중인 앱에 구현된 Activity 혹은 FlutterActivity이기 때문에 어떠한 형태로 Add To App 이 적용되었는지 확인이 필요할 것 같습니다. 현재 개발 중인 프로젝트의 Add to App 구현이 어떻게 되어있는지 설명해주시면 감사하겠습니다. ex) 플러터 단 메소드 채널 호출하는 부분 코드, 플러터에 구현된 UI 코드 등 (영상도 첨부해주시면 이슈 파악에 더 도움이 될 것 같습니다)

그리고 1.3.0 버전부터 이슈가 발생한 이유는 플러터 웹 지원 여부와는 무관하고 간헐적으로 발생하던 이슈를 수정하면서 SDK 내부 동작에 변경이 있었기 때문인 것으로 보입니다. (1.3.0 버전 이전에는 applicationContext.startActivity()를 통해 카카오톡을 호출했고, 1.3.0버전 부터 activity.startActivity()를 통해 카카오톡을 호출하는데, 앱 실행 중에는 applicationContext가 non-null이므로 문제없이 동작한 것으로 추측됩니다)


참고) 테스트

테스트는 다음과 같은 형태로 진행했습니다. (코드는 아래에 첨부했습니다)

  1. Flutter Module 생성 (이하 module)
  2. module에서 kakao_flutter_sdk 의존성 추가 (1.3.0, 최신 버전 1.5.0으로 테스트 진행)
  3. module 내부에 StatefulWidget을 구현하고 initState() 내부에서 MethodChannel.setMethodCallHandler() 호출
  4. 구현한 MethodCallHandler 에서 ShareClient.instance.shareDefault(), ShareClient.instance.launchKakaoTalk() 호출하도록 구현
  5. flutter build aar 명령어 실행
  6. Android 프로젝트에 module 적용
  7. Android 단에 FlutterEngine, MethodChannel` 등록
  8. Android 단에서 버튼 클릭했을 때 MethodChannel.invokeMethod 호출
Android, Flutter 코드 Android 코드 ```kotlin const val METHOD_CHANNEL_NAME = "com.example.embed.module.test" const val FLUTTER_ENGINE = "Engine" class MainActivity : AppCompatActivity() { lateinit var methodChannel: MethodChannel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) warmupFlutterEngine() setContentView(R.layout.activity_main) findViewById
riflockle7 commented 11 months ago

첨부해주신 에러 로그 상의 activity는 구현 중인 앱에 구현된 Activity 혹은 FlutterActivity이기 때문에 어떠한 형태로 Add To App 이 적용되었는지 확인이 필요할 것 같습니다. 현재 개발 중인 프로젝트의 Add to App 구현이 어떻게 되어있는지 설명해주시면 감사하겠습니다.

안녕하세요 답변이 늦어 죄송합니다

프로젝트 구조

이전에 말씀드린대로 Add to App 구조이며 아래 특징을 갖고 있습니다.


플러터 단 메소드 채널 호출하는 부분 및 자세한 로직

플러터 단 메소드 채널 호출하는 부분 및 자세한 로직은 이렇습니다. (Pigeon 으로 인해 생긴 generated 코드는 생략하겠습니다.)

  1. flutter 화면에서 Dialog 를 띄우라고 네이티브에 요청
    pigeon.openDialog(url);
  2. 요청을 받은 네이티브는 Dialog 를 띄움
    ...
    flutterActivity?.let {
        ShareUrlPopupDialog
            .Builder(it)
            .setShareData(shareData)
            .create()?.show()
    }
  3. Dialog 에서 확인 버튼을 눌러, 카카오 공유하라고 flutter 에 요청
    shareData?.linkUrl?.let { linkUrl ->
      SharedFlutter.pigeon.shareUrlKakaoTalk(linkUrl) { }
    }
  4. flutter 에서 카카오 공유 수행
    try {
        await ShareClient.instance.launchKakaoTalk(uri);
        return;
    } catch (e) {
        rethrow;
    }

KakaoFlutterSdkPlugin 로그 확인 내용

확인 차 KakaoFlutterSdkPlugin.kt 에 로그를 넣고 확인해보았습니다. (하단 참고) 이 에러를 보니 가장 초기에 설정된 Flutter Engine 을 사용하여, activity 가 null 이 되지 않나 추측됩니다. 위에서 싱글 엔진이 아니라고 했는데 이 부분도 문제와 연관이 있지 않을까 싶습니다.

// application class 로 추측됨
onAttachedToEngine:  io.flutter.plugin.common.MethodChannel@7d0dd0c / com.kakao.sdk.flutter.KakaoFlutterSdkPlugin@274e03f / com.example.flutter.BaseApplication@239f70a / io.flutter.embedding.engine.dart.DartExecutor@a8ff655

// SplashActivity 이후 띄워지는 MainActivity
onAttachedToEngine:  io.flutter.plugin.common.MethodChannel@5102ffc / com.kakao.sdk.flutter.KakaoFlutterSdkPlugin@d02e8ef / com.example.flutter.BaseApplication@239f70a / io.flutter.embedding.engine.dart.DartExecutor@f6b6685
onAttachedToActivity: io.flutter.plugin.common.MethodChannel@5102ffc / com.kakao.sdk.flutter.KakaoFlutterSdkPlugin@d02e8ef / null / com.example.MainActivity@9c19d30

// MainActivity 에서 SecondActivity 진입
onAttachedToEngine:  io.flutter.plugin.common.MethodChannel@4bd932f / com.kakao.sdk.flutter.KakaoFlutterSdkPlugin@55dd0e / com.example.flutter.BaseApplication@239f70a / io.flutter.embedding.engine.dart.DartExecutor@cea2d3c
onAttachedToActivity: io.flutter.plugin.common.MethodChannel@4bd932f / com.kakao.sdk.flutter.KakaoFlutterSdkPlugin@55dd0e / null / com.example.flutter.SecondActivity@52e60b3

// SecondActivity 에서 launchKakaoTalk 호출 (at "3. Dialog 에서 확인 버튼을 눌러, 카카오 공유하라고 flutter 에 요청 ~ ")
launchKakaoTalk io.flutter.plugin.common.MethodChannel@7d0dd0c / com.kakao.sdk.flutter.KakaoFlutterSdkPlugin@274e03f

// activity NullPointerException 발생
Failed to handle method call
java.lang.NullPointerException
    at com.kakao.sdk.flutter.KakaoFlutterSdkPlugin.getActivity(KakaoFlutterSdkPlugin.kt:36)
    at com.kakao.sdk.flutter.KakaoFlutterSdkPlugin.onMethodCall(KakaoFlutterSdkPlugin.kt:132)
    at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:258)
    at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295)
    at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:322)
    at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(Unknown Source:12)
    at android.os.Handler.handleCallback(Handler.java:942)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loopOnce(Looper.java:226)
    at android.os.Looper.loop(Looper.java:313)
    at android.app.ActivityThread.main(ActivityThread.java:8757)
    at java.lang.reflect.Method.invoke(Native Method)
    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:571)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)
mbkim95 commented 11 months ago

최대한 자세하게 적어주신 것 같은데 제가 구현 상황을 정확하게 이해하지 못한 것 같습니다

상황상 구체적인 코드 공개가 어려우신 것 같고, 텍스트로만 커뮤니케이션하다보니 상황 전달이 잘 되지 않고 있는 것 같아요.

조금 번거로우시겠지만 이슈 재현되는 간단한 샘플 프로젝트 만들어서 첨부해주시거나 tony.mb@kakaocorp.com 이메일로 보내주실 수 있을까요?

이슈 파악이 계속 지연되고 있는 것 같아 죄송하네요ㅠㅠ

riflockle7 commented 11 months ago

안녕하세요 확인한 결과 내부에서 처리가 가능할 듯 하여 Issue close 해두겠습니다! (activity 의 pigeon 생성 및 참조하여 해결)

그 동안 친절하게 답변해주시고, 예시 코드도 만들어 확인해주셔서 감사했습니다 🙏 🙇


위 이슈 close 와 별개로, 라이브러리 내에서 Application 클래스에서 만들어진 FlutterEngine 일 때 대응 처리가 필요해보입니다.

지난 번 로그 분석 이후 좀 더 확인된 내용을 말씀드리면 FlutterEngine 은 context 를 통해 생성할 수 있어서 (하단 FlutterEngine.java 코드 참고)

// FlutterEngine.java

...

public FlutterEngine(@NonNull Context context) {
  this(context, null);
}

application 클래스내에서 만들어진 FlutterEngine의 경우, KakaoFlutterSdkPlugin.kt 내 activity 값이 무조건 null 이 됩니다.

flutterEngine = FlutterEngine(context) // activity context 가 아닌 경우 KakaoFlutterSdkPlugin.kt 내 activity 가 무조건 null

Add to App 에서 싱글 FlutterEngine 을 사용할 경우, 겪을 수 있는 문제라 검토 및 개선 부탁드리겠습니다 🙇

개인적으로는 activity 가 null 인 경우 applicationContext 를 활용하도록 처리해주심이 어떨까 싶습니다! (아니면 공식문서 내에서, Application 클래스 내에서 FlutterEngine 생성 시 주의하라는 내용이 있으면 좋겠습니다!)