Android - ANR causing process to be killed instead of a Dialog

    Recently, i came across few devices which wouldn't display a ANR dialog ( to give user a chance to wait) instead would just terminate the application process with a dialog saying "Unfortunately, application has stopped". This is the same dialog display for fatal exceptions (both java and native). So why doesn't Android display the usual dialog for an ANR?

   How about a bad behavior application to trigger an ANR and test it out on an emulator based on stock AOSP, but in this case no dialog was displayed instead the application window would be closed and the logs would indicate that the process was killed after user request. Turns out, this is a feature where in dialogs aren't shown in certain configurations,

    /**
     * Decide based on the configuration whether we should shouw the ANR,
     * crash, etc dialogs.  The idea is that if there is no affordnace to
     * press the on-screen buttons, we shouldn't show the dialog.
     *
     * A thought: SystemUI might also want to get told about this, the Power
     * dialog / global actions also might want different behaviors.
     */
    private static final boolean shouldShowDialogs(Configuration config) {
        return !(config.keyboard == Configuration.KEYBOARD_NOKEYS
                && config.touchscreen == Configuration.TOUCHSCREEN_NOTOUCH);
    }

   Now the same application behaves differently in Oneplus one and Nexus 4 (running CM 11). As suspected, they just display a dialog indicating that the application has stopped unexpectedly. A look at the log indicates a forked framework change causing this,

11-28 10:37:53.277   803   855 I Process : Sending signal. PID: 19749 SIG: 3
11-28 10:37:53.277 19749 19754 I dalvikvm: threadid=3: reacting to signal 3
11-28 10:37:53.297 19749 19754 I dalvikvm: Wrote stack traces to '/data/anr/traces.txt'
11-28 10:37:56.457   803   855 I Process : Sending signal. PID: 19749 SIG: 6

11-28 10:37:56.577   222   222 I DEBUG   : *** *** *** *** *** *** *** *** *** *** *** *** *** ***
11-28 10:37:56.577   222   222 I DEBUG   : Build fingerprint: 'oneplus/bacon/A0001:4.4.4/KTU84Q/XNPH44S:user/release-keys'
11-28 10:37:56.577   222   222 I DEBUG   : Revision: '0'
11-28 10:37:56.577   222   222 I DEBUG   : pid: 19749, tid: 19749, name: anrapp1  >>>
11-28 10:37:56.577   222   222 I DEBUG   : signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------


   These custom ROMs similar to stock AOSP sends a SIGNAL_QUIT to the application process to generate its stack trace and update traces file. But then another signal SIBABRT is sent by the framework and this is what causes a signal with SI_TKILL code to be sent to the process and for it to be killed. This is probably being done to generate the process's native stack traces via tombstone.

11-28 10:37:56.457   803 19857 E ActivityManager: Error reading /data/anr/traces.txt
11-28 10:37:56.457   803 19857 E ActivityManager: java.io.FileNotFoundException: /data/anr/traces.txt: open failed: ENOENT (No such file or directory)
11-28 10:37:56.457   803 19857 E ActivityManager:       at libcore.io.IoBridge.open(IoBridge.java:409)
11-28 10:37:56.457   803 19857 E ActivityManager:       at java.io.FileInputStream.<init>(FileInputStream.java:78)
11-28 10:37:56.457   803 19857 E ActivityManager:       at android.os.FileUtils.readTextFile(FileUtils.java:236)
11-28 10:37:56.457   803 19857 E ActivityManager:       at com.android.server.am.ActivityManagerService$19.run(ActivityManagerService.java:10152)
11-28 10:37:56.457   803 19857 E ActivityManager: Caused by: libcore.io.ErrnoException: open failed: ENOENT (No such file or directory)
11-28 10:37:56.457   803 19857 E ActivityManager:       at libcore.io.Posix.open(Native Method)
11-28 10:37:56.457   803 19857 E ActivityManager:       at libcore.io.BlockGuardOs.open(BlockGuardOs.java:110)
11-28 10:37:56.457   803 19857 E ActivityManager:       at libcore.io.IoBridge.open(IoBridge.java:393)
11-28 10:37:56.457   803 19857 E ActivityManager:       ... 3 more


  Besides, the dropbox thread has a 100% failure when it tries to read the traces file. The generic traces file is probably being renamed to reflect the application name but the dropbox thread tries to read the generic file (traces.txt) and fails with FileNotFound error.

 Update: Turns out this isn't a change specific to CM instead is a change distributed by Qualcomm via Code Aurora. It looks like Google isn't using this patch and this is one of the few reasons why Nexus program should stay alive for the rest of the folks in Android ecosystem to use a benchmark at all times.

