Android 자료실/버전 이슈

안드로이드(ANDROID) Q(10) - 백그라운드 서비스 엑티비티 실행 제한

Victorywskim 2020. 3. 27. 18:45
반응형

작년 하반기에 기존에 운영하던 앱에서 안드로이드 10에 이슈가 확인되어 급히 수정했었던 기억이 있는데

구글은 이미 안드로이드 11을 프리뷰 버전으로 출시하여 테스트를 거치고 있다. (너무 열일하는거 아닌가)

 

안드로이드 11 버전 먼저 포스팅을 할까 하다가 우선 가장 최근에 접한 안드로이드 10 먼저 포스팅하는 것이 순서인 것 같아서

안드로이드 10 버전에 대한 내용 먼저 다뤄보려고 한다.

 

내가 겪은 가장 큰 이슈는 포어그라운드 서비스에서 특정 시점에 엑티비티를 실행시켜야 하는데 딱 제한을 받게되서 엑티비티 실행이 되지 않는 부분이였다.

 

다행이도 안드로이드 개발자 페이지를 정독하다보니 실행 제한에서 제외되는 방법이 여럿있었다.

 

백그라운드에서 Activity 시작에 대한 제한 사항  |  Android 개발자  |  Android Developers

Android 10 (API 레벨 29) 이상은 앱이 백그라운드에서 실행될 때 Activity를 시작할 수 있는 시점에 제한이 있습니다. 이 제한은 사용자에 대한 방해를 최소화하고 사용자가 화면에 표시되는 내용을 더욱 잘 제어할 수 있도록 합니다. 참고: Activity를 시작하기 위해 포그라운드 서비스를 실행하는 앱은 여전히 “백그라운드에 있는 것”으로 간주됩니다. 이 가이드는 백그라운드에서 Activity를 시작하는 대신 알림을 표시하는 방법을 제시합

developer.android.com

하지만 나의 경우에는 가이드에 명시된 특정 상황들 보다도 내 앱의 알고리즘에 따라 엑티비티를 띄워야 하는 케이스여서 아래와 같은 케이스는 사용하지 못했다.

  • 앱에 가시적 창이 있을 경우(예: 포그라운드에서 실행되는 Activity).
  • 앱에 포그라운드 작업의 백 스택에 있는 Activity가 있을 경우.
  • 앱에 최근 화면의 기존 작업 백 스택에 있는 Activity가 있을 경우.

그래서 가이드 문서 맨 하단에 있는

를 통해 문제를 해결하였다.

 

단 개발자 입장에서 적용 방법은 간단하지만 유저에게서는 권한을 부여받아야 하기 때문에 반드시 서비스의 방향성과 맞는지 고려한 뒤 사용하는 것을 권장한다.

 

설명은 코드로 대신하도록 하겠다.

 

1. 매니페스트에 "다른 화면 위에 그리기" 권한을 추가한다.

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

2. 항상 "다른 화면 위에 그리기" 권한을 요청한다.

val resultCode = 12345
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_overlay_request)

    if(
    // 엑티비티 실행에만 사용할 예정이라서 이슈가 있는 10 버전인지 확인하기 위함
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
        &&
        // 다른 화면 위에 그리기 권한이 잇는지 확인
        !Settings.canDrawOverlays(this)
    ){
        // 사용자에게 이 권한이 왜 필요한지에 대해 설명하기 위한 다이얼로그
        val builder = AlertDialog.Builder(this).apply {
            title = "권한 요청"
            setMessage("다른 화면 위에 그리기라는 권한이 필요합니다.\n수락 해주실거죠?")
            setCancelable(false)
            setNegativeButton("취소") { dialog, _ ->
                // 취소 버튼 터치
                dialog.dismiss();
            }
            .setPositiveButton("수락") { dialog, _ ->
                val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION
                , Uri.parse("package:$packageName"))
                startActivityForResult(intent, resultCode)
                dialog.dismiss();
            }
        }

        builder.show();
    }else{
    	// 기능 실행하기
    }
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    if (resultCode == this.resultCode) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this@OverlayRequest)) {
                Toast.makeText(this@OverlayRequest, "권한을 수락해주세요.", Toast.LENGTH_SHORT).show()
            } else {
                // 기능 실행하기
            }
        }
    }
}

로직에 대해서 간략하게 설명을 덧붙이자면 

if(
    // 엑티비티 실행에만 사용할 예정이라서 이슈가 있는 10 버전인지 확인하기 위함
        Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q
        &&
        // 다른 화면 위에 그리기 권한이 잇는지 확인
        !Settings.canDrawOverlays(this)
    )

엑티비티 실행 시 권한의 존재 여부를 확인한다. 주석과 같이 만약 안드로이드 10에서만 사용할게 아니라면 빌드 버전 확인하는 로직을 제거하면 되고, 10버전만 사용할 예정이라면 예제대로 이용하면 된다.

 

// 사용자에게 이 권한이 왜 필요한지에 대해 설명하기 위한 다이얼로그
val builder = AlertDialog.Builder(this).apply {
      title = "권한 요청"
      setMessage("다른 화면 위에 그리기라는 권한이 필요합니다.\n수락 해주실거죠?")
      setCancelable(false)
      setNegativeButton("취소") { dialog, _ ->
          // 취소 버튼 터치
          dialog.dismiss();
      }
      setPositiveButton("수락") { dialog, _ ->
           val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION
           , Uri.parse("package:$packageName"))
           startActivityForResult(intent, resultCode)
           dialog.dismiss();
      }
}

사실 다이얼로그 없이 바로 실행 시켜도 문제는 없지만 무작정 권한 페이지로 보내는 것은 맞지 않다고 생각하여 추가한것이니 다이얼로그는 필요에 따라 제거해도 무방하다.

 

val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION
           , Uri.parse("package:$packageName"))
startActivityForResult(intent, resultCode)

실제로 권한을 요청하는 코드는 이게 전부고 권한 페이지 이동 후 다시 현재 엑티비티로 돌아왔을때 아래 코드를 타게 된다.

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)

    if (resultCode == this.resultCode) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            if (!Settings.canDrawOverlays(this@OverlayRequest)) {
                Toast.makeText(this@OverlayRequest, "권한을 수락해주세요.", Toast.LENGTH_SHORT).show()
            } else {
                // 기능 실행하기
            }
        }
    }
}

생각보다 간단한 기능을 너무 장황하게 설명한게 아닌가 싶긴하다.

 

이상으로 안드로이드 10에서 백그라운드 엑티비티 실행 제한에 대한 내용을 마치도록 하겠다.

 

 

 

참고 자료 :

https://source.android.google.cn/setup/start/android-10-release?hl=ko

 

Android 출시 노트  |  Android 오픈소스 프로젝트  |  Android Open Source Project

https://developer.android.com/about/versions/10?hl=ko

 

Android 개발자  |  Android Developers

Android 10용으로 앱을 준비하세요!

developer.android.com

 

반응형