Category Archives: Android 5.0

Help! My custom font does not work on Android 5.0

Issue description

On Android Lollipop versions before 5.1 some custom fonts loaded using the API Typeface.createFromAsset() do not render using the correct font. This issue was fixed with the release of Android 5.1, but will still affect your users running Android Lollipop 5.0.x.

This should not cause a crash in your app, it will just cause your view to render using the default typeface rather than the custom font you have specified.

What causes the issue?

The cause of this issue is badly formed TTF font files. Minor issues in font files may be ignored or tolerated by most systems rendering the font, however the tolerance level in the early builds of Lollipop was not so high and some fonts fail to render causing the system to fall back to using the default font. It is similar to having a badly written audio file that will play in some media apps but is flagged as corrupted in others.

Is my app affected?

The first thing to check is if your app loads custom fonts. Take a look for any font files found in your Android assets/fonts folder. Then look for the code that is loading the font so you can see where in the apps UI the custom font is used. Loading a custom font from your apps Assets folder and setting it into a View is quite straight forward.

Typeface tf = Typeface.createFromAsset(getAssets(), "fonts/myfont.ttf");
myView.setTypeface(tf);

Having custom fonts doesn’t always mean your app will be affected by this issue however. The quickest way to check is to visually verify those custom fonts are being rendered correctly. Another check suggested by the Android team is to run your font through the Sanitiser for OpenType engine. To determine a potential issue look for this in the output:

WARNING at ots-read-only/src/cmap.cc:160: bad id_range_offset

Solution

The root cause of the problem is badly written font files, so it makes sense that the solution is to re-encode the offending font.

If you only have a couple of font files you would like to re-encode a great place to start is with an online converting tool such as http://www.freefontconverter.com/. Upload your dodgy font file and even if you select the same input and output font format (ie. TTF), the file will still be re-encoded and should now be able to be used in your app. This web service has successfully fixed all the non conforming fonts I have thrown at it.

However if that online tool did not help, or you have a huge repository of files you would like to re-encode you may like to use https://github.com/behdad/fonttools/ to convert your TTF to XML and then back to a TTF.

Be careful running any software that will re-encode your font files. As this process will be changing the file there may be side effects such as loss of quality. Also it is possible the tool you are using to re-encode the file will also produce non-compliant font files that fail in some environments.

Why did my Unit tests not catch this error?

If you have had to fix this error then you may be thinking to yourself, Why did my unit test not alert me to this issue? or How can I write a unit test to stop this issue next time?

Don’t be too hard on yourself; for all intents and purposes the code appears to function correctly without visually verifying the rendering. The typeface is loaded correctly and it can be set into a view without issue or complaint. Even after the view has been invalidated and rendered there is still no exceptions or warnings of an issue raised. The problem only becomes obvious on visual inspection of the View.

References

Android issue tracker for this issue:

https://code.google.com/p/android-developer-preview/issues/detail?id=608

Android Typeface documentation:

http://developer.android.com/reference/android/graphics/Typeface.html

Advertisements

Leave a comment

Filed under Android 5.0, Android SDK, Testing

Goodbye launcher drawables, hello mipmaps!

(UPDATE 18th FEB:  Android Studio 1.1 has been released!)

The upcoming release of Android Studio 1.1 will largely be a bug fixing release. There is one change however that might catch your eye when creating a new project. Projects now create launcher icons in the mipmap resource folders instead of the drawable folders. I am sure we will hear a lot more about this with release of Android Studio 1.1 coming very soon.

Why use mipmaps for your launcher icons?

Using mipmaps for your launcher icon is described as best practice by the Android team. The advantage you get is that you can keep resources in the mipmap folders for all device densities and then strip out other resources from the drawable folders that are not relevant to the specific users device density.

Here is an example, say a user has a device that is classified as xxhdpi. The drawable resources in your apk for all other densities, such as xxxhdpi, are not required and can be stripped out. The launcher icon is different to other resources as it is possible that a higher resolution icon is displayed in the users launcher. If that higher resolution image has been stripped from your drawables folder, then a lower density icon will programmatically be up-scaled. This may cause an unattractive blurry icon.

You may not be producing multiple apk files with stripped out resources to minimize apk size yet. Therefore making the change to mipmaps for your launcher icon will currently not make any difference. That does not mean you should not do it. The best reason for using mipmaps folder is that the Android team want you to and describe it as best practice. They know what new features are in the pipeline for Android and who knows, but maybe they are planning to introduce a new feature where unused resources are automatically stripped for the user when they download apps.

History of launcher mipmaps in Android

The mipmaps resource folders in Android started appearing long before they were introduced to the wider Android development community to use, which started in Android Jelly Bean 4.3. Very little official information came from the Android team about why to use them. Around the time of the release of 4.3, Android framework developer @hackbod announced:

…if you are building different versions of your app for different densities, you should know about the “mipmap” resource directory.  This is exactly like “drawable” resources, except it does not participate in density stripping when creating the different apk targets.

Since that time if you have taken a close look at the apps produced out of Google you will notice that some apps were doing exactly that.  Shipping only the required density but including all densities in the mipmap folders for the launcher icons.

As time went on still there was not much noise out of the Android team about using launcher mipmaps, that was until Lollipop hit AOSP and the new Nexus devices hit the market.  The Android Developers blog posted about preparing your apps for the new Nexus 6 and 9.  This detailed a compelling reason you should use mipmap folders for your app icon and announced the shift of best practice to using mipmaps:

