본문으둜 κ±΄λ„ˆλ›°κΈ°

πŸ€– Android μ„€μ •

이 λ¬Έμ„œλŠ” 'Test Solution' Flutter SDKλ₯Ό λ„€μ΄ν‹°λΈŒ Android 앱에 ν†΅ν•©ν•˜κΈ° μœ„ν•œ μ„€μ • 방법을 μ•ˆλ‚΄ν•©λ‹ˆλ‹€.

μ‹œμž‘ν•˜κΈ° > μ„€μΉ˜ κ°€μ΄λ“œ λ¬Έμ„œμ— 따라 ν”„λ‘œμ νŠΈμ— Flutter Module이 μΆ”κ°€λ˜μ—ˆλ‹€κ³  κ°€μ •ν•©λ‹ˆλ‹€.


1. AndroidManifest.xml 섀정​

  • SDKλŠ” API μ„œλ²„μ™€ ν†΅μ‹ ν•˜κΈ° μœ„ν•΄ 인터넷 κΆŒν•œμ΄ ν•„μš”ν•©λ‹ˆλ‹€. app/src/main/AndroidManifest.xml νŒŒμΌμ— μ•„λž˜ κΆŒν•œμ„ μΆ”κ°€ν–ˆλŠ”μ§€ ν™•μΈν•˜μ„Έμš”.
  • μΆ”κ°€λ‘œ, FlutterλŠ” μ•ˆλ“œλ‘œμ΄λ“œ μ•± λ‚΄μ—μ„œ ν”ŒλŸ¬ν„° 화면을 ν‘œμ‹œν•˜κΈ° μœ„ν•΄ FlutterActivityλ₯Ό μ œκ³΅ν•©λ‹ˆλ‹€. λ‹€λ₯Έ λͺ¨λ“  Activity와 λ§ˆμ°¬κ°€μ§€λ‘œ FlutterActivity 도 AndroidManifest.xml에 λ°˜λ“œμ‹œ 등둝해야 ν•©λ‹ˆλ‹€.
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<uses-permission android:name="android.permission.INTERNET" />

<application
...>
<activity
android:name="io.flutter.embedding.android.FlutterActivity"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize"
/>
</application>
</manifest>

2. SDK μ‹€ν–‰ 및 톡신 섀정​

Host μ•±κ³Ό SDK(Flutter)λŠ” **MethodChannel**을 톡해 ν†΅μ‹ ν•©λ‹ˆλ‹€. ν…ŒμŠ€νŠΈλ₯Ό μ‹œμž‘ν•˜κ³ , κ²°κ³Όλ₯Ό 콜백으둜 λ°›κΈ° μœ„ν•œ 섀정이 ν•„μš”ν•©λ‹ˆλ‹€.

MethodChannel 섀정​

MainActivity.kt λ˜λŠ” SDKλ₯Ό ν˜ΈμΆœν•  Activityμ—μ„œ MethodChannel을 μ„€μ •ν•©λ‹ˆλ‹€. μ„±λŠ₯을 μœ„ν•΄ FlutterEngine을 μΊμ‹±ν•˜μ—¬ μ‚¬μš©ν•˜λŠ” 것을 ꢌμž₯ν•©λ‹ˆλ‹€.

λ‹€μŒμ€ Application.kt 의 캐싱 예제 μ½”λ“œ μž…λ‹ˆλ‹€.

Application.kt
class MyApplication : Application() {
companion object {
const val FLUTTER_ENGINE_ID = "test_solution_engine"
const val CHANNEL_NAME = "com.mscbrain.sdk.test_solution_sdk/channel"
}

lateinit var flutterEngine: FlutterEngine
lateinit var methodChannel: MethodChannel

override fun onCreate() {
super.onCreate()

flutterEngine = FlutterEngine(this)
flutterEngine
.dartExecutor
.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)

FlutterEngineCache
.getInstance()
.put(FLUTTER_ENGINE_ID, flutterEngine)

methodChannel = MethodChannel(
flutterEngine.dartExecutor.binaryMessenger,
CHANNEL_NAME
)

methodChannel.setMethodCallHandler { call, result ->
when (call.method) {
"onResult" -> {
// ν…ŒμŠ€νŠΈ μ™„λ£Œ λ˜λŠ” κ²°κ³Ό μˆ˜μ‹ 
println("Flutter sent a result: ${call.arguments}")
result.success(null) // 응닡 전솑
}

"onInfo" -> {
// SDK version μˆ˜μ‹  (getSDKVersion μš”μ²­ μ‹œ)
println("Flutter sent a version: ${call.arguments}")
result.success(null)
}

"onLog" -> {
// SDK 둜그 μˆ˜μ‹  (enableHostLogging = true 일 λ•Œ)
println("Flutter sent a log: ${call.arguments}")
result.success(null)
}

else -> {
result.notImplemented()
}
}
}
}
}

