Android App Project Structure

Android Directory Structure

Android App Project Structure

Within an Android app project structure, the most frequently edited folders are:

  • src - Java source files associated with your project. This includes the Activity “controller” files as well as your models and helpers.
  • res - Resource files associated with your project. All graphics, strings, layouts, and other resource files are stored in the resource file hierarchy under the res directory.
  • res/layout - XML layout files that describe the views and layouts for each activity and for partial views such as list items.
  • res/values - XML files which store various attribute values. These include [[strings.xml|Understanding-App-Resources#defining-a-string-resource]], dimens.xml, [[styles.xml|Styles-and-Themes]], colors.xml, [[themes.xml|Developing-Custom-Themes]], and so on.
  • res/drawable - Here we store the various density-independent graphic assets used in our application.
  • res/drawable-hdpi - Series of folders for density specific images to use for various resolutions.

The most frequently edited files are:

  • res/layout/activity_foo.xml - This file describes the layout of the activity’s UI. This means the placement of every view object on one app screen.
  • src/.../FooActivity.java - The Activity “controller” that constructs the activity using the view, and handles all event handling and view logic for one app screen.
  • AndroidManifest.xml - This is the Android application definition file. It contains information about the Android application such as minimum Android version, permission to access Android device capabilities such as internet access permission, ability to use phone permission, etc.

Other less edited folders include:

  • gen - Generated Java code files, this library is for Android internal use only.
  • assets - Uncompiled source files associated with your project; Rarely used.
  • bin - Resulting application package files associated with your project once it’s been built.
  • libs - Contains any secondary libraries (jars) you might want to link to your app.

Understanding App Resources

In Android, almost everything is a resource. Defining resources that you can then access in your app is an essential part of Android development.

Resources are used for anything from defining colors, images, layouts, menus, and string values. The value of this is that nothing is hardcoded. Everything is defined in these resource files and then can be referenced within your application’s code. The simplest of these resources and the most common is using string resources to allow for flexible, localized text.

Types of Resources

The following are the most common types of resources within Android apps:

Name Folder Description
Property Animations animator XML files that define property animations.
Tween Animations anim XML files that define tween animations.
Drawables drawable Bitmap files or XML files that act as graphics
Layout layout XML files that define a user interface layout
Menu menu XML files that define menus or action bar items
Values values XML files with values such as strings, integers, and colors.

In addition, note the following key files stored within the values folder mentioned above:

Name File Description
Colors res/values/colors.xml For color definitions such as text color
Dimensions res/values/dimens.xml For dimension values such as padding
Strings res/values/strings.xml For string values such as the text for a title
Styles res/values/styles.xml For style values such as color of the AppBar

For the full list of resource types, check out the Providing a Resource guide.

Providing App Resources

Defining a String Resource

For every piece of text you want to display within your application (i.e the label of a button, or the text inside a TextView), you should first define the text in the res/values/strings.xml file. Each entry is a key (representing the id of the text) and a value (the text itself). For example, if I want a button to display “Submit”, I might add the following string resource to res/values/strings.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="hello">Hello!</string>
    <string name="submit_label">Submit</string>
</resources>

Now if I ever reference the string resource for submit_label, the default will be for “Submit” to be displayed. Later though, you could create qualified resource files that change this value for different countries or between devices. We can also store more complex strings (with html or special characters) by using CDATA to escape the string such as:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <string name="feedback_label">
  <![CDATA[
    Please <a href="http://highlight.com">let us know</a> if you have feedback on this or if 
    you would like to log in with another identity service. Thanks! This is a longer string!  
  ]]>
  </string>
</resources>

For more details on defining string resources, check this guide. You can also refer to this guide for style resources and this guide for other types.

Referencing an App Resource

Now that we have defined our string resource, we can access that resource in either our Java code or our XML layouts at any time. To access, the resource in the XML Layout file, simply use the @ syntax used to access any resource:

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="@string/submit_label" />

To access the resource directly in your Java code, simply use the getResources.getString or getString methods to access the value given the resource id:

String submitText = getResources().getString(R.string.submit_label)

And the string value will be retrieved. This similar pattern works for almost any resource from images (drawables) to colors. The getResources() method returns a Resources object with many resource fetching methods. Each resource is defined within different folders and files within the res directory depending on their type.

Defining Color Resources

In addition to string resources shown above, the following common resource types can be found below. First, let’s take a look at the colors file which is used to define all colors used within an application. Colors should be defined within res/values/colors.xml and the XML file looks like the following:

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <color name="white">#FFFFFF</color>
   <color name="yellow">#FFFF00</color>
   <color name="fuchsia">#FF00FF</color>
</resources>

The colors can be accessed in Java code with:

// getResources().getColor() is now deprecated
// Resources res = getResources();
// int color = res.getColor(R.color.yellow); 

// Use ContextCompatResources instead of getColor()
int color = ContextCompat.getColor(context, R.color.yellow);

It is important to note that the most current way of accessing color resources (since API 24) requires providing context in order to resolve any custom [[theme|Styles and Themes]] attributes. See this article for more context.

and referenced within any view in the XML using:

<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textColor="@color/fuchsia"
    android:text="Hello"/>

That’s all you need for color resources. Be sure to keep hardcoded colors out of your layout files.

Defining Dimension Resources

Next, let’s take a look at the dimensions file which is used to define all size dimensions used within an app. A dimension is specified with a number followed by a unit of measure. For example: 10px, 5sp. Dimensions should be defined within res/values/dimens.xml and the XML file looks like the following:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="textview_height">25dp</dimen>
    <dimen name="textview_width">150dp</dimen>
    <dimen name="ball_radius">30dp</dimen>
    <dimen name="font_size">16sp</dimen>
</resources>

The dimensions can be accessed in Java code with:

Resources res = getResources();
float fontSize = res.getDimension(R.dimen.font_size);

and referenced within any view in the XML layouts using:

<TextView
    android:layout_height="@dimen/textview_height"
    android:layout_width="@dimen/textview_width"
    android:textSize="@dimen/font_size"/>

That’s all you need for dimension resources. Be sure to use this to keep hardcoded font sizes, padding and margin values out of your layout files. There are many other resource types to explore.

Dynamic Resource Retrieval

In certain cases, you might want to dynamically retrieve resources using just the key name rather than by “hardcoding” the resource id. For example, suppose I wanted to retrieve the “submit_label” string based on just that key name alone. This can be achieved using the getIdentifier method within an Activity:

public String getStringValue(String key) {
    // Retrieve the resource id
    String packageName = getBaseContext().getPackageName();
    Resources resources = getBaseContext().getResources();
    int stringId = resources.getIdentifier(key, "string", packageName);
    if (stringId == 0) { return null; }
    // Return the string value based on the res id
    return resources.getString(stringId);
}

Now you can reference your string resources dynamically using:

public String myKey = "submit_label"; // Maps to R.string.submit_label
public String myStringValue = getStringValue(myKey); // Returns string text

This can be done similarly for other types of resources as well. For example, for dynamically retrieving a view by a string ID:

// getViewById("tvTest");
public View getViewById(String id) {
    // Retrieve the resource id
    String packageName = getBaseContext().getPackageName();
    Resources resources = getBaseContext().getResources();
    int viewId = resources.getIdentifier(id, "id", packageName);
    if (viewId == 0) { return null; }
    // Return the string value based on the res id
    return findViewById(viewId);
}

Check out the getResources object and getIdentifier for more details.

More Resource Types

Bool

A boolean value defined in XML.

Note: A bool is a simple resource that is referenced using the value provided in the name attribute (not the name of the XML file). As such, you can combine bool resources with other simple resources in the one XML file, under one <resources> element.

File Location

The file should be in res/values/filename.xml. The filename is arbitrary. The element’s name will be used as the resource ID.

Resource Reference

In Java: R.bool.bool_name

In XML: @[package:]bool/bool_name

Syntax

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool
        name="bool_name"
        >[true | false]</bool>
</resources>

Example

XML file saved at res/values-small/bools.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <bool name="screen_small">true</bool>
    <bool name="adjust_view_bounds">true</bool>
</resources>

This application code retrieves the boolean:

Resources res = getResources();
boolean screenIsSmall = res.getBoolean(R.bool.screen_small);

This layout XML uses the boolean for an attribute:

<ImageView
    android:layout_height="fill_parent"
    android:layout_width="fill_parent"
    android:src="@drawable/logo"
    android:adjustViewBounds="@bool/adjust_view_bounds" />

Color

A color value defined in XML. The color is specified with an RGB value and alpha channel. You can use a color resource any place that accepts a hexadecimal color value. You can also use a color resource when a drawable resource is expected in XML (for example, android:drawable="@color/green").

The value always begins with a pound (#) character and then followed by the Alpha-Red-Green-Blue information in one of the following formats:

  • #RGB
  • #ARGB
  • #RRGGBB
  • #AARRGGBB

Note: A color is a simple resource that is referenced using the value provided in the name attribute (not the name of the XML file). As such, you can combine color resources with other simple resources in the one XML file, under one <resources> element.

File Location - res/values/colors.xml. The filename is arbitrary. The <color> element’s name will be used as the resource ID.

Resource Reference

In Java: R.color.color_name In XML: @[package:]color/color_name

Syntax

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color
        name="color_name"
        >hex_color</color>
</resources>

Example

XML file saved at res/values/colors.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
   <color name="opaque_red">#f00</color>
   <color name="translucent_red">#80ff0000</color>
</resources>

This application code retrieves the color resource:

Resources res = getResources();
int color = res.getColor(R.color.opaque_red);

This layout XML applies the color to an attribute:

<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:textColor="@color/translucent_red"
    android:text="Hello"/>

Dimension

A dimension value defined in XML. A dimension is specified with a number followed by a unit of measure. For example: 10px, 2in, 5sp. The following units of measure are supported by Android:

  • dp - Density-independent Pixels - An abstract unit that is based on the physical density of the screen. These units are relative to a 160 dpi (dots per inch) screen, on which 1dp is roughly equal to 1px. When running on a higher density screen, the number of pixels used to draw 1dp is scaled up by a factor appropriate for the screen’s dpi. Likewise, when on a lower density screen, the number of pixels used for 1dp is scaled down. The ratio of dp-to-pixel will change with the screen density, but not necessarily in direct proportion. Using dp units (instead of px units) is a simple solution to making the view dimensions in your layout resize properly for different screen densities. In other words, it provides consistency for the real-world sizes of your UI elements across different devices.

  • sp - Scale-independent Pixels - This is like the dp unit, but it is also scaled by the user’s font size preference. It is recommend you use this unit when specifying font sizes, so they will be adjusted for both the screen density and the user’s preference.

  • pt - Points - 172 of an inch based on the physical size of the screen, assuming a 72dpi density screen.

  • px - Pixels - Corresponds to actual pixels on the screen. This unit of measure is not recommended because the actual representation can vary across devices; each devices may have a different number of pixels per inch and may have more or fewer total pixels available on the screen.

  • mm - Millimeters - Based on the physical size of the screen.

  • in - Inches - Based on the physical size of the screen.

Note: A dimension is a simple resource that is referenced using the value provided in the name attribute (not the name of the XML file). As such, you can combine dimension resources with other simple resources in the one XML file, under one <resources> element.

File Location - res/values/filename.xml. The filename is arbitrary. The element’s name will be used as the resource ID.

Resource Reference

In Java: R.dimen.dimension_name

In XML: @[package:]dimen/dimension_name

Syntax

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen
        name="dimension_name"
        >dimension</dimen>
</resources>

Example

XML file saved at res/values/dimens.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <dimen name="textview_height">25dp</dimen>
    <dimen name="textview_width">150dp</dimen>
    <dimen name="ball_radius">30dp</dimen>
    <dimen name="font_size">16sp</dimen>
</resources>

This application code retrieves a dimension:

Resources res = getResources();
float fontSize = res.getDimension(R.dimen.font_size);

This layout XML applies dimensions to attributes:

<TextView
    android:layout_height="@dimen/textview_height"
    android:layout_width="@dimen/textview_width"
    android:textSize="@dimen/font_size"/>

ID

A unique resource ID defined in XML. Using the name you provide in the <item> element, the Android developer tools create a unique integer in your project’s R.java class, which you can use as an identifier for an application resources (for example, a View in your UI layout) or a unique integer for use in your application code (for example, as an ID for a dialog or a result code).

Note: An ID is a simple resource that is referenced using the value provided in the name attribute (not the name of the XML file). As such, you can combine ID resources with other simple resources in the one XML file, under one <resources> element. Also, remember that an ID resources does not reference an actual resource item; it is simply a unique ID that you can attach to other resources or use as a unique integer in your application.

File location - res/values/filename.xml. The filename is arbitrary.

Resource reference

In Java: R.id.name

In XML: @[package:]id/name

Syntax

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item
        type="id"
        name="id_name" />
</resources>

Example

XML file saved at res/values/ids.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item type="id" name="button_ok" />
    <item type="id" name="dialog_exit" />
</resources>

Then, this layout snippet uses the “button_ok” ID for a Button widget:

<Button android:id="@id/button_ok"
    style="@style/button_style" />

Notice that the android:id value does not include the plus sign in the ID reference, because the ID already exists, as defined in the ids.xml example above. (When you specify an ID to an XML resource using the plus sign—in the format android:id="@+id/name" — it means that the “name” ID does not exist and should be created.)

As another example, the following code snippet uses the “dialog_exit” ID as a unique identifier for a dialog:

showDialog(R.id.dialog_exit);

In the same application, the “dialog_exit” ID is compared when creating a dialog:

protected Dialog onCreateDialog(int id) {
    Dialog dialog;
    switch(id) {
    case R.id.dialog_exit:
        ...
        break;
    default:
        dialog = null;
    }
    return dialog;
}

Integer

An integer defined in XML.

Note: An integer is a simple resource that is referenced using the value provided in the name attribute (not the name of the XML file). As such, you can combine integer resources with other simple resources in the one XML file, under one <resources> element.

File location - res/values/filename.xml The filename is arbitrary. The <integer> element’s name will be used as the resource ID.

Resource Reference

In Java: R.integer.integer_name

In XML: @[package:]integer/integer_name

Syntax

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer
        name="integer_name"
        >integer</integer>
</resources>

Example

XML file saved at res/values/integers.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="max_speed">75</integer>
    <integer name="min_speed">5</integer>
</resources>

This application code retrieves an integer:

Resources res = getResources();
int maxSpeed = res.getInteger(R.integer.max_speed);

Integer Array

An array of integers defined in XML.

Note: An integer array is a simple resource that is referenced using the value provided in the name attribute (not the name of the XML file). As such, you can combine integer array resources with other simple resources in the one XML file, under one <resources> element.

File location - res/values/filename.xml. The filename is arbitrary. The <integer-array> element’s name will be used as the resource ID.

Compiled resource data type - Resource pointer to an array of integers.

Resource reference

In Java: R.array.integer_array_name

In XML: @[package:]array.integer_array_name

Syntax

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer-array
        name="integer_array_name">
        <item
            >integer</item>
    </integer-array>
</resources>

Example

XML file saved at res/values/integers.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer-array name="bits">
        <item>4</item>
        <item>8</item>
        <item>16</item>
        <item>32</item>
    </integer-array>
</resources>

This application code retrieves the integer array:

Resources res = getResources();
int[] bits = res.getIntArray(R.array.bits);

Typed Array

A TypedArray defined in XML. You can use this to create an array of other resources, such as drawables. Note that the array is not required to be homogeneous, so you can create an array of mixed resource types, but you must be aware of what and where the data types are in the array so that you can properly obtain each item with the TypedArray’s get…() methods.

Note: A typed array is a simple resource that is referenced using the value provided in the name attribute (not the name of the XML file). As such, you can combine typed array resources with other simple resources in the one XML file, under one <resources> element.

**File location **- res/values/filename.xml. The filename is arbitrary. The <array> element’s name will be used as the resource ID.

Compiled resource data type - Resource pointer to a TypedArray.

Resource reference

In Java: R.array.array_name

In XML: @[package:]array.array_name

Syntax

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <array
        name="integer_array_name">
        <item>resource</item>
    </array>
</resources>

Example

XML file saved at res/values/arrays.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <array name="icons">
        <item>@drawable/home</item>
        <item>@drawable/settings</item>
        <item>@drawable/logout</item>
    </array>
    <array name="colors">
        <item>#FFFF0000</item>
        <item>#FF00FF00</item>
        <item>#FF0000FF</item>
    </array>
</resources>

This application code retrieves each array and then obtains the first entry in each array:

Resources res = getResources();
TypedArray icons = res.obtainTypedArray(R.array.icons);
Drawable drawable = icons.getDrawable(0);

TypedArray colors = res.obtainTypedArray(R.array.colors);
int color = colors.getColor(0,0);

Providing Alternate Resources

Responsive Design

In order to create an outstanding UI design, it is essential for app developers to create apps that work properly across a wide variety of devices. To do this, we first divide android mobile devices into various categories (buckets) based on screen size and display as shown below:

Apps must be designed to work across any different screen densities as well as screen sizes. This can be done leveraging a variety of systems provided by the Android framework.

Introducing Alternate Resources

One of the most powerful tools available to developers is the option to provide “alternate resources” based on specific qualifiers such as phone size, language, density, and others. Common uses for alternate resources include:

  • Alternative layout files for different form factors (i.e phone vs tablet)
  • Alternative string resources for different languages (i.e English vs Italian)
  • Alternative drawable resources for different screen densities (shown below)
  • Alternate style resources for different platform versions (Holo vs Material)
  • Alternate layout files for different screen orientations (i.e portrait vs landscape)

To specify configuration-specific alternatives for a set of resources, we create a new directory in res in the form of [resource]-[qualifiers]. For example, one best practice is to ensure that all images are provided for [[multiple screen densities|Working-with-the-ImageView#supporting-multiple-densities]].

Densities

This is achieved by having res/drawable-hdpi, res/drawable-xhdpi, and res/drawable-xxhdpi folders with different versions of the same image. The correct resource is then selected automatically by the system based on the device density. The directory listing might look like the following:

res/
    drawable/   
        icon.png
        background.png    
    drawable-hdpi/  
        icon.png
        background.png

Note the resource files need to all have the same name within the different folders. This system works for any type of resource with a wide variety of qualifiers.

Understanding Qualifiers

Android supports several configuration qualifiers and you can add multiple qualifiers to one directory name, by separating each qualifier with a dash. The most common qualifiers are listed below:

Configuration Examples Description
Language en, fr Language code selected on the device
Screen size sw480dp,sw600dp Minimum width of the screen’s height or width.
Screen orientation port, land Screen is in portrait or landscape mode.
Screen density hdpi, xhdpi Screen density often used for alternate images.
Platform version v7, v11, v21 Platform version often used for styles.

You can specify multiple qualifiers for a single set of resources, separated by dashes. For example, drawable-en-sw600dp-land applies to English tablets in landscape orientation. Note that if you use multiple qualifiers for a resource directory, you must add them to the directory name in the order they are listed in the table above. See the official docs for the complete set of qualifiers available.

Creating Alternate Resources

In Android Studio, the easiest way to create alternate resources is to right-click on a resource subfolder (i.e layout) in the Android project sidebar and then use the New => Layout resource file wizard to specify the qualifiers you’d like (i.e orientation):

This will create two versions of the layout file, one for “portrait” mode (vertical) and one for “landscape” (horizontal). If you were to add a different label into the second version of the layout then you would see this effect automatically when the screen is rotated:

To summarize, you can create as many versions of a resource file as is needed for different situations and then the most appropriate version of the resource file is selected automatically by the system.

Determining Configuration at Runtime

When the app is running, we can always check the current configuration (orientation, screen size, etc) by accessing the Configuration object through getResources().getConfiguration() within an activity or context object. For example, to determine the orientation (portrait or landscape) inside an activity, we can do:

String image;
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_PORTRAIT) {
   image = "image_portrait.png";
   // ...
} else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
   image = "image_landscape.png";
   // ...
}

We can similarly access this within any object by getting access to a [[Context object|Using-Context]]. For example, within an ArrayAdapter by using getContext().getResources().getConfiguration() to access the configurations.

Alternate Layout Files

Often alternative resources are used to specify different layout files for phones and tablets. This can be done using the “smallest width” qualifier of sw. The folder structure might be set up as follows:

res/
    layout/   
        activity_main.xml
        item_photo.xml    
    layout-sw600dp/ 
        activity_main.xml
    layout-sw600dp-land/
        activity_main.xml 
    layout-sw720dp/ 
        activity_main.xml
        item_photo.xml
    layout-land/
        activity_main.xml
        item_photo.xml

Generally speaking phones and phablets are between sw240 and sw480. 7” tablets are sw600 and 10” tablets are sw720. You can also simply add qualifiers such as layout-land to be applied for all devices in landscape mode. Here’s an illustrated example of the above procedure:

For a detailed tutorial on how to manage responsive layouts for tablets, review our [[Flexible User Interfaces]] guide. You can also review this article on UI design best practices and this official doc on resources for more details.

There’s also a list of Android phone screens and dimensions each one use that you can find here

Layout Best Practices

Here is a quick checklist about how you can ensure that your application displays properly on different screens:

  • Avoid using hard coded pixel values in your application code
  • Use RelativeLayout properly and never use AbsoluteLayout
  • Use wrap_content, match_parent, or dp units when specifying dimensions
  • Use alternate layouts and drawables to ensure a responsive design when needed

Review the rest of the best practices for screen independence on the official guide.

Alias Resources

When you have a resource that you’d like to use for more than one device configuration, you do not need to put the same resource in more than one alternative resource directory. Instead, you can create an alternative resource that acts as an alias for a resource saved in your default resource directory.

Best Resource Match

When you request a resource for which you provide alternatives, Android selects which alternative resource to use at runtime, depending on the current device configuration. Read the official resource guide for a detailed overview of how the match is selected.

Organizing Source Files

Android applications should always be neatly organized with a clear folder structure that makes your code easy to read. In addition, proper naming conventions for code and classes are important to ensure your code is clean and maintainable.

Naming Conventions

Be sure to check out the Ribot Code and Style Guidelines for a more extensive breakdown of suggested style and naming guidelines.

For Java Code

The following naming and casing conventions are important for your Java code:

Type Example Description Link
Variable incomeTaxRate All variables should be camel case Read More
Constant DAYS_IN_WEEK All constants should be all uppercase Read More
Method convertToEuroDollars All methods should be camel case Read More
Parameter depositAmount All parameter names should be camel case Read More

See this naming guide for more details.

For Android Classes

Android classes should be named with a particular convention that makes their purpose clear in the name. For example all activities should end with Activity as in MoviesActivity. The following are the most important naming conventions:

Name Convention Inherits
Activity CreateTodoItemActivity AppCompatActivity, Activity
List Adapter TodoItemsAdapter BaseAdapter, ArrayAdapter
Database Helper TodoItemsDbHelper SQLiteOpenHelper
Network Client TodoItemsClient N/A
Fragment TodoItemDetailFragment Fragment
Service FetchTodoItemService Service, IntentService

Use your best judgement for other types of files. The goal is for any Android-specific classes to be identifiable by the suffix.

Android Folder Structure

There are several best practices for organizing your app’s package structure.

Organize packages by category

The way to do this is to group things together by their category. Each component goes to the corresponding package:

  • com.example.myapp.activities - Contains all activities
  • com.example.myapp.adapters - Contains all custom adapters
  • com.example.myapp.models - Contains all our data models
  • com.example.myapp.network - Contains all networking code
  • com.example.myapp.fragments - Contains all fragments
  • com.example.myapp.utils - Contains all helpers supporting code.
  • com.example.myapp.interfaces - Contains all interfaces

Keeping these folders in each app means that code is logically organized and scanning the code is a pleasant experience. You can see a slight variation on this structure as suggested by Futurice on their best-practices repo.

Organize packages by application features

Alternatively, we can package-by-feature rather than layers. This approach uses packages to reflect the feature set. Consider the following package structure as outlined in this post:

  • com.example.myapp.service.* - Is a subpackage for all background related service packages/classes
  • com.example.myapp.ui.* - Is a subpackage for all UI-related packages/classes
  • com.example.myapp.ui.mainscreen - Contains classes related to some app’s Main Screen
  • com.example.myapp.ui.detailsscreen - Contains classes related to some app’s Item Details Screen

This feature allows you to place DetailsActivity, DetailsFragment, DetailsListAdapter, DetailsItemModel in one package, which provides comfortable navigation when you’re working on “item details” feature.

DetailsListAdapter and DetailsItemModel classes and/or their properties can be made package-private, and thus not exposed outside of the package. Within the package you may access their properties directly without generating tons of boilerplate “setter” methods.

This can make object creation really simple and intuitive, while objects remain immutable outside the package.

Organizing Resources

Resources should be split up into the following key files and folders:

Name Path Description
XML Layouts res/layout/ This is where we put our XML layout files.
XML Menus res/menu/ This is where we put our AppBar menu actions.
Drawables res/drawable This is where we put images and XML drawables.
Colors res/values/colors.xml This is where we put color definitions.
Dimensions res/values/dimens.xml This is where we put dimension values.
Strings res/values/strings.xml This is where we put strings.
Styles res/values/styles.xml This is where we put style values.

See the full list of resources here and note the following:

  • Don’t hardcode color hex values in the layout. Instead of hardcoding these values, be sure to move all colors into res/values/colors.xml and reference the colors in layouts with @color/royal_blue.
  • Don’t hardcode margin / padding dimensions in the layout. Instead of hardcoding these values, be sure to move all dimension values into res/values/dimens.xml and reference these in layouts with @dimen/item_padding_left.
  • To support multiple devices, we can then use the alternative resources system to provide different colors, strings, dimens, styles, etc based on the device type, screen size, API version and much more.

Be sure to start properly organizing your resources early on in the development of an application. Be sure to check out the Ribot Code and Style Guidelines for a more extensive breakdown of suggested style and naming guidelines.

Organizing Resources into Subfolders

Often there are questions about organizing not just the source files but also better organizing the application resources. In a modern app, there are often hundreds of different layout files, drawables, styles, etc and by default these are all grouped together in a flat list within a single subdirectory (i.e res/layout).

Refer to stackoverflow post for a discussion of the other options.

Conclusion

It is up to you to decide which of the aforementioned approaches suits your project best.

However, in general Java programming, packaging apps by feature is considered preferable and makes a lot of sense.

Activity Lifecycle Background

As a user navigates throughout an app, Android maintains the visited activities in a stack, with the currently visible activity always placed at the top of the stack.

At any point in time a particular activity can be in one of the following 4 states:

Activity State Description
Running Activity is visible and interacting with the user
Paused Activity is still visible, but no longer interacting with the user
Stopped Activity is no longer visible
Killed Activity has been killed by the system (low memory) or its finish() method has been called

Activity Lifecycle

The following diagram shows the important state paths of an Activity. The square rectangles represent callback methods you can implement to perform operations when the Activity moves between states. These are described further in the table below the diagram.

Imgur

Lifecycle Method Description Common Uses
onCreate() The activity is starting (but not visible to the user) Most of the activity initialization code goes here. This is where you setContentView() for the activity, initialize views, set up any adapters, etc.
onStart() The activity is now visible (but not ready for user interaction) This lifecycle method isn’t used much, but can come in handy to register a BroadcastReceiver to monitor for changes that impact the UI (since the UI is now visible to the user).
onResume() The activity is now in the foreground and ready for user interaction This is a good place to start animations, open exclusive-access devices like the camera, etc.
onPause() Counterpart to onResume(). The activity is about to go into the background and has stopped interacting with the user. This can happen when another activity is launched in front of the current activity. It’s common to undo anything that was done in onResume() and to save any global state (such as writing to a file).
onStop() Counterpart to onStart(). The activity is no longer visible to the user. It’s common to undo anything that was done in onStart().
onDestroy() Counterpart to onCreate(...). This can be triggered because finish() was called on the activity or the system needed to free up some memory. It’s common to do any cleanup here. For example, if the activity has a thread running in the background to download data from the network, it may create that thread in onCreate() and then stop the thread here in onDestroy()
onRestart() Called when the activity has been stopped, before it is started again It isn’t very common to need to implement this callback.

Handling Configuration Changes

The Activity lifecycle is especially important because whenever an activity leaves the screen, the activity can be destroyed. When an activity is destroyed, when the user returns to the activity, the activity will be re-created and the lifecycle methods will be called again. Activities are also re-created whenever the orientation changes (i.e the screen is rotated). In order to ensure your application is robust to recreation amongst a wide variety of situations, be sure to review the [[handling configuration changes|Handling-Configuration-Changes]].

Calling the super class

When overriding any of the methods, you may need to call the superclass implementation. The rule of thumb is that during initialization, you should always call the superclass first:

public void onCreate() {
   super.onCreate();
   // do work after super class function
   // setContentView(R.layout.main);
}

During de-initialization, you should do the work first before calling the super class:

public void onPause() {
   // do work here first before super class function
   // LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
   super.onPause();
}

See this Stack Overflow article for more context.

Handling Configuration Changes

There are various situations such as when the screen orientation is rotated where the Activity can actually be destroyed and removed from memory and then re-created from scratch again. In these situations, the best practice is to prepare for cases where the Activity is re-created by properly saving and restoring the state.

Saving and Restoring Activity State

As your activity begins to stop, the system calls onSaveInstanceState() so your activity can save state information with a collection of key-value pairs. The default implementation of this method automatically saves information about the state of the activity’s view hierarchy, such as the text in an EditText widget or the scroll position of a ListView.

To save additional state information for your activity, you must implement onSaveInstanceState() and add key-value pairs to the Bundle object. For example:

public class MainActivity extends Activity {
    static final String SOME_VALUE = "int_value";
    static final String SOME_OTHER_VALUE = "string_value";

    @Override
    protected void onSaveInstanceState(Bundle savedInstanceState) {
        // Save custom values into the bundle
        savedInstanceState.putInt(SOME_VALUE, someIntValue);
        savedInstanceState.putString(SOME_OTHER_VALUE, someStringValue);
        // Always call the superclass so it can save the view hierarchy state
        super.onSaveInstanceState(savedInstanceState);
    }
}

The system will call that method before an Activity is destroyed. Then later the system will call onRestoreInstanceState where we can restore state from the bundle:

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    // Always call the superclass so it can restore the view hierarchy
    super.onRestoreInstanceState(savedInstanceState);
    // Restore state members from saved instance
    someIntValue = savedInstanceState.getInt(SOME_VALUE);
    someStringValue = savedInstanceState.getString(SOME_OTHER_VALUE);
}

Instance state can also be restored in the standard Activity#onCreate method but it is convenient to do it in onRestoreInstanceState which ensures all of the initialization has been done and allows subclasses to decide whether to use the default implementation. Read this stackoverflow post for details.

Note that onSaveInstanceState and onRestoreInstanceState are not guaranteed to be called together. Android invokes onSaveInstanceState() when there’s a chance the activity might be destroyed. However, there are cases where onSaveInstanceState is called but the activity is not destroyed and as a result onRestoreInstanceState is not invoked.

Read more on the Recreating an Activity guide.

Saving and Restoring Fragment State

Fragments also have a onSaveInstanceState() method which is called when their state needs to be saved:

public class MySimpleFragment extends Fragment {
    private int someStateValue;
    private final String SOME_VALUE_KEY = "someValueToSave";
   
    // Fires when a configuration change occurs and fragment needs to save state
    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putInt(SOME_VALUE_KEY, someStateValue);
        super.onSaveInstanceState(outState);
    }
}

Then we can pull data out of this saved state in onCreateView:

public class MySimpleFragment extends Fragment {
   // ...

   // Inflate the view for the fragment based on layout XML
   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.my_simple_fragment, container, false);
        if (savedInstanceState != null) {
            someStateValue = savedInstanceState.getInt(SOME_VALUE_KEY);
            // Do something with value if needed
        }
        return view;
   }
}

