Why Activity.onDestroy() and Activity.onRestoreInstanceState() are not always invoked

First, the facts:

  1. Activity.onStop() is not always followed by Activity.onDestroy() since Android can decide to kill your application just after invoking Activity.onStop()
  2. Activity.onSaveInstanceState() is not always followed by Activity.onRestoreInstanceState()

1. When is Activity.onDestroy() invoked ?

First, check the Android activity lifecycle: as you can see, Android can kill your application without invoking onDestroy() if “an app with higher priority needs memory“.

In fact, the javadoc for onDestroy() also states that “There are situations where the system will simply kill the activity’s hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away“.

I wrote a simple application containing only one (main) activity and used a couple of scenarios to have a better understanding of when onDestroy() was called:

Scenario #1-1: HOME key is hit while the activity is running

  • Android invokes onSaveInstanceState() -> onPause() -> onStop()
  • You are now back on the HOME screen
  • When the application is relaunched, Android invokes onCreate() -> onStart() -> onResume()

Note: onCreate() may actually not be invoked when the application is started again because it’s up to the Android OS to decide whether or not to kill your activity just after invoking onStop(). So if onCreate() is not invoked, this simply means that your activity was still in the background in a “stopped” state.

Scenario #1-2: BACK key is hit while the activity is running

  • Android invokes onPause() -> onStop() -> onDestroy()
  • You go back to the screen you were before launching the application
  • When the application is relaunched, Android invokes onCreate() -> onStart() -> onResume()

Note: this time, onCreate() is always invoked because hitting the BACK key destroys your activity.

Scenario #1-3: screen orientation changes while the activity is running (CTRL-F12 in the Android emulator)

  • Android invokes onSaveInstanceState() -> onPause() -> onStop() -> onDestroy()
  • It then invokes: onCreate() -> onStart() -> onRestoreInstanceState() -> onResume()

Scenario #1-4: another application is launched while the activity is running (click on the phone button in the Android emulator)

  • Android invokes onSaveInstanceState() -> onPause() -> onStop()
  • If you now hit BACK to come back to your application: onStart() -> onResume()

 

2. When is Activity.onRestoreInstanceState() invoked ?

If you check again the various scenarios above, you will see that onSaveInstanceState() is indeed not always followed by onRestoreInstanceState().

Why ?

