Search code examples
javaandroidclasscastexception

ClassCastException when casting Activity to MainActivity


I'm having a weird problem in my Android app when casting a Activity to my MainActivity. I'm using GeoFences to broadcasts events to a base activity called NotificationActivity. The NotificationActivity is used in all the other activities I use so when a GeoFence triggers an AlertDialog pops up. Now when a GeoFence triggers and I'm in an activity other than MainActivity, I need to finish the current activity and do a certain action on the MainActivity (switch to a certain tab). In my Application class I'm implementing the Application.ActivityLifeCycleCallbacks, in the onActivityResumed callback I set my currentActivity to the resumed activity (I know a static reference causes memory leaks but I need to fix this).

Here's my application class:

private static Activity currentActivity;

@Override
public void onCreate() {
    super.onCreate();

    // Setup Fabric
    if (AppConfig.getEnvironment() != AppConfig.Environment.Development) {
        Fabric.with(this, new Crashlytics(), new Answers());
    }

    // Init realm
    Realm.init(this);

    // Init Firebase
    FirebaseApp.initializeApp(this);

    if (AppConfig.getEnvironment() == AppConfig.Environment.Development) {
        // Init Stetho with realm plugin
        Stetho.initialize (
                Stetho.newInitializerBuilder(this)
                        .enableDumpapp(Stetho.defaultDumperPluginsProvider(this))
                        .enableWebKitInspector(RealmInspectorModulesProvider.builder(this).build())
                        .build());
    }

    registerActivityLifecycleCallbacks(this);
}

@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
    Log.d(Constants.DEBUG, "Activity created: " + activity.toString());
}

@Override
public void onActivityStarted(Activity activity) {
    Log.d(Constants.DEBUG, "Activity started: " + activity.toString());
}

@Override
public void onActivityResumed(Activity activity) {
    Log.d(Constants.DEBUG, "Activity resumed: " + activity.toString());
    currentActivity = activity;
}

@Override
public void onActivityPaused(Activity activity) {
    Log.d(Constants.DEBUG, "Activity paused: " + activity.toString());
}

@Override
public void onActivityStopped(Activity activity) {
    Log.d(Constants.DEBUG, "Activity stopped: " + activity.toString());
}

@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
    Log.d(Constants.DEBUG, "Activity SaveInstanceState: " + activity.toString());
}

@Override
public void onActivityDestroyed(Activity activity) {
    Log.d(Constants.DEBUG, "Activity Destroyed: " + activity.toString());
}

public static Activity getCurrentActivity() {
    return currentActivity;
}

And here's my NotificationActivity (base) activity:

public abstract class NotificationActivity extends AppCompatActivity {

private BroadcastReceiver onNoticeWithinPoi = new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {
        abortBroadcast();
        Bundle bundle = intent.getExtras();
        if (bundle != null) {
            Realm realm = Realm.getDefaultInstance();
            Poi poi = realm.where(Poi.class).equalTo("ref", bundle.getString(Constants.POI_KEY_REF)).findFirst();
            showAlertWithinPoi(context, poi);
        }
    }
};

private BroadcastReceiver onNoLocationProviderSet = new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {
        warnUserNoLocationProviderSet();
    }
};

private BroadcastReceiver onNoticeOutOfRange = new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {
        abortBroadcast();
        alertNoticeOutOfRange();
    }
};

public Fragment getActiveFragment() {
    if (getFragmentManager().getBackStackEntryCount() == 0) {
        return null;
    }

    String tag = getFragmentManager().getBackStackEntryAt(getFragmentManager().getBackStackEntryCount() - 1).getName();
    return getFragmentManager().findFragmentByTag(tag);
}

private void showAlertWithinPoi(final Context context, final Poi poi) {
    AlertDialog.Builder builder =  new AlertDialog.Builder(this);
    builder.setTitle(getString(R.string.poi_popup_title));
    builder.setMessage(getString(R.string.poi_popup_subtitle));
    builder.setPositiveButton(getString(R.string.yes), new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Game currentGame = Helper.getCurrentGame(context);
            Game poiGame = null;

            Realm realm = Realm.getDefaultInstance();
            for (Game game : realm.where(Game.class).findAll()) {
                for (Tag tag : game.tags) {
                    if (tag.poi.ref.equals(poi.ref)) {
                        poiGame = game;
                        break;
                    }
                }

                if (poiGame != null) {
                    break;
                }
            }

            if (poiGame != null && poiGame.ref.equals(currentGame.ref)) {
                realm.beginTransaction();
                currentGame.lastSeenPoi = poi.ref;
                realm.commitTransaction();
                checkCurrentActivity();
            } else if (poiGame != null && !poiGame.ref.equals(currentGame.ref)) {
                showAlertDifferentGame(context, poiGame, poi);
            }
        }
    });

    builder.setNegativeButton(getString(R.string.later), null);
    builder.setIcon(R.drawable.poi_unvisited);
    builder.create().show();
}

