Developing Android Apps

You can find the corresponding course at udacity.

Table of Contents

  1. Hard facts
  2. Basics
  3. Intent Framework
  4. Data Persistence in Android
  5. Settings or Preferences
  6. Activity Lifecycle
  7. Data Storage

Hard facts

Key Mobile challenges:

Android 1.0 launched in 2008.

Android OS structure from top to bottom:

  1. Application Layer
  2. Application Framework
  3. C/C++ Libs, Android Runtime
  4. Linux Kernel

App generation/deployment process:

Parto Karwat

Use responsive design for Android Apps, because your app will run on many different devices with different screen sizes. Provide at least small phone, large phone, medium tablet and large tablet design.

Have a mobile first policy. Mobile experience is the first consideration when building products. Most internet users come from mobile. Even children use mobiles not desktops to access the internet.

A good app should work like a good buttler: Giving you what you want before you even have to ask for it! NO Refresh-Button or Save-Button. Though using a Refresh-Button for debugging is allowed.

The master-detail flow is one of the most used Android App patterns.

The 4 types of components make off apps:

UI Thread needs 60 frames per second (FPS) < 17 ms computation time

Basics

Android Studio Hints

Press shift 2x to access the search everywhere.

Layouts

Layouts all extend from ViewGroup. All layouts are LayoutManagers (?).

Logging

The existing log levels are:

ListView

  1. Create visible items + 1 invisible above and underneath
  2. Create new items just in time and hold all created items (visible + invisible) in memory

⚠ The more items, the more memory!

RecyclerView

  1. Create visible items + 1 above and underneath
  2. New list item comes into view: Udapte data in recycle bin before destroying invisible item

++ Less memory overhead, less view management, smoother scrolling

RecyclerView LayoutManagers

Adapter

Knows how many list items are in the data set and how to build them. ListView asks the size of the data set and then asks what items to build.

Intent Framework

Intents are like envelopes:

Explicit Intent

new Intent(context, DetailActivity.class)

Often used in startActiviy(intent)

Implicit Intent

new Intent(Intent.ACTION_VIEW);

Possible actions to perform are VIEW, PICK, DIAL, MUSIC, CAMERA, …

Share Intent

Is the most used implicit intent.

Write a ShareActionProvider to make it work.

Share Intents will be addressed to anyone who can perform action SEND.

Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.addFlage(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET); 
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, string);

ShareActionProvider mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(menuItem);

if(mShareActionProvider != null) {
    mShareActionProvider.setShareIntent(shareIntent);
} else {
    Log.e("ShareActionProvider null?");
}

FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET prevents the activity we are sharing to run completely, when we press back, we come back to our app, not back in the shared app.

Broadcast Intents

Broadcast a message to many apps.

Use sendBroadcast()method to implement it.

Broadcast Receiver

Broadcast Receiver best use in:

Implementation:

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Contect c, Intent i) {
        //Handle receive
    }
}

Ways to register your BroadcastReceiver:

  1. in Manifest: triggered when app running and terminated
  2. in Code/dynamically in Activity: triggered only when app running
Manifest registration
<receiver
    android:names=".MyReceiver">
    <intent-filter>
    ...
    </intent-filter>
</reciver>

Example: GCM with Syncadapter

Dynamic registration
IntentFilter intentFilter = new IntentFilter("com.myapp.MEW_LIFEFORM");
registerReceiver(myReceiver, intentFilter);

Example: Headphones unplugged/plugged change while hearing music

Intent Filters

Define <intent-filter> for every Activity that should be launchable from an implicit intent. (In your manifest.xml)

<intent-filter>
    <action:name="android.intent.action.VIEW"/>
    <data android:scheme="geo"/>
</intent-filter>

Data Persistence in Android

Data Persistence is the act of saving some data to the phone.

Bundle

Temporary store. Only use it if the user is actively using your app = while the app is open. Example: onSaveInstanceState

The data is saved as a key-complexValue pair

SharedPreferences

Saves Key-primitiveValue-Pairs to a file on the Android Filesystem. The data is persisted unless you install the app or break the preference file.

SQLite Database

Organize more complicated text/numeric/boolean data.

Internal/External Storage

Save multimedia or large data files to the internal or external Storage. Internal Storage is on the phone. External storage can be an SD-Card or similar.

Server

Servers are for data that multiple phones will access. The data can be persisted also when deleting the app or using a different phone. An example for a cloud service is Firebase.

Settings or Preferences

Settings can always be added later. Less settings at the beginning are better.

Generally the flow goes like this:

  1. User edits and updates a preference.
  2. PreferenceChangeListener triggered for that preference.
  3. The new value is saved to the SharedPreference file.
  4. onSharedPreferenceChanged listeners are triggered.

Adding a setting is easier for the user than removing a setting. Removing a setting risk having a subset of users angry about the feature being taken away from them.

Should it be a setting?

Parto Karwat

PreferenceFragment