Because there are only 2 scenarios where onSaveInstanceState() is followed by onRestoreInstanceState():

  1. The screen orientation changes (see scenario #1-3)
  2. Another activity gets the focus and then Android decides to kill the previous one (possibly because the device is low on resources)

To test#2 using the Android emulator, you need to do this:

  • Launch your application so that your activity is running and has the focus
  • Launch another application (for ex: click on the phone button in the Android emulator to launch the dialer application)
  • Launch an ADB shell and use the “ps” command to kill your application
  • From the dialer application, hit the BACK button
  • Android will invoke onCreate() -> onStart() -> onRestoreInstanceState -> onResume()

 

Laurent KUBASKI

Advertisements

Running Android unit tests in Intellij

It turns out that running Android unit tests in Intellij is not as easy as creating a unit test in the [ANDROID_MODULE_HOME]/src folder.

You instead need to create a new “Android test module” inside your main module and run your tests from there:

If you choose the default options, your new test module (“myAndroidTestProject”) will be created inside the main module (“myAndroidProject”):

This test module has a dependency on the main module:

And the nice thing is that Intellij automatically creates an Android unit test configuration that automatically executes all the unit tests in this new test module:

From there, you can create your unit tests in this new module. Here is a simple one:


public class MyAndroidTestCase extends AndroidTestCase {

public void test1() throws IOException, JSONException {
 Log.d(this.getClass().toString(), (">> test1"));
 Context ctx = getContext();
 FileOutputStream fos = ctx.openFileOutput("test.dat", Context.MODE_PRIVATE);
 fos.write("hello the world".getBytes());
 fos.close();
 assertTrue(Arrays.equals(new String[]{"test.dat"}, ctx.fileList()));
 ctx.deleteFile("test.dat");
 Log.d(this.getClass().toString(), ("<< test1"));
 }
}

Note that Android unit tests cannot be directly ran from Intellij: you need to run them from the emulator (or from a real device):

About the Android toolbox binary

1. What is Android toolbox ?

While browsing the filesystem of my Android emulator, I was surprised to discover that most of the items in the /system/bin folder are actually symlinks to the toolbox binary:


C:\android-sdk\platform-tools>adb shell
# ls -l /system/bin
ls -l /system/bin
[SNIP]
lrwxr-xr-x root shell 2012-08-23 07:01 cat -> toolbox
-rwxr-xr-x root shell 129416 2012-08-23 07:00 check_prereq
lrwxr-xr-x root shell 2012-08-23 07:01 chmod -> toolbox
lrwxr-xr-x root shell 2012-08-23 07:01 chown -> toolbox
[SNIP]
-rwxr-xr-x root shell 181002 2012-08-23 07:01 toolbox
[SNIP]
#

So… what is this toolbox binary ?

Toolbox is like a “swiss army knife”: it allows you to run multiple commands from a single binary. So instead of having different binaries for each standard Unix command (like ls or rm), you can have a single binary (toolbox) that can execute all these commands.

To use it, simply execute “toolbox XX” instead of “XX”. So to list the content of the “/system/bin” folder, I could have used “toolbox ls -l /system/bin”:


# toolbox ls -l /system/bin
toolbox ls -l /system/bin
[SNIP]
lrwxr-xr-x root shell 2012-08-23 07:01 cat -> toolbox
-rwxr-xr-x root shell 129416 2012-08-23 07:00 check_prereq
lrwxr-xr-x root shell 2012-08-23 07:01 chmod -> toolbox
lrwxr-xr-x root shell 2012-08-23 07:01 chown -> toolbox
[SNIP]
-rwxr-xr-x root shell 181002 2012-08-23 07:01 toolbox
[SNIP]
#

Check the toolbox git repository to see which commands can be used.

A popular alternative to toolbox is BusyBox, which provides even more functionnalities.

2. How do toolbox symlinks work ?

As explained above, the “ls” command is actually a symlink that references toolbox:


# ls -l /system/bin/ls
ls -l /system/bin/ls
lrwxr-xr-x root shell 2012-08-23 07:01 ls -> toolbox

The question is: since we have dozens of symlinks that reference toolbox…how does toolbox know which symlink was originally executed ?

It turns out that is actually easy to know, and I’m going to write a Linux bash script showing how you can reproduce this behavior.

First, I have a “script.sh” script and 2 symlinks (named “cmd1” and “cmd2”) that reference this script:


[laurent@localhost toolbox_example]$ ll
total 4
lrwxrwxrwx. 1 laurent laurent 9 Nov 22 11:18 cmd1 -> script.sh
lrwxrwxrwx. 1 laurent laurent 9 Nov 22 11:18 cmd2 -> script.sh
-rwxrwxr-x. 1 laurent laurent 211 Nov 22 11:22 script.sh

And here is the content of my script:


[laurent@localhost toolbox_example]$ cat script.sh
#!/usr/bin/bash
command=`basename $0`
case "$command" in
cmd1) echo "executing cmd1 with args=[$*]"
 ;;
cmd2) echo "executing cmd2 with args=[$*]"
 ;;
*) echo "Invalid option"
 ;;
esac

Let’s see this in action:


[laurent@localhost toolbox_example]$ ./cmd1 arg1
executing cmd1 with args=[arg1]
[laurent@localhost toolbox_example]$ ./cmd2 arg2
executing cmd2 with args=[arg2]

Of course, this concept can be easily ported to C/C++.

Any questions ?

Laurent KUBASKI

Adding LXDE taskbar shortcuts

OK, so after creating new menu entries & desktop shortcuts and after creating new menu sections, let’s see how to create taskbar shortcuts.

On my LXDE taskbar (which is actually called lxpanel in LXDE), you can see that I have a shortcut for leafpad and one for gedit:

To accomplish that, right click on the taskbar and choose the “Add/Remove Panel items” option. This brings up the “Panel Preference” dialog. From there, go to the “Panel Applets” tab which displays the different elements of your taskbar:

 

The “Spacer” plugin is just a way to add a separation between the different plugins. On the screenshot above, you can see that my second spacer is “stretched”, which means that all plugins that comes after it (System Tray + Digital Clock in my case) will end up on the right side.

OK, so to add application shortcuts, you need to add the “Application launch bar” plugin to your taskbar. To do this, just click on the “Add” button, select “Application launch bar” in the list of available plugins and put it in the 4th position:

 

