Android ActivityGroup - NullPointerException

3.3k views Asked by At

I'm trying to use activity groups - since I use tabs, and want to have the tabs when loading and activity after the list item was clicked,. but I'm getting nullpointerexception in the following line:

View view1 = S1_Group.group.getLocalActivityManager()
                           .startActivity("S1", intent)
                           .getDecorView();

Code is .. .

lv.setOnItemClickListener(new OnItemClickListener() {
    public void onItemClick(AdapterView<?> parent, View view,
          int position, long id) {


        Intent intent = new Intent(getApplicationContext(), S1.class);

        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        Log.d("test","Before view");
        try{
            View view1 = S1_Group.group.getLocalActivityManager()
               .startActivity("S1", intent)
               .getDecorView();
               Settings_Group.group.setContentView(view1);      
        }
        catch (Exception e){
            Log.e("test","view failded:"+e);
        }
....

update: this how my group activity is: I couldn't find what was the issue.,

public class S1_Group extends ActivityGroup {

    public static S1_Group group;
    private ArrayList<View> history;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        this.history = new ArrayList<View>();
        group = this;

        View view = getLocalActivityManager().startActivity("F1", 
                new Intent(this, F1.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)).getDecorView();

        setContentView(view);
    }
}
3

There are 3 answers

0
Tosa On

not sure how you implemented the ActivityGroup, but the group needs to be a static variable.

I found this tutorial on google and it describes how to implement an ActivityGroup. http://blog.henriklarsentoft.com/2010/07/android-tabactivity-nested-activities/

Hope this helps, tobias

0
James123 On

I had the same problem. To solve it I decomposed the multi-call line into three lines as the first answer suggested:

            LocalActivityManager processManager =activityGroup.group.getLocalActivityManager();
            Window w = processManager.startActivity("ActivityOne", myIntent);
            View view = w.getDecorView();

Then discovered that the first line is the problem one. Because I am calling ActivityOne from an activity outside of the activity group (it is an activity under a different tab), the static "activityGroup.group...." has not yet been initialised. So the rather kludgy fix is to switch to that tab and back to this tab before launching the intent. So add the following just before the code above:

            AppName.switchToTab(2);  
            AppName.switchToTab(1); 

That did the trick and you can't see the tab switch. A more tidy solution would be maybe to switch to all the tabs on startup to ensure they are all initialised...

0
Delblanco On

Came across the same issue and it seems that there is(or was) a bug with the LocalActivityManager that occurs when you're trying to (re)start an Activity with the same ID as a previously destroyed Activity. It will simply return null as Window, because of a bug inside the destroyActivity method. The workaround I'm using, uses reflection to properly destroy a activity (detailed explanation after the workaround) :

public boolean destroyActivityWorkAround(String id) {
    final LocalActivityManager activityManager = getLocalActivityManager();
    if(activityManager != null){
        activityManager.destroyActivity(id, false);             
        try {
            // Use reflection to get to the HashMaps with the records(which activities are started ect.)
            // to remove the records properly 
            // http://code.google.com/p/android/issues/detail?id=10083
            final Field mActivitiesField = LocalActivityManager.class.getDeclaredField("mActivities");
            if(mActivitiesField != null){
                mActivitiesField.setAccessible(true);
                @SuppressWarnings("unchecked")
                final Map<String, Object> mActivities = (Map<String, Object>)mActivitiesField.get(activityManager);
                if(mActivities != null){
                    mActivities.remove(id);
                }
                final Field mActivityArrayField = LocalActivityManager.class.getDeclaredField("mActivityArray");
                if(mActivityArrayField != null){
                    mActivityArrayField.setAccessible(true);
                    @SuppressWarnings("unchecked")
                    final ArrayList<Object> mActivityArray = (ArrayList<Object>)mActivityArrayField.get(activityManager);
                    if(mActivityArray != null){
                        for(Object record : mActivityArray){
                            final Field idField = record.getClass().getDeclaredField("id");
                            if(idField != null){
                                idField.setAccessible(true);
                                final String _id = (String)idField.get(record);
                                if(id.equals(_id)){
                                    mActivityArray.remove(record);
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        } catch (Exception e) {
            Log.e(LOGTAG, this.getClass().getSimpleName() + ".destroyActivityWorkAround() removing activity using reflection failed with error:", e);
            //e.printStackTrace();
        }
        return true;
    }
    return false;
}

This is a workaround because the LocalActivityManager.destroyActivity(...) contains a bug in several API-versions. The method doesn't remove the Activity properly from its HashMap's (LocalActivityManager's source):

     public Window destroyActivity(String id, boolean finish) {
         LocalActivityRecord r = mActivities.get(id);   //<-- id's are the key's for the HashMap
         Window win = null;
         if (r != null) {
             win = performDestroy(r, finish);
             if (finish) {
                    mActivities.remove(r);  //--> This works on id's not the 'r object', this doesn't remove anything
             }
         }
         return win;
     } 

the if(finish) statement should be as followed to remove the LocalActivityRecord of the activity being destroyed:

if (finish) {
    mActivities.remove(id);    //--> mActivities should remove the id
    mActivityArray.remove(r);  //--> mActivitiesArray should remove the 'r object' (LocalActivityRecord)
}

Although they say it's being fixed for Froyo but I still encountered it on a Samsung galaxy S2 running 2.3.3