μ•„λž˜λŠ” MainActivity.kt의 전체 예제 μ½”λ“œμž…λ‹ˆλ‹€.

MainActivity.kt
package com.example.yourapp

import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import com.example.myandroidapp.MyApplication.Companion.FLUTTER_ENGINE_ID
import com.example.myandroidapp.databinding.ActivityMainBinding
import io.flutter.embedding.android.FlutterActivity

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

findViewById<Button>(R.id.start_test_button).setOnClickListener {
startTestSolution()
}
}

private fun startTestSolution() {
startActivity(
FlutterActivity
.withCachedEngine(FLUTTER_ENGINE_ID)
.build(this)
)

// ν…ŒμŠ€νŠΈ μ‹œμž‘ νŒŒλΌλ―Έν„° ꡬ성
val arguments = mapOf(
// ν•„μˆ˜ κ°’
"goodsId" to "[GOODS_ID]",
"suid" to "[SUID]",
"language" to "[LANGUAGE]",
// 선택 κ°’
"isDevelopment" to true,
"enableHostLogging" to true,
"themeColor" to 0xFF7E57C2, // μ˜ˆμ‹œ: 보라색
"themeMode" to "light",
"titleText" to "λ‚˜μ˜ 성격 μœ ν˜• μ°ΎκΈ°",
"customData" to mapOf("userId" to "12345", "source" to "app_event")
)

// `startTest` λ©”μ†Œλ“œ 호좜
(application as MyApplication).methodChannel.invokeMethod("startTest", arguments)
}
}

μ½”λ“œ μ„€λͺ…​

  1. FlutterEngine 캐싱: μ•± μ‹€ν–‰ μ‹œ 미리 FlutterEngine을 μƒμ„±ν•˜μ—¬ μΊμ‹œμ— μ €μž₯ν•©λ‹ˆλ‹€. μ΄λ ‡κ²Œ ν•˜λ©΄ SDK ν™”λ©΄(FlutterActivity)을 λ„μšΈ λ•Œ λ‘œλ”© 속도가 맀우 λΉ¨λΌμ§‘λ‹ˆλ‹€. 이 μ½”λ“œλŠ” Application 클래슀의 onCreate()μ—μ„œ μ‹€ν–‰ν•˜λŠ” 것이 κ°€μž₯ μ΄μƒμ μž…λ‹ˆλ‹€.
  2. MethodChannel μ΄ˆκΈ°ν™”: Host와 SDKκ°€ 톡신할 채널을 μƒμ„±ν•©λ‹ˆλ‹€. 채널 이름(CHANNEL_NAME)은 SDK 츑에 μ •μ˜λœ 이름과 λ°˜λ“œμ‹œ 동일해야 ν•©λ‹ˆλ‹€.
  3. setMethodCallHandler: SDKκ°€ Host μͺ½μœΌλ‘œ λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•  λ•Œ(onResult, onLog, onInfo) 이λ₯Ό μˆ˜μ‹ ν•˜λŠ” λ¦¬μŠ€λ„ˆμž…λ‹ˆλ‹€. when ꡬ문을 μ‚¬μš©ν•˜μ—¬ 각 λ©”μ†Œλ“œ 이름에 따라 λΆ„κΈ° 처리λ₯Ό ν•©λ‹ˆλ‹€.
  4. ν…ŒμŠ€νŠΈ μ‹œμž‘: λ²„νŠΌ 클릭 λ“± νŠΉμ • μ΄λ²€νŠΈκ°€ λ°œμƒν–ˆμ„ λ•Œ μΊμ‹œλœ FlutterEngine을 μ‚¬μš©ν•˜μ—¬ FlutterActivityλ₯Ό μ‹€ν–‰ν•©λ‹ˆλ‹€.
  5. invokeMethod("startTest", ...): FlutterActivityκ°€ μ‹€ν–‰λœ ν›„, startTest λ©”μ†Œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ ν•„μˆ˜ 및 선택 νŒŒλΌλ―Έν„°λ₯Ό Map ν˜•νƒœλ‘œ SDK에 μ „λ‹¬ν•©λ‹ˆλ‹€.
정보

Activity 뿐만 μ•„λ‹ˆλΌ Fragment 도 μ§€μ›ν•©λ‹ˆλ‹€. FlutterFragment(Flutter Docs)λ₯Ό μ°Έκ³ ν•΄μ£Όμ„Έμš”.