Files
2nd/10_Wiki/Topic_Programming/Coding/Android_ML_Kit_Health.md
T
Antigravity Agent 9148c358d0 docs(10_Wiki): 위키 전체 재구성 — Topic_* 폴더를 4개 카테고리로 통합 + 대규모 중복 제거
Topic_Agent/Topic_Blog/Topics/Topics_Biz/Topics_Meeting/Topics_Rag의 마크다운 지식 문서를
Topic_General/Topic_Programming/Topic_Graphic/Topic_Business 4개 카테고리로 재분류.

- 중복 제거: frontmatter의 status:duplicate/merged + duplicate_of/redirect_to 필드로
  자기 자신을 중복으로 선언한 리다이렉트 stub 1032개 제거, 완전 동일 내용 파일 472개 제거,
  동일 파일명·다른 내용 충돌 시 더 큰(완전한) 버전만 유지(162개 제거) — 총 1639개 중복 제거.
- 분류: 폴더 단위로 명확한 항목(AI_and_ML/Coding/Architecture 등 → Programming,
  Comfyui/Visual_Effects → Graphic, Topics_Biz/Topics_Meeting/사업 등 → Business,
  Poetic_Blog_Writing/창의성/Game_Design 등 → General)은 폴더 우선순위로,
  나머지 혼재 폴더(Topic_Agent/Topic_Blog/Topics 루트/Thinking & Reasoning/Other/UI_UX_Assets)는
  title/tags 키워드 스코어링으로 파일 단위 분류(불명확한 경우 General로 폴백).
  원본 폴더명은 "From_*" 서브폴더로 보존해 추적 가능성 유지.
- 최종 배치: Programming 2784 / General 1608 / Graphic 285 / Business 249 = 4926개 문서.
- 에이전트 운영 상태(.astra/.agent/.obsidian/sessions/memory/_company/docs/lessons/_shared/src)는
  지식 콘텐츠가 아니므로 재분류 대상에서 제외하고 원위치 유지.
- Topics/Topic_email(상위 보호 폴더 Topic_email과 파일명 100% 중복) 삭제 — 보호 폴더 자체는 미변경.
- 완전히 비게 된 Topic_Agent/Topic_Blog/Topics_Biz/Topics_Rag 폴더 제거.
2026-07-05 00:33:48 +09:00

11 KiB

id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
id title category status source_trust_level verification_status created_at updated_at tags tech_stack applied_in aliases
android-ml-kit-health Android ML Kit / Health Connect / On-device AI Coding draft B conceptual 2026-05-09 2026-05-09
android
mlkit
health
on-device
vibe-coding
language applicable_to
Kotlin
Android
ML Kit
Health Connect
MediaPipe
on-device ML
AICore
Gemini Nano

Android ML Kit / Health Connect / On-device AI

Google ML Kit (built-in ML), Health Connect (cross-app health), MediaPipe (advanced ML), Gemini Nano (on-device LLM, Pixel 9+).

📖 핵심 개념

  • ML Kit: 일반 ML task 빠른 사용.
  • Health Connect: data 통합 + permissions.
  • MediaPipe: vision / LLM 자체 모델.
  • Gemini Nano: AICore — on-device LLM.

💻 코드 패턴

ML Kit — Text recognition

implementation("com.google.mlkit:text-recognition:16.0.0")
import com.google.mlkit.vision.text.TextRecognition
import com.google.mlkit.vision.text.latin.TextRecognizerOptions

val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)

val image = InputImage.fromBitmap(bitmap, rotation)
val result = recognizer.process(image).await()  // suspend extension

for (block in result.textBlocks) {
    for (line in block.lines) {
        println(line.text)
    }
}

→ OCR. 영수증 / 명함.

ML Kit — Barcode

implementation("com.google.mlkit:barcode-scanning:17.2.0")

val scanner = BarcodeScanning.getClient()
val barcodes = scanner.process(image).await()

for (barcode in barcodes) {
    when (barcode.valueType) {
        Barcode.TYPE_URL -> println("URL: ${barcode.url?.url}")
        Barcode.TYPE_WIFI -> println("WiFi: ${barcode.wifi?.ssid}")
        Barcode.TYPE_TEXT -> println(barcode.rawValue)
    }
}

ML Kit — Face detection

val options = FaceDetectorOptions.Builder()
    .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
    .setLandmarkMode(FaceDetectorOptions.LANDMARK_MODE_ALL)
    .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
    .build()

val detector = FaceDetection.getClient(options)
val faces = detector.process(image).await()

