Friday, 13 December 2013

On Android NDK and Activity Lifecycle

After activity restarts calls to native methods may fail.

This can happen because shared library does not get reloaded. There is no symmetric method to System.loadLibrary(String libName) to manually unload or reload library. The shared library on Android will be unloaded when there are no processes that use it. Even when activity finishes its lifecycle and its onDestroy() method is called the host process of activity may still run. From the Android docs "the process hosting the activity may killed by the system at any time" after activity is finished. I think it would be killed when OS will need more memory for other tasks.

So, if the old process still sticks around so does the shared library. After activity starts again, its onCreate() called etc, it attaches to the same running process and deals with the same shared library instance. The shared library's data section where all static and global variables are stored is still in the state it was carrying old values.

Now, to highlight the possible problem, suppose we have a Singleton class on a native side. We call in to a native function to create it from activity's onCreate method. The native method may look like this.

CSomeSingleton* CSomeSingleton::GetInstance()
{
    if (m_Instance == NULL)
        m_Instance = new CSomeSingleton();
    return m_Instance;
}


In activity's onDestroy we call in to another native method that destroys it. Suppose it looks like this.

void CSomeSingleton::FreeInstance()
{
    delete m_Instance;
}


After acivity restarts and attaches to same process the problem arises. m_Instance will not be NULL, it will point to invalid address in memory.

There are 3 variants to unravel this issue.

1. Do not deinitialize your native-side objects at all (in activity's onDestroy()). When new activity instance will start it would attach to running process with valid pointers and global variables. If the process would be killed by the time, it would be started again, reloading the shared library, reinitializing the data section.

2. Find all places where the global/static variables where left in inconsistent state. For the above example that would be

void CSomeSingleton::FreeInstance()
{
    delete m_Instance;
    m_Instance = NULL;
}


3. Call System.exit(0) in onDestory() to stop the process and thus unload the shared library. Considered as a bad practice, because you may have yet another component that is running in that process, and this component may be not in the appropriate state to stop its execution (because it may have some unsaved data).

No comments :

Post a Comment