I have the below stack trace from leak canary with which I am not sure how my Activity is getting leaked
static LGCobtextHelper.mLGContext
references LGContext.mContext
references
ResourcesContextWrapperFactory$WebViewContextWrapper.mBase
references
com.*.*.activity.MyActivity.networkMonitor
references
com.*.*.NetworkMonitor.mPendingResult
references
android.app.LoadedApk$ReceiverDispatcher$Args.this$0
references
LoadedAok$ReceiverDispathcer.mContext
leaks MyActivity instance
MyActivity extends BaseActivity, which registers onResume() and unregisters onPause(), so not sure which leaks the activity
NetworkMonitor.java
public class NetworkMonitor extends BroadcastReceiver {
private final WebSocketClient webSocketClient;
private final ArmingHelper armingHelper;
private final ShutdownManager shutdownManager;
private final CameraThumbnailCache cameraThumbnailCache;
private final CameraAccessManager cameraAccessManager;
private final JoustLogger joustLogger;
private Activity registeredActivity;
private String currentNetworkName;
private List<NetworkStatusChangeListener> networkChangeListeners;
public interface NetworkStatusChangeListener {
void onNetworkUp();
void onNetworkDown();
}
public NetworkMonitor(WebSocketClient webSocketClient, ArmingHelper armingHelper, ShutdownManager shutdownManager, CameraThumbnailCache cameraThumbnailCache, CameraAccessManager cameraAccessManager, JoustLogger joustLogger) {
this.webSocketClient = webSocketClient;
this.armingHelper = armingHelper;
this.shutdownManager = shutdownManager;
this.cameraThumbnailCache = cameraThumbnailCache;
this.cameraAccessManager = cameraAccessManager;
this.joustLogger = joustLogger;
networkChangeListeners = new ArrayList<>();
}
// Activities *must* call this method in onResume() in order for
// the app to watch for network changes
public void startListeningForNetworkChanges(Activity registeringActivity) {
if (!(registeringActivity instanceof NetworkStatusChangeListener)) {
throw new IllegalArgumentException("Registering Activity must implement NetworkStatusChangeListener");
}
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
intentFilter.addAction(Intent.ACTION_AIRPLANE_MODE_CHANGED);
intentFilter.addAction(GlobalConstants.ANDROID_NET_WIFI_WIFI_STATE_CHANGED);
registeringActivity.registerReceiver(this, intentFilter);
this.registeredActivity = registeringActivity;
registerListenerForNetworkChanges((NetworkStatusChangeListener)registeringActivity);
}
// Activities *must* call this method in onPause() in order to properly
// unregister the receiver that was set in onResume()
public void stopListeningForNetworkChanges(Activity registeringActivity) {
registeringActivity.unregisterReceiver(this);
unregisterListenerForNetworkChanges((NetworkStatusChangeListener)registeringActivity);
registeredActivity = null;
}
// Fragments can use this method to register for Network change updates, call in onResume()
public void registerListenerForNetworkChanges(NetworkStatusChangeListener listener) {
networkChangeListeners.add(listener);
}
// Fragments need to unregister in onPause()
public void unregisterListenerForNetworkChanges(NetworkStatusChangeListener listener) {
networkChangeListeners.remove(listener);
}
#Override
public void onReceive(Context context, Intent intent) {
checkNetworkConnection();
}
public void checkNetworkConnection() {
if (registeredActivity != null) {
final ConnectivityManager connectivityManager = (ConnectivityManager) registeredActivity.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
if (networkInfo != null && networkInfo.isConnectedOrConnecting()) {
String newNetworkName = networkInfo.getTypeName();
if (currentNetworkName == null || !currentNetworkName.equals(newNetworkName)) {
Timber.d("Network(%s) Connected", newNetworkName);
// Our network was down, but now it's up. Validate the Websocket
currentNetworkName = newNetworkName;
cameraThumbnailCache.clearInternalURLPreferences();
webSocketClient.reopenWebsocketIfPossible();
cameraAccessManager.onNetworkUp();
if (ActivityBehaviorHelper.needsSecurityCountdown(registeredActivity)) {
armingHelper.startTimerIfReady();
}
for (NetworkStatusChangeListener listener : networkChangeListeners) {
listener.onNetworkUp();
}
joustLogger.onNetworkUp();
}
} else {
Timber.w("Network Down");
currentNetworkName = null;
cameraAccessManager.onNetworkDown();
joustLogger.onNetworkDown();
shutdownManager.onNetworkDown();
for (NetworkStatusChangeListener listener : networkChangeListeners) {
listener.onNetworkDown();
}
}
}
}
}
BaseActivity.java
#Override
protected void onResume() {
super.onResume();
networkMonitor.startListeningForNetworkChanges(this);
}
#Override
protected void onPause() {
networkMonitor.stopListeningForNetworkChanges(this);
super.onPause();
}
It looks like you probably don't need to be holding a reference to the Activity in that NetworkMonitor class. That's probably the source of your memory leak - the Activity reference is likely being held after the Activity is destroyed. Looks like you could just pass the context in as a parameter to the methods that need it.
Also, For a few of the spots where Activity context is being used here, like context.getSystemService(Context.CONNECTIVITY_SERVICE), you could use Application context instead and possibly avoid needing an Activity reference altogether.
Related
I have a activity in which in onCreate method I have to check whether activity is in foreground or background .if activity is in foreground then I have to send request to to server and if app goes in background then I have to send that server request through Intent service.How can I do that.
Is there any way if app goes in background stop server request which is in foreground .
Try This for check your app is in foreground or not
public static boolean isAppInForeground(Context ctx) {
ActivityManager activityManager = (ActivityManager) ctx
.getApplicationContext().getSystemService(
Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> services = activityManager
.getRunningTasks(Integer.MAX_VALUE);
if (services == null) {
return false;
}
if (services.size() > 0
&& services.get(0).topActivity
.getPackageName()
.toString()
.equalsIgnoreCase(
ctx.getApplicationContext().getPackageName()
.toString())) {
return true;
}
return false;
}
Track visibility of your application by yourself using Activity.onPause, Activity.onResume methods.
Example Implement custom Application class :
public class MyApplication extends Application {
public static boolean isActivityVisible() {
return activityVisible;
}
public static void activityResumed() {
activityVisible = true;
}
public static void activityPaused() {
activityVisible = false;
}
private static boolean activityVisible;
}
Register your application class in AndroidManifest.xml
and then so something like this :
#Override
protected void onResume() {
super.onResume();
MyApplication.activityResumed();
}
#Override
protected void onPause() {
super.onPause();
MyApplication.activityPaused();
}
The best approach would be you should trigger your service in onStop() call of your activity, as the service gets start your logic in onStatCommand() will be execute and if you want your service should not be dependent on app lifecycle return START_STICKY with in onStatCommand().
Here is a solution using Application class.
public class AppSingleton extends Application implements Application.ActivityLifecycleCallbacks {
private WeakReference<Context> foregroundActivity;
#Override
public void onActivityResumed(Activity activity) {
foregroundActivity=new WeakReference<Context>(activity);
}
#Override
public void onActivityPaused(Activity activity) {
String class_name_activity=activity.getClass().getCanonicalName();
if (foregroundActivity != null &&
foregroundActivity.get().getClass().getCanonicalName().equals(class_name_activity)) {
foregroundActivity = null;
}
}
//............................
public boolean isOnForeground(#NonNull Context activity_cntxt) {
return isOnForeground(activity_cntxt.getClass().getCanonicalName());
}
public boolean isOnForeground(#NonNull String activity_canonical_name) {
if (foregroundActivity != null && foregroundActivity.get() != null) {
return foregroundActivity.get().getClass().getCanonicalName().equals(activity_canonical_name);
}
return false;
}
}
If you have a reference to the required Activity or using the canonical name of the Activity, you can find out whether it's in the foreground or not. This solution may not be foolproof. Therefore your comments are really welcome.
Check this library. It can ctrack current activity status.
I can't figure out where am I wrong. I have set up a listener for one of my classes (class A) where I save some user informations.
Back in main activity (class B) I have implemented the first class and I initialise the listener. Then in class B I create a constructor for the interface to finish initialisation here the value of the listener is somewhat ok: "value of the listeners on initialisation: com.fideli.MainActivity#425b0500", but bellow in the method I would like to use it I get always null, and my app crashes.
Where am I wrong? THANKS!!
Class A:
public class GCMActivity {
private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
Context context;
public void setCallback(regidListener rListener) {
this.rListener = rListener;
//here it is ok, it is not null
System.out.println("value of the listeneris on initialisation: " + rListener);
}
public static interface regidListener {
public void onRegIdSaved(String regId);
}
public regidListener rListener;
public GCMActivity(Context context) {
this.context = context;
}
public void registerIfNeeded() {
// here is already null
System.out.println("value of the listeneris: " + this.rListener);
if (rListener != null){
rListener.onRegIdSaved("HEY!!");
}
if (checkPlayServices()) {
gcm = GoogleCloudMessaging.getInstance(context);
regid = getRegistrationId(context);
System.out.println("Class Started!!");
if (regid == null) {
registerInBackground();
}
Main activity, class B:
public class MainActivity extends FragmentActivity implements GCMActivity.regidListener {
...
#Override
protected void onResume() {
super.onResume();
uiHelper.onResume();
if(!isOnline()){
showGpsButton();
}
//initialising listener for regId ready
GCMActivity gcm = new GCMActivity(this);
gcm.setCallback(this);
}
#Override
public void onRegIdSaved(String regId) {
System.out.println("regId ready" + regId);
}
....
Is it because in your onResume method gcm is a local variable that goes out of scope when onResume returns? Seems like you need to make it a class variable, e.g. mGcm.
into my application i use an intent:
private Handler mHandler = new Handler();
.
.
mServiceIntent = new Intent(this, ObdGatewayService.class);
mServiceConnection = new ObdGatewayServiceConnection();
mServiceConnection.setServiceListener(mListener);
// bind service
Log.d(TAG, "Binding service..");
bindService(mServiceIntent, mServiceConnection,
Context.BIND_AUTO_CREATE);
here my activity at onCreate start a new service. this is my onDestroy:
#Override
protected void onDestroy() {
super.onDestroy();
mServiceIntent = null;
mServiceConnection = null;
mListener = null;
mHandler = null;
}
this is mServiceConnection:
public class ObdGatewayServiceConnection implements ServiceConnection{
private static final String TAG = "com.echodrive.io.ObdGatewayServiceConnection";
private IPostMonitor service = null;
private IPostListener listener = null;
public void onServiceConnected(ComponentName name, IBinder binder) {
service = (IPostMonitor) binder;
service.setListener(listener);
}
public void onServiceDisconnected(ComponentName name) {
service = null;
Log.d(TAG, "Service disconnesso.");
}
public boolean isRunning() {
if (service == null) {
return false;
}
return service.isRunning();
}
public void addJobToQueue(ObdCommandJob job) {
if (null != service)
service.addJobToQueue(job);
}
public void setServiceListener(IPostListener listener) {
this.listener = listener;
}
mListener is a listener from interface:
public interface IPostListener {
void fineTest(DatiTest risultati);
void startAcquisizione();
void aquisizioneTerminata();
void aquisizioneInterrotta(String motivo);
void connessioneCorretta();
void gpsStato(boolean stato);
}
my problem is.. how save all this code after rotation? thanks!
The recommended way to save state across rotations is to save them on the outState. This is accomplished by overriding the onSaveInstanceState method. This method gives you a Bundle outState object that you can add Parcelable and Serializable objects to. This should work fine for your Intent object since it implements Parcelable but it may not work for say Handler because it only extends Object.
Another solution is to make these members static. However, be very careful if you decide to do this. Make sure that the value of the static member never holds on to a Context or a view hierarchy, etc, or you could easily introduce memory leaks.
If neither of these is acceptable to you, there is the option suggested by Tushar. However, unless you're careful this will make your life very difficult very fast. A large reason why activities are destroyed and re-created is so that resources can be re-loaded. So if you have layouts, strings, colors, dimens, or basically any resource specifically for landscape, or tablets, or different versions, you'll have to reload the entire UI yourself.
I'm trying to track down a new null pointer exception which is appearing in my ACRA logs and which I can't reproduce. Here's the relevant code:
public class MyApplication extends Application {
public void onCreate() {
DataManager.instance().initializeData(this);
}
}
public class DataManager {
private static DataManger instance = new DataManger();
private List<DataModel> dataModels;
private List<I_Callback> callbacks = new ArrayList<I_Callback>();
private boolean isInitialized = false;
private DataManager(){}
public static DataManager instance() {
return instance;
}
public void initializeData(Context context) {
new DataManagerInitializer().execute(context);
}
public void setDataModels(List<DataModel> models) {
dataModels = models;
}
public void synchronized registerInitializeCallbacks(I_Callback callback) {
if (isInitialized) {
callback.executeCallback();
} else {
callbacks.add(callback);
}
}
public void synchronized setInitialized() {
isInitialized = true;
for (I_Callback callback:callbacks) {
callback.executeCallback();
}
callbacks.clear();
}
}
public class DataManagerInitializer extends AsyncTask<Context, Void, Void>{
protected Void doInBackground(Context... contexts){
List<DataModel> dataModels = new ArrayList<DataModel>();
/*various code to create DataModel objects and add to dataModels list*/
DataManager.instance().setDataModels(dataModels);
return null;
}
protected void onPostExecute(Void result) {
DataManager.instance().setInitialized();
}
}
public class ActivityA extends Activity implements I_Callback{
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.graphical_layout);
DataManager.instance().registerInitializeCallbacks(this);
}
public void executeCallback() {
/* wire up button to call Activity B */
}
}
public class ActivityB extends Activity {
public void onCreate(Bundle savedInstanceState) {
List<DataModel> dataModels = DataManager.instance().getDataModels();
/* The following line of code throws a null pointer exception
in the stack trace*/
for (int i=0; i < dataModels.size(); i++){
/* do something with the data model */
}
}
}
To break down the above more simply, the application is launched which kicks off the initializion of the data manager singleton. ActivityA, the main activity, launches and waits for the data manager to complete initialization before allowing any actions, wiring up any events, etc. From ActivityA, its not possible to get to ActivityB without the call back method executing and ActivityB is only reachable from ActivityA. The only way for the list of data models to be null in the DataManager is for it to not have been initialized, but I'm struggling to see how this is possible. Any suggestions on how my null pointer may have occurred?
private static DataManger instance = new DataManger();
...
public static DataManager instance() {
return instance;
}
Is where the problem is. So your instance variable is getting garbage collected. As it is instantiated when it is declared, it is not being appropriately re-instantiated. So, try this instead:
private static DataManger instance = null;
...
public static DataManager instance() {
if (instance == null){
instance = new DataManager();
}
return instance;
}
This will ensure the call to instance() (usually called getInstance() but this is only convention), will return a valid single instance of the datamanager. Try to avoid instantiating global variables with their declaration, to avoid this specific problem.
Let's assume that:
you are interacting with the Activity B
press the home button:
start playing with other apps (consuming memory)
at some point the so needs memory and it's gonna start garbage collecting objects, included your "instance".
If that happens when you launch your app the framework will resume the activity B and the npe will happen.
You need to re-create the instance (in the activity B) if it is null.
i've seen this thread : How to implement a listener about implement listeners.
its actually pretty simple but i don't get how exactly its done and how to implement in my own code.
i have this static variable variable: AppLoader.isInternetOn.
i want to build a listener which will listen to this variable changes and update a TextView.
should i do this: ?
build an interface:
public interface InternetStateListener {
public void onStateChange();
}
run it in my activity:
public class MyActivity extends Activity {
private InternetStateListener mListener;
setTheListener(this);
public void setTheListener(InternetStateListener listen) {
mListener = listen;
}
private void onStateChange() {
if (mListener != null) {
if (AppLoader.isInternetOn)
text.setText("on")
else
text.setText("off")
}
}
}
Your Activity does nothing special, just register itself (since the interface is implemented directly in the class) with the Other class that provides the listener.
public class MyActivity extends Activity implements InternetManager.Listener {
private TextView mText;
private InternetManager mInetMgr;
/* called just like onCreate at some point in time */
public void onStateChange(boolean state) {
if (state) {
mText.setText("on");
} else {
mText.setText("off");
}
}
public void onCreate() {
mInetMgr = new InternetManager();
mInetMgr.registerListener(this);
mInetMgr.doYourWork();
}
}
The other class has to do pretty much all the work. Besides that it has to handle the registration of listeners it has to call the onStateChange method once something happend.
public class InternetManager {
// all the listener stuff below
public interface Listener {
public void onStateChange(boolean state);
}
private Listener mListener = null;
public void registerListener (Listener listener) {
mListener = listener;
}
// -----------------------------
// the part that this class does
private boolean isInternetOn = false;
public void doYourWork() {
// do things here
// at some point
isInternetOn = true;
// now notify if someone is interested.
if (mListener != null)
mListener.onStateChange(isInternetOn);
}
}
The part that you're missing it the class that actually notifies the listener. So you would need a class (most likely a service) that runs and pings the state of the network. Then when it detects a change it should call onStateChange() in any registered listeners. Then you would call setTheListener on that service, not on your activity.
Here's a link that thoroughly describes this design pattern: http://en.wikipedia.org/wiki/Observer_pattern