How activity leak happen in this code - android

By passing activity object to the constructor of the AsyncTask. Is there any possibility of
Activity leaking. if so then how it happen, can anyone please explain this. and how to avoid the activity leaking if it happening.
public class RemoteLoader extends AsyncTask<Void, Void, Void> {
private Activity activity;
public RemoteLoader(Activity context){
this.activity = context;
}
#Override
protected Void doInBackground(Void... Pages) {
// do in bg
}
#Override
protected void onPostExecute(Void result) {
// Set title into TextView
TextView txttitle = (TextView)activity.findViewById(R.id.txtProtip);
txttitle.setText(protip);
}
}

The AsyncTask keeps a reference to the Activity, therefore the Activity instance cannot be garbage collected while the AsyncTask is alive/running.
In certain cases, such as orientation change, the "old" Activity instance is no longer required as another Activity instance is created by the framework. In such cases, if your AsyncTask is keeping a strong reference to the Activity instance, then there will be a memory leak.
To avoid this issue, use WeakReference as follows:
public class RemoteLoader extends AsyncTask<Void, Void, Void>{
private WeakReference<Activity> activity;
public RemoteLoader(Activity context){
this.activity = new WeakReference<Activity>(context);
}
...
...
}
When using WeakReference you can ensure that the Activity instance is garbage collectable if needed. You can read more about WeakReference here.

Try having a WeakReference to your Activity in the AsyncTask. And upon completion, check if the Activity still exists.

Related

Android 'leaking context' error

I'm running an AsyncTask to retrieve some info via HTTP.
I'm calling this method from my main activity (do_get_from_url() is triggered by a button click):
private void do_get_from_url() {
new getFromURL(this).execute();
}
The class that this calls is in the same .java file and starts as follows:
class getFromURL extends AsyncTask<Void, Void, String> {
private MainActivity activity;
getFromURL(MainActivity activity){
this.activity = activity;
}
...other code here...
String linkURL = activity.link_url.getText().toString();
String getFromURLrequestURL = activity.getString(R.string.short_url_request_url_base);
...other code here...
}
It seems that I need to use activity in order to access both the string resources and the UI element link_url from the main activity BUT the line this.activity = activity displays a warning in Android Studio that it is leaking a context.
Is there any way to avoid this?
Root cause: You are passing an activity as a context to a background thread. So the thread will keep a reference to the activity. As long as the thread running, the activity cannot be destroyed (by calling finish() method or users press back key to finish the activity). The term leak simply mean when an object or instance no longer used but the system cannot reclaim memory where they live on.
Solution: In Android you can use an API called WeakReference, Simply it will keep a weak reference to your object (in this case your activity), so when the activity get destroyed, it will not keep the reference anymore.
getFromURL.java
class getFromURL extends AsyncTask<Void, Void, String> {
private WeakReference<MainActivity> mainActivityWeakReference;
getFromURL(MainActivity activity){
mainActivityWeakReference = new WeakReference<>(activity);
}
...other code here...
MainActivity activity = mainActivityWeakReference.get();
// Always check this to make sure the activity haven't got destroyed yet.
if (activity != null) {
String linkURL = activity.link_url.getText().toString();
String getFromURLrequestURL = activity.getString(R.string.short_url_request_url_base);
}
...other code here...
}
Yes, there is. Correct way is to use a WeakRefrence like this
WeakReference<MainActivity> parent;
GetFromURL(MainActivity activity){
parent = new WeakReference(activity);
}
...other code here...
String linkURL = parent.get().link_url.getText().toString();
String getFromURLrequestURL = parent.get().getString(R.string.short_url_request_url_base);
...other code here...

Avoid memory leak with WeakReference Android

I have this code example below and i want to make sure that no memory will happen. By passing the whole activity to an async task will lead to a memory leak if the task itself will have a bigger lifecycle than activity's. But if i declare a weakReference, means that if a rotation is about to happen, the activity will be destroyed without memory leak. Am right guys? Do i have to set something to null also?
Main .java
public class Main extends Activity {
private TextView mMessageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView();
new Background(this /*getBaseContext()*/ ).execute();
}
}
Background.java
public class Background extends AsyncTask<String, Void, String>
{
private WeakReference activity;
public void Background(Activity act)
{
activity = new WeakReference(act);
}
#Override
protected String doInBackground(String... params)
{
}
#Override
protected void onPostExecute(String result)
{
if(activity.get()!=null)
//ToDo
}
}
There is no need for explicit nulling when using WeakReference. Regarding the use of AsyncTask inside an Activity, just be careful not to create an anonymous instance because it will have an implicit reference to the enclosing class which can lead to memory leaks.
The code you posted seems ok, don't forget to terminate the AsyncTask gracefully when the activity is recreated.

Add view outside of onCreate in android

