박상권의 삽질블로그

[안드로이드]Only fullscreen opaque activities can request orientation 본문

IT/Android-TIP (한글)

[안드로이드]Only fullscreen opaque activities can request orientation

박상권 2018.07.18 00:54



"Only fullscreen opaque activities can request orientation"


이 오류 메세지는 갈길바쁜 저희를 힘들게 만듭니다.

이 문장으로 검색하신분들은 대부분 targetSdkVersion을 26이상으로 올리기위한 작업을 한뒤에 이 오류를 받아보셨을 것입니다.

더 정확히는 targetSdkVersion를 27이상으로 설정했을경우 문제가 발생합니다.









이 오류는 왜 발생하는가


여러가지 이유로 투명한 Activity를 만들기위해서 style.xml에 android:windowIsTranslucent를 사용합니다.

<style name="AppTheme.Transparent" parent="@style/TransparentBase">
<item name="android:windowIsTranslucent">true</item>
</style>

문제는 이 android:windowIsTranslucent로 인해서 발생합니다.

API 26(8.0) SDK에서 아래와 같은 코드가 추가되었습니다.



위의 내용은 아래 파일에서 직접 확인가능 합니다. 

Activity.java 


API26에서는 Translucent/Floating 으로 만든 투명한 Activity들은 화면 회전고정을 하지 못하게 의도한 것입니다.

아래 commit 메세지에서 위와같은 코드를 추가한 것입니다.

https://android.googlesource.com/platform/frameworks/base/+/39791594560b2326625b663ed6796882900c220f


이 이슈덕분에 안드로이드 버전별 SDK코드도 다 까보고 commit 메세지까지 다 확인해보고 내가 구글 안드로이드 개발자가 된 기분은 개뿔


이와 같은 히스토리로 인해서 우리는 에러를 만나게 되었습니다.





병주고 약주고


Google 개발자들도 이건 아니다 싶었는지 API 27(8.1)에서는 이 코드를 삭제해 버렸습니다.

https://android.googlesource.com/platform/frameworks/base/+/d4ecffae67fa0dea03c581ca26f76b87a14be763%5E%21/



API 27(8.1)기기 이후부터는 windowIsTranslucent을 사용해도 오류를 발생시키지 않게 됩니다.

즉, 이 오류 메세지는 API 26(8.0) 기기에서만 발생합니다.





대처하기


1. targetSdkVersion 26미만으로 내리기


build.gradle에서 targetSdkVersion을 26으로 내려버리면 이 문제는 발생하지 않습니다.

(참고로 targetSdkVersion를 올렸다가 내리는건 23->22만 불가능하고 다른경우에는 가능합니다.)

하지만 이방법은 임시방편일뿐 올해안에 언젠가 다시 또 그이상으로 올리도록 대응을 해야하기 때문에 추천하지 않습니다.




2. Orientation 주지 않기


1) xml

Manifest파일에 스크린을 고정하기위해 android:screenOrientation="portrait" 을 넣으셨을겁니다.

<activity android:name=".MainActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.Transparent"
>

여기에서 android:screenOrientation="unspecified" 로 설정해주시면 가로/세로 고정을 지정하지 않기때문에 에러가 발생하지 않습니다.


android:screenOrientation="unspecified"


2) Java

만약 가로/세로 고정을 Java코드에서 BaseActivity에서 아래와 같이 항상 고정을 하도록 구현하신분들도 계실겁니다.

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

이러한 경우에는 투명Activity로 구현해야하는 부분에서 setRequestedOrientation() 함수를 override해서 no-op 처리해주면 됩니다.


@Override
public void setRequestedOrientation(int requestedOrientation) {
/*
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.O) {
// no-op
}else{
super.setRequestedOrientation(requestedOrientation);
}
*/
if (Build.VERSION.SDK_INT != Build.VERSION_CODES.O) {
super.setRequestedOrientation(requestedOrientation);
}
}

대부분의 개발자들이 이 방법으로 해결하셨을겁니다.

제가 만든 오픈소스 라이브러리들에서 Proxy Activity개념으로 동작하는 투명한 액티비티들에도 이 방법으로 해결했습니다.

https://github.com/ParkSangGwon/TedRxOnActivityResult/pull/6

https://github.com/ParkSangGwon/TedPermission/pull/74


단, 화면회전에 대한 고정을 하지 않았기 때문에 사용자가 기기를 회전하는 것에 대해 이슈가 없는지 꼭 테스트를 해보아야 합니다.





3. try/catch

만약 2번에서 2번째 방법인 BaseActivity에서 setRequestedOrientation()로 고정해주고 있다면 해당 코드만 try/catch로 처리해줍니다.

try {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
} catch (IllegalStateException ignore) {

}

이렇게 처리해주면 투명한 Activity에서 에러가 발생하더라도 이를 무시하고 투명한 화면을 보여줄 수 있습니다.





4. API 26에서만 windowIsTranslucent 안쓰기


위에서 말씀드렸던것처럼 이 이슈는 오직 API 26(8.0)기기에서만 문제가 발생한다는것을 기억하실 겁니다.

이를 이용해서 style.xml을 API 버전별로 다른 동작을 하도록 분기시켜줍니다.


위와 같이 style.xml 파일을 values, values-v26, values-v27 폴더에 각각 만들어줍니다.

여기서 values-v26에 있는 style.xml에만 windowIsTranslucent을 구현해주지 않으면 해결할 수 있습니다.


values/style.xml

<style name="TransparentBase" parent="@style/AppTheme">
<item name="android:windowBackground">@android:color/transparent</item>
</style>

<style name="AppTheme.Transparent" parent="@style/TransparentBase">
<item name="android:windowIsTranslucent">true</item>
</style>


values-v26/style.xml

<style name="AppTheme.Transparent" parent="@style/TransparentBase" />


values-v27/style.xml

<style name="AppTheme.Transparent" parent="@style/TransparentBase">
<item name="android:windowIsTranslucent">true</item>
</style>

위의 코드에서 확인하실 수 있듯이 values-v26/style.xml에서는 아무 태그를 넣어주지 않게 됩니다.


단, 이 방법으로 구현할경우 API 26(8.0)기기에서는 투명한 화면으로 나타나지 않기 때문에 이에 대한 처리를 해줘야 합니다.

(Activity를 시작하기전에 현재화면의 내용을 캡쳐해서 ImageView에 그려서 background로 지정하는 등의 방법)


ProxyActivity개념으로 사용하는 코드라면 이 방법은 맞지 않습니다.

만약 투명액티비티를 fade-in의 효과를 내면서 나타나게 하기위한 용도로 사용했다면 이 방법을 사용하시면 좋습니다.

API 26(8.0) 기기에서만 fade-in 없이 바로 나오는 정도가 될것이기때문에 큰 이슈가 되지는 않을것입니다.







구글님 저희가 잘못했습니다.. 어린양을 괴롭히지 말아주세요..

안드로이드 기기의 파편화만으로도 대응하기 너무 힘든데 API레벨 OS별로 대응을 하도록 하지 않도록 도와주세요.

하지만 우린 답을 찾을것이다. 늘 그랬듯이



피쓰~


3 Comments
댓글쓰기 폼