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.
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 ?
There's MvxAndroidSetupSingleton class in MvvmCross what provides several virtual methods but at the same time has private only constructor
public class MvxAndroidSetupSingleton : MvxSingleton<MvxAndroidSetupSingleton>
{
private MvxAndroidSetupSingleton()
{
}
protected virtual void CreateSetup(Context applicationContext){}
protected virtual Type FindSetupType(){}
}
I'd like to git rid of reflection-based implementation and to initialize Setup on my own, but don't see any way to do that (due to the private constructor). Is there any way to handle that?
Or at least would be glad to know reason of existing virtual methods in class with private constructor.
For the preset time there's no way to accomplish that. As I've said above, the reason I want to do that is to improve startup performance as much as possible. What I've done already is:
overrided FillValueConverters to specify Value Converters explicitly
overrided FillViewTypes and filled cache on my own.
It saved around ~1.5s on start
Update
Thanks to #CheeseBaron, private constructor was changed to protected in next commit
I need to pass objects to my fragments in order to initialize them.
Currently I am doing this with ((MyActivity)getActivity()).getX(). (direct access to the activity)
However, I would like to pass the required objects as parameter.
I definitely do not want to add parcelable objects to the bundle, since they require an excessive amount of useless boilerplate code. My goal is to reduce complexity, not increasing it.
And I do not want to add serializable objects to the bundle, since they are slow and cause an unnecessary overhead.
What is the best way to pass objects to fragments?
Any ideas to solve the problem in a more convenient way?
I definitely do not want to add parcelable objects to the bundle, since they require an excessive amount of useless boilerplate code. My goal is to reduce complexity, not increasing it.
You write this code in your model classes which is separated from your activities and fragments. There is no complexity in implementing Parcelable. And it is a common way to pass objects to a Fragment.
Any other solutions? Well, you still can do this ((MyActivity)getActivity()).getX() as long as your fragment is attached to your activity. In this case it is even faster than Parcelable because there is no serialization at all.
Other ways would be to write objects to database, pass their ids to a Fragment and then use a query to retrieve objects.
You can also use SharedPreferences, but that's rarely used. For this you will need to convert your object to String.
You can do the Android way: Parcelable.
You can serialize then.
You can do the poor way : static
You can do the retained way: Create a Fragment with setRetainInstance(true) and save your objects references.
I understand you don't want to use parcelable / serializable objects to a Bundle. I also agree with you since I got lazy, and my phone app is getting complicated.
Here's what you can do, and it works reliably.
Make a public method in your Fragment class, sample below.
Have the Activity, preferably no other place, call that public method. Remember Activity is always present, Fragments and Adapters may not due to its lifecycle.
The timing of the call is crucial if you're not using Bundles. I have used it without any problems.
The advantage of this technique is that it is fast, especially compared to Bundles. Many developers do not consider this however.
Note: If you are using simple fundamental Java types, do use Bundles! As suggested by Google.
Sample code:
public class MyFragment extends Fragment {
...
public void setList(final ArrayList<String> arrayList) {
...
}
In the Activity:
MyFragment fragment1 = MyFragment.newInstance(<parameters>);
fragment1.setList( arrayList );
Do you need to change the properties once they have been set on the fragment? If not, you can use setArguments(Bundle). If it is a fairly light object you can even skip implementing Parcelable and just set each property individually. The advantage is that the arguments are preserved upon orientation change. The disadvantage is that you need to call this method before attaching your fragment, hence it is not very useful once the fragment is in use.
It's way too late for my answer, but if someone else is wondering. The recommended way is to use Parcelable or Serializable, but you can always do something like this:
public class ObjectManager {
private static final String TAG = "ObjectManager";
private static ObjectManager instance;
private Object currentObject;
public static ObjectManager getInstance() {
if (instance == null)
instance = new ObjectManager();
return instance;
}
public Object getCurrentObject() {
return currentObject;
}
public void setCurrentObject(Object object) {
this.currentObject = object;
}
}
And then use it: where you needed as long as your app is running
//Use on the object you would like to save
ObjectManager.getInstance().setCurrentObject(object);
//Get the instance from pretty much everywhere
Object = ObjectManager.getInstance().getCurrentObject();
You can use it always, but it will be most likely to be useful, if you pass objects bigger than the Bundle max size.
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.
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