Now select it, click on the “Edit button”: this will display the launch bar configuration dialog. The right side of the dialog displays the applications that are part of your start menu: simply add the ones that you want to the left side of the dialog.

 

And… that’s it !

Now if you don’t want to use graphical tools, here is the lxpanel configuration file that controls everything: ~/.config/lxpanel/LXDE/panels

Adding LXDE start menu sections (or “sub-menus”)

This is a follow-up to my previous post about adding LXDE start menu entries.

So you are now an expert in the .desktop file format and you know how to create one and how to “link” it to one of the existing LXDE start menu sections (Accessories, Graphics, Internet, Office, Other, Sound & Video, System Tools).

But what if you want to create a new section ? Or what if you want to add a sub-section to an existing section ? Well: easy as pie !

OK, I’m lying: you will need to create and edit some XML files, but isn’t this even more funny than clicking on a  “create section” button in a UI ? 😉

How the LXDE main menu is configured

The default LXDE start menu layout is configured here: /etc/xdg/menus/lxde-applications.menu

It’an XML file that contains a bunch of <Menu> tags describing the menu layout. On my Fedora box, the “Accessories” section is described like this:

<Menu>
   <Name>Accessories</Name>
   <Directory>lxde-utility.directory</Directory>
   <Include>
      <And>
         <Category>Utility</Category>
         <Not><Category>System</Category></Not>
      </And>
   </Include>
</Menu>

This translates to “the ‘Accessories’ section is described in the lxde-utility.directory file and a .desktop file will appear in this section if its category is ‘Utility’“. Said differently: “if you want your program shortcut it to be displayed in the ‘Accessories’ section, then create a .desktop file containing “Categories=Utility”“.

If you are looking for the directory that contains the .directory files, it’s here: usr/share/desktop-directories.

How to customize the main menu

To customize your start menu, you will NOT modify the global  /etc/xdg/menus/lxde-applications.menu file, but you will instead create a file that will contain your own extensions. This means that each user will be able to get its own customized menu:

  • Custom main menu extensions must be configured in ~/.config/menus/lxde-applications.menu
  • Custom .directory files must be created in ~/.local/share/desktop-directories

Exercise #1: creating a new section in the main menu

First, create this file: ~/.config/menus/lxde-applications.menu file:

<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN" "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
<Menu>
   <Name>My Menu Extension</Name>
   <MergeFile type="parent">/etc/xdg/menus/lxde-applications.menu</MergeFile>
   <Menu>
      <Name>CustomMenu</Name>
      <Directory>custom_menu.directory</Directory>
      <Include>
         <And><Category>CustomMenuCategory</Category></And>
      </Include>
   </Menu>
</Menu>
  • The <MergeFile> tag says that we are going to extend the existing /etc/xdg/menus/lxde-applications.menu configuration file.
  • Our new custom section is configured in the ~/.local/share/desktop-directories/custom_menu.directory file.
  • Each .desktop file containing “Categories=CustomMenuCategory” will belong to this new section.

Next, create ~/.local/share/desktop-directories/custom_menu.directory:

[laurent@localhost desktop-directories]$ cat ~/.local/share/desktop-directories/custom_menu.directory
[Desktop Entry]
Encoding=UTF-8
Name=The Custom Menu Name

Then, create ~/.local/share/applications/custom_menu_entry.desktop (because the new section will not appear if there is no item inside):

[laurent@localhost applications]$ cat ~/.local/share/applications/custom_menu_entry.desktop
[Desktop Entry]
Name=custom menu entry
Exec=/usr/bin/foo
Comment=
Icon=
NoDisplay=false
Categories=CustomMenuCategory;
Type=Application

Finally, reload the menu by executing “lxpanelctl restart” and there you are:

Exercise #2: creating a sub-section in an existing main menu section

OK, we are now going to add a sub-section in the “Accessories” section.

First, the updated ~/.config/menus/lxde-applications.menu

<!DOCTYPE Menu PUBLIC "-//freedesktop//DTD Menu 1.0//EN" "http://www.freedesktop.org/standards/menu-spec/1.0/menu.dtd">
<Menu>
   <Name>My Menu Extension</Name>
   <MergeFile type="parent">/etc/xdg/menus/lxde-applications.menu</MergeFile>
   <Menu>
      <!-- parent section name: -->
      <Name>Accessories</Name>
      <Menu>
        !-- our new sub-section name: -->
        <Name>SubMenu</Name>
        <Directory>sub_menu.directory</Directory>
        <Include>
           <And><Category>SubMenuCategory</Category></And>
        </Include>
     </Menu>
   </Menu>
