Android 8.1 기능 및 API

Android 8.1(API 레벨 27)에는 사용자와 개발자를 위한 다양한 새로운 기능이 있습니다. 이 문서에서는 개발자를 위한 새로운 기능을 소개합니다.

Android Oreo(Go 버전)

Android Go는 전 세계 수십억 명의 온라인 사용자를 위해 Android 환경을 최적화하기 위한 이니셔티브입니다. Google은 Android 8.1부터 Android를 보급형 기기에 적합한 플랫폼으로 개발하고 있습니다. Android Oreo(Go 에디션) 구성의 기능은 다음과 같습니다.

  • 메모리 최적화. RAM이 1GB 이하인 기기에서 앱을 효율적으로 실행할 수 있도록 플랫폼 전체의 메모리 사용량을 개선했습니다.
  • 유연한 타겟팅 옵션. 새로운 하드웨어 기능 상수를 활용하여 Google Play를 통해 RAM이 보통이거나 낮은 기기에 앱 배포를 타겟팅할 수 있습니다.
  • Google Play. Android Oreo(Go 에디션)를 실행하는 기기에서 모든 앱을 사용할 수 있지만, Google Play는 수십억 사용자를 위한 앱 빌드 가이드라인을 구축하여 개발자가 수십억 명의 사용자에게 뛰어난 환경을 제공하기 위해 최적화한 앱을 쉽게 눈에 띄도록 표시합니다.

Google은 Android Oreo(Go 에디션) 실행 기기에 맞게 앱을 최적화하는 방법에 관한 추가 가이드라인으로 수십억 사용자를 위한 앱 빌드 가이드라인을 업데이트했습니다. RAM이 낮은 기기에 APK의 버전을 타겟팅하려는 대부분의 개발자는 기존 APK를 최적화하거나 Google Play의 다중 APK 기능을 사용하는 것이 Android Oreo(Go 에디션) 실행 기기를 준비하는 가장 좋은 방법입니다. 앱을 더 가볍게, 더 효율적으로 만들면 기기와 상관없이 모든 사용자에게 도움이 됩니다.

Neural Networks API

Neural Networks API는 TensorFlow Lite(Google의 모바일용 크로스 플랫폼 ML 라이브러리), Caffe2 등의 온디바이스 머신러닝 프레임워크에 빠른 계산 및 추론 기능을 제공합니다. TensorFlow Lite 오픈소스 저장소를 방문하여 필요한 파일과 문서를 다운로드하세요. TensorFlow Lite는 Neural Networks API와 호환되어 휴대기기에서 MobileNets, Inception v3Smart Reply와 같은 모델을 효율적으로 실행할 수 있습니다.

자동 완성 프레임워크 업데이트

Android 8.1(API 레벨 27)은 앱에 통합할 수 있는 자동 완성 프레임워크의 몇 가지 개선 사항을 제공합니다.

이제 BaseAdapter 클래스에 setAutofillOptions() 메서드가 포함되어, 어댑터에 있는 값을 문자열로 표현할 수 있습니다. 이는 어댑터에서 값을 동적으로 생성하는 스피너 컨트롤에 유용합니다. 예를 들어 사용자가 신용카드 만기일의 일부로 선택할 수 있는 연도 목록을 문자열로 표현하려면 setAutofillOptions() 메서드를 사용하면 됩니다. 자동 완성 서비스는 데이터가 필요한 뷰를 적절하게 채우기 위해 문자열 표현을 사용할 수 있습니다.

또한 AutofillManager 클래스에는 가상 구조에서 프레임워크에 뷰의 가시성 변경 사항에 관해 알리기 위해 호출할 수 있는 notifyViewVisibilityChanged(View, int, boolean) 메서드가 포함되어 있습니다. 비 가상 구조용 메서드의 오버로드도 있습니다. 그러나 비 가상 구조에서는 대개 프레임워크에 명시적으로 알릴 필요가 없습니다. View 클래스에 의해 메서드가 이미 호출되기 때문입니다.

또한, Android 8.1에서는 SaveInfo 내에 CustomDescription and Validator를 추가로 지원하여 저장 UI 유도성을 맞춤설정하는 기능이 자동 완성 서비스에 추가됩니다.