For the fragment state to be saved properly, we need to be sure that we aren’t unnecessarily recreating the fragment on configuration changes. This means being careful not to reinitialize existing fragments when they already exist. Any fragments being initialized in an Activity need to be looked up by tag after a configuration change:

public class ParentActivity extends AppCompatActivity {
    private MySimpleFragment fragmentSimple;
    private final String SIMPLE_FRAGMENT_TAG = "myfragmenttag";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        if (savedInstanceState != null) { // saved instance state, fragment may exist
           // look up the instance that already exists by tag
           fragmentSimple = (MySimpleFragment)  
              getSupportFragmentManager().findFragmentByTag(SIMPLE_FRAGMENT_TAG);
        } else if (fragmentSimple == null) { 
           // only create fragment if they haven't been instantiated already
           fragmentSimple = new MySimpleFragment();
        }
    }
}

This requires us to be careful to include a tag for lookup whenever putting a fragment into the activity within a transaction:

public class ParentActivity extends AppCompatActivity {
    private MySimpleFragment fragmentSimple;
    private final String SIMPLE_FRAGMENT_TAG = "myfragmenttag";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ... fragment lookup or instantation from above...
        // Always add a tag to a fragment being inserted into container
        if (!fragmentSimple.isInLayout()) {
            getSupportFragmentManager()
                .beginTransaction()
                .replace(R.id.container, fragmentSimple, SIMPLE_FRAGMENT_TAG)
                .commit();
        }
    }
}

