Skip to content

Latest commit

Β 

History

History
464 lines (307 loc) Β· 20.4 KB

Service.md

File metadata and controls

464 lines (307 loc) Β· 20.4 KB

Service

λ°œν‘œμž : μ†‘μ‹œμ€

λ°œν‘œμΌμž : 2018-09-28

λ°œν‘œμ£Όμ œ : Service


[TOC]

0. μ°Έκ³  λ¬Έμ„œ

1. κ°œμš”

μ„œλΉ„μŠ€: λ°±κ·ΈλΌμš΄λ“œμ—μ„œ 였래 μ‹€ν–‰λ˜λŠ” μž‘μ—…μ„ μˆ˜ν–‰ν•  수 μžˆλŠ” μ• ν”Œλ¦¬μΌ€μ΄μ…˜ ꡬ성 μš”μ†Œ

  • μ„œλΉ„μŠ€λŠ” 이름 κ·ΈλŒ€λ‘œ λ‹€λ₯Έ μ»΄ν¬λ„ŒνŠΈμ—κ²Œ μ„œλΉ„μŠ€λ₯Ό μ œκ³΅ν•˜λ €λŠ” λͺ©μ μœΌλ‘œ μ‚¬μš©

  • 였래 κ±Έλ¦¬λŠ” μž‘μ—…μ„ λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μ‹€ν–‰ν•˜κ²Œ ν•˜κ³ , λˆˆμ— λ³΄μ΄λŠ” 화면을 가지지 μ•ŠλŠ”λ‹€.

    • ex) 파일 λ‹€μš΄λ‘œλ“œ, μŒμ•… μž¬μƒ
  • https://github.com/taeiim/Android-Study/tree/master/study/week14/Service/BadThreadExam

  • μ„œλΉ„μŠ€ μ»΄ν¬λ„ŒνŠΈλ₯Ό μ‚¬μš©ν•˜λ©΄ μœ„ μ˜ˆμ œμ™€ 같은 문제λ₯Ό ν•΄κ²°ν•  수 μžˆλ‹€!

2. μ„œλΉ„μŠ€ μ‚¬μš©ν•˜κΈ°

μ„œλΉ„μŠ€λŠ” μŠ€νƒ€νŠΈ μ„œλΉ„μŠ€μ™€ λ°”μΈλ“œ μ„œλΉ„μŠ€, 두 가지 방식을 제곡

두 가지 쀑 μ„ νƒν•˜μ—¬ μ‚¬μš©ν•  μˆ˜λ„ 있고, λ‘˜ λ‹€ μ‚¬μš©ν•  μˆ˜λ„ μžˆλ‹€.

  • μŠ€νƒ€νŠΈ μ„œλΉ„μŠ€
    • μ‹œμž‘ν•΄ 두면 μ™„μ „νžˆ λ…λ¦½μ μœΌλ‘œ μˆ˜ν–‰λ˜λŠ” μž‘μ—…μ„ ν•œλ‹€.
    • λ‹€λ₯Έ μ»΄ν¬λ„ŒνŠΈκ°€ 쀑간에 μ’…λ£Œμ‹œν‚¬ 수 μžˆλ‹€.
  • λ°”μΈλ“œ μ„œλΉ„μŠ€
    • μ„œλΉ„μŠ€μ™€ λ‹€λ₯Έ μ»΄ν¬λ„ŒνŠΈμ™€ 연결고리λ₯Ό λ§Œλ“ λ‹€.
    • λ”°λΌμ„œ μ—°κ²°λœ μ»΄ν¬λ„ŒνŠΈμ˜ μƒνƒœμ— 따라 μ„œλΉ„μŠ€μ˜ λ™μž‘μ„ μ œμ–΄ν•  수 μžˆλ‹€.
    • 독립적이지 μ•Šκ³  λ‹€λ₯Έ μ»΄ν¬λ„ŒνŠΈμ— μ˜μ‘΄ν•œλ‹€.

2.1. μŠ€νƒ€νŠΈ μ„œλΉ„μŠ€

μŠ€νƒ€νŠΈ μ„œλΉ„μŠ€λŠ” 파일 λ‹€μš΄λ‘œλ“œ, μŒμ•… μž¬μƒκ³Ό 같은 κΈ°λŠ₯을 κ΅¬ν˜„ν•  λ•Œ μ‚¬μš©(λ¬Όλ‘  도쀑에 μ’…λ£Œ κ°€λŠ₯)

앱을 μ’…λ£Œν–ˆλ‹€κ°€ λ‹€μ‹œ μ‹œμž‘ν•˜λ”λΌλ„ μ„œλΉ„μŠ€λŠ” 계속 λ™μž‘ν•˜μ—¬ λ‹€μš΄λ‘œλ“œ μž‘μ—…μ„ λ©ˆμΆ”μ§€ 말아야 ν•œλ‹€.

https://github.com/taeiim/Android-Study/tree/master/study/week14/Service/ServiceExamΒ 

=> μ„œλΉ„μŠ€λŠ” 앑티비티와 λ…λ¦½μ μœΌλ‘œ λ™μž‘ν•˜λ―€λ‘œ 앱을 μ’…λ£Œν•˜κ³  λ‹€μ‹œ 싀행해도 μ„œλΉ„μŠ€μ˜ 생λͺ…μ£ΌκΈ°μ—λŠ” 아무 영ν–₯이 μ—†λ‹€.

Q. μ„œλΉ„μŠ€μ™€ μŠ€λ ˆλ“œ 쀑 μ–΄λŠ 것을 μ‚¬μš©ν•΄μ•Ό ν• κΉŒ?