commit 47aa5dedc57dd6e5690811622ffcbfccecb8ecb6
Author: xxxxxxxx xxxxxxx <xxxxx@codeaurora.org>
Date:   Fri Jan 13 18:22:40 2012 -0800

    frameworks/base: creating sperate files for ANRs traces
    
    If an ANR comes just after the framework reboot, the traces for the
    framework reboot will get over-written with the ANR traces. So, We
    are missing the traces for the framework reboot which makes debugging
    the framework reboot difficult.
    
    Even for multiple ANRs in different apps, we will get the traces for
    the last ANR.
    
    Most of the time traces show that the call is stuck in some native call
    and we do not have any info about the native call stack becuase we do
    not collect the tombstones for teh ANR.
    
    This patch will create one trace file for each application in case of ANR
    and the filename would be traces_<app_name>.txt and the traces for
    framework reboot will be stored in traces.txt
    

    Change-Id: I5b78ce62b26f25d3dc14097e2988ba12c2d77826 


Android - Netflix like Swipe-able Banner (Spotlight)

   Netflix's Android application features a spotlight which has a swipe-able banner view recommending few movies. This could be implemented via a android's ViewPager API and open source Circular Page Indicator.



  The pager adapter returns the set of ImageViews and ViewPager invokes adapter APIs based on the user swipe action. A simple adapter looks like,

private static class BannerAdapter extends PagerAdapter {

        private Map<Integer, ImageView> mBannerImageMap = new HashMap<Integer, ImageView>();

        public BannerAdapter(Context context) {
            ImageView image1 = new ImageView(context);
            image1.setImageResource(R.drawable.beach);

            ImageView image2 = new ImageView(context);
            image2.setImageResource(R.drawable.northstar);

            ImageView image3 = new ImageView(context);
            image3.setImageResource(R.drawable.lake_tahoe);
            
            mBannerImageMap.put(new Integer(0), image1);
            mBannerImageMap.put(new Integer(1), image2);
            mBannerImageMap.put(new Integer(2), image3);
        }

        @Override
        public int getCount() {
            return 3;
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ImageView imageView = mBannerImageMap.get(new Integer(position));
            container.addView(imageView);
            return imageView;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            ImageView imageView = mBannerImageMap.get(new Integer(position));
            container.removeView(imageView);
        }
    }


    private void setBannerView() {
        ViewPager pager = (ViewPager) findViewById(R.id.view_pager);
        pager.setAdapter(new BannerAdapter(this));
    }

  Applications having many set of banner views or each with multiple Images could implement their own caching logic in the PagerAdapter.

Android - Musixmatch's floating lyrics for Spotify

   Android developer's blog recently had a post about Musixmatch and Android's unique feature (Intents) being used effectively by the app.



  It looks like Musixmatch finds the track info and then displays the lyrics in a floating dialog window via SYSTEM_ALERT_WINDOW. So what kind of intent is musixmatch listening to? Turns out, Spotify has a feature where in an intent is broadcast as soon as Playback is started. This can be turned off by the user via Spotify's native app settings "Device Broadcast Status". musixmatch just registers for this intent and acts accordingly.

    BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String artist = intent.getStringExtra("artist");
            String album = intent.getStringExtra("album");
            String track = intent.getStringExtra("track");

            Toast.makeText(MyActivity.this, "Artist [" + artist + "], Album [" + album + "], Track [" + track + "]", Toast.LENGTH_SHORT).show();
        }
    };

    private void registerSpotifyBroadcastIntents() {
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.spotify.music.metadatachanged");
        this.registerReceiver(mReceiver, filter);
    }

This was tested in Spotify version 1.7.0.830 and could potentially change in a future version.

Android - Identify source of activity creation

     Few applications might want to know the use case in response to which its activity is created to handle internal state etc. Generally, activity is created when it is launched for the first time or in response to a configuration change ( screen orientation, keyboard, language change etc) or when the user resumes the activity from background after a low memory kill. In case of a low memory kill, when the user resumes the activity from the recent apps list, Android creates a new process and starts a new instance of the activity with the previously saved instance state. So how do we identify these three different scenarios? Thanks to an activity API isChangingConfigurations, the logic is quite simple.

    private final static String CONFIG_CHANGED = "ConfigurationChanged";

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBoolean(CONFIG_CHANGED, isChangingConfigurations());
    }

    private void detectActivityCreationSource(Bundle savedInstanceState) {
        String toastMsg;
        if ( savedInstanceState == null ) {
            toastMsg = "Activity created for first time";
        } else {
            if ( savedInstanceState.getBoolean(CONFIG_CHANGED) ) {
                toastMsg = "Activity created in response to configuration change";
            } else {
                toastMsg = "Activity created after low memory kill";
            }
        }
        Toast.makeText(this, toastMsg, Toast.LENGTH_SHORT).show();
    }

    isChangingConfigurations() would return true ONLY in old activity instance. Hence, its safe to assume that it would always return false once the activity is resumed (before onSaveInstanceState is called once again after a configuration change).

