You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Fragment中使用Glide+Firebase时getActivity()空指针问题求助

问题描述

我正在开发一款允许用户点击ImageView更换头像的应用,头像来自用户相册并上传至FirebaseStorage。用户的个人信息单独存储在FirebaseDatabase中,图片URL会后续添加。

选择图片并上传后,出现如下错误:

java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.app.Activity.isDestroyed()' on a null object reference

但图片仍成功上传至FirebaseStorage,用户信息中的ImageUrl也已更新,重启应用能正常显示刚上传的图片。错误指向Glide.with(getActivity())代码段,该代码位于FirebaseDatabase.addValueEventListener中。

以下是相关代码片段:

onCreateView方法

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
    // Inflate the layout for this fragment 
    FrameLayout rootView = (FrameLayout)inflater.inflate(R.layout.fragment_tch_profile, container, false); 
    mDataRef.addValueEventListener(new ValueEventListener() { 
        @Override 
        public void onDataChange(DataSnapshot dataSnapshot) { 
            tv_email.setText(dataSnapshot.child("email").getValue(String.class)); 
            tv_name.setText(dataSnapshot.child("name").getValue(String.class)); 
            tv_gs.setText(dataSnapshot.child("cassSection").getValue(String.class)); 
            if(dataSnapshot.hasChild("tImage")){ 
                Glide.with(getActivity()) 
                     .load(dataSnapshot.child("tImage").getValue(String.class)) 
                     .crossFade() 
                     .placeholder(R.mipmap.ic_loader) 
                     .thumbnail(0.1f) 
                     .diskCacheStrategy(DiskCacheStrategy.ALL) 
                     .into(iv_image); 
            } 
        } 
        @Override 
        public void onCancelled(DatabaseError databaseError) { 
            Toast.makeText(getActivity(),"The read failed: " + databaseError.getCode(),Toast.LENGTH_SHORT).show(); 
        } 
    }); 
    return rootView; 
}

onRequestPermissionsResult方法

@Override 
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { 
    super.onRequestPermissionsResult(requestCode, permissions, grantResults); 
    switch (requestCode) { 
        case READ_EXTERNAL_STORAGE: 
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) 
                callGallery(); 
            return; 
    } 
}

onActivityResult方法

@Override 
public void onActivityResult(int requestCode, int resultCode, Intent data) { 
    super.onActivityResult(requestCode, resultCode, data); 
    if (requestCode == GALLERY_INTENT && resultCode == RESULT_OK) { 
        mImageURI = data.getData(); 
        iv_image.setImageURI(mImageURI); 
        StorageReference filePath = mStorageRef.child("userImage").child(mImageURI.getLastPathSegment()); 
        filePath.putFile(mImageURI).addOnSuccessListener(new OnSuccessListener<UploadTask.TaskSnapshot>() { 
            @Override 
            public void onSuccess(UploadTask.TaskSnapshot taskSnapshot) { 
                Uri downloadUri = taskSnapshot.getDownloadUrl(); //Ignore This error 
                mDataRef.child("tImage").setValue(downloadUri.toString()); 
            } 
        }); 
    } 
}

辅助方法callGallery

private void callGallery() { 
    Intent i = new Intent(Intent.ACTION_PICK); 
    i.setType("image/*"); 
    startActivityForResult(i, GALLERY_INTENT); 
}

Logcat错误信息:

FATAL EXCEPTION: main
Process: kayaba.akihiro.educ_games, PID: 4502
java.lang.NullPointerException: Attempt to invoke virtual method 'boolean android.app.Activity.isDestroyed()' on a null object reference
at com.bumptech.glide.manager.RequestManagerRetriever.assertNotDestroyed(RequestManagerRetriever.java:133)
at com.bumptech.glide.manager.RequestManagerRetriever.get(RequestManagerRetriever.java:102)
at com.bumptech.glide.Glide.with(Glide.java:653)
at kayaba.akihiro.educ_games.TchProfile$3.onSuccess(TchProfile.java:169)
at kayaba.akihiro.educ_games.TchProfile$3.onSuccess(TchProfile.java:161)
at com.google.firebase.storage.zzi.zzi(Unknown Source)
at com.google.firebase.storage.zzz.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:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)

解决方案

哈哈,这个问题我太熟了!本质就是Fragment生命周期和Firebase异步回调的冲突——当Firebase的ValueEventListener触发回调时,你的Fragment可能已经和Activity脱钩了(比如用户在上传头像的过程中切到了其他页面),这时候getActivity()返回null,Glide拿着空的Activity去初始化就炸了。

给你几个实用的解决办法,按推荐程度排序:

1. 直接绑定Glide到Fragment本身(最省心)

Glide本身支持和Fragment的生命周期绑定,你不用再纠结getActivity()是否有效,直接把Fragment实例传给Glide就行:

if(dataSnapshot.hasChild("tImage")){
    Glide.with(TchProfile.this) // 这里传Fragment自己,不是getActivity()
         .load(dataSnapshot.child("tImage").getValue(String.class))
         .crossFade()
         .placeholder(R.mipmap.ic_loader)
         .thumbnail(0.1f)
         .diskCacheStrategy(DiskCacheStrategy.ALL)
         .into(iv_image);
}

Glide内部会自动监听Fragment的生命周期,一旦Fragment销毁,它会自动取消图片加载请求,既避免了空指针,还能防止内存泄漏,一举两得!

2. 手动移除Firebase监听器(从根源切断问题)

你现在用的是addValueEventListener,这个监听器会一直监听数据变化,哪怕Fragment已经销毁了,它可能还会触发回调。所以最好在Fragment销毁时主动移除它:

  • 先把监听器存成Fragment的成员变量:
private ValueEventListener mProfileDataListener;
  • 在onCreateView里初始化并添加:
mProfileDataListener = new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        // 原来的更新UI逻辑...
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        Toast.makeText(getActivity(),"The read failed: " + databaseError.getCode(),Toast.LENGTH_SHORT).show();
    }
};
mDataRef.addValueEventListener(mProfileDataListener);
  • 在Fragment的onDestroyView方法里移除监听器:
@Override
public void onDestroyView() {
    super.onDestroyView();
    if (mDataRef != null && mProfileDataListener != null) {
        mDataRef.removeEventListener(mProfileDataListener);
    }
}

这样Fragment销毁后,监听器就不会再触发回调,从根源上避免了空指针的问题,还能优化内存使用。

3. 提前检查Context有效性(应急方案)

如果上面两种办法暂时不想改,也可以在调用Glide前先检查Context是否有效:

if(dataSnapshot.hasChild("tImage")){
    // 先判断Fragment是否还和Activity绑定
    if (isAdded()) {
        Glide.with(requireContext())
             .load(dataSnapshot.child("tImage").getValue(String.class))
             .crossFade()
             .placeholder(R.mipmap.ic_loader)
             .thumbnail(0.1f)
             .diskCacheStrategy(DiskCacheStrategy.ALL)
             .into(iv_image);
    }
}

isAdded()方法可以判断Fragment是否已经附加到Activity上,requireContext()是AndroidX Fragment提供的安全获取Context的方法(如果Context为空会抛出明确异常,方便调试)。

内容的提问来源于stack exchange,提问作者a.kyaba

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:50:09