A. μ„œλΉ„μŠ€λŠ” ν•„μš”ν•  λ•Œλ§Œ μƒμ„±ν•˜λŠ” 게 μ’‹λ‹€.

  • λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μˆ˜ν–‰ν•˜μ§€λ§Œ μ‚¬μš©μžκ°€ 앱을 μ‚¬μš© 쀑일 λ•Œλ§Œ λ™μž‘ν•΄μ•Ό ν•˜λŠ” μƒν™©μ—λŠ” μ„œλΉ„μŠ€ λŒ€μ‹  μŠ€λ ˆλ“œ!
  • μ„œλΉ„μŠ€λ₯Ό μ‚¬μš©ν•  λ•Œ μ£Όμ˜ν•  μ μœΌλ‘œλŠ” λͺ¨λ“  μ»΄ν¬λ„ŒνŠΈλŠ” 메인 μŠ€λ ˆλ“œμ—μ„œ λ™μž‘ν•˜λ―€λ‘œ μ„œλΉ„μŠ€κ°€ μˆ˜ν–‰ν•˜λŠ” μž‘μ—…μ΄ 메인 μŠ€λ ˆλ“œλ₯Ό μ μœ ν•˜λŠ” 일이라면 UIλ₯Ό μ°¨λ‹¨ν•˜μ§€ μ•Šλ„λ‘ μ„œλΉ„μŠ€ λ‚΄μ—μ„œ μŠ€λ ˆλ“œλ₯Ό μƒμ„±ν•˜μ•Ό ν•œλ‹€λŠ” 점
μ„œλΉ„μŠ€μ˜ 생쑴을 κ²°μ •ν•˜λŠ” 법

μ•ˆλ“œλ‘œμ΄λ“œ μ‹œμŠ€ν…œμ€ μžμ›μ΄ λΆ€μ‘±ν•˜λ©΄ λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μ‹€ν–‰λ˜λŠ” ν”„λ‘œμ„ΈμŠ€λ₯Ό ν•˜λ‚˜μ”© κ°•μ œλ‘œ μ’…λ£Œν•˜λ―€λ‘œ μ„œλΉ„μŠ€ μ—­μ‹œ κ°•μ œ μ’…λ£Œ λŒ€μƒμ΄ 될 수 μžˆλ‹€!

onStartCommand() λ©”μ„œλ“œκ°€ λ°˜ν™˜ν•˜λŠ” μƒμˆ˜κ°€ 그런 역할을 ν•œλ‹€. κ°•μ œ μ’…λ£Œλœ μ„œλΉ„μŠ€λ₯Ό 살릴 수 μžˆλŠ” λ‹€μ–‘ν•œ μ˜΅μ…˜

μƒμˆ˜ 의미
START_NOT_STICKY 전달할 μΈν…νŠΈκ°€ μžˆμ„ λ•Œλ₯Ό μ œμ™Έν•˜κ³ , μ„œλΉ„μŠ€κ°€ μ€‘λ‹¨λ˜λ©΄ μž¬μƒμ„±ν•˜μ§€ μ•ŠλŠ”λ‹€. κ°€μž₯ 일반적인 μ˜΅μ…˜
START_STICKY μ„œλΉ„μŠ€κ°€ μ€‘λ‹¨λ˜λ©΄ μ„œλΉ„μŠ€λ₯Ό μž¬μ‹œμž‘ν•˜μ§€λ§Œ, μΈν…νŠΈλŠ” λ‹€μ‹œ μ „λ‹¬ν•˜μ§€ μ•ŠλŠ”λ‹€. λ―Έλ””μ–΄ ν”Œλ ˆμ΄μ–΄μ™€ 같이 μ„œλΉ„μŠ€κ°€ λ¬΄κΈ°ν•œμœΌλ‘œ λ™μž‘ν•΄μ•Ό ν•  λ•Œ 적합
START_REDELIVER_INTENT START_STICKY와 λ‹€λ₯΄κ²Œ, μΈν…νŠΈμ™€ ν•¨κ»˜ μ„œλΉ„μŠ€λ₯Ό μž¬μ‹œμž‘ν•œλ‹€. 파일 λ‹€μš΄λ‘œλ“œμ™€ 같이 무엇을 λ‹€μš΄λ‘œλ“œ ν–ˆμ—ˆλŠ”μ§€ μ•Œμ•„μ•Ό ν•  λ•Œ 적합
μ„œλΉ„μŠ€μ—μ„œ μŠ€λ ˆλ“œλ₯Ό μ‚¬μš©ν•˜λŠ” 이유

μ„œλΉ„μŠ€λ₯Ό ν¬ν•¨ν•œ μ•ˆλ“œλ‘œμ΄λ“œ 4λŒ€ μ»΄ν¬λ„ŒνŠΈλŠ” 메인 μŠ€λ ˆλ“œμ—μ„œ μ‹€ν–‰λ˜λ―€λ‘œ 였래 κ±Έλ¦¬λŠ” 처리λ₯Ό ν•˜κ²Œ 되면 화면이 λ²„λ²…λŒ€λ©΄μ„œ ANR이 λ°œμƒν•  수 μžˆλ‹€. ANR λ°œμƒμ„ λ§‰μœΌλ €λ©΄ μ„œλΉ„μŠ€ λ‚΄μ—μ„œ μŠ€λ ˆλ“œλ₯Ό 생성해야 ν•œλ‹€.

μ„œλΉ„μŠ€μ˜ μ’…λ£Œ

μ„œλΉ„μŠ€μ˜ μ’…λ£ŒλŠ” 앑티비티와 같은 μ»΄ν¬λ„ŒνŠΈμ—μ„œ stopService() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•˜μ˜€λŠ”λ°, μ„œλΉ„μŠ€ λ‚΄μ—μ„œ stopSelf() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•΄λ„ λœλ‹€.