맞춤형 설명은 자동 완성 서비스가 저장 중인 내용을 명확하게 하는 데 도움이 됩니다. 예를 들어, 화면에 신용카드가 포함된 경우 신용카드 은행 로고, 신용카드 번호의 마지막 네 자리 숫자 및 만료일 숫자를 표시할 수 있습니다. 자세한 내용은 CustomDescription 클래스를 참조하세요.

Validator 조건이 충족되지 않을 때 자동 완성 저장 UI가 표시되지 않도록 Validator 객체가 사용됩니다. 자세한 내용은 Validator 클래스와 하위 클래스, LuhnChecksumValidatorRegexValidator를 참조하세요.

알림

Android 8.1에서는 알림이 다음과 같이 변경됩니다.

  • 이제 앱은 초당 한 번만 경고음을 낼 수 있습니다. 이 속도를 초과하는 경고음은 대기열에 추가되지 않고 손실됩니다. 이 변경사항은 알림 동작의 다른 부분에 영향을 미치지 않으며 알림 메시지는 여전히 예상대로 게시됩니다.
  • NotificationListenerServiceConditionProviderServiceActivityManager.isLowRamDevice()가 호출될 때 true를 반환하는 RAM이 낮은 Android 기기에서 지원되지 않습니다.

EditText 업데이트

API 레벨 27부터 EditText.getText() 메서드는 Editable을 반환합니다. 전에는 CharSequence를 반환했습니다. 이 변경사항은 EditableCharSequence를 구현하기 때문에 이전 버전과 호환됩니다.

Editable 인터페이스는 중요한 추가 기능을 제공합니다. 예를 들어 EditableSpannable 인터페이스도 구현하므로 EditText의 인스턴스 내에서 콘텐츠에 마크업을 적용할 수 있습니다.

프로그래밍 방식의 세이프 브라우징 작업

Safe Browsing API의 WebView 구현을 사용하면, Google이 알려진 위협으로 분류한 URL로 WebView의 인스턴스가 이동하려고 할 때 앱에서 이를 감지할 수 있습니다. 기본적으로 WebView에는 알려진 위협에 대해 사용자에게 경고하는 전면 광고가 표시됩니다. 이 화면은 사용자에게 URL을 로드하거나 안전한 이전 페이지로 돌아가는 옵션을 제공합니다.

Android 8.1에서는 앱이 알려진 위협에 어떻게 대응하는지를 프로그래밍 방식으로 정의할 수 있습니다.

  • 알려진 위협을 앱이 세이프 브라우징에 보고할지 여부를 제어할 수 있습니다.
  • 알려진 위협으로 분류된 URL이 발생할 때마다 앱이 특정 작업(예: 안전 상태로 복귀)을 자동으로 수행하도록 할 수 있습니다.

참고: 알려진 위협을 차단하려면 WebView 객체의 loadUrl() 메서드를 호출하기 전에 세이프 브라우징이 초기화될 때까지 기다리는 것이 가장 좋습니다.

다음 코드 스니펫은 알려진 위협이 발생한 후 항상 안전 상태로 돌아가도록 앱의 WebView 인스턴스에 지시하는 방법을 보여줍니다.

AndroidManifest.xml

    <manifest>
        <application>
            ...
            <meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
                       android:value="true" />
        </application>
    </manifest>

MyWebActivity.java

Kotlin

    private var superSafeWebView: WebView? = null
    private var safeBrowsingIsInitialized: Boolean = false

    // ...

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

        superSafeWebView = WebView(this).apply {
            webViewClient = MyWebViewClient()
            safeBrowsingIsInitialized = false
            startSafeBrowsing(this@SafeBrowsingActivity, { success ->
                safeBrowsingIsInitialized = true
                if (!success) {
                    Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!")
                }
            })
        }
    }
    

자바

    private WebView superSafeWebView;
    private boolean safeBrowsingIsInitialized;

    // ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        superSafeWebView = new WebView(this);
        superSafeWebView.setWebViewClient(new MyWebViewClient());
        safeBrowsingIsInitialized = false;

        superSafeWebView.startSafeBrowsing(this, new ValueCallback<Boolean>() {
            @Override
            public void onReceiveValue(Boolean success) {
                safeBrowsingIsInitialized = true;
                if (!success) {
                    Log.e("MY_APP_TAG", "Unable to initialize Safe Browsing!");
                }
            }
        });
    }
    