With this simple pattern, we can properly re-use fragments and restore their state across configuration changes.

Retaining Fragments

In many cases, we can avoid problems when an Activity is re-created by simply using fragments. If your views and state are within a fragment, we can easily have the fragment be retained when the activity is re-created:

public class RetainedFragment extends Fragment {
    // data object we want to retain
    private MyDataObject data;

    // this method is only called once for this fragment
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // retain this fragment when activity is re-initialized
        setRetainInstance(true);
    }

    public void setData(MyDataObject data) {
        this.data = data;
    }

    public MyDataObject getData() {
        return data;
    }
}

This approach keeps the fragment from being destroyed during the activity lifecycle. They are instead retained inside the Fragment Manager. See the Android official docs for more information.

Now you can check to see if the fragment already exists by tag before creating one and the fragment will retain it’s state across configuration changes. See the Handling Runtime Changes guide for more details.

Properly Handling List State

ListView

Often when you rotate the screen, the app will lose the scroll position and other state of any lists on screen. To properly retain this state for ListView, you can store the instance state onPause and restore onViewCreated as shown below:

// YourActivity.java
private static final String LIST_STATE = "listState";
private Parcelable mListState = null;

// Write list state to bundle
@Override
protected void onSaveInstanceState(Bundle state) {
    super.onSaveInstanceState(state);
    mListState = getListView().onSaveInstanceState();
    state.putParcelable(LIST_STATE, mListState);
}

// Restore list state from bundle
@Override
protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);
    mListState = state.getParcelable(LIST_STATE);
}


@Override
protected void onResume() {
    super.onResume();
    loadData(); // make sure data has been reloaded into adapter first
    // ONLY call this part once the data items have been loaded back into the adapter
    // for example, inside a success callback from the network
    if (mListState != null) {
        myListView.onRestoreInstanceState(mListState);
        mListState = null;
    }
}

Check out this blog post and stackoverflow post for more details.

Note that you must load the items back into the adapter first before calling onRestoreInstanceState. In other words, don’t call onRestoreInstanceState on the ListView until after the items are loaded back in from the network or the database.

RecyclerView

Often when you rotate the screen, the app will lose the scroll position and other state of any lists on screen. To properly retain this state for RecyclerView, you can store the instance state onPause and restore onViewCreated as shown below:

// YourActivity.java
public final static int LIST_STATE_KEY = "recycler_list_state";
Parcelable listState;

protected void onSaveInstanceState(Bundle state) {
     super.onSaveInstanceState(state);
     // Save list state
     listState = mLayoutManager.onSaveInstanceState();
     state.putParcelable(LIST_STATE_KEY, mListState);
}

protected void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);
    // Retrieve list state and list/item positions
    if(state != null)
        listState = state.getParcelable(LIST_STATE_KEY);
}

@Override
protected void onResume() {
    super.onResume();
    if (listState != null) {
        mLayoutManager.onRestoreInstanceState(listState);
    }
}

Check out this blog post and stackoverflow post for more details.

Locking Screen Orientation

If you want to lock the screen orientation change of any screen (activity) of your android application you just need to set the android:screenOrientation property of an <activity> within the AndroidManifest.xml:

<activity
    android:name="com.techblogon.screenorientationexample.MainActivity"
    android:screenOrientation="portrait"
    android:label="@string/app_name" >
    <!-- ... -->
</activity>

Now that activity is forced to always be displayed in “portrait” mode.

Manually Managing Configuration Changes

If your application doesn’t need to update resources during a specific configuration change and you have a performance limitation that requires you to avoid the activity restart, then you can declare that your activity handles the configuration change itself, which prevents the system from restarting your activity.

However, this technique should be considered a last resort when you must avoid restarts due to a configuration change and is not recommended for most applications. To take this approach, we must add the android:configChanges node to the activity within the AndroidManifest.xml:

<activity android:name=".MyActivity"
          android:configChanges="orientation|screenSize|keyboardHidden"
          android:label="@string/app_name">

Now, when one of these configurations change, the activity does not restart but instead receives a call to onConfigurationChanged():

// Within the activity which receives these changes
// Checks the current device orientation, and toasts accordingly
@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    // Checks the orientation of the screen
    if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
    } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
        Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
    }
}