2.2 μΈν…νŠΈ μ„œλΉ„μŠ€

λŒ€λΆ€λΆ„μ˜ μ„œλΉ„μŠ€λŠ” μ—¬λŸ¬ μš”μ²­μ„ λ™μ‹œμ— μ²˜λ¦¬ν•˜μ§€ μ•Šμ•„λ„ 되기 λ•Œλ¬Έμ—, 일반적으둜 Serviceλ₯Ό ν™•μž₯ν•œ IntentService 클래슀λ₯Ό μ‚¬μš©ν•œλ‹€. 이 ν΄λž˜μŠ€λŠ” κΈ°λ³Έ Service ν΄λž˜μŠ€μ™€ λΉ„κ΅ν•˜μ—¬ λ‹€μŒκ³Ό 같은 μž‘μ—…μ„ μˆ˜ν–‰ν•œλ‹€.

  • onStartCommand() λ©”μ„œλ“œμ— μ „λ‹¬λœ μΈν…νŠΈλ§ˆλ‹€ λ³„λ„μ˜ μŠ€λ ˆλ“œλ₯Ό μžλ™μœΌλ‘œ 생성해 μ€€λ‹€.
  • ν•œ λ²ˆμ— ν•˜λ‚˜μ˜ μΈν…νŠΈλ₯Ό μ²˜λ¦¬ν•˜λŠ” onHandleIntent() λ©”μ„œλ“œλ₯Ό μ œκ³΅ν•œλ‹€.
  • λͺ¨λ“  μˆ˜ν–‰μ΄ λλ‚˜λ©΄ μžλ™μœΌλ‘œ stopSelf() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ μ’…λ£Œλœλ‹€.

κ°€μž₯ 큰 νŠΉμ§•μ€ μΈν…νŠΈ μ„œλΉ„μŠ€λŠ” 기본적으둜 μž‘μ—… μŠ€λ ˆλ“œμ—μ„œ λ™μž‘ν•˜λ―€λ‘œ 일반 μ„œλΉ„μŠ€λ³΄λ‹€ μ½”λ“œλ₯Ό κ°„κ²°ν•˜κ²Œ μž‘μ„±!!