How can I add view bye function, outside of onCreate() in Android ?
My MainActivity.java
public class Main extends Activity {
static RelativeLayout mainRel;
static LinearLayout ll;
static TextView title;
static Context context;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
RelativeLayout mainRel=(RelativeLayout) findViewById(R.id.mainRel);
}
public static void refresh(){
LinearLayout ll = new LinearLayout(this); // actually "this" just works in onCreate;
TextView title = new TextView(this);// actually "this" just works in onCreate;
ll.setOrientation(LinearLayout.HORIZONTAL);
ll.addView(title);
title.setText("TV");
mainRel.addView(ll);
}
}
}
Thanks in advance
Usually, UI-related operations are not done in static methods. If you remove the static keyword, the this pointer will work even outside onCreate().
If you insist on keeping the static keyword (because you need the method to be static) then, you should pass a Context parameter to the method, by changing it to refresh(Context context)
Edit: if you need to call this method from another class, you might want to create a reference to your Main Activity and pass it to this other class, then call myMainActivity.refresh()
Calling static method is really bad idea.
Instead you can provide reference of the Activity to your AsyncTask in constructor. Then call non-static refresh() method from AsyncTask#onPostExecute().
When you store reference to Activity in AsyncTask use WeakReference. In case your activity is destroyed while background task is working, it won't be held in memory till background ends.
public class YourAsyncTask extends AsyncTask<Void, Void, Void> {
private WeakRefrence<Main> mainRef;
public YourAsyncTask(Main activity) {
mainRef = new WeakReference<Main>(activity);
}
protected void onPostExecute(Void result) {
Main main = mainRef.get();
if (main != null) {
main.refresh();
}
}
}

How to check if an activity is continuing

In my app, I am trying to run multiple photos through a photo editor (one at a time). I have it set up in a for-loop at the moment, but I feel like it is overloading the photo editor and not actually waiting until the current edit session is over, so I wanted to put a control statement in my for-loop to check if the session was still active.
Is this possible?
Easier to tell when the current is over than polling to see if it is still active. If you start it with startActivityForResult, your calling activity will be notified when the invoked activity ends.
Have a look at Starting Activities and Getting Results in the Activity docs for an example.
You might also want to consider running this in an async task. This will pull your heavy processing away from the UI thread. Async task let's you do progress updates as well.
http://developer.android.com/reference/android/os/AsyncTask.html
public class MainActivity extends ListActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.logo_page);
// Call async task.
my_async as = new my_async(this);
as.execute();
}
--
my_async:
public class my_async extends AsyncTask<Object, Integer, String> {
private parentClass activity;
public my_async (parentClass activity) {
this.activity = activity;
}
#Override
protected String doInBackground(Object... arg0) {
// Do stuff
return "MyString";
}
protected void onPostExecute(String contents) {
activity.contents = contents;
}

What happens to an AsyncTask when the launching activity is stopped/destroyed while it is still running?

I've seen few questions nearly identical to mine, but I couldn't find a complete answer that satisfies all my doubts.. so here I am.. Suppose that you have an activity with an inner class that extends the AsyncTask class like this:
public class MyActivity extends Activity {
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
protected Bitmap doInBackground(String... urls) {
return DownloadImage(urls[0]);
}
protected void onPostExecute(Bitmap result) {
ImageView img = (ImageView) findViewById(R.id.img);
img.setImageBitmap(result);
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new DownloadImageTask().execute("http://mysite.com/image.png")
}
}
Suppose that the activity is paused or destroyed (maybe the two cases are different) while the DownloadImageTask is still running in background.. then, the DownloadImageTask's methods that run on the activity UI thread can be triggered and the DownloadImageTask may try to access Activity's methods (it is an inner class, so it can access the methods and instance variables of the outer class) with a paused or destroyed Activity, like the call to findViewByID in the example below.. what happens then? Does it silently fail? Does it produce any exception? Will the user be notified that something has gone wrong?
If we should take care that the launching thread (the Activity in this case) is still alive when running-on-UI methods are invoked, how can we accomplish that from within the AsyncTask?
I'm sorry if you find this as a duplicate question, but maybe this question is a bit more articulated and someone can answer with greater detail
Consider this Task (where R.id.test refers to a valid view in my activity's layout):
public class LongTaskTest extends AsyncTask<Void, Void, Void>{
private WeakReference<Activity> mActivity;
public LongTaskTest(Activity a){
mActivity = new WeakReference<Activity>(a);
}
#Override protected Void doInBackground(Void... params) {
LogUtil.d("LongTaskTest.doInBackground()");
SystemClock.sleep(5*60*1000);
LogUtil.d("mActivity.get()==null " + (mActivity.get()==null));
LogUtil.d("mActivity.get().findViewById(R.id.frame)==null " + (mActivity.get().findViewById(R.id.test)==null));
return null;
}
}
If I run this task from an Activity's onCreate like so:
public class Main extends Activity {
#Override
public void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.testlayout);
new LongTaskTest(this).execute();
finish();
}
}
No matter how long I sleep the background thread, my log always shows:
LongTaskTest.doInBackground()
mActivity.get()==null false
mActivity.get().findViewById(R.id.frame)==null false
Which is to say that the activity and its views appear to stay alive (even if I manually issue GCs via DDMS). If I had more time I'd look at a memory dump, but otherwise I don't really know why this is the case ... but in answer to your questions it appears that:
Does it silently fail? No
Does it produce any exception? No
Will the user be notified that something has gone wrong? No
The doInBackground() will keep on running even if your Activity gets destroyed(i,e your main thread gets destroyed) because the doInBackground() method runs on the worker's/background thread. There will be a 'problem' in running the onPostExecute() method as it runs on the main/UI thread and you may experience running into unrelated data but there will be no exception shown to the user. Thus, it is always better to cancel your AsyncTask when your activity gets destroyed as there is no reason to run AsyncTask when the Activity is no longer present. Use android Service if you continuously want to download something from the network even when your Component/Activity gets destroyed. Thanks.

Categories

Resources