See the Handling the Change docs. For more about which configuration changes you can handle in your activity, see the android:configChanges documentation and the Configuration class.

Migrating to the AppCompat Library

The AppCompat support library enables the use of the ActionBar and Material Design specific implementations such as Toolbar for older devices down to Android v2.1. Currently, new projects created through Android Studio incorporate this library by default. You can confirm by looking at the build.gradle file to see the AppCompat library being set:

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"
}

dependencies {
    compile 'com.android.support:appcompat-v7:25.2.0'
}

Note that the AppCompat library has an implicit dependency on the support-v4 library. The support-v4 declaration however does not necessarily need to be declared. Since the release of the support-v4 version 24.2.0, the library has been divided into separate modules: support-compat, support-core-utils, support-core-ui, support-media-compat, and support-fragment. However, because the AppCompat library usually depends on support-fragment, which still contains a dependency to all the other modules, you currently cannot take advantage of this change to reduce the number of overall dependencies.

Also notice that once you upgrade to AppCompat v7 v24, you will also be forced to update your Build Tools and compileSDKVersion to API 24 too.

There is a current bug that precludes you from compiling to lower versions. Once you are using this API version 23 or higher, be aware that the Apache HTTP Client library has been removed.

Search and replacing changes

Older projects may not include this library, so migrating requires changing the theme references and many of the main imports described in this blog post. Because the support class declarations are not compatible with the standard Android ones, you need to make sure you are using the imports from the support library entirely. Otherwise your app is likely to crash.

The simplest is often to do a search-and-replace to start changing the following statements to start using the support libraries.

Activities Changes

  • import android.app.Activity -> import android.support.v7.app.AppCompatActivity
  • extends Activity -> extends AppCompatActivity

Fragment Changes

  • extends FragmentActivity -> extends AppCompatActivity
  • import android.support.v4.app.FragmentActivity -> import android.support.v7.app.AppCompatActivity
  • import android.app.Fragment -> import android.support.v4.app.Fragment
  • getFragmentManager() -> getSupportFragmentManager()

ActionBar Changes

  • import android.app.ActionBar -> import android.support.v7.app.ActionBar
  • getActionBar() -> getSupportActionBar()

AlertDialog Changes

Your AlertDialogs should import from the AppCompat support library instead, which takes advantage of the new Material Design theme.

  • import android.app.AlertDialog -> import android.support.v7.app.AlertDialog

Theme XML Changes

If you were migrating from the Holo theme, your new theme would inherit from Theme.AppCompat instead of android:Theme.Holo.Light:

    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

If you wish to have a style that disables the ActionBar in certain Activity screens but still wish to use many of the ones you custom defined, you can inherit from AppTheme and apply the same styles declared in Theme.AppCompat.NoActionBar:

    <style name="AppTheme.NoActionBar">
        <item name="windowActionBar">false</item>
        <item name="windowNoTitle">true</item>
    </style>

If you see AppCompat does not support the current theme features, it’s likely the windowNoTitle setting has not been set or is set to false. There is more strict enforcement on what this value must be set on newer AppCompat libraries. See this Stack Overflow article for more context.

For your menu/ layout files, add an app: namespace . For menu items, the showAsAction must be from the app namespace instead of android namespace. It is considered a custom attribute of the support library and will no otherwise be processed correctly without making this change.

<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
 <item android:id="@+id/myMenuItem"
       android:title="@string/select"
       android:showAsAction="ifRoom"
       app:showAsAction="ifRoom"

If you are using widgets such as the SearchView, you must also use android.support.v7.widget.SearchView instead of android.widget.SearchView. Note that the app namespace must also be used.

+<menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto">
 
     <item android:id="@+id/contentSearch"
           android:orderInCategory="2"
           android:title="@string/search"
           app:showAsAction="ifRoom"
           app:actionViewClass="android.support.v7.widget.SearchView">

Changes to Menu Options

The MenuItemCompat helper class has a few static methods that should be used in lieu of MenuItem:

  @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.my_menu, menu);
        mSearchView = (SearchView) MenuItemCompat.getActionView(menu.findItem(R.id.contentSearch));

Changing targetSDKVersion

In addition, setting the targetSdkVersion to the latest SDK version ensures that the AppCompat library will attempt to apply the Material Design assuming the device itself can support it. The support library will still check to see if the minimum SDK version is being used on the device.