2.2.1. 같이 μ‹€μŠ΅~

  1. MyIntentService 클래슀 생성

  2. μƒμ„±μž, onHandleIntent() λ©”μ„œλ“œλ§Œ 남기고 λ‚˜λ¨Έμ§€ μ½”λ“œ 제거(μΈν…νŠΈ μ„œλΉ„μŠ€ 기본적인 μ‚¬μš©λ°©λ²•μ„ μ•ŒκΈ° μœ„ν•΄)

  3. onHandleIntent() λ©”μ„œλ“œ(μž‘μ—… μŠ€λ ˆλ“œλ‘œ λ™μž‘ν•˜λ―€λ‘œ)에 였래 κ±Έλ¦¬λŠ” 처리λ₯Ό μž‘μ„±

    @Override
        protected void onHandleIntent(Intent intent) {
            for (int i = 0; i < 5; i++) {
                try {
                    // 1μ΄ˆλ§ˆλ‹€ 쉬기
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                // 1μ΄ˆλ§ˆλ‹€ 둜그 남기기
                Log.d("MyIntentService", "μΈν…νŠΈ μ„œλΉ„μŠ€ λ™μž‘ 쀑 " + i);
            }
        }
  4. xml에 μΈν…νŠΈ μ„œλΉ„μŠ€ μ‹œμž‘ λ²„νŠΌ μž‘μ„±

  5. 메인 앑티비티에 μΈν…νŠΈ μ„œλΉ„μŠ€λ₯Ό μ‹œμž‘ν•˜λŠ” onStartIntentService() λ©”μ„œλ“œ μž‘μ„±

  6. 둜그캣 확인(μΈν…νŠΈ μ„œλΉ„μŠ€ μ‹œμž‘ μ—¬λŸ¬λ²ˆ 클릭)

μœ„μ— λ§ν–ˆλ‹€ μ‹œν”Ό μ—”ν…νŠΈ μ„œλΉ„μŠ€λŠ” μ°¨λ‘€λŒ€λ‘œ 싀행됨을 확인할 수 μžˆλ‹€!!!!!

μΈν…νŠΈ μ„œλΉ„μŠ€λŠ” μˆœμ„œλŒ€λ‘œ 일을 μ²˜λ¦¬ν•˜κ³  슀슀둜 stopSelf() λ₯Ό ν˜ΈμΆœν•˜λ―€λ‘œ 도쀑에 stopService() λ©”μ„œλ“œ 호좜 X

2.3. ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€

  • μ„œλΉ„μŠ€λŠ” 기본적으둜 λ°±κ·ΈλΌμš΄λ“œμ—μ„œ λ™μž‘ν•˜λŠ” 것이 κΈ°λ³Έ!μ΄μ§€λ§Œ~ ν¬κ·ΈλΌμš΄λ“œμ—μ„œλ„ μ‹€ν–‰λ˜κ²Œ ν•  수 있음!
  • ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€λŠ” λ©”λͺ¨λ¦¬κ°€ λΆ€μ‘±ν•  λ•Œλ„ μ•ˆλ“œλ‘œμ΄λ“œ μ‹œμŠ€ν…œμ΄ κ°•μ œλ‘œ μ’…λ£Œν•  수 μ—†κ³ , μ„œλΉ„μŠ€λ₯Ό μ€‘λ‹¨ν•˜κ±°λ‚˜ μ•Œλ¦Όμ—μ„œ μ œκ±°λ˜μ–΄μ•Όλ§Œ μ’…λ£Œμ‹œν‚¬ 수 μžˆλ‹€.
  • ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€λ₯Ό λ§Œλ“€λ €λ©΄
    • μƒνƒœ ν‘œμ‹œμ€„μ— ν‘œμ‹œλ˜λŠ” μ•Œλ¦Ό(Notification)을 μ œκ³΅ν•΄μ•Ό ν•œλ‹€.
    • μ•Œλ¦Όμ˜ idκ°€ 0이 μ•„λ‹ˆμ–΄μ•Ό ν•œλ‹€.
    • μ„œλΉ„μŠ€ λ‚΄λΆ€μ—μ„œ startForeground() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ—¬ ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€ μ‹œμž‘

2.3.1. 같이 μ‹€μŠ΅~~

  1. MyService에 ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€ λ§Œλ“€κΈ°
... μƒλž΅ ...
	private void startForegroundService() {
        // default 채널 ID둜 μ•Œλ¦Ό 생성
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "default");
        builder.setSmallIcon(R.mipmap.ic_launcher);
        builder.setContentTitle("ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€");
        builder.setContentText("ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€ μ‹€ν–‰ 쀑");
        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
        builder.setContentIntent(pendingIntent);
        // μ˜€λ ˆμ˜€μ—μ„œλŠ” μ•Œλ¦Ό 채널을 λ§€λ‹ˆμ €μ— 생성해야 ν•œλ‹€
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationManager manager = (NotificationManager) 
                getSystemService(NOTIFICATION_SERVICE);
            manager.createNotificationChannel(new NotificationChannel("default", "κΈ°λ³Έ 채널", 
                NotificationManager.IMPORTANCE_DEFAULT));
        }
        // ν¬κ·ΈλΌμš΄λ“œλ‘œ μ‹œμž‘
        startForeground(1, builder.build());
    }
}
  • μ˜€λ ˆμ˜€λΆ€ν„°λŠ” μ•Œλ¦Όμ— 채널 IDλ₯Ό 지정해 μ€˜μ•Ό ν•œλ‹€.
  • μ—¬κΈ°μ„œλŠ” defaultλΌλŠ” 채널 IDλ₯Ό 지정, 채널 IDλŠ” μΉ΄ν…Œκ³ λ¦¬λΌκ³  μƒκ°ν•˜λ©΄ λœλ‹€.
  • NotificationManager에 채널 생성을 μš”μ²­ν•΄μ•Ό 채널이 생성 됨(채널 ID, 채널 이름, μ€‘μš”λ„)
  • 채널을 μƒμ„±ν•˜λŠ” createNotificationChannel() λ©”μ„œλ“œλŠ” 이전 버전에 μ—†κΈ° λ•Œλ¬Έμ— 버전 λΆ„κΈ° μ²˜λ¦¬ν•¨
  • μ£Όμ˜ν•  점은 startForeground(int, Notification)의 첫 번째 νŒŒλΌλ―Έν„°μΈ μ•Œλ¦Ό IDλŠ” 0이면 μ•ˆλ¨!
  • μ•Œλ¦Όμ˜ ν•„μˆ˜ μš”μ†ŒμΈ μ•„μ΄μ½˜, 제λͺ©, λ‚΄μš©μ€ κΌ­ μžˆμ–΄μ•Όν•¨(ν•˜λ‚˜λΌλ„ μ—†μœΌλ©΄ μ•Œλ¦Ό 등둝 X)
  1. μΈν…νŠΈμ— μ•‘μ…˜μ„ μ„€μ •ν•˜κ³  ν•΄λ‹Ή μ•‘μ…˜μ„ μ²˜λ¦¬ν•  수 μžˆλ„λ‘ onStartCommand() λ©”μ„œλ“œ μˆ˜μ •

    @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            if ("startForeground".equals(intent.getAction())) {
                // ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€ μ‹œμž‘
                startForegroundService();
            } else if (mThread == null) {
                ... μƒλž΅ ...
            }
            return START_STICKY;
        }
  2. xml νŒŒμΌμ— ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€ μ‹œμž‘ λ²„νŠΌ μΆ”κ°€

  3. 메인앑티비티에 ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€ μ‹œμž‘ λ²„νŠΌμ˜ λ™μž‘ μ •μ˜(μ—¬κΈ°μ„œ μΈν…νŠΈμ— startForeground μ•‘μ…˜ μΆ”κ°€)

    public void onStartForegroundService(View view) {
            Intent intent = new Intent(this, MyService.class);
            intent.setAction("startForeground");
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                startForegroundService(intent);
            } else {
                startService(intent);
            }
        }
  4. 버전 λΆ„κΈ° μˆ˜ν–‰(μ˜€λ ˆμ˜€μ—μ„œλŠ” ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€λ₯Ό μ‹œμž‘ν•˜κΈ° μœ„ν•΄ startForegroundService() λ©”μ„œλ“œ 호좜 ν›„ 5초 이내 μ„œλΉ„μŠ€μ—μ„œ startForeground() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•΄μ•Ό 함)

ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€ μ’…λ£Œν•˜κΈ°
  • stopForeground() λ©”μ„œλ“œ μ‚¬μš©ν•˜κΈ°
  • stopForeground() λ©”μ„œλ“œκ°€ μ„œλΉ„μŠ€λ₯Ό μ’…λ£Œμ‹œν‚€λŠ” 게 μ•„λ‹ˆλΌ, μ„œλΉ„μŠ€κ°€ μ‹œμŠ€ν…œμ— μ˜ν•΄ κ°•μ œ μ’…λ£Œ λŒ€μƒμ΄ λ˜λŠ” 것