Android - Shortcut to expand and collapse Notification Panel


     Heads up, this post is meant for platform developers and not application developers. One of the annoying thing about Android and other operation systems is having to reach out to expand and collapse the notification bar. This wasn't a problem with devices less than 4.7 inch dimensions. However, this became really annoying with Nexus 6 and its not possible to use one hand to expand the notification bar anymore (not even with large ones).

    This could easily be resolved by providing a short cut in the navigation bar which can be handled one hand at all times. Wonder why Google doesn't implement something similar in stock AOSP.
Anyways, with the shortcut the usage looks something similar to this,


     In this case, the recent apps button is hijacked to illustrate but the actual functionality could very well be provided with an additional button in the Navigation Bar.

--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -741,10 +741,19 @@ public class PhoneStatusBar extends BaseStatusBar implements DemoMode {
         return mNaturalBarHeight;
     }

+    private boolean mNotificationExpanded = false;
+
     private View.OnClickListener mRecentsClickListener = new View.OnClickListener() {
         public void onClick(View v) {
+            if ( mNotificationExpanded ) {
+                PhoneStatusBar.this.animateCollapsePanels();
+            } else {
+                PhoneStatusBar.this.animateExpandNotificationsPanel();
+            }
+
+            mNotificationExpanded = !mNotificationExpanded;
         }
     };

   Note that StatusBarManager has APIs to expand and collapse panels (expandNotificationsPanel and collapsePanels) . However, the entire class is hidden from the SDK. Any attempt to use these APIs via reflection is going to fail as StatusBarManager Service enforces permission checks and that can't be bypassed.

Android - Enable Navigation Bar in emulator

     Ever landed up with an emulator without Navigation bar and needed it to be enabled quickly?
Well, start the emulator as usual and once its ready ensure that the system partition is mounted for write access.

       adb shell mount -o rw,remount /system

  Next, pull the build.prop from /system and edit it to include this property and push it back to /system

      qemu.hw.mainkeys=0

Finally, reboot the framework process,

     adb shell ps system_server
     adb shell kill <pid>

And voila, we have the navigation bar enabled.

Android - Spinner Progress in Notifications

  Thanks to RemoteViews's setViewVisibility, its trivial to show Spinner progress bar in the Notification. Here is an application suggesting new books to download via a background service.


    A good feedback would be to show a progress spinner as the book is being downloaded. Here is the notification layout to achieve the same,

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/preview"
        android:layout_width="128dp"
        android:layout_height="128dp"/>

    <FrameLayout
        android:paddingLeft="40dp"
        android:layout_gravity="center"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    
        <ImageButton
            android:id="@+id/download_button"
            android:background="@android:color/transparent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/images2" />

        <ProgressBar
            android:id="@+id/progress_bar"
            android:clickable="false"
            android:visibility="gone"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </FrameLayout>

</LinearLayout>

    The ProgressBar's default visibility is set to hidden and is updated only when the intent is fired as the user clicks on the download button.

        Notification.Builder mBuilder = new Notification.Builder(this)
                .setContentTitle("Notification")
                .setContentText("Progress Notification")
                .setSmallIcon(R.drawable.ic_launcher);

    private void updateNotification(boolean showProgress) {

        RemoteViews remoteView = new RemoteViews(getPackageName(), R.layout.service_notification);
        remoteView.setImageViewResource(R.id.preview, R.drawable.gameofthrones);

        if ( showProgress ) {

            remoteView.setViewVisibility(R.id.download_button, View.GONE);
            remoteView.setViewVisibility(R.id.progress_bar, View.VISIBLE);

        } else {

            remoteView.setViewVisibility(R.id.download_button, View.VISIBLE);
            remoteView.setViewVisibility(R.id.progress_bar, View.GONE);

        }

        Notification notification = mBuilder.build();
        notification.contentView = remoteView;
        notification.bigContentView = remoteView;

        NotificationManager manager;
        manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        manager.notify(NOTIFICATION_ID, notification);

    }

    Now as and when the book is being downloaded, the service can update the Notification via updateNotification() and we have a spinner.


