When would Activity's instance die? - java

Here is a sample code which make me a little missing:
package com.leak;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
public class WindowLeakActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new LeakThread().execute();
}
class LeakThread extends AsyncTask<Void, Void,Void>{
ProgressDialog dialog;
#Override
protected void onPreExecute() {
dialog=new ProgressDialog(WindowLeakActivity.this);
dialog.show();
}
#Override
protected Void doInBackground(Void... params) {
try {
Thread.sleep(2000);
finish();
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
#Override
protected void onPostExecute(Void result) {
//that would be ok
if(WindowLeakActivity.this!=null && !WindowLeakActivity.this.isFinishing())
dialog.dismiss();
}
}}
As you see,I create a LeakThread and finish the WindowLeakActivity at doInBackground()method.
In order to prevent window leak error,I have to check if the Activity has been finished at onPostExecute()method.That make me a little missing.I have following questions:
Is do Activity instance isFinish() check at onPostExecute safe?If my Thread class is not a inner class of Activity.Do I have to check Activity instance is not null at first?
When would Activity instance die?As Activity's lifecycle description,it will terminal when callback call onDestroy().But however,the Activity's Thread is still going.Though it's window been not visible,I can also get it's instance.
If I call the System.gc().Will it collect Activity's instance?
Sorry for my bad description.Thank you for reading my question very much.

1) Generally as a rule, avoid using any reference to the activity inside doInBackground().
Managing AsyncTask along with the life cycle of an Activity is tricky at best. Look at this StackOverflow thread for a good discussion on AsyncTask and its pitfalls.
2) You are not in control of when the instance of the activity will die, so don't go about depending on it. The destruction of the instance of the activity depends on several factors, which are determined by the system. So try and ensure that you don't use a reference to the Activity anywhere outside the scope of the activity object itself. You however, do receive a callback when your Activity's execution is about to stop, so make sure you clean up memory there.
3) System.gc() is more like a request to the JVM, asking it to run the garbage collector as soon as it is conveniently possible. Take a look at this thread.
From personal experience, I can tell you this, try and avoid using ProgressDialog when using AsyncTask. It is painful to manage, can leak your Window object pretty easily, crash the application as soon as your devices configuration changes and pretty much give you hell trying to debug it. I've not even seen the Google apps on Android utilize ProgressDialog perfectly (uptil Gingerbread i.e. ). That's just my experience however.

Related

Best way to return data to MainActivity from AsyncTask