μ•ˆλ“œλ‘œμ΄λ“œ μ˜€λ ˆμ˜€λΆ€ν„°λŠ” λ‹€λ₯΄λ‹€
  • μ•ˆλ“œλ‘œμ΄λ“œ μ˜€λ ˆμ˜€μ—μ„œλŠ” startService() λŒ€μ‹ μ— startForegroundService() λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•΄μ•Ό ν•˜λ©°, 5초 이내에 startForeground() λ©”μ„œλ“œλ₯Ό μ„œλΉ„μŠ€μ—μ„œ μ‹€ν–‰ν•΄μ•Ό ν•œλ‹€.
  • λ§Œμ•½ 5초 이내에 startForeground() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜μ§€ μ•ŠμœΌλ©΄ μ‹œμŠ€ν…œμ€ μ„œλΉ„μŠ€κ°€ μ‘λ‹΅ν•˜μ§€ μ•ŠλŠ”λ‹€κ³  νŒλ‹¨ν•˜μ—¬ ANR μ—λŸ¬λ₯Ό λ°œμƒμ‹œν‚¨λ‹€.
  • μ•Œλ¦Ό 채널이 ν•„μˆ˜μ—¬μ„œ 채널을 μƒμ„±ν•˜μ§€ μ•Œλ¦Ό ν‘œμ‹œκ°€ λ˜μ§€ μ•ŠλŠ”λ‹€.
  • μ΄λŸ¬ν•œ 변경사항을 이전 버전과 μƒˆλ‘œμš΄ 버전에 λͺ¨λ‘ μ μš©ν•˜λ €λ©΄ μ‹€μŠ΅μ—μ„œμ²˜λŸΌ 버전 λΆ„κΈ° 처리λ₯Ό ν•΄μ•Όν•œλ‹€.
μΈν…νŠΈ μ„œλΉ„μŠ€μ™€ ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€ 정리
  • μ•ˆλ“œλ‘œμ΄λ“œλŠ” λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μž‘λ™ν•˜λŠ” λͺ¨λ“  μ„œλΉ„μŠ€λ„ μ–Έμ  κ°€λŠ” κ°•μ œλ‘œ μ’…λ£Œν•  수 μžˆλ‹€. ν¬κ·ΈλΌμš΄λ“œ μ„œλΉ„μŠ€λ₯Ό μ‚¬μš©ν•˜λ©΄ 죽지 μ•ŠλŠ” μ„œλΉ„μŠ€λ‘œ μ‚¬μš©ν•  수 μžˆλ‹€.
  • 단, 항상 μ•Œλ¦Όμ„ ν‘œμ‹œν•΄μ•Ό ν•˜κ³  μ‚¬μš©μžκ°€ μ•Œλ¦Όμ„ μ œκ±°ν•˜λ©΄ μ„œλΉ„μŠ€λ„ 쀑지될 수 μžˆλ‹€.
  • ServiceλŠ” λͺ¨λ“  μ„œλΉ„μŠ€μ˜ κΈ°λ³Έ 클래슀이며, 이 클래슀λ₯Ό 상속할 경우 λͺ¨λ“  μž‘μ—…μ€ Threadλ₯Ό μƒμ„±ν•˜μ—¬ μž‘μ—…ν•΄μ•Ό ν•œλ‹€. μ„œλΉ„μŠ€λŠ” 메인 μŠ€λ ˆλ“œμ—μ„œ λ™μž‘ν•˜λ―€λ‘œ μ•‘ν‹°λΉ„ν‹°μ˜ μ„±λŠ₯에 영ν–₯을 쀄 수 있기 λ•Œλ¬Έμ΄λ‹€.
  • IntentServiceλŠ” Service의 μ„œλΈŒν΄λž˜μŠ€μ΄λ©°, λͺ¨λ“  μž‘μ—…μ„ μž‘μ—… μŠ€λ ˆλ“œμ—μ„œ μˆ˜ν–‰ν•˜λ©° ν•œ λ²ˆμ— ν•˜λ‚˜μ”©λ§Œ μˆ˜ν–‰ν•œλ‹€. μ„œλΉ„μŠ€κ°€ μ—¬λŸ¬ 개의 μž‘μ—…μ„ λ™μ‹œμ— ν•  ν•„μš”κ°€ 없을 λ•Œ μ΅œμ„ μ˜ 선택!! onHandleIntent() 만 κ΅¬ν˜„ν•˜λ©΄ 됨.

2.3. λ°”μΈλ“œ μ„œλΉ„μŠ€

μ„œλΉ„μŠ€μ™€ λ‹€λ₯Έ κ΅¬μ„±μš”μ†Œλ₯Ό μ„œλ‘œ μ—°κ²°ν•˜λ €λ©΄ λ‹€λ₯Έ κ΅¬μ„±μš”μ†Œκ°€ μ„œλΉ„μŠ€μ— λ°”μΈλ“œν•΄μ•Ό ν•œλ‹€.

μ§€κΈˆκΉŒμ§€λŠ” μΉ΄μš΄νŒ… 정보λ₯Ό λ‘œκ·ΈμΊ£μ—μ„œ ν™•μΈν–ˆλŠ”λ°, μ—¬κΈ°μ„œλŠ” μ•‘ν‹°λΉ„ν‹°λ₯Ό μ„œλΉ„μŠ€μ— μ—°κ²°(λ°”μΈλ“œ)ν•˜μ—¬ μ„œλΉ„μŠ€λ‘œλΆ€ν„° μΉ΄μš΄νŒ… 정보λ₯Ό μ œκ³΅λ°›μ•„ ν† μŠ€νŠΈ λ©”μ‹œμ§€λ‘œ ν‘œμ‹œν•΄ 보겠닀!

