Mobile Development 19 min read

Adapting to Android P Behavior Changes (Part 1)

The guide’s first part explains Android P’s major behavior changes—official notch detection, restrictions on hidden APIs, new App Standby Buckets and foreground‑service rules, removal of the legacy Apache HTTP client, privacy updates, HEIF image support, and the ImageDecoder API with practical code examples.

Tencent Music Tech Team
Tencent Music Tech Team
Tencent Music Tech Team
Adapting to Android P Behavior Changes (Part 1)

Android P introduces many behavior changes that require developers to adapt their apps. This article (Part 1) details the main changes and provides practical code examples.

1. Full‑screen (notch) detection

Before Android P, manufacturers implemented their own notch‑detection logic. Android P provides an official API. The following code shows a common but problematic implementation that may cause the navigation bar to become transparent on some devices:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    decorView.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
        @RequiresApi(api = 28)
        @Override
        public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
            if (windowInsets != null) {
                DisplayCutout cutout = windowInsets.getDisplayCutoff();
                if (cutout != null) {
                    List<Rect> rects = cutout.getBoundingRects();
                    // Determine notch screen by checking rects
                    if (rects != null && rects.size() > 0) {
                        isNotchScreen = true;
                    }
                }
            }
            return windowInsets;
        }
    });
}

This code works for detection but may affect system UI. The recommended implementation uses decorView.getRootWindowInsets() :

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    WindowInsets windowInsets = decorView.getRootWindowInsets();
    if (windowInsets != null) {
        DisplayCutout displayCutout = windowInsets.getDisplayCutout();
        if (displayCutout != null) {
            List<Rect> rects = displayCutout.getBoundingRects();
            if (rects != null && rects.size() > 0) {
                isNotchScreen = true;
            }
        }
    }
}

2. Non‑SDK API adaptation

Android P restricts the use of hidden (non‑SDK) APIs. The APIs are divided into three lists: light‑grey, dark‑grey, and black. Developers should avoid dark‑grey and black‑list APIs.

Google provides a scanning tool to locate such calls in an APK:

#1: Linking dark greylist Landroid/os/SystemProperties;->get(Ljava/lang/String;)Ljava/lang/String; use(s):
    Ltmsdkobf/gv;->a(Ljava/lang/String;)Ljava/lang/String;
#2: Linking dark greylist Landroid/os/SystemProperties;->get(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String; use(s):
    Ltmsdkobf/gp;->b(Landroid/content/Context;)Ljava/lang/String;
...

After locating the calls, developers should replace them with public APIs, request Google to move needed APIs to the light‑grey list, or add runtime guards:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    // Android P or above
} else {
    // below Android P
}

3. Power‑management improvements

Android P introduces App Standby Buckets (Active, Working Set, Frequent, Rare, Never Used) that affect background execution, alarms, and network access. Apps can query their bucket via UsageStatsManager.getAppStandbyBucket() . Background execution limits apply to all apps, and foreground services must use startForegroundService or JobIntentService when the app moves to background.

4. Apache HTTP client removal

When compiling with compileSdkVersion 28 , the legacy Apache HTTP client classes are no longer available. To keep using them, add the following line to the manifest:

<uses-library android:name="org.apache.http.legacy" android:required="false"/>

Alternatively, keep the module that uses Apache HTTP client compiled with compileSdkVersion below 28.

5. Other adaptations

Foreground services now require the FOREGROUND_SERVICE permission. Privacy changes include the deprecation of Build.SERIAL (now returns UNKNOWN ) and restrictions on multi‑process WebView data sharing.

6. New features – HEIF support

Android P adds full support for the HEIF image format. HEIF offers higher compression (≈2.4× JPEG) and supports animation, transparency, and thumbnails. Support can be checked with:

fun supportHEIF() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.P

Decoding HEIF to Bitmap or Drawable is straightforward:

@TargetApi(28)
fun decodeHEIFDrawable(filePath: String): Drawable? {
    if (!supportHEIF()) return null
    val source = ImageDecoder.createSource(File(filePath))
    return ImageDecoder.decodeDrawable(source)
}

@RequiresApi(28)
fun decodeHEIFBitmap(filePath: String): Bitmap? {
    if (!supportHEIF()) return null
    val source = ImageDecoder.createSource(File(filePath))
    return ImageDecoder.decodeBitmap(source)
}

When uploading HEIF images, developers must consider devices that cannot decode HEIF and either convert to JPEG on the client or provide both formats from the server.

7. New features – ImageDecoder

ImageDecoder replaces BitmapFactory and can decode PNG, JPEG, WEBP, GIF, and HEIF. It also supports animated images via AnimatedImageDrawable . Basic usage:

var drawable: Drawable = ImageDecoder.decodeDrawable(source)
if (drawable is AnimatedImageDrawable) {
    image.setImageDrawable(drawable)
    drawable.start()
}

Developers can set a header listener to obtain image dimensions and adjust sampling:

val listener = object : OnHeaderDecodedListener {
    fun onHeaderDecoded(decoder: ImageDecoder, info: ImageInfo, source: Source) {
        decoder.setTargetSampleSize(2)
    }
}
val drawable = ImageDecoder.decodeDrawable(source, listener)

Custom post‑processing (e.g., rounded corners) is possible via setPostProcessor :

var drawable = ImageDecoder.decodeDrawable(source) { decoder, info, src ->
    decoder.setPostProcessor { canvas ->
        val path = Path()
        path.setFillType(Path.FillType.INVERSE_EVEN_ODD)
        val width = canvas.getWidth()
        val height = canvas.getHeight()
        path.addRoundRect(0, 0, width, height, 20, 20, Path.Direction.CW)
        val paint = Paint()
        paint.isAntiAlias = true
        paint.color = Color.TRANSPARENT
        paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC)
        canvas.drawPath(path, paint)
        PixelFormat.TRANSLUCENT
    }
}

If decoding fails partially, setOnPartialImageListener can allow the partially decoded image to be displayed instead of throwing a DecodeException :

var drawable = ImageDecoder.decodeDrawable(source) { decoder, info, src ->
    decoder.setOnPartialImageListener { e -> true }
}

References

https://developer.android.google.cn/about/versions/pie/android-9.0 https://mp.weixin.qq.com/s?__biz=MzI0MjgxMjU0Mg==&mid=2247486857&idx=1&sn=bb7777b6f69cba31ce9d68ffee9c8f47&scene=21#wechat_redirect https://blog.csdn.net/GenlanFeng/article/details/79496359 https://developer.android.com/about/versions/pie/power https://segmentfault.com/a/1190000015947004
AndroidImageDecoderBehavior ChangesHEIFNon‑SDK APIpower management
Tencent Music Tech Team
Written by

Tencent Music Tech Team

Public account of Tencent Music's development team, focusing on technology sharing and communication.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.