박상권의 삽질블로그

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

IT/Android-TIP (한글)

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

박상권 2018. 7. 18. 00:54

안드로이드 개발자들이 모여있는 오픈채팅방에 참여해보세요 .
Q&A 및 팁을 공유하는 방입니다..
오픈채팅방 참여



블로그를 Medium으로 옮겨서 운영하고 있습니다.
앞으로 새로운 글은 모두 미디엄 블로그를 통해서 올릴 예정입니다.
미디엄에서 다양하고 유익한 포스팅을 살펴보세요
미디엄 블로그 보기




"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별로 대응을 하도록 하지 않도록 도와주세요.

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



피쓰~


Comments