MyWebViewClient.java

Kotlin

    class MyWebViewClient : WebViewClient() {
        // Automatically go "back to safety" when attempting to load a website that
        // Safe Browsing has identified as a known threat. An instance of WebView
        // calls this method only after Safe Browsing is initialized, so there's no
        // conditional logic needed here.
        override fun onSafeBrowsingHit(
                view: WebView,
                request: WebResourceRequest,
                threatType: Int,
                callback: SafeBrowsingResponse
        ) {
            // The "true" argument indicates that your app reports incidents like
            // this one to Safe Browsing.
            callback.backToSafety(true)
            Toast.makeText(view.context, "Unsafe web page blocked.", Toast.LENGTH_LONG).show()
        }
    }
    

자바

    public class MyWebViewClient extends WebViewClient {
        // Automatically go "back to safety" when attempting to load a website that
        // Safe Browsing has identified as a known threat. An instance of WebView
        // calls this method only after Safe Browsing is initialized, so there's no
        // conditional logic needed here.
        @Override
        public void onSafeBrowsingHit(WebView view, WebResourceRequest request,
                int threatType, SafeBrowsingResponse callback) {
            // The "true" argument indicates that your app reports incidents like
            // this one to Safe Browsing.
            callback.backToSafety(true);
            Toast.makeText(view.getContext(), "Unsafe web page blocked.",
                    Toast.LENGTH_LONG).show();
        }
    }
    

동영상 미리보기 이미지 Extractor

MediaMetadataRetriever 클래스의 새 메서드인 getScaledFrameAtTime()은 지정된 시간 위치 근처에서 프레임을 찾아 소스 프레임과 가로세로 비율이 같은 비트맵을 반환하지만, 지정된 너비와 높이의 직사각형에 맞게 크기를 조정합니다. 이는 동영상에서 미리보기 이미지를 생성할 때 유용합니다.

소스 동영상과 해상도가 같은 비트맵을 반환하므로, 메모리를 낭비할 수 있는 getFrameAtTime() 메서드보다는 이 메서드를 사용하는 것이 좋습니다. 예를 들어 4K 동영상의 프레임은 16MB 비트맵이므로 미리보기 이미지에 필요한 것보다 훨씬 큽니다.

Shared Memory API

Android 8.1(API 레벨 27)은 새로운 SharedMemory API를 제공합니다. 이 클래스를 사용하면 익명의 SharedMemory 인스턴스를 만들고 매핑하고 관리할 수 있습니다. SharedMemory 객체에서 읽기 및/또는 쓰기에 대해 메모리 보호를 설정하는데, SharedMemory 객체는 Parcelable이므로 AIDL을 통해 다른 프로세스로 쉽게 전달할 수 있습니다.

SharedMemory API는 NDK의 ASharedMemory 기능과 상호 운용됩니다. ASharedMemory가 파일 설명어에 액세스를 부여하면, 파일 설명어를 읽기 및 쓰기로 매핑할 수 있습니다. 이 방법을 사용하면 앱 간에 또는 단일 앱의 여러 프로세스 간에 대량의 데이터를 편리하게 공유할 수 있습니다.

WallpaperColors API

Android 8.1(API 레벨 27)에서는 라이브 배경화면이 시스템 UI에 색상 정보를 제공할 수 있습니다. 비트맵이나 드로어블에서 WallpaperColors 객체를 만들 수도 있고 직접 선택한 세 가지 색상을 사용할 수도 있습니다. 이 색상 정보를 검색할 수도 있습니다.

WallpaperColors 객체를 만들려면 다음 중 하나를 실행하세요.

  • 세 가지 색상을 사용하여 WallpaperColors 객체를 만들려면 기본 색상, 보조 색상 및 3차 색상을 전달하여 WallpaperColors 클래스의 인스턴스를 만듭니다. 기본 색상은 null이 아니어야 합니다.
  • 비트맵에서 WallpaperColors 객체를 만들려면 비트맵 소스를 매개변수로 전달하여 fromBitmap() 메서드를 호출합니다.
  • 드로어블에서 WallpaperColors 객체를 만들려면 드로어블 소스를 매개변수로 전달하여 fromDrawable() 메서드를 호출합니다.

