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에서는 수십억 명을 위한 빌드 가이드라인에 따라 개발자가 특별히 최적화한 앱을 표시합니다.

Android Oreo (Go 버전)를 실행하는 기기에서 앱을 최적화하는 방법에 관한 추가 안내가 포함되도록 수십억 사용자를 위한 빌드 가이드라인이 업데이트되었습니다. 대부분의 개발자에게는 기존 APK를 최적화하거나 Google Play의 여러 APK 기능을 사용하여 APK 버전을 RAM 부족 기기를 타겟팅하는 것이 Android Oreo (Go 버전)를 실행하는 기기를 준비하는 가장 좋은 방법입니다. 앱을 더 가볍고 효율적으로 만들면 기기에 관계없이 전체 잠재고객에게 도움이 됩니다.

Neural Networks API

Neural Networks API는 TensorFlow Lite(Google의 모바일용 크로스 플랫폼 ML 라이브러리) 및 Caffe2와 같은 온디바이스 머신러닝 프레임워크의 가속화된 컴퓨팅 및 추론을 제공합니다. 다운로드 및 문서는 TensorFlow Lite 오픈소스 저장소를 참고하세요. TensorFlow Lite는 신경망 API와 함께 작동하여 MobileNets, Inception v3, Smart 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 객체는 Validator 조건이 충족되지 않을 때 자동 완성 저장 UI가 표시되지 않도록 하는 데 사용됩니다. 자세한 내용은 Validator 클래스와 하위 클래스인 LuhnChecksumValidator 및 RegexValidator를 참고하세요.

알림

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

  • 이제 앱은 초당 한 번만 경고음을 낼 수 있습니다. 이 비율을 초과하는 알림 소리는 대기열에 추가되지 않고 손실됩니다. 이 변경사항은 알림 동작의 다른 측면에 영향을 미치지 않으며 알림 메시지는 계속 예상대로 게시됩니다.
  • ActivityManager.isLowRamDevice()가 호출될 때 true를 반환하는 RAM 부족 Android 지원 기기에서는 NotificationListenerService 및 ConditionProviderService가 지원되지 않습니다.

EditText 업데이트

API 수준 27부터 EditText.getText() 메서드는 Editable를 반환합니다. 이전에는 CharSequence를 반환했습니다. Editable가 CharSequence를 구현하므로 이 변경사항은 하위 호환됩니다.

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

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

Safe Browsing API의 WebView 구현을 사용하면 앱이 WebView 인스턴스가 Google에서 알려진 위협으로 분류한 URL로 이동하려고 할 때 이를 감지할 수 있습니다. 기본적으로 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() 메서드를 호출하세요. 이 메서드는 새 WallpaperColors 객체를 제공할 수 있는 onComputeColors() 수명 주기 이벤트를 트리거합니다.

색상 변경사항에 대한 리스너를 추가하려면 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 유형이었지만 이제는 OpenSSLSocketImpl를 확장하는 ConscryptFileDescriptorSocket 또는 ConscryptEngineSocket 유형입니다.
  • null 참조가 전달될 때 IllegalArgumentException을 발생시키는 데 사용된 SSLSession 메서드가 이제 NullPointerException을 발생시킵니다.
  • RSA KeyFactory에서는 더 이상 인코딩된 키보다 큰 바이트 배열에서 키를 생성할 수 없습니다. 키 구조가 전체 버퍼를 채우지 않는 KeySpec를 제공하는 generatePrivate() 및 generatePublic() 호출은 InvalidKeySpecException를 초래합니다.
  • 소켓이 닫혀 소켓 읽기가 중단되면 Conscrypt는 읽기에서 -1을 반환했습니다. 이제 읽기에서 SocketException이 발생합니다.
  • 루트 CA 인증서 집합이 변경되었습니다. 대부분은 오래된 인증서가 많이 삭제되었지만 WoSign 및 StartCom의 루트 인증서도 삭제되었습니다. 이 결정에 대한 자세한 내용은 Google 보안 블로그 게시물 WoSign 및 StartCom 인증서에 대한 신뢰 최종 삭제를 참고하세요.