[Android] 6(3). BaseActivity 설계하기

3 minute read


BaseActivity 설계하기

권한 처리와 같은 반복적인 코드들을 BaseActivity를 하나 만들어두고, 각각의 액티비티에서 상속받아서 사용하는 것이 훨씬 효율적입니다.


BaseActivity 만들기


권한 처리 전용 액티비티인 BaseActivity는 다른 액티비티에서 상속받아서 사용되기 때문에, 직접 실행되는 것을 방지하고 상속받은 액티비티(구현체)에서만 사용할 수 있게 만들어야 합니다. 그래서 액티비티(Activity)이지만 일반적으로 추상 클래스(Class)로 설계합니다.


1. BaseActivity 추상 클래스 생성

abstract 키워드를 앞에 붙이고, AppCompatActivity를 상속받습니다.

또한 상속받은 액티비티에 권한 승인/미승인 확인 시의 메서드 구현을 강제하기 위해 추상 메서드를 선언합니다.

// 액티비티이지만 직접 실행되는 것을 방지하고 상속받은 액티비티(구현체)에서만
// 사용할 수 있게 '추상 클래스'로 정의
abstract class BaseActivity: AppCompatActivity() {
  
    // 상속받은 액티비티에게 구현을 강제함
    abstract fun permissionGranted(requestCode: Int)
    abstract fun permissionDenied(requestCode: Int)
}


2. 자식 액티비티에서 권한 요청 시 호출되는 requirePermissions 메서드 정의

    // 자식 액티비티에서 권한 요청 시 직접 호출하는 메서드
    // 파라미터: 요청권한 배열, 리퀘스트 코드
    fun requirePermissions(permissions: Array<String>, requestCode: Int){
        // 안드로이드 버전 체크
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M){ // 6.0 미만
            permissionGranted(requestCode)
        }
        else{ // 권한 체크를 해야하는 버전
            // 권한이 모두 승인되었는 지 확인
            val isAllPermissionsGranted = permissions.all{
                checkSelfPermission(it) == PackageManager.PERMISSION_GRANTED
            }
            // 권한 승인여부에 따라 분기
            if (isAllPermissionsGranted){ // 모두 승인되었다면,
                permissionGranted(requestCode)
            }
            else{ // 미승인 권한이 있다면,
                ActivityCompat.requestPermissions(this, permissions, requestCode)
            }

        }
    }

requirePermissions 메서드 안에서는 다음 요소들을 확인합니다.

  • 안드로이드 버전이 6.0 미만이라면 승인 확인 메서드 호출
  • 6.0 이상이라면 요청권한들의 승인 여부 확인
    • 모두 승인상태라면, 승인 확인 메서드 호출
    • 미승인 상태가 잇다면, 승인 요청 메서드 호출

Array의 all 메서드를 사용하면 배열 속에 들어있는 모든 값을 한 번에 확인할 수 있습니다. PackageManager.PERMISSION_GRANTED는 문자열로 정의된 상수로, 비교 대상이 String일 경우 checkSelfPermission() 메서드로 감싸주어야 하고, Int일 경우 바로 비교합니다.


3. 사용자 권한 선택 후 호출되는 onRequestPermissionsResult 메서드 오버라이드

requirePermissions 메서드에서 안드로이드 버전 6.0 이상이고 권한이 미승인 상태라면, 사용자에게 승인을 요청하는 팝업창을 띄우는 requestPermissions 메서드가 실행됩니다.

이제 사용자가 팝업창에서 권한 승인 여부를 선택한 후에 호출되는 onRequestPermissionsResult 메서드를 오버라이드합니다.

    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        if (grantResults.all{ it == PackageManager.PERMISSION_GRANTED}){
            permissionGranted(requestCode)
        }
        else{
            permissionDenied(requestCode)
        }
    }

super()를 호출하는 코드는 지웠습니다.

최종 권한 승인 여부를 확인합니다. 사용자가 승인했다면 승인 확인 메서드를 호출하고 아니라면 미승인 확인 메서드를 호출합니다.

다시 한번 말하지만, 승인/미승인 확인 시 호출 메서드는 baseActivity를 상속받은 액티비티에서 직접 구현합니다.