Android - Orange WiFi Icon in Quick Settings

     Android's Quick Settings occasionally shows an orange WiFi icon of varying strengths and this kind of indicates connectivity issues even though the device is connected to a wireless network. So who is responsible for updating this and what is the trigger entry point?

   The actual drawable resources used is specific to the SystemUI application provided as,

              R.drawable.ic_qs_wifi_0,
              R.drawable.ic_qs_wifi_1,
              R.drawable.ic_qs_wifi_2,
              R.drawable.ic_qs_wifi_3,
              R.drawable.ic_qs_wifi_4,
              R.drawable.ic_qs_wifi_0,
              R.drawable.ic_qs_wifi_full_1,
              R.drawable.ic_qs_wifi_full_2,
              R.drawable.ic_qs_wifi_full_3,
              R.drawable.ic_qs_wifi_full_4

   An appropriate drawable resource is picked up based on the reported connectivity status via broadcast intent ConnectivityManager.INET_CONDITION_ACTION. This intent is supposed to be fired with an integer extra (ConnectivityManager.EXTRA_INET_CONDITION) indicating the status. Connectivity is considered to be good if the reported status is more than 50, otherwise the WiFi icon turns orange and a corresponding drawable resource based on the signal strength is picked up.

    So who fires this Intent and does it need any permission? The intent action INET_CONDITION_ACTION is declared as a protected broadcast and is fired by the framework's ConnectivityService when the status is reported.

<protected-broadcast android:name="android.net.conn.INET_CONDITION_ACTION" />

  This status change is reported by a ConnectivityManager API, reportInetCondition which is hidden from the SDK and any update is actually validated by the framework by enforcing a permission check. The client for this status update should have android.Manifest.permission.STATUS_BAR permission in its manifest.

   But whats the point of having a hidden API and how can it possibly be executed? Thanks to a Null Pointer Exception, it turns out that few google applications actually invoke this API via reflection,

E/GTalkService( 1291): calling reportInetCondition failed
E/GTalkService( 1291): java.lang.reflect.InvocationTargetException
E/GTalkService( 1291):  at java.lang.reflect.Method.invokeNative(Native Method)
E/GTalkService( 1291):  at java.lang.reflect.Method.invoke(Method.java:507)
E/GTalkService( 1291):  at com.google.android.gsf.gtalkservice.service.GTalkService.reportInetCondition(GTalkService.java:329)
E/GTalkService( 1291):  at com.google.android.gsf.gtalkservice.GTalkConnection.doConnect(GTalkConnection.java:2004)
E/GTalkService( 1291):  at com.google.android.gsf.gtalkservice.GTalkConnection.access$300(GTalkConnection.java:110)
E/GTalkService( 1291):  at com.google.android.gsf.gtalkservice.GTalkConnection$WorkerHandler.handleMessage(GTalkConnection.java:278)
E/GTalkService( 1291):  at android.os.Handler.dispatchMessage(Handler.java:99)
E/GTalkService( 1291):  at android.os.Looper.loop(Looper.java:134)
E/GTalkService( 1291):  at com.google.android.gsf.gtalkservice.service.GTalkService$WorkerThread.run(GTalkService.java:244)

  So how about a test application using the same reflection approach?

    private void reportINetCondition() {
        ConnectivityManager manager = (ConnectivityManager) this.getSystemService(CONNECTIVITY_SERVICE);

        Class<?> c = manager.getClass();
        Method[] allMethods = c.getDeclaredMethods();

        try {

            for (Method method : allMethods) {
                if (method.getName().startsWith("reportInetCondition")) {
                    method.invoke(manager, ConnectivityManager.TYPE_WIFI, 25);
                    break;
                }
            }

        } catch (Exception e ) {
        }
    }

    Unfortunately, this fails with a Security Exception,

Caused by: java.lang.SecurityException: ConnectivityService: Neither user 10086 nor current process has android.permission.STATUS_BAR.

   The permission STATUS_BAR is meant only for system applications and indeed quite a few google applications are installed as system applications and they get to update the connectivity status.

updated-package name="com.google.android.googlequicksearchbox" codePath="/system/priv-app/googlequicksearchbox.apk"
updated-package name="com.android.vending" codePath="/system/priv-app/Phonesky.apk"

 Lets try pushing our application to the system partition on a rooted device.