2.3.1. λ°”μΈλ“œ μ„œλΉ„μŠ€λ₯Ό μž‘μ„±ν•˜κΈ° 전에..

  • λ°”μΈλ“œ μ„œλΉ„μŠ€λ₯Ό μ΄μš©ν•˜λ©΄ 앑티비티와 같은 λ‹€λ₯Έ μ»΄ν¬λ„ŒνŠΈμ™€ μ—°κ²°ν•˜μ—¬ μ„œλ‘œ 데이터λ₯Ό 주고받을 수 μžˆλ‹€. μ„œλΉ„μŠ€λŠ” λ³„λ„μ˜ ν”„λ‘œμ„ΈμŠ€λ‘œ λ™μž‘ν•˜κΈ° λ•Œλ¬Έμ— μ„œλΉ„μŠ€λΌλ¦¬ 데이터λ₯Ό μ£Όκ³ λ°›μœΌλ €λ©΄ '바인더(binder)'λ₯Ό μ‚¬μš©ν•œλ‹€.
  • λ¦¬λˆ…μŠ€μ˜ IPC(Inter Process Communication)κ³Ό RPC(Remote Procedure Call) 기술이 바인더에 μ μš©λ˜μ–΄ λ‹€λ₯Έ ν”„λ‘œμ„ΈμŠ€μž„μ—λ„ λ©”μ„œλ“œλ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.
  • μ›λž˜λŠ” ν”„λ‘œμ„ΈμŠ€λΌλ¦¬ μžμ›μ„ κ³΅μœ ν•  수 μ—†λŠ”λ° 그것을 κ°€λŠ₯ν•˜κ²Œ ν•΄μ£ΌλŠ” 것이 바인더라고 μƒκ°ν•˜λ©΄ 쉽닀.

  • IPC(Inter Process Communication)

λ¦¬λˆ…μŠ€μ—μ„œλͺ¨λ“  ν”„λ‘œμ„ΈμŠ€κ°€ κ³΅μœ ν•˜λŠ” λ©”λͺ¨λ¦¬μ— μ ‘κ·Όν•΄μ„œ 데이터λ₯Ό 읽고 μ“Έ 수 μžˆκ²Œν•˜λŠ” κ°œλ…

  • RPC(Remote Procedure Call)

λ‹€λ₯Έ ν”„λ‘œμ„ΈμŠ€μ˜ ν•¨μˆ˜(λ©”μ„œλ“œ)λ₯Ό ν˜ΈμΆœν•  수 μžˆλ„λ‘ ν•˜λŠ” 기술

