Glide Version:4.0.0-RC1 I even tried 3.7 version
Integration libraries:No
Device/Android Version:Xiomi Redmi Note 4 Android version 6.0.1
Issue details / Repro steps / Use case background:
I am using FragmentStatePagerAdapter to show around 5 fragments in an activity.On each activity I am showing the images which I am fetching from FirebaseListAdapter/FirebaseRecyclerAdapter. As it is FragmentStagePagerAdapter adjacent views/Fragments will be initialized even they are not visible.If the tabs are scrolled in a fast manner (forward/backward) App is crashing with above mentioned error.I found out some similar issues on the internet but not able to fix this issue as I am new to android.
Glide load line / GlideModule (if any) / list Adapter code (if any):
adapt= new FirebaseListAdapter<MyClassStudent>(getActivity(), MyClassStudent.class, R.layout.mychild_grid_template, myRef) {
@Override
protected void populateView(final View v, MyClassStudent model, int position) {
final TextView uname = (TextView) v.findViewById(R.id.mychild_uname);
final CircleImageView profileImage=(CircleImageView)v.findViewById(R.id.mychild_image);
studRef.child(model.getUsername()).addListenerForSingleValueEvent(new ValueEventListener() {
public void onDataChange(DataSnapshot dataSnapshot) {
Child child=dataSnapshot.getValue(Child.class);
uname.setText(child.getUsername());
Glide.with(getActivity()).load(child.getProfileImage()).into(profileImage);
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
};
Layout XML:
<FrameLayout xmlns:android="...
Stack trace / LogCat:
E/UncaughtException: java.lang.NullPointerException: You cannot start a load on a not yet attached View or a Fragment where getActivity() returns null (which usually occurs when getActivity() is called before the Fragment is attached or after the Fragment is destroyed).
at com.bumptech.glide.util.Preconditions.checkNotNull(Preconditions.java:27)
at com.bumptech.glide.Glide.getRetriever(Glide.java:509)
at com.bumptech.glide.Glide.with(Glide.java:563)
at com.mycompany.educareteacher.FragmentStudents$2$1.onDataChange(FragmentStudents.java:184)
at com.google.firebase.database.zzp.onDataChange(Unknown Source)
at com.google.android.gms.internal.tl.zza(Unknown Source)
at com.google.android.gms.internal.vg.zzHW(Unknown Source)
at com.google.android.gms.internal.vm.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5438)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
D/FA: Logging event (FE): app_exception(_ae), Bundle[{firebase_event_origin(_o)=crash, firebase_screen_class(_sc)=ClassDetailActivity, firebase_screen_id(_si)=-4663411025690523798, timestamp=1500710706898, fatal=1}]
V/FA: Recording user engagement, ms: 3544
D/FA: Logging event (FE): user_engagement(_e), Bundle[{firebase_event_origin(_o)=auto, engagement_time_msec(_et)=3544, firebase_screen_class(_sc)=ClassDetailActivity, firebase_screen_id(_si)=-4663411025690523798}]
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.mycompany.com.educareteacher, PID: 5154
java.lang.NullPointerException: You cannot start a load on a not yet attached View or a Fragment where getActivity() returns null (which usually occurs when getActivity() is called before the Fragment is attached or after the Fragment is destroyed).
at com.bumptech.glide.util.Preconditions.checkNotNull(Preconditions.java:27)
at com.bumptech.glide.Glide.getRetriever(Glide.java:509)
at com.bumptech.glide.Glide.with(Glide.java:563)
at com.mycompany.educareteacher.FragmentStudents$2$1.onDataChange(FragmentStudents.java:184)
at com.google.firebase.database.zzp.onDataChange(Unknown Source)
at com.google.android.gms.internal.tl.zza(Unknown Source)
at com.google.android.gms.internal.vg.zzHW(Unknown Source)
at com.google.android.gms.internal.vm.run(Unknown Source)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5438)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:738)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:628)
Glide asks you to only start loads with views/fragments that are not attached : if the view is not visible, no point in loading an image for it.
Your onDataChange event is called asynchronously when the data is sent. At this point the view may or may not be attached.
It is up to you to manage the lifecycle of this request in order to check that it is valid to launch a glide request when you get its result
Or simply to manage the data independently from the view (don't load data when a view has to be displayed in populateView but before)
@Teovald Thanks for giving more information.I am calling above method to get data in the onCreateView of the fragment.
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
}
And this method is called even if the view is not visible as I am using FragmentStatePagerAdapter.
How can I manage the lifecycle of this request?How can I solve this problem in a correct way?Do I need to add code in onDestroy() {} method?
Well, this is more of an issue with how you use Firebase than with Glide really.
What you need to understand is that the populateView is called each time you need to create a view.
In turn, in that method, you register a listener onDataChange that will be called asynchronously.
You don't know when it will be called. The view you call it for might have been recycled at this point (you have scrolled the adapter and it has been reused for another item) or might even be detached (the ViewPager does not use it to display at this moment but keeps it in a pool of views to reuse).
I don't know the rest of your app or your usecase ..
The best solution would be to populate your dataset independently from the part where you populate the views. I am pretty sure that Firebase must provide you with samples on how to do so.
This is not just going to cause issues with Glide, uname.setText will also be called for a recycled item.
@Teovald how to populate dataset independently?I think I am not able to interpret it properly.I am ready to share the use-case/code with you.
What people usually do :
-in the activity/fragment onCreate/onStart method, launch a request that will give you your list of Child objects.
-While this request executes, you only display the 'loading' spinning circle
-When you get your data, you replace this spinning loader with the content of List<Child>
It is a general code.I am looking for a glide specific code/help which can handle this issue.
Maybe you can have a try in LazyLoadFragment in ViewPager and it will load a Fragment every time.
'
public abstract class LazyLoadFragment extends BaseFragment {
protected boolean isViewInitiated;
protected boolean isVisibleToUser;
protected boolean isDataInitiated;
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
isViewInitiated = true;
prepareFetchData();
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
this.isVisibleToUser = isVisibleToUser;
prepareFetchData();
}
public abstract void fetchData();
public boolean prepareFetchData() {
return prepareFetchData(false);
}
private boolean prepareFetchData(boolean forceUpdate) {
if (isVisibleToUser && isViewInitiated && (!isDataInitiated || forceUpdate)) {
fetchData();
isDataInitiated = true;
return true;
}
return false;
}
}
`
You should handle your data in the fetchData method.
And hope it can help you.
This issue has been automatically marked as stale because it has not had activity in the last seven days. It will be closed if no further activity occurs within the next seven days. Thank you for your contributions.
Most helpful comment
What people usually do :
-in the activity/fragment onCreate/onStart method, launch a request that will give you your list of
Childobjects.-While this request executes, you only display the 'loading' spinning circle
-When you get your data, you replace this spinning loader with the content of
List<Child>