adb root
adb shell mount -o rw, remount /system
adb push test.apk /system/app

  Unfortunately, this too doesn't work as the permission STATUS_BAR is granted only to those system applications which are signed by the platform signature. But yes, once the test application is signed with an appropriate signature, we would be able to update the connectivity status with ease.

Android - Handling errors from Loaders

      Android's Loader API is generalized just to handle results from the background tasks.

                             android.content.AsyncTaskLoader<D>

 This is fine in most cases as the primary reason to use Loader is to do some operation in background and get the results. However, this by itself is not good enough as background tasks like network operations usually come with strings attached and is associated with multiple error scenarios like network timeout, lost connection, unreachable destination. Besides, the application layer protocol might have other custom errors too. So how to deal with this possibility and update UI accordingly?

  Here is a Loader which would fail randomly,

    private static class FailSometimeLoader extends AsyncTaskLoader<List> {

        int mErrorCode;
        List<String> mData;

        public FailSometimeLoader(Context context) {
            super( context );
        }

        @Override
        protected void onStartLoading() {
            mErrorCode = 0;            
            forceLoad();
        }

        @Override
        public List loadInBackground() {
            mData = new ArrayList<String>();

            if ( System.currentTimeMillis()% 2 == 0 ) {
                mErrorCode = 0;
                mData.add("Call Of Duty : Modern Warfare");
                mData.add("Madden 15");
                mData.add("NHL 15");
                mData.add("Madden 25");
            }
            else {
                mErrorCode = -1;
            }

            return mData;
        }

        public int getErrorCode() {
            return mErrorCode;
        }
    }


   The activity implementing the LoaderManager.LoaderCallbacks would get a reference to the loader in the onLoadFinished() callback. This generic loader reference can be typecasted to the specific concrete FailSometimeLoader and getErrorCode() can be invoked to check for the error status.

    @Override
    public void onLoadFinished(Loader<List> listLoader, List list) {

        FailSometimeLoader loader = ( FailSometimeLoader ) listLoader;

        int errorCode = loader.getErrorCode();

        if ( errorCode == 0 ) {
            ListView listView = (ListView) findViewById( R.id.sample_list );
            listView.setAdapter( new ArrayAdapter<String>( this, android.R.layout.simple_list_item_1, list ));
        }
        else {
            Toast.makeText( this, "Loading in background failed with [" + errorCode + "]", Toast.LENGTH_SHORT).show();
        }
    }

Android - Check Dialog's Visibility

    Android's Dialog provides helper isShowing() to determine if the dialog is shown or not, but it doesn't necessarily mean that the Dialog window is shown. The dialog built using the activity's context is requested to be shown via show(). This API involves a communication between multiple processes starting from the application process's Dialog ViewRootImpl and Framework's Window Manager Service and Surface Flinger and there is a tiny delay between when the original request and the actual window being rendered. Eventually, a new Window is created and tracked by framework. This new window would now host the requested dialog. This entire process involves a set of IPC between the application and framework.

   However, any attempt to check the status via isShowing() after show() is going to return true.

        AlertDialog dialog = builder.create();
        dialog.show();
        Log.d(TAG,  "Dialog Status [" + dialog.isShowing() + "]");

This doesn't really make any sense especially as the UI thread requesting for the dialog is still executing and isn't finished. So is there any other reliable way to ensure or check if the Dialog is actually shown?

  As the dialog is shown in a new window, the window corresponding to the activity requesting for the dialog should actually lose focus and the new dialog window should gain focus.

getWindow().getDecorView().getViewTreeObserver().addOnWindowFocusChangeListener(new ViewTreeObserver.OnWindowFocusChangeListener() {
            @Override
            public void onWindowFocusChanged(boolean hasFocus) {

                Log.d(TAG, "Activity window focus [" + hasFocus + "]");

            }
        });

    Requesting for the window focus change notifications serves as a good point to determine the actual showing status of the Dialog. Why does this matter? Ideally, for an end user the delay between requesting a dialog and the actual dialog being shown might not be noticeable. However, test frameworks could actually exploit this tiny window and could actually trigger click events in the Activity.