2.3.2. 같이 μ‹€μŠ΅~~

  1. MyService.java 파일 μˆ˜μ •

    • λ°”μΈλ“œ μ„œλΉ„μŠ€λ₯Ό μ œκ³΅ν•˜λ €λ©΄ Service 클래슀의 onBind() λ©”μ„œλ“œμ—μ„œ 바인더 객체λ₯Ό λ°˜ν™˜ν•΄μ•Ό ν•œλ‹€.
    • 바인더 κ°μ²΄λŠ” Binderλ₯Ό μƒμ†ν•˜μ—¬ λ§Œλ“€κ³  μ„œλΉ„μŠ€μ˜ 레퍼런슀λ₯Ό 전달할 수 μžˆλ„λ‘ getService() λ₯Ό λ§Œλ“ λ‹€.
    ... μƒλž΅ ...
    // MyService의 레퍼런슀λ₯Ό λ°˜ν™˜ν•˜λŠ” Binder 객체
        private IBinder mBinder = new MyBinder();
    
        public class MyBinder extends Binder{
            public MyService getService(){
                return MyService.this;
            }
        }
    
        // λ°”μΈλ“œλœ μ»΄ν¬λ„ŒνŠΈμ— μΉ΄μš΄νŒ… λ³€μˆ«κ°’ 제곡
        public int getmCount() {
            return mCount;
        }
    ... μƒλž΅ ...
    • onBind() 콜백 λ©”μ„œλ“œλŠ” λ‹€λ₯Έ κ΅¬μ„±μš”μ†Œμ— μ„œλΉ„μŠ€κ°€ 바인딩(μ—°κ²°)되면 호좜되며, μ΄λ•Œ Binderλ₯Ό λ°˜ν™˜ν•œλ‹€.
    • 이 객체λ₯Ό 톡해 앑티비티와 μ„œλΉ„μŠ€λŠ” 톡신할 수 있게 λœλ‹€.
  2. ServiceConnection 객체 μ •μ˜

    • 앑티비티와 같은 μ»΄ν¬λ„ŒνŠΈλ₯Ό μ„œλΉ„μŠ€μ— μ—°κ²°ν•˜λ €λ©΄ ServiceConnection 객체가 ν•„μš”ν•˜λ‹€.
    • 이 κ°μ²΄λŠ” 두 가지 콜백 λ©”μ„œλ“œ 제곡
      • 첫 λ²ˆμ§ΈλŠ” μ„œλΉ„μŠ€μ— μ—°κ²°λ˜μ—ˆλŠ”μ§€ μ•Œ 수 μžˆλŠ” 콜백
      • 두 λ²ˆμ§ΈλŠ” μ„œλΉ„μŠ€κ°€ μ‹œμŠ€ν…œμ— μ˜ν•΄ κ°•μ œλ‘œ μ’…λ£Œλ˜μ—ˆμ„ λ•Œ 감지할 수 μžˆλŠ” 콜백
    • 메인앑티비티에 ServiceConnection 객체λ₯Ό μ •μ˜ν•˜κ³ ,
    • onStart() λ©”μ„œλ“œμ—μ„œ bindService() λ©”μ„œλ“œλ‘œ μ„œλΉ„μŠ€μ— μ—°κ²°ν•œλ‹€.
    • μ•‘ν‹°λΉ„ν‹°κ°€ ν¬κ·ΈλΌμš΄λ“œμ—μ„œ λ™μž‘ 쀑이 아닐 λ•ŒλŠ” μ„œλΉ„μŠ€μ™€ 연결을 ν•΄μ œν•΄μ•Ό ν•˜λŠ”λ°, onStop() λ©”μ„œλ“œμ—μ„œ unBindService() λ©”μ„œλ“œλ‘œ 연결을 ν•΄μ œν•œλ‹€.
    @Override
        protected void onStart() {
            super.onStart();
            // μ„œλΉ„μŠ€μ— 바인딩
            Intent intent = new Intent(this, MyService.class);
            bindService(intent, mConnection, BIND_AUTO_CREATE);
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            // μ„œλΉ„μŠ€μ™€ μ—°κ²° ν•΄μ œ
            if (mBound) {
                unbindService(mConnection);
                mBound = false;
            }
        }
    
        /**
         * bindService() λ₯Ό 톡해 μ„œλΉ„μŠ€μ™€ 연결될 λ•Œμ˜ 콜백 μ •μ˜
         */
        private ServiceConnection mConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                // MyBinder와 연결될 것이며 IBinder νƒ€μž…μœΌλ‘œ λ„˜μ–΄μ˜€λŠ” 것을 μΊμŠ€νŒ…ν•˜μ—¬ μ‚¬μš©
                MyService.MyBinder binder = (MyService.MyBinder) service;
                mService = binder.getService();
                mBound = true;
            }
            @Override
            public void onServiceDisconnected(ComponentName name) {
                // 예기치 μ•Šμ€ μ’…λ£Œ
            }
        };
    • μ—¬κΈ°μ„œ ServiceConnection 객체의 콜백 쀑에 onServiceDisconnected() λ©”μ„œλ“œλŠ” μ„œλΉ„μŠ€μ™€ 연결을 μ’…λ£Œν•  λ•Œ 항상 ν˜ΈμΆœλ˜λŠ” 콜백이 μ•„λ‹ˆλΌ, κ°•μ œ μ’…λ£Œμ™€ 같은 예기치 μ•Šμ€ μ’…λ£Œμ—λ§Œ ν˜ΈμΆœλœλ‹€.
    • onStart() λ©”μ„œλ“œμ—μ„œ bindService() λ©”μ„œλ“œμ˜ μ„Έ 번째 νŒŒλΌλ―Έν„°μΈ ν”Œλž˜κ·Έλ‘œ BIND_AUTO_CREATE κ°€ μ„€μ •λ˜μ–΄ μžˆλ‹€. μ„œλΉ„μŠ€μ— μ—°κ²°ν•˜λ €λ©΄ μ„œλΉ„μŠ€κ°€ μ‹œμž‘λ˜μ–΄μ•Ό ν•˜λŠ”λ° 이 ν”Œλž˜κ·Έλ₯Ό μ„€μ •ν•˜λ©΄ μ„œλΉ„μŠ€λ₯Ό λ”°λ‘œ μ‹œμž‘ν•˜μ§€ μ•Šκ³ λ„ λ°”μΈλ“œ μ„œλΉ„μŠ€λ₯Ό μ‚¬μš©ν•  수 μžˆλ‹€.
    • onServiceConnected() λ©”μ„œλ“œκ°€ 호좜되면 μ„œλΉ„μŠ€μ— 잘 μ—°κ²°λœ 것이며, μ΄λ•Œ μ„œλΉ„μŠ€μ—μ„œ Binder 객체가 λ„˜μ–΄μ˜€λ‹ˆ μ—¬κΈ°μ„œ getService() λ©”μ„œλ“œλ₯Ό 톡해 μ—°κ²°λœ μ„œλΉ„μŠ€μ˜ μΈμŠ€ν„΄μŠ€λ₯Ό μ°Έμ‘°ν•  수 μžˆλ‹€.
  3. 바인딩 μƒνƒœ 확인

    • 앱을 μ‹€ν–‰ν•˜κΈ° 전에 앑티비티와 μ„œλΉ„μŠ€μ˜ μ—°κ²° μƒνƒœλ₯Ό λͺ¨λ‹ˆν„°λ§ν•˜κΈ° μœ„ν•΄ 둜그 μΆ”κ°€

      @Override
          public IBinder onBind(Intent intent) {
              Log.d(TAG, "onBind: ");
              return mBinder;
          }
      
          @Override
          public boolean onUnbind(Intent intent) {
              Log.d(TAG, "onUnbind: ");
              return super.onUnbind(intent);
          }
    • onBind() 와 λ°˜λŒ€λ‘œ onUnBind() λŠ” 연결이 λŠμ–΄μ‘Œμ„ λ•Œ ν˜ΈμΆœλ˜λŠ” λ©”μ„œλ“œ

  4. xml νŒŒμΌμ— μΉ΄μš΄νŒ… κ°’ 좜λ ₯ λ²„νŠΌ μΆ”κ°€

  5. 메인 μ•‘ν‹°λΉ„ν‹°μ—μ„œ ν† μŠ€νŠΈ 값이 λ‚˜μ˜¬ 수 μžˆλ„λ‘ getCountValue() λ©”μ„œλ“œ μˆ˜μ •

    public void getCountValue(View view) {
            if (mBound){
                Toast.makeText(this, "μΉ΄μš΄νŒ… : " + mService.getCount(), 
                               Toast.LENGTH_SHORT).show();
            }
        }