android {
    targetSdkVersion 24

Known issues

The AppCompat library has issues with Samsung v4.2.2 devices. See this issue for more details.

Understanding App Permissions

By default, an Android app starts with zero permissions granted to it. When the app needs to use any of the protected features of the device (sending network requests, accessing the camera, sending an SMS, etc) it must obtain the appropriate permission from the user to do so.

Before Marshmallow, permissions were handled at install-time and specified in the AndroidManifest.xml within the project. Full list of permissions can be found here. After Marshmallow, permissions must now be requested at runtime before being used. There are a number of libraries available to make runtime permissions easier. If you to get started quickly, check out our guide on [[managing runtime permissions with PermissionsDispatcher]].

Permissions before Marshmallow

Permissions were much simpler before Marshmallow (API 23). All permissions were handled at install-time. When a user went to install an app from the Google Play Store, the user was presented a list of permissions that the app required (some people referred to this as a “wall of permissions”. The user could either accept all the permissions and continue to install the app or decide not to install the app. It was an all or nothing approach. There was no way to grant only certain permissions to the app and no way for the user to revoke certain permissions after the app was installed.

Example of pre-Marshmallow permissions requested by the Dropbox app:

Imgur

For an app developer, permissions were very simple. To request one of the many permissions, simply specify it in the AndroidManifest.xml:

For example, an application that needs to read the user’s contacts would add the following to it’s AndroidManifest.xml:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.app.myapp" >
    
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    ...
</manifest>

That’s all there was to it. The user had no way of changing permissions, even after installing the app. This made it easy for developers to deal with permissions, but wasn’t the best user experience.

Permission Updates in Marshmallow

Marshmallow brought large changes to the permissions model. It introduced the concept of runtime permissions. These are permissions that are requested while the app is running (instead of before the app is installed). These permission can then be allowed or denied by the user. For approved permissions, these can also be revoked at a later time.

This means there are a couple more things to consider when working with permissions for a Marshmallow app. Keep in mind that your targetSdkVersion must be >= 23 and your emulator / device must be running Marshmallow to see the new permissions model. If this isn’t the case, see the [[backwards compatibility|Understanding App Permissions#backwards-compatibility]] section to understand how permissions will behave on your configuration.

Normal Permissions

When you need to add a new permission, first check this page to see if the permission is considered a PROTECTION_NORMAL permission. In Marshmallow, Google has designated certain permissions to be “safe” and called these “Normal Permissions”. These are things like ACCESS_NETWORK_STATE, INTERNET, etc. which can’t do much harm. Normal permissions are automatically granted at install time and never prompt the user asking for permission.

Important: Normal Permissions must be added to the AndroidManifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.app.myapp" >
    
    <uses-permission android:name="android.permission.INTERNET" />
    ...
</manifest>

Runtime Permissions

If the permission you need to add isn’t listed under the normal permissions, you’ll need to deal with “Runtime Permissions”. Runtime permissions are permissions that are requested as they are needed while the app is running. These permissions will show a dialog to the user, similar to the following one:

Imgur

The first step when adding a “Runtime Permission” is to add it to the AndroidManifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.codepath.androidpermissionsdemo" >

    <uses-permission android:name="android.permission.READ_CONTACTS" />
    ...
</manifest>

Next, you’ll need to initiate the permission request and handle the result. The following code shows how to do this in the context of an Activity, but this is also possible from within a Fragment.

// MainActivity.java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        // In an actual app, you'd want to request a permission when the user performs an action
        // that requires that permission.
        getPermissionToReadUserContacts();
    }

    // Identifier for the permission request
    private static final int READ_CONTACTS_PERMISSIONS_REQUEST = 1;

    // Called when the user is performing an action which requires the app to read the
    // user's contacts
    public void getPermissionToReadUserContacts() {
        // 1) Use the support library version ContextCompat.checkSelfPermission(...) to avoid
        // checking the build version since Context.checkSelfPermission(...) is only available
        // in Marshmallow
        // 2) Always check for permission (even if permission has already been granted)
        // since the user can revoke permissions at any time through Settings
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
                != PackageManager.PERMISSION_GRANTED) {

            // The permission is NOT already granted.
            // Check if the user has been asked about this permission already and denied
            // it. If so, we want to give more explanation about why the permission is needed.
            if (shouldShowRequestPermissionRationale(
                    Manifest.permission.READ_CONTACTS)) {
                // Show our own UI to explain to the user why we need to read the contacts
                // before actually requesting the permission and showing the default UI
            }

            // Fire off an async request to actually get the permission
            // This will show the standard permission request dialog UI
            requestPermissions(new String[]{Manifest.permission.READ_CONTACTS},
                    READ_CONTACTS_PERMISSIONS_REQUEST);
        }
    }

    // Callback with the request from calling requestPermissions(...)
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           @NonNull String permissions[],
                                           @NonNull int[] grantResults) {
        // Make sure it's our original READ_CONTACTS request
        if (requestCode == READ_CONTACTS_PERMISSIONS_REQUEST) {
            if (grantResults.length == 1 &&
                    grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "Read Contacts permission granted", Toast.LENGTH_SHORT).show();
            } else {
                // showRationale = false if user clicks Never Ask Again, otherwise true
                boolean showRationale = shouldShowRequestPermissionRationale( this, Manifest.permission.READ_CONTACTS);

                if (showRationale) {
                   // do something here to handle degraded mode
                } else {
                   Toast.makeText(this, "Read Contacts permission denied", Toast.LENGTH_SHORT).show();
                }
            }
        } else {
            super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

Permission Groups

Permission Groups avoids spamming the user with a lot of permission requests while allowing the app developer to only request the minimal amount of permissions needed at any point in time.

Related permissions are grouped into one of the permission groups. When an app requests a permission that belongs to a particular permission group (i.e. READ_CONTACTS), Android asks the user about the higher level group instead (CONTACTS). This way when the app later needs the WRITE_CONTACTS permission, Android can automatically grant this itself without prompting the user.

In most of your interaction with the permission API’s you’ll be working with the individual permissions and not the permission groups, but pay close attention to what the API expects as both permissions and permission groups are Strings.

Backwards Compatibility

There are 2 main scenarios to think about when it comes to backwards compatibility:

  1. Your app is targeting an API less than Marshmallow (TargetSdkVersion < 23), but the emulator / device is Marshmallow:
    • Your app will continue to use the old permissions model.
    • All permissions listed in the AndroidManifest will be asked for at install time.
    • Users will be able to revoke permissions after the app is installed. It’s important to test this scenario since the results of certain actions without the appropriate permission can be unexpected.
  2. The emulator / device is running something older than Marshmallow, but you app targets Marshmallow (TargetSdkVersion >= 23):
    • Your app will continue to use the old permissions model.
    • All permissions listed in the AndroidManifest will be asked for at install time.

How to Ask For Permissions

Google recommends in this video that there are four patterns to consider when thinking about permissions:

Each pattern dictates a different way of requesting permissions. For instance, when requesting for critical but unclear permissions, use a warm welcome screen to help understand a permission is requested. For critical permissions, such as a camera app that needs camera permission, ask up-front for it. Secondary features can be requested later in context, such as a geotagging app when asking for a location permission. For permissions that are secondary and unclear, you should include a rationale explanation if you really need them.

Storage permissions

Rethink about whether you need read/write storage permissions (i.e. android.permission.WRITE_EXTERNAL_STORAGE or android.permission.READ_EXTERNAL_STORAGE), which give you all files on the SD card. Instead, you should use methods on Context to access package-specific directories on external storage. Your app always have access to read/write to these directories,so there is no need to permissions to request it:

// Application-specific call that doesn't require external storage permissions
// Can be Environment.DIRECTORY_PICTURES, Environment.DIRECTORY_PODCASTS, Environment.DIRECTORY_RINGTONES, 
// Environment.DIRECTORY_NOTIFICATIONS, Environment.DIRECTORY_PICTURES, or Environment.MOVIES
File dir = MyActivity.this.getExternalFilesDir(Environment.DIRECTORY_PICTURES);

Managing Permissions using ADB

Permissions can also be managed on the command-line using adb with the following commands.

Show all Android permissions:

$ adb shell pm list permissions -d -g

Dumping app permission state:

$adb shell dumpsys package com.PackageName.enterprise

Granting and revoking runtime permissions:

$adb shell pm grant com.PackageName.enterprise some.permission.NAME
$adb shell pm revoke com.PackageName.enterprise android.permission.READ_CONTACTS

Installing an app with all permissions granted:

$adb install -g myAPP.apk

Managing Runtime Permissions with PermissionsDispatcher

As of API 23 (Marshmallow), the permission model for Android [[has changed significantly|Understanding-App-Permissions]]. Now, rather than all being setup at install-time, certain dangerous permissions must be checked and activated at runtime instead.

For a full breakdown of how runtime permissions work, check out the [[understanding app permissions|Understanding-App-Permissions#permission-updates-in-marshmallow]] guide. This guide is focused on the practical approach for managing runtime permissions, requesting access to a feature, and managing error cases where the permission is denied.

The easiest way to manage runtime permissions is by using third-party libraries. In this guide, we will be taking a look at the PermissionsDispatcher library. The library is 100% reflection-free and as such does not cause significant performance penalties.

Dangerous Permissions

First, we need to recognize the dangerous permissions that require us to request runtime permissions. This includes but is not limited to the following common permissions:

Name Description
Manifest.permission.READ_CALENDAR Read calendar events
Manifest.permission.WRITE_CALENDAR Write calendar events
Manifest.permission.CAMERA Access camera object
Manifest.permission.READ_CONTACTS Read phone contacts
Manifest.permission.WRITE_CONTACTS Write phone contacts
Manifest.permission.ACCESS_FINE_LOCATION Access precise location
Manifest.permission.ACCESS_COARSE_LOCATION Access general location
Manifest.permission.RECORD_AUDIO Record with microphone
Manifest.permission.CALL_PHONE Call using the dialer
Manifest.permission.READ_EXTERNAL_STORAGE Read external or SD
Manifest.permission.WRITE_EXTERNAL_STORAGE Write to external or SD

The full list of dangerous permissions contains all permissions that require runtime management. Please note that permissions in the normal permission group do not require run-time checks as [[outlined here|Understanding-App-Permissions#normal-permissions]].

Installation

Make sure to [[upgrade|Getting-Started-with-Gradle#upgrading-gradle]] to the latest Gradle version.

And on your app module in app/build.gradle:

dependencies {
  compile 'com.github.hotchemi:permissionsdispatcher:2.0.7'
  annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:2.0.7'
}

Be sure to use the latest version: Download available.

Usage

Suppose we wanted to record be able to call a number using the phone’s dialer. Since this is a dangerous permission, we need to ask the user for the permission at runtime with the Manifest.permission.CALL_PHONE permission. This requires us to do the following:

  1. Annotate the activity or fragment with @RuntimePermissions
  2. Annotate the method that requires the permission with @NeedsPermission
  3. Delegate the permissions events to a compiled helper class
  4. Invoke the helper class in order to trigger the action with permission request
  5. Optional: Annotate a method which explains the reasoning with a dialog
  6. Optional: Annotate a method which fires if the permission is denied
  7. Optional: Annotate a method which fires if the permission will never be asked for again.

First, we need to annotate the activity or fragment with @RuntimePermissions:

@RuntimePermissions
public class MainActivity extends AppCompatActivity {
  // ...
}

Next, we need to annotate the method that requires the permission with @NeedsPermission tag:

@RuntimePermissions
public class MainActivity extends AppCompatActivity {
    @NeedsPermission(Manifest.permission.CALL_PHONE)
    void callPhone() {
        // Trigger the calling of a number here
    }
}

After compiling the project, we need to delegate the permission events to the generated helper class ([Activity Name] + PermissionsDispatcher):

@RuntimePermissions
public class MainActivity extends AppCompatActivity {
    // ...
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        // NOTE: delegate the permission handling to generated method
        MainActivityPermissionsDispatcher.onRequestPermissionsResult(this, requestCode, grantResults);
    }
}

In the code base, we can trigger the call with the appropriate runtime permission checks using the generated methods suffixed with WithCheck on the helper class:

// NOTE: delegate the permission handling to generated method
MainActivityPermissionsDispatcher.callPhoneWithCheck(this);

This will invoke the callPhone method wrapped with the appropriate permission checks.

Permission Event Handling

We can also optionally configure the rationale dialog, handle the denial of a permission or manage when the user requests never to be asked again:

@RuntimePermissions
public class MainActivity extends AppCompatActivity {

    // ...

    // Annotate a method which explains why the permission/s is/are needed. 
    // It passes in a `PermissionRequest` object which can continue or abort the current permission
    @OnShowRationale(Manifest.permission.CALL_PHONE)
    void showRationaleForPhoneCall(PermissionRequest request) {
        new AlertDialog.Builder(this)
            .setMessage(R.string.permission_phone_rationale)
            .setPositiveButton(R.string.button_allow, (dialog, button) -> request.proceed())
            .setNegativeButton(R.string.button_deny, (dialog, button) -> request.cancel())
            .show();
    }

    // Annotate a method which is invoked if the user doesn't grant the permissions
    @OnPermissionDenied(Manifest.permission.CALL_PHONE)
    void showDeniedForPhoneCall() {
        Toast.makeText(this, R.string.permission_call_denied, Toast.LENGTH_SHORT).show();
    }

    // Annotates a method which is invoked if the user 
    // chose to have the device "never ask again" about a permission
    @OnNeverAskAgain(Manifest.permission.CALL_PHONE)
    void showNeverAskForPhoneCall() {
        Toast.makeText(this, R.string.permission_call_neverask, Toast.LENGTH_SHORT).show();
    }
}

With that we can easily handle all of our runtime permission needs.

Alternatives

In addition the the PermissionsDispatcher outlined above, there are many other popular permissions libraries with various APIs and alternate designs including the following:

A full list of permissions libraries can be found here.

Understanding the Android Application Class

The Application class in Android is the base class within an Android app that contains all other components such as activities and services. The Application class, or any subclass of the Application class, is instantiated before any other class when the process for your application/package is created.

This class is primarily used for initialization of global state before the first Activity is displayed. Note that custom Application objects should be used carefully and are often not needed at all.

Custom Application Classes

In many apps, there’s no need to work with an application class directly. However, there are a few acceptable uses of a custom application class:

  • Specialized tasks that need to run before the creation of your first activity
  • Global initialization that needs to be shared across all components (crash reporting, persistence)
  • Static methods for easy access to static immutable data such as a shared network client object

Note that you should never store mutable shared data inside the Application object since that data might disappear or become invalid at any time. Instead, store any mutable shared data using [[persistence strategies|Persisting-Data-to-the-Device]] such as files, SharedPreferences or SQLite.

Defining Your Application Class

If we do want a custom application class, we start by creating a new class which extends android.app.Application as follows:

import android.app.Application;

public class MyCustomApplication extends Application {
        // Called when the application is starting, before any other application objects have been created.
        // Overriding this method is totally optional!
	@Override
	public void onCreate() {
	    super.onCreate();
            // Required initialization logic here!
	}

        // Called by the system when the device configuration changes while your component is running.
        // Overriding this method is totally optional!
	@Override
	public void onConfigurationChanged(Configuration newConfig) {
	    super.onConfigurationChanged(newConfig);
	}

        // This is called when the overall system is running low on memory,
        // and would like actively running processes to tighten their belts.
        // Overriding this method is totally optional!
	@Override
	public void onLowMemory() {
	    super.onLowMemory();
	}
}

And specify the android:name property in the the <application> node in AndroidManifest.xml:

<application
   android:name=".MyCustomApplication"
   android:icon="@drawable/icon"
   android:label="@string/app_name"
   ...>

That’s all you should need to get started with your custom application.

Limitations and Warnings

There is always data and information that is needed in many places within your app. This might be a session token, the result of an expensive computation, etc. It might be tempting to use the application instance in order to avoid the overhead of passing objects between activities or keeping those in persistent storage.

However, you should never store mutable instance data inside the Application object because if you assume that your data will stay there, your application will inevitably crash at some point with a NullPointerException. The application object is not guaranteed to stay in memory forever, it will get killed. Contrary to popular belief, the app won’t be restarted from scratch. Android will create a new Application object and start the activity where the user was before to give the illusion that the application was never killed in the first place.

So how should we store shared application data? We should store shared data in one of the following ways:

  • Explicitly pass the data to the Activity through the intent.
  • Use one of the many ways to persist the data to disk.

Bottom Line: Storing data in the Application object is error-prone and can crash your app. Prefer storing your global data on disk if it is really needed later or explicitly pass to your activity in the intent’s extras.

Resources