Android - Background tasks across screen orientation (AsyncTaskLoaders)

     One of the annoying tasks for new developers is to deal with the use case where in the activity is destroyed and re created in response to a configuration change like changing orientation from portrait to landscape or vice versa, changing device language, adding a keyboard accessory etc. Handling background tasks in this scenario makes it difficult as most of the ASyncTask implementations hold an explicit reference to the activity or the fragment. This hard dependency could be solved by a broadcast mechanism but then there is a potential race where in broadcast receiver might not be registered as the new instance of the activity might be in the process of recreation and the background task could have fired of the intent to notify the results. This could be solved by using the application context instead of using the activity context to register a broadcast receiver. This would need more work like relaying this event to an appropriate activity and could be complex in an application having multiple activities.

   Android's SDK offers AsyncTaskLoader and LoaderManager to ease this pain for application developers. But how exactly did it solve the underlying problem that application developers couldn't solve all the while or found it difficult? These APIs depend on the framework's ability to detect this scenario where in the activity is relaunched in response to an configuration change.

    Each application process has a data structure to keep track of the activity info, ActivityClientRecord. As and when the configuration changes, the desired activity client record is fetched and processed. One of the important updates is to a variable mChangingConfigurations in the Activity. This is a package private variable and access is restricted to the framework classes. Note that this is set to true in the instance of the activity that is going to be destroyed. So why bother doing this on a instance that is to be destroyed?

        r.activity.mChangingConfigurations = true;

  This serves as hint for the activity to save its loaders. It invokes doRetain on the current LoaderManager, if any. From this point on the activity is paused, its state is saved, and is destroyed.

        performPauseActivity(r.token, false, r.isPreHoneycomb());
       mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
       handleDestroyActivity(r.token, false, configChanges, true);

   As the activity is destroyed, the non configuration instances are saved.

     r.lastNonConfigurationInstances  = r.activity.retainNonConfigurationInstances();

The retainNonConfigurationInstances() has the logic to save existing loader managers.

    NonConfigurationInstances retainNonConfigurationInstances() {
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        ArrayList<Fragment> fragments = mFragments.retainNonConfig();
        boolean retainLoaders = false;
        if (mAllLoaderManagers != null) {
            // prune out any loader managers that were already stopped and so
            // have nothing useful to retain.
            final int N = mAllLoaderManagers.size();
            LoaderManagerImpl loaders[] = new LoaderManagerImpl[N];
            for (int i=N-1; i>=0; i--) {
                loaders[i] = mAllLoaderManagers.valueAt(i);
            }
            for (int i=0; i<N; i++) {
                LoaderManagerImpl lm = loaders[i];
                if (lm.mRetaining) {
                    retainLoaders = true;
                } else {
                    lm.doDestroy();
                    mAllLoaderManagers.remove(lm.mWho);
                }
            }
        }
        if (activity == null && children == null && fragments == null && !retainLoaders) {
            return null;
        }
        
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.activity = activity;
        nci.children = children;
        nci.fragments = fragments;
        nci.loaders = mAllLoaderManagers;
        return nci;
    }

   This non configuration instance is passed on to the new activity instance and its mAllLoaderManagers is restored to tracks existing Loaders. Activities typical usage of LoaderManager is something like this and it registers for a callback as and when the loader task is finished.

     getLoaderManager().initLoader(0, null, this);

getLoaderManager() is defined in the base Activity class as,

    LoaderManagerImpl getLoaderManager(String who, boolean started, boolean create) {
        if (mAllLoaderManagers == null) {
            mAllLoaderManagers = new ArrayMap<String, LoaderManagerImpl>();
        }
        LoaderManagerImpl lm = mAllLoaderManagers.get(who);
        if (lm == null) {
            if (create) {
                lm = new LoaderManagerImpl(who, this, started);
                mAllLoaderManagers.put(who, lm);
            }
        } else {
            lm.updateActivity(this);
        }
        return lm;
    }

   The LoaderManager has a reference to the activity and the callback provided by the application developer. This callback could too be an activity if the developer choose the activity to implement LoaderManager.LoaderCallbacks. So if the loader manager has a reference to an old instance of the activity, how can it possibly be reused? What is the point of saving and restoring? Wouldn't holding a reference to the old activity cause a memory leak?

   This is handled by passing on the responsibility to the application developers by the expected usage of LoaderManager API. As and when the new activity is created and it is expected to initialize loader once again. This call to initLoader() in the new activity instance provides the old loader manager with reference to the new callback and the implementation of getLoaderManager() ensures that the reference of the new activity is set on old Loader Managers via lm.updateActivity(this). From this point on as and when the Loader is finished, it would invoke the desired callback. It also handles the potential race where in the ASyncLoaderTask might complete before the new activity instance initializes the loader again. This works because Loader is not just a task executor, it also manages the data, listens for changes in underlying source etc. This is how Loaders work across configuration changes.

   But the important take away is for applications developers to call getLoaderManager() and not try to book keep it in a singleton helper or in the Application class.
   

