A collection of subtle but important details I noticed while reading the Android developer documentation.

FragmentTransaction: Using the replace Method

replace() removes all existing fragments in the container before adding the new one. Combined with addToBackStack(), the user can navigate back to the previous fragment.

1
2
3
4
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
transaction.commit();

Note: addToBackStack(null) pushes the entire transaction (not a single fragment) onto the back stack. Pressing back reverses all operations in the transaction.

Reference: FragmentTransaction API

BaseColumns in Database Contract Classes

Table contract inner classes should implement BaseColumns to automatically include the _ID and _COUNT constants.

1
2
3
4
5
6
7
8
/* Inner class that defines the table contents */
public static abstract class FeedEntry implements BaseColumns {
    public static final String TABLE_NAME = "entry";
    public static final String COLUMN_NAME_ENTRY_ID = "entryid";
    public static final String COLUMN_NAME_TITLE = "title";
    public static final String COLUMN_NAME_SUBTITLE = "subtitle";
    ...
}

Reference: BaseColumns API

Validating Implicit Intents Before Launching

Always verify that at least one activity can handle an implicit Intent, or the app will crash with ActivityNotFoundException.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
PackageManager packageManager = getPackageManager();
List activities = packageManager.queryIntentActivities(intent,
        PackageManager.MATCH_DEFAULT_ONLY);
boolean isIntentSafe = activities.size() > 0;

// Using a Chooser
Intent chooser = Intent.createChooser(intent, title);
if (intent.resolveActivity(getPackageManager()) != null) {
    startActivity(chooser);
}

Key differences:

  • queryIntentActivities() returns all matching activities
  • resolveActivity() returns the best matching activity (or Chooser)

Reference: Intents and Intent Filters

Exposing an Activity to Third-Party Apps

Declare intent-filters in the Manifest for other apps to call your Activity:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
<activity android:name="ShareActivity">
    <!-- Handle sending SMS -->
    <intent-filter>
        <action android:name="android.intent.action.SENDTO"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="sms" />
        <data android:scheme="smsto" />
    </intent-filter>
    <!-- Handle sharing text/images -->
    <intent-filter>
        <action android:name="android.intent.action.SEND"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="image/*"/>
        <data android:mimeType="text/plain"/>
    </intent-filter>
</activity>

Handle the incoming intent based on type:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Intent intent = getIntent();
    Uri data = intent.getData();

    if (intent.getType().indexOf("image/") != -1) {
        // Handle image data
    } else if (intent.getType().equals("text/plain")) {
        // Handle text
    }
}

Return results to the caller:

1
2
3
4
Intent result = new Intent("com.example.RESULT_ACTION",
        Uri.parse("content://result_uri"));
setResult(Activity.RESULT_OK, result);
finish();

Correct Handler Usage

If you create a Handler with new Handler() in an outer class, its Looper depends on the current thread. Without specifying a Looper, there’s no guarantee post(Runnable) runs on the main thread.

Recommended singleton pattern:

1
private static Handler INSTANCE = new Handler(Looper.getMainLooper());

Always use Looper.getMainLooper() to explicitly target the main thread.

Reference: Handler API

WebView Automatically Requests Favicon on Every Page Load

WebView sends these requests regardless of whether the page has a favicon (even from iframes):

1
2
3
"GET /favicon.ico HTTP/1.1" 404 183
"GET /apple-touch-icon-precomposed.png HTTP/1.1" 404 197
"GET /apple-touch-icon.png HTTP/1.1" 404 189

Workaround

Use a data URI to avoid unnecessary network requests:

1
<link rel="icon" href="data:;base64,iVBORw0KGgo=">

This works across Safari, Chrome, and Firefox.

Background: Browsers request favicons after page load by default. Android WebView, being based on Chromium, exhibits the same behavior.

References: