I was wondering, during activity re-creation, or fragment re-creation or service re-creation, is there a possibility, that the same instance of class being re-used?
For example
//public class HomeFragment extends Activity {
//public class HomeFragment extends Service {
public class HomeFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Can instrumentSearchMonitor has possibility to become null right here, due to onDestroy?
instrumentSearchMonitor.doSomething();
}
#Override
public void onDestroy() {
super.onDestroy();
instrumentSearchMonitor = null;
}
private InstrumentSearchMonitor instrumentSearchMonitor = new InstrumentSearchMonitor();
}
For the above case, is there a possibility that onCreate will encounter a null object, due to nullify action in onDestroy?
My testing is, after onDestroy is being called, the next call of onCreate will happen on the different class instance.
I was wondering, is there any possibility, that the next call of onCreate, will happen on the same class instance?
Once onDestroy() has been called, the fragment is going to be completely removed and can not be reused.
This can be seen in the Fragment lifecycle:
https://i.stack.imgur.com/fRxIQ.png
In the image you can seee that onDestroyView() can be called, and then it goes back to onCreateView(), but once onDestroy() is called, you're safe to null/delete whatever you want in that class instance.
Put your code above onDestroy
#Override
public void onDestroy() {
instrumentSearchMonitor = null;
super.onDestroy();
}
Related
I have a BaseFragment which within it's onCreateView method, creates a MyObject class. Both of these are inside a ViewPager.
Two different fragments extends from the BaseFragment - FragmentA, FragmentB.
This means FragmentA and FragmentB both have their own instances of the MyObject object.
Within the BaseFragment, I call myObject.initialise(); on the MyObject object from the onStart(); method and cleanUp(); from the onStop();
#Override
public void onStart()
{
super.onStart();
myObject.initialise();
}
#Override
public void onStop()
{
myObject.cleanUp();
super.onStop();
}
Again - this lives inside the BaseFragment so both FragmentA and FragmentB have this in their lifecycle.
The initialise(); function and cleanUp(); functions look like this:
#Override
public void initialise()
{
BusManager.register(this);
}
#Override
public void cleanUp()
{
BusManager.unregister(this);
}
FragmentA will generally close first and it successfully unregisters. When FragmentB closes however, it crashes because it think this was not registered.
I checked the memory address of this and it appears that it tries to unregister the same thing twice.
Missing event handler for an annotated method. Is class com.example.app.MyObject registered?
Why is it doing this? I have made sure that MyObject is a new instance.
For the comment above, note that onDestroy() is not necessary called:
https://developer.android.com/reference/android/app/Activity.html#onDestroy()
You should not count on that for Otto's register / unregister call.
In regarding to Subby's question: I've had scenarios where onStart() / onStop() being called twice. What I ended up is placing a try-catch block. Definitely not a clean solution, but that's how I do before finding out why the lifecycle is messed up.
There are a lot of answered questions in Stackoverflow about checking if activity is null from a fragment, using getActivity()==null
How do I check if activity is not null in the activity itself?
My specific case is this:
activity starts an asynctask, then activity is destroyed, then asynctask returns in onPostExecute, it invokes a method in the activity (which is registered as a listener for that task) and this method uses a reference to THIS to pass a context in a method. The context is null, though.
EDIT: Here is some code.
public interface OnGetStuffFromServerListener {
void onGetStuffSuccess();
}
public class SomeActivity implements OnGetStuffFromServerListener {
#Override
public whatever onCreate() {
new GetStuffFromServer(this).execute();
}
#Override
public void onGetStuffFromServerSuccess() {
deleteSomeFiles(this); // NPE -> How do I check if activity still exists here?
}
private void deleteSomeFiles(Context context) {
...
context.getExternalFilesDir(null).toString(); // NPE toString on a null object reference
}
}
public class GetSomeStuffFromServer extends AsyncTask<Void, Void, Void> {
private OnGetSomeStuffFromServerListener listener;
public GetSomeStuffFromServer (OnGetSomeStuffFromServerListener listener) {
this.listener = listener;
}
...doInBackground
onPostExecute() {
if(listener!=null) {
listener.onGetSomeStuffFromServerSuccess();
}
}
}
Actually, if I am using getApplicationContext() instead of this, maybe I will not have a problem at all?
I'm not sure why your Activity is being destroyed. Although you may be able to recreate the Activity using a Bundle. Google's documentation on Activities gives the following sample for saving and restoring an instance of your activity.
The following will save the state of your Activity:
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
...
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state
savedInstanceState.putInt(STATE_SCORE, mCurrentScore);
savedInstanceState.putInt(STATE_LEVEL, mCurrentLevel);
// Always call the superclass so it can save the view hierarchy state
super.onSaveInstanceState(savedInstanceState);
}
The following will be called to restore your Activity's previous state. Note that the logic is contained in onCreate(), so it sounds like you will have initialize your Activity again.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
// Check whether we're recreating a previously destroyed instance
if (savedInstanceState != null) {
// Restore value of members from saved state
mCurrentScore = savedInstanceState.getInt(STATE_SCORE);
mCurrentLevel = savedInstanceState.getInt(STATE_LEVEL);
} else {
// Probably initialize members with default values for a new instance
}
...
}
Let me know if that helps!
edit:
Try canceling the operation in onDestroy(). If the Activity has called onDestroy() its memory has been released by the device. Make sure you aren't disposing of your Activity anywhere else in your code.
#Override
protected void onDestroy() {
asynctask.cancel(true);
super.onDestroy();
}
Use myActivity.isDestroyed()
reference to doc
I have an abstract Activity that serves as a basis for other activities:
Since I am settings the content view in the derived Activities and I have common UI elements in all activities, I would like to execute code in the derived Activities after the content view has been set.
How can can I do this in the abstract class without putting the method call in every derived Activity?
abstract public class BaseActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
public void methodToBeCalledAfterOnCreateOfDerivedActivity(){
//method that does work on common UI elements, so setContentView() needs to have been called
}
}
public class myActivity extends BaseActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
//how can I call this method automatically at the end of this onCreate without explicitly putting the method here:
methodToBeCalledAfterOnCreateOfDerivedActivity();
}
}
The sequence of calls when an Activity is created the first time or the view needs to be recreated is:
onCreate()
onCreateView()
onViewCreated()
onViewStateRestored()
onStart()
onResume()
So, if you only need your method to be called when the view is created, you could do it in onViewCreated(). If you need it to run whenever your activity is restarted, you would do it in onStart().
onStart() is invoked after onCreate(). You can probably put your post-onCreate() functionality there. Use a flag set in the abstract class onCreate() to distinguish between onStart() invocations following onCreate() and onStop()-onRestart().
I just recently started learning how to build android apps, and encountered a problem:
I want, when users leave the app (go to the homescreen, multitask), and they return, that the app calls a certain method. How can I do that?
This problem is more tricky than it may look like. When you return to app after leaving it, then is called method onResume of activity which was active when app was interrupted. But same happens when you go from one activity to another (onResume of second activity is called). If you just call method from onResume, it will be called every time onResume of any activity is called.
Take a look at this solution...
First, you have BaseActivity which is extended by all activities that need to call that method:
abstract public class BaseActivity extends Activity implements IName {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
protected void onResume() {
if (AppClass.getPausedActivity() != null) {
if (this.getClassName().equals(AppClass.getPausedActivity()))
//call specific method
}
AppClass.setPausedActivity("");
super.onResume();
}
#Override
protected void onPause() {
AppClass.setPausedActivity(this.getClassName());
super.onPause();
}
#Override
abstract public String getClassName();
}
As you can see it implements interface IName:
public interface IName
{
String getClassName();
}
BaseActivity in onPause (when it is interrupted) calls setPausedActivity method of AppClass which remembers last activity name that was interrupted. In onResume (when app and activity is continued) we compare name of current activity and last paused activity.
So, when app is interrupted, these names will be same because you paused one activity and you got back to the same one. When you call activity from some other activity these names will not be same and method will not be called.
Here is code for AppClass:
public class AppClass extends Application {
public static String pausedActivity;
public static String getPausedActivity() {
return pausedActivity;
}
public static void setPausedActivity(String _pausedActivity) {
pausedActivity = _pausedActivity;
}
}
Also, here is example of activity that extends BaseActivity:
public class MainActivity extends BaseActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
}
//here you set name of current activity
#Override
public String getClassName() {
return "MainActivity";
}
}
You are bound to the Activity lifecycle. You will need to implement corresponding logic to figure out if the user has been in your app before (i.e. using SharedPreferences).
I have a little problem about needing Activity/Application everywhere...
More precisely I have classes defined like this:
public class MyApp extends Application {
private static Activity currentActivity;
private static MyApp instance;
#Override
public void onCreate() {
super.onCreate();
instance = this;
}
public static void setCurrentActivity(Activity activity) {
currentActivity = activity;
}
}
And I have an Extended activity like this:
public class MyActivity extends Activity {
....
#Override
public void onResume() {
super.onResume();
MyApp.setCurrentActivity(this);
}
....
#Override
public void onDestroy() {
super.onDestroy();
MyApp.setCurrentActivity(null); // for nullifying static reference
}
}
My questions are:
Is the above code snippet fair ?
Should I use super.onDestroy(); after the MyApp.setCurrentActivity(null); to verify that activity is not reffered and destroyed succesfully?
And most important, what about static reference instance? where can I nullify it to not prevent from GC ?
Thanks.
Is the above code snippet fair ?
Nope. The biggest problem is in MyActivity.onDestroy(). This can be called at any time when the activity is no longer in use (ie: after it has finished). If you null out currentActivity in onDestroy() you will not be clearing the reference to MyActivity, you will be clearing the reference to whatever activity happens to be currently active at the moment. I you really want to have currentActivity point to the current activity, the best you can do is something like this:
#Override
public void onPause() {
super.onPause();
if (MyApp.getCurrentActivity() == this) {
MyApp.setCurrentActivity(null);
}
}
Also, be aware that there is a small window between onPause() of an activity and onResume() of the next activity so that it is possible that currentActivity will be null during this time. Watch out for that!
Should I use super.onDestroy(); after the
MyApp.setCurrentActivity(null); to verify that activity is not
reffered and destroyed succesfully?
See my comment above. You cannot null out this reference in onDestroy().
And most important, what about static reference instance? where can I
nullify it to not prevent from GC ?
You don't need to. There is only ever one instance of MyApp created and it lives for as long as your process exists. When Android doesn't need your process anymore it just kills the process, which will clean up everything automagically.