for (face in faces) {
    val bounds = face.boundingBox
    val smilingProb = face.smilingProbability ?: 0f
    val leftEye = face.getLandmark(FaceLandmark.LEFT_EYE)?.position
}

ML Kit — Translation

implementation("com.google.mlkit:translate:17.0.2")

val options = TranslatorOptions.Builder()
    .setSourceLanguage(TranslateLanguage.KOREAN)
    .setTargetLanguage(TranslateLanguage.ENGLISH)
    .build()

val translator = Translation.getClient(options)
translator.downloadModelIfNeeded().await()

val translation = translator.translate("안녕").await()
// → "Hello"

ML Kit — Pose / Body

implementation("com.google.mlkit:pose-detection:18.0.0-beta3")

val options = PoseDetectorOptions.Builder()
    .setDetectorMode(PoseDetectorOptions.STREAM_MODE)
    .build()

val detector = PoseDetection.getClient(options)
val pose = detector.process(image).await()

val nose = pose.getPoseLandmark(PoseLandmark.NOSE)?.position
val leftWrist = pose.getPoseLandmark(PoseLandmark.LEFT_WRIST)?.position

→ Fitness app.

MediaPipe (advanced)

implementation("com.google.mediapipe:tasks-vision:0.10.20")

val options = ImageClassifier.ImageClassifierOptions.builder()
    .setBaseOptions(BaseOptions.builder().setModelAssetPath("model.tflite").build())
    .setMaxResults(5)
    .build()

val classifier = ImageClassifier.createFromOptions(context, options)
val result = classifier.classify(MPImage.fromBitmap(bitmap))

for (cat in result.classifications()[0].categories()) {
    println("${cat.categoryName()}: ${cat.score()}")
}

→ Custom TFLite 모델.

Gemini Nano (on-device, Pixel 9+)

implementation("com.google.ai.edge.aicore:aicore:0.0.1-exp01")

val options = generationConfig {
    context = context  // Activity / Application
    temperature = 0.2f
    topK = 16
    maxOutputTokens = 256
}

val generativeModel = GenerativeModel(generationConfig = options)

val response = generativeModel.generateContent("Summarize this article: ...").text

→ Cloud 호출 없이. Privacy + offline + free.

⚠️ Pixel 9 / 일부 device 만. Compatibility check.

Health Connect setup

implementation("androidx.health.connect:connect-client:1.1.0-alpha07")
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.health.READ_STEPS" />
<uses-permission android:name="android.permission.health.WRITE_STEPS" />
<uses-permission android:name="android.permission.health.READ_HEART_RATE" />

<queries>
    <package android:name="com.google.android.apps.healthdata" />
</queries>
import androidx.health.connect.client.HealthConnectClient
import androidx.health.connect.client.records.StepsRecord
import androidx.health.connect.client.permission.HealthPermission

val healthConnectClient = HealthConnectClient.getOrCreate(context)

val permissions = setOf(
    HealthPermission.getReadPermission(StepsRecord::class),
    HealthPermission.getWritePermission(StepsRecord::class),
)

// Request
val launcher = registerForActivityResult(
    PermissionController.createRequestPermissionResultContract()
) { granted -> /* ... */ }

launcher.launch(permissions)

Read steps

val response = healthConnectClient.readRecords(
    ReadRecordsRequest(
        recordType = StepsRecord::class,
        timeRangeFilter = TimeRangeFilter.between(
            Instant.now().minusSeconds(3600 * 24),
            Instant.now()
        )
    )
)

val totalSteps = response.records.sumOf { it.count }

Write steps

healthConnectClient.insertRecords(listOf(
    StepsRecord(
        count = 5000,
        startTime = Instant.now().minusSeconds(3600),
        endTime = Instant.now(),
        startZoneOffset = ZoneOffset.UTC,
        endZoneOffset = ZoneOffset.UTC,
    )
))

Aggregation

val agg = healthConnectClient.aggregate(
    AggregateRequest(
        metrics = setOf(StepsRecord.COUNT_TOTAL),
        timeRangeFilter = TimeRangeFilter.between(start, end)
    )
)

val total = agg[StepsRecord.COUNT_TOTAL] ?: 0L

Background sync

class HealthSyncWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
    override suspend fun doWork(): Result {
        val steps = readSteps()
        syncToServer(steps)
        return Result.success()
    }
}

// Schedule
val request = PeriodicWorkRequestBuilder<HealthSyncWorker>(15, TimeUnit.MINUTES).build()
WorkManager.getInstance(ctx).enqueueUniquePeriodicWork("health-sync", KEEP, request)