private void showAlertDifferentGame(final Context context, final Game game, final Poi poi) {
    AlertDialog.Builder builder = new AlertDialog.Builder(context);
    builder.setTitle(getString(R.string.game_switch_title));
    builder.setMessage(getString(R.string.game_switch_message) + " " + LanguageHelper.getGameTitle(game) + " ?");
    builder.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            Realm realm = Realm.getDefaultInstance();
            realm.beginTransaction();
            game.lastSeenPoi = poi.ref;
            realm.commitTransaction();

            SharedPreferencesHelper.saveString(context, Constants.PREF_SELECTED, game.ref);
            GeofenceService.updateGeoFences(game, context);
            checkCurrentActivity();
        }
    });
    builder.setNegativeButton(R.string.no, new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            checkCurrentActivity();
        }
    });
    builder.create().show();
}

private void checkCurrentActivity() {
    final Activity currentActivity = GeoFortApplication.getCurrentActivity();
    if (currentActivity instanceof MainActivity) {
        ((MainActivity) currentActivity).switchTab();
    } else {
        currentActivity.finish();
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                try {
                    Log.d(Constants.DEBUG, "CurrentActivity: " +  currentActivity.toString());
                    ((MainActivity) currentActivity).switchTab();
                } catch (ClassCastException e) {
                    Log.e(Constants.EXCEPTION, e.getLocalizedMessage());
                }
            }
        }, 5000);
    }
}


private void alertNoticeOutOfRange() {
    new AlertDialog.Builder(this)
            .setTitle(R.string.error_location_not_close_enough_title)
            .setMessage(R.string.error_location_not_close_enough_alert)
            .setPositiveButton(R.string.ok, null)
            .setIcon(R.drawable.ic_launcher)
            .show();
}

private void warnUserNoLocationProviderSet() {
    new AlertDialog.Builder(this)
            .setTitle(R.string.error_location_not_available_title)
            .setMessage(R.string.error_location_services_not_available_text)
            .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    // TODO no user location set?
                }
            })
            .setIcon(null)
            .show();
}

@Override
protected void onResume() {
    super.onResume();

    IntentFilter filterWithinPoi = new IntentFilter(Constants.NOTIFICATION_WITHIN_POI);
    filterWithinPoi.setPriority(2);
    registerReceiver(onNoticeWithinPoi, filterWithinPoi);

    IntentFilter filterOutOfRange = new IntentFilter(Constants.NOTIFICATION_LOCATION_OUT_OF_RANGE);
    filterOutOfRange.setPriority(2);
    registerReceiver(onNoticeOutOfRange, filterOutOfRange);

    IntentFilter filterLocationProviderOff = new IntentFilter(Constants.NOTIFICATION_LOCATION_PROVIDER_OFF);
    registerReceiver(onNoLocationProviderSet, filterLocationProviderOff);
}

@Override
protected void onPause() {
    super.onPause();

    unregisterReceiver(onNoticeWithinPoi);
    unregisterReceiver(onNoticeOutOfRange);
    unregisterReceiver(onNoLocationProviderSet);
}

}


Solution

  • you are doing something wrong on else

    private void checkCurrentActivity() {
        final Activity currentActivity = GeoFortApplication.getCurrentActivity();
        if (currentActivity instanceof MainActivity) {
            ((MainActivity) currentActivity).switchTab();
        } else {
            currentActivity.finish();
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    try {
                        Log.d(Constants.DEBUG, "CurrentActivity: " +  currentActivity.toString());
                        ((MainActivity) currentActivity).switchTab();
                    } catch (ClassCastException e) {
                        Log.e(Constants.EXCEPTION, e.getLocalizedMessage());
                    }
                }
            }, 5000);
        }
    }
    

    Within run method the activity is not a MainActivity, as you do check it before.