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 기능을 사용하여 RAM이 적은 기기에 APK 버전을 타겟팅하는 것이 Android Oreo (Go 버전)를 실행하는 기기를 준비하는 가장 좋은 방법입니다. 앱을 더 가볍고 효율적으로 만들면 기기와 상관없이 전체 잠재고객에게 도움이 됩니다.

Neural Networks API

Neural Networks API는 TensorFlow Lite(Google의 모바일용 크로스 플랫폼 ML 라이브러리), Caffe2 등의 온디바이스 머신러닝 프레임워크에 빠른 계산 및 추론을 제공합니다. TensorFlow Lite 오픈소스 저장소를 방문하여 필요한 파일과 문서를 다운로드하세요. TensorFlow Lite는 Neural Networks 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 어포던스를 맞춤설정할 수 있는 기능을 더 많이 제공합니다.

맞춤 설명은 자동 완성 서비스에서 저장 내용을 명확히 하는 데 유용합니다. 예를 들어 화면에 신용카드가 포함되어 있으면 신용카드 은행 로고, 신용카드 번호의 마지막 4자리 숫자, 만료 번호를 표시할 수 있습니다. 자세한 내용은 CustomDescription 클래스를 참고하세요.

Validator 객체는 검사기 조건이 충족되지 않을 때 자동 완성 저장 UI가 표시되지 않도록 하는 데 사용됩니다. 자세한 내용은 유효성 검사 도구 클래스와 서브클래스인 LuhnChecksumValidatorRegexValidator를 참고하세요.

알림

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

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

EditText 업데이트

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

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

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

Safe Browsing API의 WebView 구현을 사용하면 앱에서 WebView 인스턴스가 알려진 위협으로 분류한 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!")
            }
        })
    }
}

Java

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()
    }
}

Java

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