Remark: PreferenceActivity is depricated since Honeycomb in favor of the more flexible fragment version!

  1. Add dependency compile 'com.android.support:preference-v7:25.1.0'
  2. Create a SettingsFragment and extend it from PreferenceFragmentCompat.
  3. Create a new resource directory called xml and create preference resource files in it like for example pref_main.xml
  4. AddaddPreferencesFromResource(R.xml.pref_main); to the onCreatePreferences() method
  5. Add to your AppTheme <item name="preferenceTheme">@style/PreferenceThemeOverlay</item>

PreferenceScreen

Is the root of a Preference hierarchy. It is the container that holds a couple of Preferences like CheckBoxPreference, ListPreference, etc. or even PreferenceScreens.

Common Preferences

SharedPreferences

SharedPreferences describe the file where the preferences are stored. Store private primitve data in key-value pair with a SharedPreference.

Read your SharedPreferences file with PreferenceManager.getDefaultSharedPreferences(this)

SharedPreferences.Editor

Write your SharedPreferences with the Editor. SharedPreferences.Editor editor = sharedPreferences.edit(); editor.putBoolean(KEY, value); editor.apply();

onSharedPreferencesChangeListener

  1. Let the concerned Activity implement SharedPreferences.OnSharedPreferenceChangeListener
  2. Implement onSharedPreferenceChanged
  3. Register the listener in onCreate with sharedPreferences.registerOnSharedPreferenceChangeListener(this);
  4. Unregister the listener in onDestroy with PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this);

PreferenceChangeListener

Is triggered before a value is saved to the SharedPreferences file. Can prevent an invalid update to a preference.

  1. Implement the Preference.OnPreferenceChangeListener in your activity. implements Preference.OnPreferenceChangeListener
  2. Attach the listener to the preference to listen from in onCreatePreferences with Preference preference = findPreference(getString(R.string.pref_name_key)); preference.setOnPreferenceChangeListener(this);
  3. Implement onPreferenceChange()

Activity Lifecycle

Parto Karwat

⚠ Save your app state in Bundle instance within onSaveInstanceState(). Restore your app state in onCreate() if the Bundle is not null. Like this actions like rotating your device won’t affect the user experience.

Loaders

Create one in 3 steps:

  1. Create a Loader ID
  2. Fill-in the Loader Callbacks
  3. Init the Loader with the LoaderManager

AsyncTaskLoader

Use an AsyncTaskLoader for threads bound to an Activity rather than AsyncTask.

AsyncTaskLoader is a better choice for Activity-bound thread management, because it handles lifecycle changes correctly, delivering the result to the current active activity, preventing duplication of background threads, and helping to eliminate duplication of zombie activities.

The functions of AsyncTaskLoader:

Best practice

AsyncTask

4 steps:

execute(), onPreExecute(), onProgressUpdate() and onPostExecute() run on the main UI thread. onProgressUpdate() and doInBackground() run on a background thread.

Storing Data in SQLite

DB Helper Content Provider
Data Contract SQLite Database Content Provider Test
Database Test URIMatcher Test

The Database Test is a write-read test on the database.

Data Contract defines all tables with its columns.

DBHelper extends SQLiteOpenHelper. DBHelper makes the database with a version number and a database filename.

Manually increment version numbers each time you release an updated database with a new schema.

onCreate() creates the DB as SQL statement executed with .execSQL(..).

Implement onUpgrade() method called, when version number has changed. Use ALTER TABLE or DROP TABLE

Database operations (CRUD)

The following operations are available on any database and known by CRUD for

Content Provider

Content Provider makes your data accessible without needing to know how you stored it. So it makes it easy to switch out the datasource.

Widgets and Search need Content Provider. Ex.: GMail Widget, Play Store Search.

SyncAdapter (Get Data from your Server) and CursorLoader (Get data from your Database) need Content Provider too.

It also exists a SharedContentProvider.

The Content Provider Implementationsteps:

  1. Determine URIs
  2. Update Contract
  3. Fill out URIMatcher
  4. Implement Funktions

Determine URIs

CONTENT:// COM.EXAMPLE.ANDROID.SUNSHINE.APP/ WEATHER/ 64111
SCHEME AUTHORITY LOCATION QUERY
PACKAGE NAME DB TABLENAME is optional!

ContentObserver refreshes automatically with URI.

Update Contract

Add URIs to Contract and create corresponding methods. (??)

Fill out URIMatcher

Implement URIMatcher in ContentProvider class

Add the ContentProvider to the Manifest

<provider
    android:authorities=[PACKAGENAME]
    android:name=[CONTENT_PROVIDER_CLASS]
/>

Implement Funktions

Implementation order:

  1. onCreate()
  2. getType(Uri)
  3. query(..)
  4. insert(..), update(..), delete(..)
  5. optional: bulkInsert(..)

insert(..), update(..), delete(..) are the write operations

Content Resolver

Access the data via Content Provider with Content Resolver.

Use Content Provider for example in an async task:

Cursor myCursor = mContext.getContentResolver().query(..);