핀수로그
  • [Android] 부팅 시 앱 실행하기 | Android 10+ Application startup on BOOT
    2022년 09월 21일 00시 28분 11초에 업로드 된 글입니다.
    작성자: 핀수
    728x90
    반응형

    들어가며

    부팅 시 앱이 실행 되었으면 좋겠다는 요구 사항이 들어왔다.

    예전에 부팅 되었을 때 관련 데이터를 삭제하는 기능을 잠깐 검토 했던 기억이 있어서

    금방 하겠거니 생각했다.

     

    그렇게 영원한 나의 친구이자 스승인 구글에 관련 기능을 검색했다.

     

    1. AndroidManifest.xml 에 권한을 명시하고 

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

    2. Receiver를 만들고

    class DeviceBootReceiver : BroadcastReceiver() {
    
        override fun onReceive(context: Context?, intent: Intent?) {
            val action = intent?.action
            if (action.equals(Intent.ACTION_BOOT_COMPLETED)){
                val appIntent = Intent(context, SplashActivity::class.java)
                intent?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                context.startActivity(appIntent)
            }
        }
    }

    3. AndroidManifest.xml 에 리시버를 등록하면

    <application>
    	...
        <receiver android:name=".util.DeviceBootReceiver"
            android:exported="true"
            android:enabled="true"
            android:directBootAware="true">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED"/>
            </intent-filter>
        </receiver>
        ...
    </application>

     

    끝나는 간단한 기능이었다.

    그렇게 코드를 작성하고 실행을 했는데


      안.된.다.

      Android 10 (API 레벨 29) 이상은 앱이 백그라운드에서 실행될 때 Activity를 시작할 수 있는 시점에 제한이 있습니다

      원인은 위와 같았다.
      정책이 변경되면서 해당 버전부터는 위의 방식은 통하지 않았다.
      직접 Activity를 시작하는 것이 아니라 서비스를 등록하고 그 서비스를 통해 특정 작업을 처리하도록 변경되었다.
      그런데 마침 내가 테스트한 기기가 버전 10이었고
      또 우리 프로젝트의 주 타겟 버전이 11이기 때문에 해당 문제를 반드시 해결해야만 했다.
      그래서 또 내 최고의최고의 스승 공식문서를 찾아보았다.

      주 내용은 background에서 activity 실행을 제한하겠다는 내용인데
      아래에 해당 제한에 대한 예외 사항들이 나와있었다.

      이 앱은 사용자로부터 SYSTEM_ALERT_WINDOW 권한을 부여받았습니다.

      예외 사항의 마지막 항목이다.

      SYSTEM_ALERT_WINDOW 권한

      해당 권한을 부여받은 앱은 다른 모든 앱 위에 표시될 수 있다는 의미같다.
      시스템 관련 앱들에서 주로 쓰일 것이다.
      좀 더 높은 우선순위를 가진 앱들이..
      그러므로 대부분의 앱들은 해당 권한을 요청할 일이 없다.
      그러나 현재 나의 앱은 성격이 좀 다르므로..
      해당 권한을 통해 기능을 구현해보려고 한다.

      권한 부여 받기

      Settings.canDrawOverlays(context) 를 통해 해당 권한을 확인할 수 있다.
      권한이 부여되지 않은 상태라면 아래와 같이 해당 화면으로 보낼 수 있다.
      버전 10까지는 내 앱의 권한 화면으로 이동할 수 있지만
      11부터는 Uri 부분은 무시되고 '다른 앱 위에 표시' 허용 가능 목록이 뜬다.
      거기서 직접 찾아 권한을 부여해야한다.

      if (!Settings.canDrawOverlays(this)){
          val intent = Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:${packageName}"))
          startActivity(intent)
      }


      *수동으로 권한 주는법
      [설정] - [애플리케이션] - 더보기 (점 세개) - 특별한 접근 - 원하는 앱 찾아 권한 부여

      기능 구현하기

      1. 권한 설정

      AndroidManifest.xml

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

       

      2. BroadcastReceiver 생성

      class DeviceBootReceiver : BroadcastReceiver() {
      
      
          override fun onReceive(context: Context?, intent: Intent?) {
              val action = intent?.action
              if (action.equals(Intent.ACTION_BOOT_COMPLETED)){
                  startService(context!!)
              }
          }
      
          private fun startService(context: Context){
              val appIntent = Intent(context, BootService::class.java)
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
                  context.startForegroundService(appIntent)
              } else {
                  context.startService(appIntent)
              }
          }
      }

       

      3. receiver 등록

      AndroidManifest.xml

      <application>
      	...
          <receiver android:name=".util.DeviceBootReceiver"
              android:exported="true"
              android:enabled="true"
              android:directBootAware="true">
              <intent-filter>
                  <action android:name="android.intent.action.BOOT_COMPLETED"/>
              </intent-filter>
          </receiver>
          ...
      </application>

       

      4. Service 생성

      class BootService : Service() {
          private val myChannelId = "ChannelId"
          private val id = 10
      
          override fun onCreate() {
              super.onCreate()
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                  createNotificationChannel()
                  startForeground(id, createNotification())
                  startApp()
              }
          }
      
          @RequiresApi(Build.VERSION_CODES.O)
          private fun createNotificationChannel() {
              val notificationChannel =
                  NotificationChannel(myChannelId, "MyService", NotificationManager.IMPORTANCE_HIGH)
              val notificationManager =
                  applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
              notificationManager.createNotificationChannel(notificationChannel)
          }
      
          private fun createNotification(): Notification {
              return NotificationCompat.Builder(this, myChannelId)
                  .setContentTitle("Service is running")
                  .setContentText("Service is running")
                  .build()
          }
      
          @RequiresApi(Build.VERSION_CODES.O)
          private fun startApp(){
              if (Settings.canDrawOverlays(this)){
                  val intent = Intent(this, SplashActivity::class.java)
                  intent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP
                  intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
                  intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                  this.startActivity(intent)
              }
          }
      
          override fun onDestroy() {
              super.onDestroy()
              stopForeground(true)
          }
      
          override fun onBind(intent: Intent?): IBinder? {
              return null
          }
      }

       

      5. Service 등록

      AndroidManifest.xml

      <service android:name=".util.BootService"/>

       

      적용화면

      그런데 부팅 후 앱이 실행되기까지 꽤나 오랜 시간이 걸린다....
      평균 1분 ~ 1분 30초 정도 소요되기 때문에 이부분은 좀 더 고려가 필요해보인다.

       


      References

      아래 글을 참고하여 작성 되었습니다.

       

      [Android Tip] 부팅 시 자동으로 앱 실행하기

      모바일 용 앱에서는 그럴 일이 거의 없지만, 일반 셋탑용 앱을 만들다보면 부팅되자마자 앱이 자동으로 실...

      blog.naver.com

       

       

      Android 10.0 Application startup on BOOT

      We have an android application which we intend to start/launch during phone boot. With some code tries in Android 10, we realized that the launching of app on boot, is not possible after Android 8.0.

      stackoverflow.com

       

       

      [Android] 다른 앱 위에 그리기 샘플코드(android.permission.SYSTEM_ALERT_WINDOW)

      다른 앱 위에 팝업창을 뛰우거나 앱을 실행해야할 때 다른 앱 위에 그리기 권한이 필요하다. 일반 권한 및 위험 권한처럼 동작하지 않는 권한이 있다. SYSTEM_ALERT_WINDOW 및 WRITE_SETTINGS는 특히 민감

      ddolcat.tistory.com

       

       

      Android - ACTION_BOOT_COMPLETED 이벤트 받기

      안드로이드는 부팅이 완료되면 ACTION_BOOT_COMPLETED 인텐트를 전달하여 앱이 실행되도록 합니다. 앱은 이 인텐트를 받고, 어떤 작업을 처리할 수 있습니다. 또한, BOOT_COMPLETED 인텐트를 받고 내 앱의

      codechacha.com

       

      728x90
      반응형
      댓글