</Menu>

So what’s happening here ? Well, we are nesting our new SubMenu under the existing Accessories menu.
In order for this to work, your must make sure that the parent section name (“Accessories” in this example), is spelled exactly as in /etc/xdg/menus/lxde-applications.menu

Then, ~/.local/share/desktop-directories/sub_menu.directory:

[Desktop Entry]
Encoding=UTF-8
Name=The Sub Menu Name

Then, ~/.local/share/applications/sub_menu_entry.desktop:

[Desktop Entry]
Name=sub menu entry
Exec=hellotheworld
Comment=
Icon=
NoDisplay=false
Categories=SubMenuCategory;
Type=Application

Finally, “lxpanelctl restart” and there you are:

Exercise #3: conditional fun

If you look at your existing /etc/xdg/menus/lxde-applications.menu file, you will see that you can use conditional expressions to define how your .desktop files are going to be linked to your menu sections.

For example, this section definition:

<Menu>
   <Name>Administration</Name>
   <Directory>lxde-settings-system.directory</Directory>
   <Include>
      <And>
         <Category>Settings</Category>
         <Category>System</Category>
      </And>
   </Include>
</Menu>

Means that a .desktop file will be linked to the “Administration” section if its categories are “Settings” and “System” (Categories=Settings;System)

And this section definition:

<Menu>
   <Name>Accessories</Name>
   <Directory>lxde-utility.directory</Directory>
   <Include>
      <And>
         <Category>Utility</Category>
         <Not><Category>System</Category></Not>
      </And>
   </Include>
</Menu>

Means that a .desktop file will be linked to the “Accessories” section if its categories are “Utility” and not “System”. So “Categories=Utility;Foo” will link the .desktop file to the Accessories section, and “Categories=Utility;System” will link it to the “System Tools” section.

You can also use the <Xor> tag, but I guess that you can easily understand how it works.

Laurent KUBASKI

Restarting LXDE when it freezes/hangs

This is going to be of interest to no-one except me, but here it is:

  • Hit CTRL-ALT-F2 to switch to console mode (CTRL-ALT-F1 will bring you back to LXDE)
  • Kill the /usr/bin/X process
  • That’s it !

Understanding file associations in LXDE and PCManFM

Or “understanding how PCManFM decides which program to launch when you double-click on a file”

Or “understanding the list of suggested programs when you right-click on a file in PCManFM”

Or “making sense of the mimeinfo.cache, defaults.list and mimeapps.list crazyness”

The problem

Create a text file (text.txt) file in PCManFM and right click on it: PCManFM displays a list of possible application that can open the file:

Now if I double-click on the text.txt file, abiword (the first item highlighted in the screenshot above) is going to be launched.

  • Where does this “AbiWord – gedit- Leafpad” list come from ?
  • I want to use gedit instead of AbiWord when double-clicking on the file: how can I change this ?
  • I want my text file to be opened by a program that is not listed: how can I do this ?
  • Where in the world is Carmen SanDiego ?

Sit back and relax, I’m going to tell you the whole story.

At the beginning, there was the .desktop files

These files are mainly used to specify which elements are going to be displayed in the LXDE start menu and on your desktop. Check my creating LXDE shortcuts post for more information.

Then there was mimeinfo.cache

This file is located in /usr/share/applications/mimeinfo.cache and it is a basically a raw reverse cache for the .desktop information. Check this link for more information.

More precisely, each .desktop file on your system (which can be located either in /usr/share/applications, in /usr/local/share/applications or in ~/.local/share/applications) can contain a “MimeType” attribute which states the mimetype that the program associated to the .desktop file can handle.

On my Fedora installation, I have 3 text editors: Abiword, Leafpad and gedit and each one of them can open files that have a text/plain mimetype:


[laurent@localhost applications]$ cat fedora-abiword.desktop | grep text/plain
MimeType=application/x-abiword;text/x-abiword;text/x-xml-abiword;text/plain;

[laurent@localhost applications]$ cat gedit.desktop | grep text/plain
MimeType=text/plain;