Privacy

- 명시적 사용자 consent
- Data minimization (read only what needed)
- 사용자 가 access / delete 가능
- GDPR / HIPAA compliance (US)

CameraX + ML Kit

val analyzer = ImageAnalysis.Analyzer { imageProxy ->
    val mediaImage = imageProxy.image ?: return@Analyzer
    val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
    
    barcodeScanner.process(image)
        .addOnSuccessListener { barcodes -> /* ... */ }
        .addOnCompleteListener { imageProxy.close() }
}

→ Real-time camera + ML.

TFLite (custom 모델)

implementation("org.tensorflow:tensorflow-lite:2.16.1")

val interpreter = Interpreter(loadModelFile())

val input = ByteBuffer.allocateDirect(...)
val output = ByteBuffer.allocateDirect(...)

interpreter.run(input, output)

→ 자체 모델 (TF / PyTorch → TFLite).

Audio classification

implementation("com.google.mediapipe:tasks-audio:0.10.20")

val options = AudioClassifier.AudioClassifierOptions.builder()
    .setBaseOptions(BaseOptions.builder().setModelAssetPath("yamnet.tflite").build())
    .build()

val classifier = AudioClassifier.createFromOptions(context, options)
val result = classifier.classify(MPAudioData.create(buffer, sampleRate))

for (cat in result.classifications()[0].categories()) {
    println("${cat.categoryName()}: ${cat.score()}")
    // "Music", "Speech", "Bark", ...
}

Subject segmentation (BG removal)

implementation("com.google.mlkit:subject-segmentation:16.0.0-beta1")

val segmenter = SubjectSegmentation.getClient()
val result = segmenter.process(image).await()

val foregroundBitmap = result.foregroundBitmap
// 배경 X — 사용자 / 사람 만

Document scanner

implementation("com.google.android.gms:play-services-mlkit-document-scanner:16.0.0-beta1")

val options = GmsDocumentScannerOptions.Builder()
    .setGalleryImportAllowed(true)
    .setPageLimit(5)
    .setResultFormats(RESULT_FORMAT_PDF, RESULT_FORMAT_JPEG)
    .build()

GmsDocumentScanning.getClient(options)
    .getStartScanIntent(activity)
    .addOnSuccessListener { intent -> startActivityForResult(intent, ...) }

→ Document scan + auto crop + PDF.

Smart reply (chat)

implementation("com.google.mlkit:smart-reply:17.0.4")

val smartReply = SmartReply.getClient()
val conversation = listOf(
    TextMessage.createForRemoteUser("Hi", System.currentTimeMillis(), "user_1"),
)

val result = smartReply.suggestReplies(conversation).await()

for (suggestion in result.suggestions) {
    println(suggestion.text)
    // "Hi!", "Hello", "Hey there"
}

Battery / performance

On-device ML = 빠름 + free + private.
But:
- Battery 사용
- Memory
- Model size (10-100 MB)

→ 측정 + throttle.

Cloud vs on-device

On-device:
+ Free (no API cost)
+ Private (no upload)
+ Offline
+ Low latency
- Limited model size
- Battery / memory

Cloud (Vertex AI / Gemini API):
+ Bigger / better model
+ Always updated
- Cost
- Privacy
- Latency

→ 일반 task = on-device. Complex / accuracy critical = cloud.

Edge AI (modern stack)

1. Quick task: ML Kit (built-in)
2. Custom: MediaPipe + TFLite
3. LLM: Gemini Nano (Pixel 9+)
4. Cloud fallback: Gemini API

🤔 의사결정 기준

작업 추천
OCR / barcode / face ML Kit
자체 모델 MediaPipe / TFLite
On-device LLM Gemini Nano (Pixel 9+)
Health data Health Connect
일반 LLM Cloud (Gemini API)
Real-time CameraX + ML Kit

안티패턴

  • 모든 거 cloud LLM: cost / privacy.
  • Health Connect 권한 한 번 + 모든 거: minimum access.
  • PII model 학습 외부 send: privacy violation.
  • Gemini Nano + 모든 device: compatibility check.
  • Battery 무시: 사용자 끄기.
  • 모델 download 큰 + first launch: progressive.

🤖 LLM 활용 힌트

  • ML Kit = 가장 단순 + 빠른 시작.
  • Health Connect = cross-app data.
  • MediaPipe = custom + advanced.
  • Gemini Nano = privacy-friendly LLM.

🔗 관련 문서