Android Marshmallow (6.0+) introduces a new list of features for developers. Some of them are required for your app to support the new OS while others are optional. In part 1, I went over new features that are required if you want to add support to your app such as the new permission model.
Overview
I am going to cover some of the optional features to Android Marshmallows and explain how they work. The features I am covering are managing auto backup for apps, app links and websites associations, and data binding. With the exception of data binding, the other features require Android API 23+.
Managing auto backup for apps
Configuring app data backups
Apps that are using API 23+ can backup application data through the user’s Google account. Once the user opts into the backup service, the backup process becomes automatic. Each app can store up to 25MB of data. If you wish to disable backup for your app, you must explicitly declare it in the Android Manifest file of your project. Insert the line “android:allowBackup=false” in the application element.
You are probably wondering if this process is automatic then why you need to care about it. There are two reasons I can think of why you should care. One, you do not know what exactly gets backup. Two, what if your app requires backing up more than 25MB of data? I will address what you can do to solve each of these problems.
By default what gets backup are the databases, shared preferences, and the files included within the app. What is not backed up are the files from these function calls:
File getCacheDir(); File getCodeCacheDir(); File getNoBackupFilesDir();
Files on the external storage are not backed up with the exception of getExternalFilesDir(String type). For reference, on what these function calls do visit here.
If your app needs more than the 25MB limit for backup, you can configure what gets backup or not with the Android Manifest. In order to do this, you will need to create a new XML file in res/xml directory of your project. The format of the xml follows the form
XML:
<full-backup-content> <include domain=["file"|"database"|"sharedpref"|"external"|"root"] path="string" /> <exclude domain=["file"|"database"|"sharedpref"|"external"|"root"] path="string" /> </full-backup-content>
Manifest:
<application ... android:fullBackupContent="@xml/xml_file_id">
Here is an example of excluding the database:
XML:
<full-backup-content> <exclude domain="database" path="test.db"/> </full-backup-content>
Manifest:
<application android:allowBackup="true" android:fullBackupContents="@xml/my_backup" ... >
Testing app backup configurations
To test your app’s backup configurations you can use the Android Debug Bridge (ADB).
To initialize the backup manager of your physical or virtual device, type the following in your terminal:
$ adb shell bmgr enabled $ adb shell setprop log.tag.GmsBackupTransport VERBOSE $ adb shell setprop log.tag.BackupXmlParserLogging VERBOSE $ adb shell bmgr run
If the last step shows no errors then the backup manager is running.
Now you can bring up Android Studio and the terminal side-by-side to see what is going on.
To initialize a full backup of your app, use the following command line:
$ adb shell bmgr fullbackup com.example.app
Note: replace “com.example.app” with the package name of your app.
Once the backup is finished you can uninstall your app and then reinstall it. You will notice that all the settings for your app are gone.
To restore the backup for your app use this command line
$ adb shell bmgr restore com.example.app
Now restart your app and the backed up settings are back on your app.
If you want to wipe the backup data by your app you can do so with the following commands:
$ adb shell bmgr list transports # note the one with an * next to it, it is the transport protocol for your backup $ adb shell bmgr wipe [* transport-protocol] com.example.app
App links and website associations
With Android Lollipop and below, implied intents depend on intent filters. The requesting app needs to declare an intent and then explicitly start an activity. If more than one app can handle the request, then the user must choose one.
For Android Marshmallow, an app can designate itself as the default handler for a certain type of request. If there is no user-defined default association then the system will handle the request in pre-Marshmallow fashion. Being able to declare an app to be the default handler brings great convenience to the user.
The benefits of app associations can also benefit websites. An app can designate itself as the default URI handler. It requires cooperation between the app and website developers for this function to work. The app must be configured for one or more websites. On the website end, it must host a Digital Asset Links JSON file.
The new app link associations system operates in a unique order of operation. Here are the criteria for choosing a handler in descending order:
- User-defined default association (the user has specified through settings for an app to handle a certain type of data)
- If no default app association is present, then the system looks through all the apps to see which one labeled itself to handle the data type in question
- Two possible scenarios can occur from this. One, no association found and one associated app present. In this case, the app gets launch to handle the data. Two, no association found and multiple associated apps present. In this case, the handler selection goes back to pre-Marshmallow, where the user makes the selection.
Here is an example of creating an intent handler for a website association:
Android Manifest
<activity ...> <intent-filter android:autoVerify="true"> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="http" /> <data android:scheme="https" /> <data android:host="www.android.com" /> </intent-filter> </activity>
The flag “autoVerify” influences how the framework behaves. When this flag is set to true, the framework will automatically go to the website and look for the Digital Asset Links JSON file. The JSON file is always called “assetlinks.json” and is placed in the well-known location.
well-known location
https://domain[:optional port]/.well-known/assetlinks.json
Data Binding
Data binding is a powerful feature that is available to the Android Marshmallow API. It is also backward compatible down to SDK 15. It allows you to setup element in your activity or fragment XML that displays some value that changes during runtime. The advantage with data binding is once you set it up, the elements (e.g. text view) in your layout will always update on its own. Proper usage of data binding can reduce the amount of code in your app and improve readability.
The downside to data binding is that it only supports string variable type. If you wish to display numbers, you will need to write a function for it and bind to that function in the XML element. Private variables are also not accessible and would require a getter function for it to work with data binding.
Here is an example of data binding with a Product class that has a description, name, and price:
Product.java
package com.example.databinding; import java.text.NumberFormat; public class Product { private String name; private String description; private double price; public String getName() { return name; } public String getDescription() { return description + "\n"; } public String getPrice() { NumberFormat number_formatter = NumberFormat.getInstance(); return number_formatter.format(price); } public Product(String name, String description, double price) { this.name = name; this.description = description; this.price = price; } }
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="product" type="com.example.databinding.Product" /> </data> <RelativeLayout ...> <TextView android:id="@+id/name_text" android:layout_width="fill_width" android:layout_height="wrap_content" android:gravity="center_horizontal" android:text="@{product.name}" android:textSize="30sp" /> <TextView android:id="@+id/price_text" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_below="@+id/name_text" android:gravity="center" android:text="@{product.getPrice}" android:textSize="24sp" /> <TextView android:id="@+id/description_text" android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_marginTop="10dp" android:text="@{product.description}" android:textSize="18sp" /> </RelativeLayout> </layout>
build.gradle (module:app)
android { ... dataBinding { enabled = true } ... }
MainActivity.java
public class MainActivity extends AppCompatActivity { ... @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Product product = new Product("Name", "Description", 20.00); MainActivityBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main); binding.setProduct(product); } }
Android Marshmallow brings some powerful features for developers like us to utilize. Knowing how to work with each new feature enables you to deliver better apps. There are much more features that Android Marshmallow offers that I have not touched on. These are just some of major ones I believe would benefit developers the most.
If you found this helpful, share it with someone else who can benefit from this. Also, follow me on twitter for more similar content.