So, I often hear that "holding on to a static activity or view, especially inside an AsyncTask that is long running will cause a memory leak and crash your app".
However, I've been unsuccessfully able to actually prove that in an Android Emulator.
What am i doing wrong?
public class MainActivity extends AppCompatActivity {
static TextView label;
static List<Activity> sHolder = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ViewGroup vg = findViewById(R.id.blah);
for (int i=0; i<1000000; i++) {
ImageView im = new ImageView(this);
im.setImageDrawable(getApplicationContext().getDrawable(R.drawable.kitten_original));
vg.addView(im);
new MyTask(this).execute();
}
sHolder.add(this);
}
@Override
protected void onStart() {
super.onStart();
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
}
class MyTask extends AsyncTask<Void, Void, Void> {
Activity activity;
public MyTask(Activity activity) {
this.activity = activity;
}
@Override
protected Void doInBackground(Void... voids) {
try {
Thread.sleep(10000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return null;
}
}
Rather than every crashing, My computer just starts lagging a little bit, and it keep seeing like 100 MB of Garbage collection happening.
How can i actually force an app to crash? (Please don't ask why i want to force an app to crash, i'm doing some limit testing)
I often hear that "holding on to a static activity or view, especially inside an AsyncTask that is long running will cause a memory leak and crash your app".
That is a simplified explanation.
However, I've been unsuccessfully able to actually prove that in an Android Emulator.
I suspect that this code is not running. You should crash after a couple of hundred passes through the loop, as there is a limit on how many queued-up AsyncTasks
you can have, and that limit is not tied to memory consumption.
Ignoring the million AsyncTasks
and the million ImageViews
, you are leaking the activity by means of sHolder
, much as how in this sample I leak an activity by holding a static
reference to a Button
from its layout:
/***
Copyright (c) 2015 CommonsWare, LLC
Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy
of the License at http://www.apache.org/licenses/LICENSE-2.0. Unless required
by applicable law or agreed to in writing, software distributed under the
License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Covered in detail in the book _The Busy Coder's Guide to Android Development_
https://commonsware.com/Android
*/
package com.commonsware.android.button;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Button;
public class ButtonDemoActivity extends Activity {
private static Button pleaseDoNotDoThis;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
pleaseDoNotDoThis=(Button)findViewById(R.id.button1);
}
}
You can demonstrate that a leak occurs using LeakCanary, Android Studio's heap analyzer, etc. However, to demonstrate that leak, you need to run the app, then press BACK, and see that your destroyed activity is not garbage-collected. Or, you need to run the app, rotate the screen (or undergo any other type of configuration change), and see that you now have two activity instances, the destroyed-and-leaked one plus the current one. If you just run the app and do nothing, you have not leaked the activity — while you have your own static reference to it, so does Android, because the activity is in the foreground and the user can see it.
A leak does not itself cause a crash. It just means that you are tying up heap space that cannot be used for other things. Eventually, you will get an OutOfMemoryError
on some allocation. If all you want to do is crash with an OutOfMemoryError
, try allocating some massive byte[]
(say, 1GB).
If you specifically want to test an OutOfMemoryError
triggered by leaked activities, you would need to:
Have an activity allocate a significant amount of heap space (e.g., a 1MB byte[]
),
Do something like your sHolder
list, and
Rotate the screen a lot, or otherwise put yourself in a situation where lots of instances of the activity would be created and destroyed, but not garbage-collected, courtesy of your sHolder