Android - Styling Action Bar at run time

    Android's style framework helps change the look and feel of UI elements without having to change the source code and invoking View specific APIs at run time. One would assume that this is true for all UI elements. Unfortunately, the Action Bar's Theme can't be changed at run time via applying a different theme. Here is a test code to demonstrate this,

  Application has two themes,

    <style name="AppTheme2" parent="android:Theme.Holo.Light.DarkActionBar">
        <item name="@android:attr/actionBarStyle">@style/MyActionBarStyle2</item>
    </style>

    <style name="AppTheme1" parent="@android:style/Theme.Holo.Light">
        <item name="@android:attr/actionBarStyle">@style/MyActionBarStyle1</item>
    </style>

Activity's is started with AppTheme1 and changes the theme at runtime based on certain events,

        <activity
            android:name=".SampleActivity"
            android:theme="@style/AppTheme1"
            android:label="@string/app_name" >
        </activity>

The following code resolves the reference attribute actionBarStyle in theme obtained from a view's and action bar's context.

            TypedValue resolvedValue;
            setContentView( R.layout.activity_sample );
            View view = findViewById( R.id.sample_text );
            Resources.Theme theme = view.getContext().getTheme();

            resolvedValue = new TypedValue();
            theme.resolveAttribute( android.R.attr.actionBarStyle, resolvedValue, true );
            int actualResourceId = resolvedValue.resourceId;

            Log.v( TAG, " Resource via View Context [" + actualResourceId + "]");

            theme = getActionBar().getThemedContext().getTheme();
            resolvedValue = new TypedValue();
            theme.resolveAttribute( android.R.attr.actionBarStyle, resolvedValue, true );
            actualResourceId = resolvedValue.resourceId;

            Log.v( TAG, " Resource via Action Bar Context [" + actualResourceId + "]");

Now, the activity changes the theme at run time and sets the content view in order to ensure that the views are inflated again using the new theme. The question is as to what happens to the action bar.

            setTheme( R.style.AppTheme2 );
            setContentView(R.layout.activity_sample);
            view = findViewById( R.id.sample_text );

            resolvedValue = new TypedValue();
            theme.resolveAttribute( android.R.attr.actionBarStyle, resolvedValue, true );
            actualResourceId = resolvedValue.resourceId;

            Log.v( TAG, " Resource via View Context for new Theme [" + actualResourceId + "]");

            theme = getActionBar().getThemedContext().getTheme();
            resolvedValue = new TypedValue();
            theme.resolveAttribute( android.R.attr.actionBarStyle, resolvedValue, true );
            actualResourceId = resolvedValue.resourceId;

            Log.v( TAG, " Resource via Action Bar Context for new Theme[" + actualResourceId + "]");

  Upon execution, the resourceId obtained from the view's context before and after changing the theme is different. However, this is not the case of the Action Bar's context. This is why Action Bar's look and feel doesn't change with run time theme change.

    The reasoning is due to the internal implementation of Action Bar initialization. Lets start from setContentView().

    public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID);
        initActionBar();
    }

   The implementation seems to suggest that entire view hierarchy is initialize again. However, this is not the case. initActionBar() works only for the first initialization of the Action Bar. It works for cases like screen orientation where in the activity is destroyed and recreated again.

   Let's say, we bypass this validation via platform change and force initialization of ActionBar every time setContentView() is invoked. Would it then work? Well, this too wouldn't solve the problem. Its because the ActionBar is initialized using a specific context whose theme doesn't change even if setTheme() is invoked.

    public ActionBarImpl(Activity activity) {
        mActivity = activity;

        Window window = activity.getWindow();
        View decor = window.getDecorView();
        init(decor);

        if (!overlayMode) {
            mContentView = decor.findViewById(android.R.id.content);
        }
    }

   The context used is that of the decor view which is initialized only once during the activity creation and its theme doesn't change at all.

    So the only way to change the style of the ActionBar is to invoke APIs at run time like setBackgroundDrawable() etc. If not, consider using the new ToolBar as that claims support of moving the control to application's layout.

