This article describes "Be careful, because non-static nested classes grab an enclosing object as an implicit reference" in Java.
The reason why you have to be careful is that it increases the possibility of causing a memory leak.
And the bottom line is, "Nested classes are better off with the static
modifier (because they don't hold the enclosing object as an implicit reference). " Enclosing objects are short-lived, especially if instances of nested classes are longer-lived!
(This article is not specific to Android application development, but as you read it, you will be involved in Java, so please be patient at first.)
It was one day. When I was making an Android app in Java, Android Studio [^ 1] issued a warning like this.
[^ 1]: IDE for Android application development made by modifying IntelliJ IDEA
This 'AsyncTask' class should be static or leaks might occur
This class named CouponAsyncTask is defined as a nested class (inner class / inner class), but the balloon says "Please add a static modifier, otherwise it may cause a memory leak". I am. Well, I'm scared.
If you look at the detailed explanation that the code check function "Lint" that Android Studio has,
Static Field Leaks A static field will leak contexts. Non-static inner classes have an implicit reference to their outer class. If that outer class is for example a Fragment or Activity, then this reference means that the long-running handler/loader/task will hold a reference to the activity which prevents it from getting garbage collected. Similarly, direct field references to activities and fragments from these longer running instances can cause leaks. ViewModel classes should never point to Views or non-application Contexts. Issue id: StaticFieldLeak
I will ask Google translation.
Static field leak Static fields leak context. Non-static inner classes have implicit references to outer classes. If the outer class is, for example, a fragment or activity, this reference means that the long-running handler / loader / task holds a reference to the activity that prevents it from getting garbage collection. Similarly, direct field references to activities and fragments from these longer running instances can cause leaks. The ViewModel class does not point to a view or non-application context. Problem ID: StaticFieldLeak
Even if a difficult English sentence is translated into a difficult Japanese ... It's Chimpung Kampung.
It was properly posted on the official website for Android application developers as "an example of a common flaw in code design using threaded objects". I wonder if there is. Improvement of performance by threading [implicit reference]
On the Japanese page of this official website, it is translated as "implicit references", but the original text is written as "Implicit references". In this article, we will use five-character idioms as "implicit reference."
Even if I suddenly talk about "implicit reference of enclosing object held by non-static nested class", there is a lot of information on this subject, so I will explain the prerequisite knowledge one by one. Hurry up. However, it may be a little frustrating.
A "nested class" is also known as an "inner class". This article is unified with "nested class".
Foo class has two nested classes
public class Foo {
class Bar {
void methodBar() {
System.out.println("methodBar");
}
}
static class Baz {
void methodBaz() {
System.out.println("methodBaz");
}
}
void methodFoo() {
System.out.println("methodFoo");
}
}
in this case,
--The Bar and Baz classes are called "foo class nested classes". ――Foo is called "Bar class enclosing class".
The "enclosing class" is also known as the "outer class". This article will be unified with "Enclosing Class".
The difference between Bar and Baz is whether or not they have the static
modifier.
--The Bar class is called "non-static nested class of Foo class". For those who find it annoying, the "Foo class nesting class" is enough. --The Baz class is called "static nested class of Foo class".
Instantiate two nested classes of Foo
class Main {
public static void main(String[] args) {
Foo foo = new Foo();
Foo.Bar bar = foo.new Bar();
Foo.Baz baz = new Foo.Baz();
}
}
It took two lines to instantiate the Bar class. If you really want to do it in one line, use the following code.
Instantiate a Bar class in one line
Foo.Bar bar = new Foo().new Bar();
In any case, if you want to instantiate a non-static nested class, you need an instance of enclosing.
And it is also characteristic that .
is added before the new
operator.
You don't need an instance of enclosing in advance. You can suddenly new
.
What is unique is that the class name is " Foo.Baz
"instead of" Baz
". It is characteristic that .
is included between the class names.
Effective java started from the first edition (published in 2001), "Static member classes rather than non-static member classes. I have a lot of things to choose. It is the Goseibai Shikimoku that remains even in the third edition published in 2018.
Memory leaks can be catastrophic.
And
Memory leaks are usually difficult to detect.
And
Keep extra references and waste memory and time.
Such as, scary things are written. Because
** If you omit the static modifier, each instance will have an irrelevant reference to the enclosing object. Saving that reference takes time and memory. The serious thing is that without that reference, the enclosing instance can be left behind if it is subject to garbage collection. ** **
Is written.
I modified the above program a little. The static nested class Baz
has been dismissed a bit.
python
public class Main {
public static void main(String[] args) {
Foo foo = new Foo();
Foo.Bar bar = foo.new Bar();
foo = null;
System.gc();
// foo.methodFoo(); //NullPointerException occurred
bar.methodBar();
}
}
I assigned null
to the Foo
instance of the enclosing class to perform garbage collection (GC), and then called the method to the Bar
instance of the non-static nested class. When I try to execute it, it is as follows.
Execution result
methodBar
Yeah, it works! I glanced at it.
As a Java spec, no matter how much I assign a null
to an instance of the enclosing class Foo
, the object named foo
will work as long as the Bar
instance of that non-static nested class works. It is not GC (it was brilliantly passed through without being considered as a GC target). Because ** bar
holds foo
as an implicit reference **.
If you make an illustration as a funny analogy, it looks like this.
The woman who is climbing the stairs step by step is full of energy at bar
.
On the other hand, the old man who wants to be a Buddhahood is foo
, but because bar
holds it, he is not easily called to heaven. It can be a stone tape.
--Gripping = referring --Called to heaven = released from memory by GC --Stone Tape = Memory Leak
Please read as.
Stop using Foo, Bar, and Baz and see the program below.
Before: Nested class is non-static
class Fizz {
private Integer x = 12;
private static Integer y = 34;
class Buzz {
void m() {
x = 56;
y = 78;
System.out.println(x + y);
}
}
}
class FizzBuzzMain {
public static void main(String[] args) {
Fizz.Buzz fb = new Fizz().new Buzz();
fb.m();
}
}
Before execution result
134
It's a non-static nested class, even though it's (intentionally) done. Now, let's simply add the static modifier to this nested class named Buzz
.
After
class Fizz {
private Integer x = 12;
private static Integer y = 34;
static class Buzz {
void m() {
x = 56; //Cannot compile because x is not a static field
y = 78;
System.out.println(x + y); //Cannot compile because x is not a static field
}
}
}
class FizzBuzzMain {
public static void main(String[] args) {
Fizz.Buzz fb = new Fizz.Buzz();
fb.m();
}
}
Immediately I couldn't compile. The IDE gets angry if it doesn't work because x
is not a static field.
Why. That's because ** static nested classes can only access static members of enclosing **.
Please see this animated GIF for the Android app you wanted to make.
As soon as you start it, the count-up will start automatically. This animated GIF is an animation that repeats START! When it reaches 3, but in reality, this app starts from START! And counts up to "9" and ends (keeps outputting "9"). ..
And the program is as follows. Even those who have no experience in Android application development should be patient.
Screen class
public class MainActivity extends AppCompatActivity {
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.text_view);
new MyTask().execute();
}
class MyTask extends AsyncTask<Void, String, Void> {
@Override
protected Void doInBackground(Void... voids) {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
publishProgress(String.valueOf(i));
} catch (InterruptedException e) {
Log.e("MyTask", e.getMessage(), e);
}
}
return null;
}
@Override
protected void onProgressUpdate(String... values) {
textView.setText(values[0]);
}
}
}
If you want to create a "screen class" on Android, start coding a class that inherits ʻAppCompatActivity`.
The class TextView
is a view (part) that displays (only can) character strings that are placed in the center of the screen and display" START! "," 0 ", or" 1 ".
I want to change this TextView
from" 0 "to" 1 "and then to" 2 "in 1000 milliseconds by asynchronous processing. This is the requirement for this app.
If you want asynchronous processing, define a subclass of ʻAsyncTask` and code the processing in it.
If you call the ʻexecute ()method on
MyTask that performs the asynchronous processing,
MyTaskwill start processing. The red box in this image is
TextView.
MyTask overwrites this
TextViewwith a string such as
" 0 ",
" 1 ", or
" 2 "`. This makes it look like it's counting up.
Now, let's add static
to the MyTask
class.
The IDE got angry red. Because the field named textView
doesn't have the static modifier!
Please take a look at the illustration of the woman climbing the stairs and the grandfather of the stone tape.
On Android, screens that are no longer needed are GC'd. The "unnecessary screen" is when the user hides (turns off) the screen by pressing the back button. In general, Android devices have less memory than PCs [^ 2]. Therefore, the screen = Activity class instance hidden by the user is quickly GCed by the runtime.
[^ 2]: There are Android "gaming" smartphones on the market with 10GB of RAM, but that's aside.
The user simply presses the back button, and that screen = Activity instance is unnecessary! Nja GC! It tends to be short-lived and fateful.
So, in the background (no, the term "background" isn't right, it's better to say "asynchronous processing in worker threads") MyTask
is my own You will live until you have completed your work (count up).
It is an IDE called Android Studio that warned me that it was an [enclosing instance <instance of a nested class] in this comparison of survival time.
Start this app I made, and even if the count-up starts with the work of MyTask
, the user presses the back button to hide the screen (turn it off, stop the app) Even so, ** the MyTask
object, which is an instance of the non-static nested class, holds an implicit reference to the MainActivity
instance **, so it shouldn't be displayed anymore, so it should be GC. In addition, it falls into a state where GC is not performed.
Where the user is not visible, MyTask
is constantly doing count-up work.
In the illustration, the woman MyTask
climbs one step at a time. But in his right hand, he firmly holds the grandfather Main Activity
, who can't be a Buddhahood. The user can't see this grandfather.
I am struck by the dilemma here.
--This MyTask
is a class that is used only in this MainActivity
, so I want to make it a nested class.
--But the IDE warns you to make it a static nested class.
--But I don't want (and shouldn't) make a field of type TextTview
a static field.
--So, is this MyTask
and this MainActivity
divided into different classes?
――But in that case, passing the reference of TextTview
is troublesome (this time, if this passing is not good, TextTview
may be a source of memory leak).
--It is MainActivity
that has (should have) TextTview
, and it is MyTask
that changes the string of that TextTview
.
――So, after all, I want this MyTask
to be a nested class of this MainActivity
.
-java.lang.ref.WeakReference <T>
Do you use ...
I've been worried about this all the time. You may have to compromise something somewhere.
that's all. I would like to write this article in the words of Dr. Fukuzawa Yukichi, as I am worried about my conscience, whether it is okay to end this.
However, the pain of being struck by a bad child is more painful than the hellish sword, and I have no choice but to hit this sword with my current body.
_ Fukuzawa Yukichi "Education" 1878 (Meiji 11) _
Hey Yukichi, "It's more painful than hellish blame" is too severe to draw. quit. I'm going to talk to Mr. Watsuji.
Before Dogen's mercy, "evil is not necessarily something to be ridiculed."
_ Tetsuro Watsuji "Study of Japanese Spiritual History" 1922 (Taisho 11) _
that's all.