…Provide at least an xxxhdpi app icon because devices can display large app icons on the launcher. It’s best practice to place your app icons in mipmap- folders (not the drawable- folders) because they are used at resolutions different from the device’s current density. For example, an xxxhdpi app icon can be used on the launcher for an xxhdpi device.

Interestingly there is still no update in the Official Android documentation and it still mentions drawable folders being the place for your launcher icons.  I am sure that will change in the coming months as the update to Android Studio is the next step in the progression of the change.

Update May 2015: The Android developer documentation has now been updated to document the purpose of mipmaps.

Creating a new project using launcher mipmaps

Update to Android Studio 1.1, any new project will use the new structure when it is created.

Updating an existing project to use launcher mipmaps

If you have used the excellent tool Android Asset Studio to generate your launcher icons recently you will notice the mipmap change is now reflected in the structure of assets generated. Even if you normally generate your icons through another process this is a great place to start as it shows the format and structure you should be using.

Your assets need to go into a structure much like your previous launcher drawables.

res/
    mipmap-mdpi/ic_launcher.png (48x48 pixels)
    mipmap-hdpi/ic_launcher.png (72x72)
    mipmap-xhdpi/ic_launcher.png (96x96)
    mipmap-xxhdpi/ic_launcher.png (144x144)
    mipmap-xxxhdpi/ic_launcher.png (192x192)

You will also need to change your AndroidManifest.xml to use mipmaps for the launcher.

android:icon="@mipmap/ic_launcher"

9 Comments

Filed under Android 5.0, Android Studio, Image, Nexus

Nexus Lollipop soundpool.load() not so sweet

When I got my first taste of the Android L pre-release I noticed something rather disturbing when running some games.  I was using a Nexus 5 and every now and again a long pause where an app would become non-responsive. Sometimes it was a couple of seconds and other times it was 10, 20 or more seconds without response, then it would finally burst back to life. After doing a little digging and I tracked it to an issue with the Nexus family using the SoundPool.load() function.  This issue lives on with the latest Android 5.0.2 build.

Issue Description

The Android SoundPool class allows developers to pre-load several short sound clips so the may be played at any time without the lag of decompressing on the fly.  This is perfect for game developers where sounds may be played many times. On the Nexus family of devices, the execution of the SoundPool.load() function may take several seconds to load a very small audio file, instead of the few milliseconds as has been the case with the same files on previous Android versions, or with other devices running Lollipop.  The issue relates to the ‘AwesomePlayer’ media engine. There is a way to confirm if this is an issue in your code.  Go to the media section of the developer options and check the ‘Use NuPlayer (experimental)’ checkbox.  Run your app again and you should see none of the unresponsiveness that has lead you to this post. If the delay remains it is likely not being caused by the SoundPool.load() issue.

Issue Status

As of Android 5.0.2 this is an unresolved issue in the Android Open Source Project (AOSP) issue tracker. It’s can be reproduced on most of the Nexus devices that have support for 5.0, from the Nexus 4 through to 5, 7 (2012 & 2013) and 9.  I have not been lucky enough to get my hands on a Nexus 6 to confirm if it suffers the same affliction.

Poor app design exposed

The Android media engine requiring seconds to load a small sound file is clearly not acceptable, but what it does is exposes apps that are executing the sound pool loading code from the UI thread. For a few small audio files that were taking a few milliseconds each, this was never noticed. But best practice dictates load functions like this should be happening on a background thread. Structure you app so the thread has maximum time to load all it needs to load without needing to hold up the user.  Once this change is made chances are this issue will no longer be a problem in your app and you won’t be waiting for the AOSP fix.

Changes to SoundPool in API 21

While on the subject of the SoundPool class, Android 5.0 (API 21) also brings some recommended usage changes.  So read on if you have not changed the way you create your SoundPool on Lollipop devices. If you take a look at the SoundPool interface the constructor public SoundPool(int maxStreams, int streamType, int srcQuality) is now deprecated. The recommended usage is to create and configure the SoundPool instance using the SoundPool builder class (SoundPool.Builder) The SoundPool Builder is only available on API 21+, so you will need to create your SoundPool differently based on the SDK version of the device your app is running on. When your code is executed on devices running 5.0+ create the SoundPool using the builder, as follows:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
protected void createSoundPoolWithBuilder(){
    AudioAttributes attributes = new AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_GAME)
        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
        .build();

    mSoundPool = new SoundPool.Builder().setAudioAttributes(attributes).setMaxStreams(6).build();
}

For devices running 4.4 and older use the SoundPool constructor:

@SuppressWarnings("deprecation")
protected void createSoundPoolWithConstructor(){
    mSoundPool = new SoundPool(6, AudioManager.STREAM_MUSIC, 0);
}

Check the device version to call the appropriate function to create the SoundPool:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    createSoundPoolWithBuilder();
} else{
    createSoundPoolWithConstructor();
}

Hopefully this has sorted your Android 5.0 SoundPool woes.

Update March 2015: This issue was still present for my Nexus devices after initially updating to Android 5.1, although it seems on a clean install of 5.1 the default media engine is now NuPlayer which does not have exhibit the SoundPool issue.

lollipop

4 Comments

Filed under Android 5.0, Android SDK, Audio, SoundPool