Android - Fragment specific theme

   Few applications might want to apply specific theme for Fragments and this could be easily achieved as long as the Fragment views are recreated when moving across the back stack. This is the case when Fragments are replaced rather than being added ( FragmentTransaction.replace ). The views corresponding to a replaced fragment in back stack would need to created again and added as child of the fragment container. So all that is needed is a layout inflater based on the desired theme and the quick trick is to set the theme before committing the fragment transaction.


    private void next(int nextThemeResId, Fragment fragment, String tag, int containerViewId) {
        setTheme( nextThemeResId );
        FragmentTransaction transaction = getFragmentManager().beginTransaction();
        transaction.replace(containerViewId, fragment, tag);
        transaction.addToBackStack(null);
        transaction.commit();
    }

    private void previous(int prevThemeResId)
    {
        setTheme( prevThemeResId );
        getFragmentManager().popBackStack();
    }


  The documentation of setTheme() could be misleading. It simply states that it should be invoked before any views are instantiated in the Context (for example before calling setContentView(View) or inflate(int, ViewGroup)). It doesn't clarify as to what happens when the setContentView(View) is invoked again. However, in case of Fragment activities setContentView() is often invoked to create the fragment container view and yet changing the theme at run time works.

   The new theme is effective as soon as it is set and it doesn't reinflate existing views created via setContentView() and the documentation is to warn about this scenario. However, any new views created using the activity context (which now uses the new theme) would be based on the new theme. This helps with Fragments as Fragment's views are created on demand via onCreateView() callback and solves the purpose of Fragment specific theme. The only catch being developers should set appropriate theme as and when the Fragment is added and removed from the back stack.

Android - Back Stack Fragment state loss after multiple screen orientation (onCreate vs onViewStateRestored)

     This post is applicable to activities with following criteria,

* Not handling screen orientation (android:configChanges="orientation")
* Hosting Fragments in Back stack via replace, with each Fragment responsible to load data over network/database and displaying in respective UI elements.
* Fragments saving their state in onSaveInstanceState and restoring it via onViewStateRestored

    The application having such activities usually move from one Fragment to the other based on user action like in this case,


    Both Fragments download their data set over network and load the data set into views. When the user in the second Fragment (Dance), the back stack has two fragments and they are managed by the Fragment Manager. Now, as and when the screen orientation changes, 

  ->  Fragment Manager invokes onSaveInstanceState() on both the fragments.
  -> Activity and both Fragments are destroyed
  -> New instance of the Activity is created
  -> Back stack is recreated with new instances of the Fragments
  -> Fragment Manager invokes onViewStateRestored on newly created focus Fragment (Dance)

So far so good, now when the user presses back key in the new orientation,

 -> Fragment Manager invokes onViewStateRestored on newly created Fragment (Explore)

So everything seems to be fine and the application logic works. But what happens when the device's orientation is changed multiple times (more than once).

  -> First orientation
  ->  Fragment Manager invokes onSaveInstanceState() on both the fragments.
  -> Activity and both Fragments are destroyed
  -> New instance of the Activity is created
  -> Back stack is recreated with new instances of the Fragments
  -> Fragment Manager invokes onViewStateRestored on newly created focus Fragment (Dance). Note that the fragments in back stack (Explore) are just created and their state isn't yet restored. Their members would basically be null at this point.
  -> Active fragment (Dance) seems to be fine.
  -> Second orientation
  ->  Fragment Manager again invokes onSaveInstanceState() on both the fragments. However, now the state of the fragments in the back stack is worthless as their state was never restored.
  -> Activity and both Fragments are destroyed
  -> New instance of the Activity is created
  -> Back stack is recreated with new instances of the Fragments
  -> Active fragment (Dance) seems to be fine.
  -> User goes back to previous Fragment
  -> Fragment Manager invokes onViewStateRestored on newly created Fragment (Explore) with a bundle having no relevant data.
  -> Boom !!! onViewStateRestored doesn't have any valid data set to restore and it basically fails.

   Application developers would assume that their logic of saving and restoring state should either fail or work at all times. A selective failure doesn't really help to write generic code. So whats happening in the Framework?

   Turns out, Fragment Manager always tries to save state of all Fragments, irrespective of the state of the Fragment in the back stack. Why try saving an inactive Fragment in the back stack? Wouldn't its last known state be the most important one and be good enough? For now, there is a proposed fix to AOSP https://android-review.googlesource.com/#/c/112492/ and is awaiting feedback.

    Meanwhile, application developers can use onCreate() callback to restore state in the fragments. Fragment Manager at least invokes onCreate() for all fragments in the back stack. The bundle passed in onCreate is more reliable than the one passed in onViewStateRestored. This ensures each new instance of the Fragment having valid data set at all times. Nevertheless, this just helps manage data set state of the Fragment and not of the views which is managed in the base class via onViewStateRestored.