κ²°κ³Ό
  • 앱을 μ‹€ν–‰ν•˜μ—¬ <μΉ΄μš΄νŒ… κ°’ 좜λ ₯> λ²„νŠΌμ„ λˆ„λ₯΄λ©΄ 0이 ν‘œμ‹œλœλ‹€.
  • <μ„œλΉ„μŠ€ μ‹œμž‘>을 λˆ„λ₯Έ ν›„ <μΉ΄μš΄νŒ… κ°’ 좜λ ₯> λ²„νŠΌμ„ λˆ„λ₯΄λ©΄, μΉ΄μš΄νŒ… 값이 λ³€ν•˜λŠ” 것을 확인할 수 μžˆλ‹€.
  • 참고둜 λ°”μΈλ“œ μ„œλΉ„μŠ€κ°€ λ°”μΈλ“œ 쀑일 λ•ŒλŠ” λ°”μΈλ“œ ν•΄μ œλ  λ•ŒκΉŒμ§€ stopService() 둜 μ„œλΉ„μŠ€λ₯Ό μ’…λ£Œν•  수 μ—†λ‹€.
  • startService() λŠ” κ°€λŠ₯ν•˜λ‹€. λ”°λΌμ„œ μ΄μ œλΆ€ν„°λŠ” μ„œλΉ„μŠ€ μ‹œμž‘ 후에 <μ„œλΉ„μŠ€ 쀑지>λ₯Ό λˆŒλŸ¬λ„ μ„œλΉ„μŠ€κ°€ μ’…λ£Œ X

3. μ„œλΉ„μŠ€μ˜ 생λͺ…μ£ΌκΈ°

public class ExampleService extends Service {
    int mStartMode;			// START_NOT_STICKY λ“±μ˜ ν”Œλž˜κ·Έ
    IBinder mBinder;		// 바인딩 객체
    boolean mAllowRebind;	// 재 바인딩을 ν—ˆμš©ν• μ§€ μ—¬λΆ€
    
    @Override
    public void onCreate() {
        // μ„œλΉ„μŠ€ 생성
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // startService()에 μ˜ν•΄ μ„œλΉ„μŠ€ μ‹œμž‘
        return mStartMode;
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        // bindService()에 μ˜ν•΄ ν΄λΌμ΄μ–ΈνŠΈμ™€ μ„œλΉ„μŠ€ μ—°κ²°
        return mBinder;
    }
    
    @Override
    public boolean onUnBind(Intent intent) {
        // unBindService()에 μ˜ν•΄ ν΄λΌμ΄μ–ΈνŠΈμ™€ μ—°κ²° ν•΄μ œ
        return mAllowRebind;
    }
    
    @Override
    public void onRebind(Intent intent) {
        // bindService()에 μ˜ν•΄ ν΄λΌμ΄μ–ΈνŠΈμ™€ μ„œλΉ„μŠ€ μ—°κ²° ν›„
        // onUnbind() 호좜 ν›„ λ‹€μ‹œ 바인딩
    }
    
    @Override
    public void onDestroy() {
        // μ„œλΉ„μŠ€ μ’…λ£Œ
    }
}
  • λ°”μΈλ“œ μ„œλΉ„μŠ€λŠ” 바인딩이 ν•΄μ œλ˜λ©΄ μ•ˆλ“œλ‘œμ΄λ“œ μ‹œμŠ€ν…œμ΄ μ„œλΉ„μŠ€λ₯Ό μ’…λ£Œν•˜λ―€λ‘œ κ°œλ°œμžκ°€ 생λͺ…μ£ΌκΈ°λ₯Ό μ‹ κ²½ 쓰지 μ•Šμ•„λ„ λœλ‹€.
  • ν•˜μ§€λ§Œ onStartCommand() λ©”μ„œλ“œλ‘œ μ‹œμž‘λœ μŠ€νƒ€νŠΈ μ„œλΉ„μŠ€λŠ” 바인딩 여뢀와 관계없이, κ°œλ°œμžκ°€ 생λͺ…μ£ΌκΈ°λ₯Ό 관리해야 ν•œλ‹€.
  • μŠ€νƒ€νŠΈ μ„œλΉ„μŠ€λŠ” stopSelf()λ‚˜ stopService()λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœν•˜κΈ° μ „κΉŒμ§€ 계속 μ‹€ν–‰λ˜κΈ° λ•Œλ¬Έμ΄λ‹€.
  • μŠ€νƒ€νŠΈ μ„œλΉ„μŠ€κ°€ 바인딩을 ν—ˆμš©ν•  λ•Œ λ‹€μŒμ— 바인딩 μ‹œ onRebind() λ©”μ„œλ“œλ₯Ό ν˜ΈμΆœλ°›κ³  μ‹Άλ‹€λ©΄ onUnbind() λ©”μ„œλ“œμ—μ„œ trueλ₯Ό λ°˜ν™˜ν•˜λ„λ‘ ν•  수 μžˆλ‹€.
  • 이 λ•Œλ„ ν΄λΌμ΄μ–ΈνŠΈμ—μ„œλŠ” onServiceConnected() μ½œλ°±μ—μ„œ 바인더λ₯Ό μ–»κ²Œ λ˜λŠ” 것은 κ°™λ‹€. μ„œλΉ„μŠ€κ°€ ν΄λΌμ΄μ–ΈνŠΈλ‘œλΆ€ν„° 항상 μƒˆλ‘œμš΄ μ ‘μ†μœΌλ‘œ μ·¨κΈ‰ν•˜λŠλƒ, μ•„λ‹ˆλ©΄ 접속과 μž¬μ ‘μ†μ„ ν—ˆμš©ν•˜λŠλƒμ˜ 차이이닀.