[laurent@localhost applications]$ cat fedora-leafpad.desktop | grep text/plain
MimeType=text/plain;

Now if I have a look in mimeinfo.cache, I can find the reverse mapping that we just talked about:

[laurent@localhost applications]$ cat mimeinfo.cache | grep text/plain
text/plain=fedora-abiword.desktop;gedit.desktop;fedora-leafpad.desktop;

So this translates to “any file with a text/plain mimetype can be opened using either abiword, gedit or leafpad”.

Astute readers will notice that:

  • This list (abiword + gedit + leafpad) is exactly the one displayed in PCManFM when I right click on a file (see screenshot above).
  • The first item in this list (in my case: abiword) is the program that is launched when I double-click on a text file in PCManFM.

However, editing this mimecache.info file (to change the order of the .desktop files that are associated with the text/plain mimetype) is not what you want to do. The truth is you may lose all your changes after installing/removing a new Linux package.

This is because this file is generated by launching the “update-desktop-database” utility. This tool parses all your .desktop files and generates the mimeinfo.cache file… more or less each time you install a new package. This is where the defaults.list files join the party

Here comes defaults.list

defaults.list is a file where you configure the default program that should be used to open a file. To be very specific, this is where you configure the default “mime type -> .desktop file” association.

Like .desktop files, you can have a defaults.list file in any of the following locations (you can even have 3 files: one at each location).

  • /usr/share/applications
  • /usr/local/share/applications
  • ~/.local/share/applications

First, there is a global defaults.list file located in /usr/share/applications and then you can have one for each user, located in ~/.local/share/applications. As you can guess, the “user-level” file has higher priority than the “global one”.

If I only have a /usr/share/applications/defaults.list file that contains a “text/plain -> gedit.desktop” mapping, then double-clicking on a text file in PCManFM will open gedit, no matter what the ordering of mimeinfo.cache is:

[laurent@localhost applications]$ cat /usr/share/applications/defaults.list | grep text/plain
text/plain=gedit.desktop

But if I also have a ~/.local/share/applications/default.list file that contains a “text/plain -> fedora-leafpad.desktop” mapping, then double-clicking on a text file in PCManFM will open leafpad (since this file has “higher priority” than the files in the 2 other locations).

[laurent@localhost applications]$ cat ~/.local/share/applications/defaults.list | grep text/plain
text/plain=fedora-leafpad.desktop

But wait, there is more !

The mimeinfo.cache, the defaults.list and the mimeapps.list

Yes that’s true, one more file to bother you (did I mention “crazyness” at the beginning of this post ?).

Now if you think that ~/.local/share/applications/defaults.list has higher priority than /usr/share/applications/defaults.list which has higher priority than /usr/share/applications/mimeinfo.cache”, you would still be incorrect because ~/.local/share/applications/mimeapps.list has even higher priority than all these bastards.

mimeapps.list allows you to:

  • add even more “right-click” options to PCManFM right click menu
  • set the default program for a mimetype (exactly like in defaults.list)

Although I’m not aware of any tool to help you deal with defaults.list file, there are at least 2 options to help you with mimeapps.list

OK, so let’s say that you would like to open your text file with yet another program that is not listed in the PCManFM right-click menu. Just choose the “Open with…” option and select an application. In my case, I’ll choose “Xpad”, and I’ll choose the “set selected application as default” option:

And there you are, xpad is now part of your right-click menu:

Behind the scene, PCManFM created a mimeapps.list file:


laurent@localhost applications]$ cat ~/.local/share/applications/mimeapps.list

[Added Associations]
text/plain=fedora-xpad.desktop;

[Default Applications]

text/plain=fedora-xpad.desktop

Another way to generate this file is to use the xdg-mime tool with “xdg-mime default fedora-xpad.desktop text/plain”

So let’s recap

The list of program that is displayed when right-clicking on a file in PCManFM is the combination of:

  • mimeinfo.cache
  • mimeapps.list

The default program that is launched when you double-click on a file in PCManFM is (first match wins):

  • The one from ~/.local/share/applications/mimeapps.list
  • The one from ~/.local/share/applications/defaults.list
  • The one from /usr/local/share/applications/defaults.list
  • The one from /usr/share/applications/defaults.list
  • The first one from /usr/share/applications/mimeinfo.cache

Laurent KUBASKI