MainActivity에서 BaseActivity 상속받고 사용하기


먼저 AndroidManifest.xml 파일에 카메라 권한을 추가하고 시작합니다.


4. MainActivity 상속 클래스 변경

기본적으로 MainActivity는 AppCompatActivity를 상속받는데, 이것을 BaseActivity로 변경합니다.

// class MainActivity : AppCompatActivity() {
class MainActivity: BaseActivity(){

    val binding by lazy {ActivityMainBinding.inflate(layoutInflater)}

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

    }
  
    ...
}


5. 승인 확인 후 처리 메서드 구현

BaseActivity에서 추상 메서드로 선언한 permissionGranted, permissionDenied 메서드를 구현합니다.

    // 추상 메서드 구현
    override fun permissionGranted(requestCode: Int) {
        val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        startActivityForResult(intent, 99)
    }

    override fun permissionDenied(requestCode: Int) {
        Toast.makeText(baseContext, "권한 거부됨", Toast.LENGTH_LONG).show()
    }

승인 상태일 경우 intent와 함께 카메라 액티비티를 호출합니다.

미승인 상태일 경우 “권한 거부됨”이라는 토스트 메시지를 띄웁니다.


6. 서브 액티비티가 보낸 값을 처리하는 onActivityResult 메서드 오버라이드

메인 액티비티에서 카메라 액티비티를 호출할 때 인텐트를 넣어서 보낸 것처럼, 카메라 액티비티도 종료할 때 메인 액티비티에 인텐트를 보낼 수 있기 때문에 그 값을 처리하는 onActivityResult 메서드를 구현합니다.

    // 호출한 액티비티에서 보내는 인텐트를 처리
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (resultCode == 10){ // 카메라 액티비티 호출 시 전달한 리퀘스트 코드
            if(resultCode == RESULT_OK){
                Log.d("카메라", "촬영 성공")
            }
            else{
                Log.d("카메라", "촬영 실패")
            }
        }
    }

7. 버튼 클릭 리스너에서 requirePermissions 메서드 호출

권한 처리를 위한 준비를 마쳤습니다.

이제 버튼 리스너를 달고 클릭 시에 BaseActivity에서 정의했던 requirePermissions( ) 메서드가 호출되도록 합니다.

결과는 아래와 같습니다.

image-20210810123059762

image-20210810123120682

image-20210810123302190



정리


  • 권한을 요청하는 코드를 작성할 때는 권한 요청 시 사용하는 BaseActivity 추상 클래스를 생성하고, 각각의 액티비티에서 이 클래스를 상속하여 권한 승인/미승인 선택 후 처리코드만 작성하는 것이 좋습니다.
  • BaseActivity의 코드 작성 흐름은 아래와 같습니다.
    • 클래스를 abstract로 선언하고 AppCompatActivity를 상속
    • 자식 액티비티에 구현을 강요할 permissionGranted, permissionDenied 추상 메서드 선언
    • 자식 액티비티에서 권한 요청 시 호출할 requirePermissions 메서드 정의
      • requirePermissions 메서드에서는 안드로이드 버전을 확인하고, 요청 권한들의 승인 상태를 검사
      • 미승인 상태라면 승인 요청 팝업창을 띄울 requestPermissions 메서드 호출
        • 실질적인 권한 요청은 이 requestPermissions 메서드에서 처리합니다.
    • 사용자가 승인/미승인 선택 후 호출되는 onRequestPermissionsResult 메서드 오버라이드
      • 모두 승인이라면 permissionGranted, 하나라도 미승인이라면 permissionDenied 메서드 호출
  • MainActivity(BaseActivity를 상속받는 액티비티)의 코드 작성 흐름은 아래와 같습니다.
    • 상속받을 클래스를 BaseActivity로 변경
    • 추상 메서드인 permissionGranted와 permissionDenied 메서드 구현
      • permissionGranted 메서드에서는 원하는 권한의 액티비티 호출
    • 호출한 액티비티가 종료할 때 전달하는 인텐트 값을 처리할 onActivityResult 메서드 오버라이드
    • 버튼 리스너를 달고 클릭 시 requirePermissions 메서드 호출

Categories:

Updated:

Leave a comment