I'm using an ASyncTask in my app to get some data (a short URL) via a REST API from a web service (Bitly).
When the ASyncTask completes I want to pass the result back to my MainActivity.
Getting the data back to the MainActivity is acheievd by using the onPostExecute method of the AsyncTask.
I've read and read and read about how to do this and there seem to be two general approaches.
Originally I was using a 'WeakReference' approach whereby at the start of the AsyncTask class you create a weak reference to your MainActivity as follows:
private class getShortURL extends AsyncTask<String, Void, String> {
private WeakReference<MainActivity> mainActivityWeakReference;
myASyncTask(MainActivity activity) {
mainActivityWeakReference = new WeakReference<>(activity);
}
{etc etc}
With this approach your AsyncTask class sits outside of your MainActivity class and so a lot of things need to be referenced via the weak reference.
This worked fine (except I suspected - possibly incorrectly - that this weak reference may have been the cause of occassional NPEs), but I then found another way of doing things.
This second approach involved moving the ASyncTask class inside of the MainActivity class.
This way I was able to access everything that was accessible in the MainActivity class directly, inlcuding UI elements and methods defined in the MainActivity. It also means that I can access resources such as strings etc and can generate toasts to advise the user what is happening.
In this case the whole of the WeakReference code above can be removed and the AsyncTask class can be made private.
I am also then able to do things like this directly in onPostExecute or to keep this in a method within the MainActivity that I can call directly from onPostExecute:
shorten_progress_bar.setIndeterminate(false);
shorten_progress_bar.setVisibility(View.INVISIBLE);
if (!shortURL.equals("")) {
// Set the link URL to the new short URL
short_link_url.setText(shortURL);
} else {
CommonFuncs.showMessage(getApplicationContext(), getString(R.string.unable_to_shorten_link));
short_link_url.setHint(R.string.unable_to_shorten_link);
}
(note that CommonFuncs.showMessage() is my own wrapper around the toast function to make it easier to call).
BUT, Android Studio then gives a warning that "the AsyncTask class should be static or leaks might occur".
If I make the method static I then get a warning that the method from the MainActivity that I want to call from onPostExecute cannot be called as it is non-static.
If I make that method from MainActivity a static method, then it cannot access string resources and any other methods that are non static - and down the rabbit hole I go!
The same is true, as you would expect, if I just move the code from the method in the MainActivity into the onPostExecute method.
So...
Is having an AsyncTask as a non-static method really a bad thing? (My
app seems to work fine with this warning in AS, but I obviously don't
want to be creating a memory leak in my app.
Is the WeakReference appraoch actually a more correct and safer approach?
If I use the WeakReference approach, how can I create things like toasts which need to be run on the UI thread and access string
resources etc from the MainActivity?
I read somewhere about creating an interface but got a bit lost and couldn't find that again. Also would this not have the same kind of reliance on the MainActivity that a WeakReference does and is that a bad thing?
I'm really looking for best practice guidance on how to get some data back to the MainActivity and the UI thread from an AsyncTask that is safe and doesn't risk memory leaks.
Is having an AsyncTask as a non-static method really a bad thing? (My app seems to work fine with this warning in AS, but I obviously don't want to be creating a memory leak in my app.
Yes, your Views and your Context will leak.
Enough rotations and your app will crash.
Is the WeakReference approach actually a more correct and safer approach?
It's lipstick on a dead pig, WeakReference in this scenario is more-so a hack than a solution, definitely not the correct solution.
What you're looking for is a form of event bus from something that outlives the Activity.
You can use either retained fragments* or Android Architecture Component ViewModel for that.
And you'll probably need to introduce Observer pattern (but not necessarily LiveData).
If I use the WeakReference approach, how can I create things like toasts which need to be run on the UI thread and access string resources etc from the MainActivity?
Don't run that sort of thing in doInBackground().
I'm really looking for best practice guidance on how to get some data back to the MainActivity and the UI thread from an AsyncTask that is safe and doesn't risk memory leaks.
The simplest way to do that would be to use this library (or write something that does the same thing yourself, up to you), put the EventEmitter into a ViewModel, then subscribe/unsubscribe to this EventEmitter inside your Activity.
public class MyViewModel: ViewModel() {
private final EventEmitter<String> testFullUrlReachableEmitter = new EventEmitter<>();
public final EventSource<String> getTestFullUrlReachable() {
return testFullUrlReachableEmitter;
}
public void checkReachable() {
new testFullURLreachable().execute()
}
private class testFullURLreachable extends AsyncTask<Void, Void, String> {
...
#Override
public void onPostExecute(String result) {
testFullUrlReachableEmitter.emit(result);
}
}
}
And in your Activity/Fragment
private MyViewModel viewModel;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
viewModel = ViewModelProviders.of(this).get(MyViewModel.class);
// ...
}
private EventSource.NotificationToken subscription;
#Override
protected void onStart() {
super.onStart();
subscription = viewModel.getTestFullUrlReachable().startListening((result) -> {
// do `onPostExecute` things here
});
}
#Override
protected void onStop() {
super.onStop();
if(subscription != null) {
subscription.stopListening();
subscription = null;
}
}

Communicate between Android Activity and Java Class

I have to communicate between an Android Activity and another Java class. In a very, very stripped down version of what I want, I want the Java Class to run, and when it's done, set some information. To get a more specific idea of what I want to happen:
Activity {
CallJavaClass(); // Doesn't return anything, would be very bad form
GetInfoFromJavaClass() // Once the JavaClass has finished what needs to be done
}
What I could do:
Set a global variable in JavaClass that my Activity can access. I'd rather not do it this way, as I would have to implement some kind of OnChangedListener on that object in the JavaClass.
Use an Interface with Setters/ Getters. The problem with this is my JavaClass is a Singleton, and most of its methods are static, which is a no-go when working with an Interface.
Create a separate class that handles these variables. I would rather keep it simple and not have to use a bunch of different classes to do this, though.
So what do you think would be the best solution? If needed (and probably will be), I can provide more information about what exactly I want done. Thanks for your help in advance.
Sounds like something you could use AsyncTask for http://developer.android.com/reference/android/os/AsyncTask.html
then again, it depends on the specifics of what you're going for
AsyncTask should resolve your problem:
private class myTask extends AsyncTask<Void, Void, Boolean> {
protected void onPreExecute() {
super.onPreExecute();
// do something before starting the AsyncTask
}
#Override
protected Boolean doInBackground(Void... params) {
// do what you want to do
return false;
}
protected void onPostExecute(Boolean success)
{
super.onPostExecute(success);
// do something right after you finish the "doInBackground"
}
}

What is the point of super.onStop()?

I started learning how to create an app on Android. I have a bit of knowledge on Java already, and now I'm trying some of the activity events.
I know how to use onCreate or onCreateOptionsMenu, and now I'm testing out onStop:
#Override
public void onStop(){
//a function that simply creates and return an AlertDialog.Builder object
displayPopup("Event", "Stopped", "OK").show();
}
I thought this would work since there's no error at compile time. But when I try to exit the app, I expect a popup dialog would show up but instead the app just crashed. It turns out that I need one extra line to make it work:
super.onStop();
It doesn't do anything (at least I can't see anything changed) and it's kind of useless, but without this line the app just keeps crashing.
Any help would be great, thanks.
It calls the onStop() method in the parent Activity class. When you look at the source code, you'll see that it does some internal housekeeping.
protected void onStop() {
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
getApplication().dispatchActivityStopped(this);
mTranslucentCallback = null;
mCalled = true;
}
Your custom Activity extends the type Activity. The onStop() method is part of the super class activity. If you don't call super.onStop() the implementation on the Activity class is never called, and only your implementation is. This implies crashes since the onStop() method in the Activity classperforms clean ups that must be called.
The android reference on onStop() : http://developer.android.com/reference/android/app/Activity.html#onStop()
Your class is an activity subclass (extends Activity), witch mean that you must call super method of the activity mother class for more information about details of super.onstop() you can check the source code

Android - Catching onPause without being the active Activity

Is there any way to catch onPause() in a java class that is Not an activity? I have native code with a Java-helper class that is used in other apps, and whenever those apps enters background I want to prepare for it too. How can I solve this? Is it enough to have my Java class extend some class to get the onPause(), even though its not registered as an activity in manifest etc?
Create a public method named onPause() or prepare() or whateverSuitYou() in your helper class, and invoke it from the onPause() of your Activity.
Today we can use registerActivityLifecycleCallbacks() of the Application class and provide onActivityPaused().
Example:
AppContext.registerActivityLifecycleCallbacks(new Application.ActivityLifecycleCallbacks() {
#Override
public void onActivityPaused(Activity activity) {
//handle here
}
}

How to get Async to run thread in background?

I am pulling around 1500 data plots and adding them to overlays for an map view. I want to run this in another thread while the rest of my program finishes starting up. I would like a progress spinner to spin only on the map portion while its loading the data plot points.
I have searched and found what I need, but Im not sure how to implement it and where in my code to put it.
What would I put in the params
Does this go in another class or in my main oncreate method.
When would I call the methods?
private class UpdateFeedTask extends AsyncTask<MyActivity, Void, Void> {
private ProgressDialog mDialog;
protected void onPreExecute() {
Log.d(TAG, " pre execute async");
mDialog = ProgressDialog.show(MyActivity.this,"Please wait...", "Retrieving data ...", true);
}
protected void onProgressUpdate(Void... progress) {
Log.d(TAG, " progress async");
}
#Override
protected Void doInBackground(MyActivity... params) {
// TODO Auto-generated method stub
return null;
}
protected void onPostExecute(Void result) {
Log.d(TAG, " post execute async");
mDialog.dismiss();
}
}
From your question I actually am not a hundred percent sure what you currently understand about AsyncTasks so this may be a little some stuff you already know but bear with me.
"Does this go in another task or in my onCreate method?":
AsyncTask is a class which you are supposed to subclass to do what you need, it is not a piece of code which can inlined in your onCreate. You could make an anonymous AsyncTask class in your onCreate, but usually you want it as either a private internal class or its own class entirely.
As for when you call the methods; you don't they are lifecycle events.
onPreExecute() is called on the UI thread just before starting the background work and is a place to do things such as modify components to show progress, or throw up a dialog.
doInBackground(Params...) is the main method which runs in the background on another thread, do your work here. Do not try to modify UI here
onPostExecute(Result) is when your task has finished and is run on the UI thread again. This is where you should handle your UI update.
You only call execute(Params..), which will start the AsyncTask, passing the objects you put as the params into the doInBackground(Params...) method of the task. So the answer as to what to put as params is whatever you need to have access to in doInBackground(Params...).
That should be a decent overview for your needs but you should really check out the docs.
To start the AsyncTask, you simply go
(new UpdateFeedTask()).execute(activityInstance);
It can go where ever you want it, though where you put it might limit access to the variables and objects you want to interact with. E.g. private internal class will have access to them while an entirely new class might not have as easy of an access.
doInBackground(MyActivity... params)
is where the parameter you passed into the execute() function will go, and you can access it via params[0].
You should not call any methods in the AsyncTask class, besides execute().
1. What would I put in the params
It depends. The First parameter is what the task will take in.
The last generic is what it will return.
2.Does this go in another class or in my main oncreate method. You call the execute method when the you want the task to run. The implementation can be in the Activity class or in a different .java file.
3.When would I call the methods?
You only call the execute method. That will make the task run in the background. Then the task will call onPostExecute when it is done.
Here is an example
private class UpdateFeedTask extends AsyncTask<String, Void, DataReturnType> {
#Override
protected DataReturnType doInBackground(String... params) {
String url = params[0];
//Get data from URL
}
#Override
protected void onPostExecute(ReturnDataType result) {
//Do something with result
}
}
Then call the task using the execute("http://foo.com")
Also add android:configChanges=true to the Activity in the manifest. This will make sure that the activity is not killed when the task is still running in the background. Otherwise the task will finish and report back to a null Activity unless you tell the task to callback the new activity.

Categories

Resources