배경화면에서 기본, 보조 및 3차 색상 세부정보를 검색하려면 다음 메서드를 호출합니다.

  • getPrimaryColor(): 배경화면에서 시각적으로 가장 대표적인 색상을 반환합니다.
  • getSecondaryColor(): 배경화면에서 두 번째로 두드러진 색상을 반환합니다.
  • getTertiaryColor(): 배경화면에서 세 번째로 두드러진 색상을 반환합니다.

라이브 배경화면의 중요한 색상 변경을 시스템에 알리려면 notifyColorsChanged() 메서드를 호출합니다. 이 메서드는 onComputeColors() 라이프사이클 이벤트를 트리거하는데, 여기서 새 WallpaperColors 객체를 제공할 수 있습니다.

색상 변경의 리스너를 추가하려면 addOnColorsChangedListener() 메서드를 호출하면 됩니다. 또한 getWallpaperColors() 메서드를 호출하여 배경화면의 기본 색상을 검색할 수도 있습니다.

지문 업데이트

FingerprintManager 클래스에 다음과 같은 오류 코드가 추가되었습니다.

  • FINGERPRINT_ERROR_LOCKOUT_PERMANENT – 사용자가 지문 판독기로 기기 잠금 해제를 너무 많이 시도했습니다.
  • FINGERPRINT_ERROR_VENDOR – 공급업체의 지문 판독기에서 오류가 발생했습니다.

암호화 업데이트

Android 8.1에서는 여러 가지 암호화 변경이 실행되었습니다.

  • Conscrypt에서 새로운 알고리즘이 구현되었습니다. Conscrypt 구현은 기존의 Bouncy Castle 구현보다 우선적으로 사용됩니다. 새로운 알고리즘은 다음과 같습니다.
    • AlgorithmParameters:GCM
    • KeyGenerator:AES
    • KeyGenerator:DESEDE
    • KeyGenerator:HMACMD5
    • KeyGenerator:HMACSHA1
    • KeyGenerator:HMACSHA224
    • KeyGenerator:HMACSHA256
    • KeyGenerator:HMACSHA384
    • KeyGenerator:HMACSHA512
    • SecretKeyFactory:DESEDE
    • Signature:NONEWITHECDSA
  • Cipher.getParameters().getParameterSpec(IvParameterSpec.class)는 이제 GCM을 사용하는 알고리즘에서 작동하지 않습니다. 대신 getParameterSpec(GCMParameterSpec.class)를 사용하세요.
  • TLS와 관련된 많은 내부 Conscrypt 클래스가 리팩터링되었습니다. 개발자가 때때로 이러한 기능에 액세스하기 때문에, 이전 사용 방식을 지원하기 위해 심(shim)이 남아 있지만 일부 세부정보가 변경되었습니다. 예를 들어 소켓이 전에는 OpenSSLSocketImpl 유형이었지만 이제는 ConscryptFileDescriptorSocket 또는 ConscryptEngineSocket 유형이며, 둘 다 OpenSSLSocketImpl을 확장합니다.
  • Null 참조를 전달할 때 IllegalArgumentException을 발생시키기 위해 SSLSession 메서드가 사용되었지만 지금은 NullPointerException을 발생시킵니다.
  • RSA KeyFactory는 더 이상 인코딩된 키보다 큰 바이트 배열에서 키를 생성할 수 없습니다. 키 구조가 전체 버퍼를 채우지 않는 KeySpec을 제공하는 generatePrivate()generatePublic()을 호출하면 InvalidKeySpecException이 발생합니다.
  • 소켓이 닫혀서 소켓 읽기가 중단되면 읽기에서 -1을 반환하기 위해 Conscrypt가 사용되었습니다. 이제 읽기가 SocketException을 발생시킵니다.
  • 루트 CA 인증서 모음이 변경되어 대량의 오래된 인증서가 대부분 삭제되었지만 WoSign 및 StartCom의 루트 인증서도 삭제되었습니다. 이 결정 사항에 관한 자세한 내용은 Google 보안 블로그 게시물 Final removal of trust in WoSign and StartCom Certificates를 참조하세요.