I have two activities, a MainActivity and a secondary activity (e.g.: an about screen), then I have an asynctask which updates the UI on the MainActivity. This part works fine, the asynctask updates the UI by calling a method inside the MainActivity which inflates the UI and sets some values. This method also makes all UI components visible.
What doesn't work is, after going to the About screen and back to the MainActivity, the UI is completely blank. I don't understand why this stops working after navigating back from a different activity, which otherwise works fine.
Can someone please advise?
Here's how I draw the UI. This is how I update it from the thread, and it works, until I go to the about screen:
private void DisplayMainContent()
{
Context context = Util.DataStruct.LoadContext();
Log.d("debug", "DisplayMainContent() loaded a context " + context.toString());
RelativeLayout parent = (RelativeLayout)((Activity)context).findViewById(R.id.action_settings);
LayoutInflater li = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = li.inflate(R.layout.activity_main, parent);
TextView version = (TextView) v.findViewById(R.id.latestVerField);
version.setText(Util.DataStruct.GetVal("version"));
}
little story about vanishing data..
advice:
do not use new activity to achieve this - do your about as dialog or dialog fragment
nice example how to show dialog using fragment
don't use static - instead use singleton pattern
Singletons preserve the conventional class approach, and don't require that you use the static keyword everywhere. They may be more demanding to implement at first, but will greatly simplify the architecture of your program. Unlike static classes, we can use singletons as parameters or objects. Also,you can use singletons with interfaces just like any other class.
where i see problem:
this line is all u need to trace yr mistake:
(i think any other fragment of yr code is irrelevant to yr problem)
version.setText(Util.DataStruct.GetVal("version"));
Explanation why:
Util.DataStruct:
should be singleton with valid hard reference to it eg. in Applictation class or any other which life is longer as the activity u use to display data.
are you aware of the existence of garbage collector?
what i'm trying to point out ? why u should avoid STATIC !?
Code(data) flow:
app launched - initializes static class/variables etc
your variables are feed (via async or else way)
your app is closed by ANDROID OS - regardless of the reason
os recreates "stack"
but not yr variables - they are empty/null/defalt - not referenced by values as they shoud in normal code flow
context:
from where do u use yr DisplayMainContent() ? for what u need context there ?
context should be "anchor" for yr app methods which need it. (it's like certain security stuff - "hi this app fragment belong to me i have the right to modify and view contents - so to do any stuuf u pass nearest context u got - from fragment activity dialog widget etc")
if u can use getContext() eg. ("from parent") - dont use any static one
example:
in fragment:
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Context context = container.getContext();
}
in adapter:
#Override
public View getView(int position, View convertView, ViewGroup parent) {
Context context = parent.getContext();
}
about inflation
- use :
LayoutInflater.from(context).inflate(res,ViewGroup,attachToRoot);
do u use parent in inflation(in fragment doubtless u use in activity doubtful)
for #bcorso:
Do not use more resources than you need.
#TomaszBest sorry, but you really don't know what you're talking
about: Util.DataStruct.GetVal() is calling a static method of the
static class Util.DataStruct, and therefore must return a static class
variable. Static class variables are singletons (only one will ever be
created), and it will not get garbage collected.
An object referenced through a static member variable is strongly referenced until the class is unloaded. A normal ClassLoader never unloads a class, but those used by application do under the right conditions.
If the static field is changed to reference a different object, the original object pointed to by the static field is eligible for GC just like any other object!
The initialization of static variables is covered in Section 2.11 Static Initializers of suns JVM spec. The specification does not define the implementation of Garbage collection - garbage collection rules for static objects will vary depending on your VM.
in sum:
If your class is holding onto this object permanently, it will only be released when the vm exits. Only Classes and interfaces loaded by the bootstrap loader may not be unloaded.
Related
I'm in the process of completely redesigning my Android app. Before, EVERYTHING was in the same class.
So I tried to redraw everything so that the code is clearer apart Admob than the doc advice to put in the Main thread, I separate the different part of my code in class. So I used two technique: I created a songleton that contains variables that I want to have access to constantly,and I call my classes via weak reference.
Here is what it looks like:
For example, the UIManager class that needs to update the game's IU have a weak reference looks like this:
private static SoftReference<UIManager> ManageUI;
static{ManageUI= new SoftReference<>(null);}
static UIManager get()
{
if(ManageUI.get()==null)
{
ManageUI= new SoftReference<>(new UIManager());
}
return ManageUI.get();
}
GameManager Manager=GameManager.getInstance();
to be able to use the findviewbyid for example I place in method argument the main class that is the mainthread
the singleton that contains all my variables that I want to have permanent access to looks like this:
private GameManager()
{}
/** Holder */
private static class Manager
{
/** Instance unique non préinitialisée */
private final static GameManager instance = new GameManager();
}
/** Point d'accès pour l'instance unique du singleton */
public static GameManager getInstance()
{
return Manager.instance;
}
To separate all in different class, I pass argument to my method so I can call au stuff belong to Activity like that:
(My main class is called GamePlay)
void OpenGlobalScene(GamePlay activity)
{
Manager.OnTitle=false;
if (!checkLayout(activity,R.id.globalscene)) {
LayoutInflater(activity,9, true);
LinearLayout GamePlan = (LinearLayout) activity.findViewById(R.id.globalscene);
GamePlan.setAlpha(Manager.AlphaBord);
}
}
For now, I have not noticed any problems except a few slownesses on old android phone 4.4.2.
Also compared to my old code were EVERYTHING was in the same class, it's much easier to change pieces of code (going to the inapp billing V3 was simpler since everything was in one class that I call like the others with weak referencre)
My questions are:
-What are the problems that such a structure might pose?
I had also chosen that structure to not load or leave in memory things that are not useful
-How are chance that Android will erase from memory an action in progress called with weak reference?
-As you can see I pass the activity has argument to the method, sometimes I pass it from a method to another. Is that fact can cause some trouble?
Thank you for your help.
Check Dagger2 is better than the clasic singleton https://developer.android.com/training/dependency-injection/dagger-android?hl=es-419
thanks for your answer and your tips. I'am gonna check this out.
Anyone else know something about consequences on memory when using weak references ?
This is related to Glide image loading with application context
I have several Fragments hosted in an Activity, with a Fragment being replaced by another as the user navigates through the app.
I'm passing a RequestManager into my MyFragment's RecyclerView adapter like so:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
MyAdapter adapter = new MyAdapter(Glide.with(this), listOfPhotos);
recyclerView.setAdapter(adapter);
...
}
My adapter:
public class MyAdapter
extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private RequestManager mGlide;
...
// constructor
public MyAdapter(RequestManager glide, List<MyStuff> listOfPhotos) {
mGlide = glide;
...
}
...
}
When I debug my app, here's what I see in the object mGlide:
The context seems to be my project's ApplicationContext. Now I'm not very familiar with Android contexts, but is this right? I assumed it will be something like com.MyFragment.....
Also, is there a simple way to check if glide is following my fragments' lifecycles?
This question would've been better suited for Glide's Google Group linked from the Readme.
Your usage looks clean and performant, and that's the way I suggest to go with.
The context seems to be my project's ApplicationContext.
Glide is a singleton, and hence it wouldn't make any sense to initialize it with the first Activity it sees (see Glide.get). If you check how RequestManager actually uses that Context you'll see it is passed all around the place, which again wouldn't be useful and would leak. It is mostly used for .getContentResolver and to acquire the Glide singleton via Glide.get(context) in those other classes.
I assumed it will be something like com.MyFragment...
What you're looking for can be found in the following fields of RequestManager:
private final Lifecycle lifecycle;
private final RequestManagerTreeNode treeNode;
private final RequestTracker requestTracker;
See all child classes / implementations of those types. Also check the Context.mActivityLifecycleCallbacks I think those are the same objects.
Is there a simple way to check if glide is following my fragments' lifecycles?
You can put breakpoints in the above mentioned classes, and/or check if the resources are freed via a heap dump (this last one may be tricky because of caching). If you want more insight you can try enabling logging as said on the Wiki: Debugging and Error Handling wiki and also write your own loggers for listener/target, like I did in glide-support/...utils.
I have a piece of code using AndroidAnnotations which is very similar to the one found at:
https://github.com/excilys/androidannotations/wiki/Adapters-and-lists
However - I want to pass an argument to the List adapter to specify which list - i.e.
#AfterInject
void initAdapter() {
persons = personFinder.findAll(companyName);
}
What is the best way to associate companyName with the Adapter? I can't use the constructor with AnroidAnnotations - and #AfterViews is called before the #AfterViews of the parent fragment, so I can't call setters then.
I have currently hacked in a call to set the params manually then refresh the view and removed the #AfterViews - but its nasty and unreliable as I duplicate the pattern down the hierarchy.
EDIT
Just calling the setter works in the most simple case - and is what I currently have.
But doesn't work well in the more complicated case. i.e
EFragment->EViewGroup->EBean ListAdapter
Since I can't use the constructor, I have to wait until the full hierarchy is rendered and laid out before the fragment tells the ViewGroup which Company to show company info, which in turn tells the ListAdapter which company so I can get which people, etc.
It doesn't take much effort for it to get very messy and if my data was on the web - the UI would probably render like a webpage from the 90s.
I was hoping to use something like #Extras - or have a way to pass arguments for #AfterInject to use, or even just put the companyId in the Fragment Context without tying my ListAdapter to only work with one type of Fragment...
Try this
#EFragment
public class MyFragment extends Fragment {
#FragmentArg("myStringArgument")
String myMessage;
#FragmentArg
String anotherStringArgument;
#FragmentArg("myDateExtra")
Date myDateArgumentWithDefaultValue = new Date();
}
Source:
https://github.com/excilys/androidannotations/wiki/FragmentArg
I've been studying up on best practices for preventing Context/Activity memory leaks when creating views, and I can't seem to find a definite answer on what is or is not allowed when it comes to static fields in classes.
Let's say I have a code of this form:
public class MyOuterClass extends Activity{
private MyInnerClass;
MyInnerClass = (MyInnerClass) findViewById(<XML call here>);
MyInnerClass.myXInt = 3;
// onCreate(), onResume(), etc.
public static class MyInnerClass extends SurfaceView implements Runnable{
// Safe variables?
private static int myXInt, myYInt;
private static boolean myBoolean;
// Potentially safe?
private static Canvas myCanvas;
// Definitely bad.
private static Context myContext;
public MyInnerClass(Context context){
myContext = context; // This is bad.
}
}
}
I am slightly confused on what the JVM actually considers the ClassLoader for MyInnerClass. Technically, since it is a SurfaceView object, it seems like the static variables should always exist once the application has instantiated MyInnerClass one time (which happens when the View is first inflated), and then remain there until the application itself is terminated. If that is the case, what prevents Bitmaps and Canvas objects from remaining open as well and filling up the heap?
The only statement I ever see repeated over and over is that you can't leak static Context like I have shown in the constructor, but it never goes beyond that. Is that really the only thing you can't do?
In Java/Android a static variable or constant will not be garbage collected. It just stays there once the class that holds it is loaded via a class loader. The class loader is afaik always the same for all classes inside your app and its the one that has static references to all your classes (to e.g. MyInnerClass.class). Since the class loader does not go away your classes won't do that either since they are referenced & therefore not garbage collectable.
Like in your example
public class SomeClass extends SurfaceView {
private static Context myContext;
public MyInnerClass(Context context){
myContext = context; // This is bad.
}
}
That is indeed bad. Even if no reference to SomeClass exists (e.g. the Activity that showed your custom SurfaceView has ended) the static reference to the Context (and any other static variable / constant in SomeClass will remain. You can consider all of them leaked since it is not possible to garbage collect that Context etc. If you have a regular variable reference something then once the instance that contains that variable has no more references to it the whole instance including its references to other things can and will be garbage collected. Java can even handle circular references fine.
For constants you want that to happen and it is usually not bad since the amount of constants and the amount of memory they occupy is not large. Also constants don't (should not) reference other instances that take up large amounts of memory like Context or Bitmap.
Besides the possibility to create memory leaks through static variables you may also create problems if you don't want to have only a single thing for all instances at the same time. For example if you save the Bitmap of your SurfaceView in a static variable you can't have two different images. Even if the two SurfaceViews are not displayed at the same time you could run into problems since each new instance will probably overwrite the old image and if you go back to the other SurfaceView you unexpectedly show the wrong image. I am almost sure you don't want to use static here.
The fact that your inner class is a static class does not mean that you have to use static variables - it just means that it behaves more like a static method since it can't use the instance variables (the ones that are not static) in your class.
To avoid memory leaks you simply should not use static variables at all. There is no need to use them unless you do special stuff (e.g. counting instances of a class). Constants are fine.
This article talks about mutable static fields: http://javabook.compuware.com/content/memory/problem-patterns/memory-leaks.aspx. Basically, avoid them and use constants instead.
The basis:
Activity - recreates(onCreate-onDestroy) on each orientatin change
View consists of ViewFlipper with two childs: simple RelativeLayout and ListView
ListView rows have complex layout and associated tags
The problem is that i have memory leak on each orientation change - activity stays in memory with whole view layout. Activity itself is a context so it'll stay in memory as long as associated objects will. So now i'm trying to find why leaks are happen.
View has setTag() method. I'm using it to store some information about rows(so every row(View) in ListView has associated tags).
But how does views and GC acts with tags ? My tag objects(holders) contains references to views but if view removes reference to it's tag this references(with tag itself) will be easily collected.
Anybody have faced similar problems with ListViews ?
P.S. i'm wondering how GC cleans layouts - tonns of cyclic references, contexts, holders, etc...
Firstly you can leak objects if you use View.setTag(int, Object) method. Tags set using this method are stored in a static WeakHashMap with a View as a key. So if you store references to child view in the parent view's tags then all these views and a context they were created with (parent activity) will be leaked. It happens because every child view holds a reference to its parent, so the parent view will never be collected by the GC.
There's a simple way to simulate this behavior:
public static class MainActivity extends ListActivity {
private final WeakHashMap<Parent, Parent.Child> mMap =
new WeakHashMap<Parent, Parent.Child>();
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// If parents were collected OOM error wouldn't be thrown.
// But they aren't collected so we get OOM here.
for (int i = 0; i < 10; ++i) {
Parent parent = new Parent();
mMap.put( parent, parent.mChild );
}
}
}
public static class Parent {
public final Child mChild = new Child();
public class Child {
private final byte[] mJunk = new byte[10*1024*1024];
}
}
Secondly it seems that the ListView class causes a memory leak. It means that the list view, all its recycled children and its parent activity are leaked. Here's some information about this bug:
http://code.google.com/p/android/issues/detail?id=12334
Android: AlertDialog causes a memory leak
I think you might have some non-static inner classes somewhere, which always save a pointer to their surrounding object instance. For example:
public class A {
private class B {
// ...
}
// b stores a reference to the instance of A
private B b = new B();
}
If you use the setTag() method (e.g. for a ViewHolder class), never store any references to parent objects there. In fact, you should declare this class static.
Plus, to avoid memory leaks, if possible you should always pass the result of getApplicationContext() to methods that require a Context - and no reference to the Activity itself.
Its easy to leak references to the Activity on orientation change. There are a handful of blog posts about this - which I feel are required reading:
http://ttlnews.blogspot.com/2010/01/attacking-memory-problems-on-android.html
http://android-developers.blogspot.com/2009/01/avoiding-memory-leaks.html
http://code.google.com/p/android/issues/detail?id=2391
In a super nutshell in your onRetainNonConfigurationInstance method you just want to be careful that you null out any references to View objects and in turn Activity references, Progress bars, etc.
A good pattern I use is having a "StateHolder" inner class which does contain an Activity reference, but I implement a setActivityForTasks method, which I just pass NULL to, it in turn sets all Activity references to NULL. Then when you're going back through your Activity after the orientation change you can just call setActivityForTasks(this) to reset the current activity.
The single take-away is just to NULL out any references to anything Activity related in onRetainNonConfigurationInstance
In Gingerbread and lower versions of Android, View.setTag (int key, Object tag) leaks memory. Do not use it. It was fixed in ICS.