lide加載網絡圖檔時,碰到了 You cannot start a load for a destroyed activity這個異常;
先看下Glide的簡單調用:
Glide.with(context).load(imageUrl).into(imageView);
根據異常的提示,我們可以确定問題應該是出在了Glide.with(context) 中的context
我們點到源碼中看一下 Glide.with() 是怎麼實作的。
public static RequestManager with(Context context) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(context);
}
public static RequestManager with(Activity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
public static RequestManager with(FragmentActivity activity) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(activity);
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public static RequestManager with(android.app.Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
public static RequestManager with(Fragment fragment) {
RequestManagerRetriever retriever = RequestManagerRetriever.get();
return retriever.get(fragment);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
可以看到這裡有很多的重構的方法,但是最終都會傳回一個retriever.get(); 我們繼續到retriever.get()裡面去,我們會看到 RequestManagerRetriever 類。
public class RequestManagerRetriever implements Handler.Callback {
...
public RequestManager get(Context context) {
if (context == null) {
throw new IllegalArgumentException("You cannot start a load on a null Context"); //這裡抛出了異常
} else if (Util.isOnMainThread() && !(context instanceof Application)) {
if (context instanceof FragmentActivity) {
return get((FragmentActivity) context);
} else if (context instanceof Activity) {
return get((Activity) context);
} else if (context instanceof ContextWrapper) {
return get(((ContextWrapper) context).getBaseContext());
}
}
return getApplicationManager(context);
}
public RequestManager get(FragmentActivity activity) {
if (Util.isOnBackgroundThread()) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity);
FragmentManager fm = activity.getSupportFragmentManager();
return supportFragmentGet(activity, fm);
}
}
public RequestManager get(Fragment fragment) {
if (fragment.getActivity() == null) {
throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached"); //這裡抛出了異常
}
if (Util.isOnBackgroundThread()) {
return get(fragment.getActivity().getApplicationContext());
} else {
FragmentManager fm = fragment.getChildFragmentManager();
return supportFragmentGet(fragment.getActivity(), fm);
}
}
@TargetApi(Build.VERSION_CODES.HONEYCOMB)
public RequestManager get(Activity activity) {
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
return get(activity.getApplicationContext());
} else {
assertNotDestroyed(activity); //檢查activity的方法
android.app.FragmentManager fm = activity.getFragmentManager();
return fragmentGet(activity, fm);
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
private static void assertNotDestroyed(Activity activity) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1 && activity.isDestroyed()) {
throw new IllegalArgumentException("You cannot start a load for a destroyed activity"); //這裡抛出了異常
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public RequestManager get(android.app.Fragment fragment) {
if (fragment.getActivity() == null) {
throw new IllegalArgumentException("You cannot start a load on a fragment before it is attached"); //這裡抛出了異常
}
if (Util.isOnBackgroundThread() || Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
return get(fragment.getActivity().getApplicationContext());
} else {
android.app.FragmentManager fm = fragment.getChildFragmentManager();
return fragmentGet(fragment.getActivity(), fm);
}
}
...
}
主要看這個方法assertNotDestroyed(Activity activity)
找到Glide 抛出異常的地方了!
也就是當 activity.isDestroyed()為true的時候
同樣的還有另外幾個異常:
You cannot start a load on a fragment before it is attached
You cannot start a load on a null Context
歸根結底都是因為我們傳入了一個已經銷毀的Activity或者是一個空的Context ,Fragment 挂載的Activity為空導緻的
回顧使用的場景,是在聯網請求成功之後調用的Glide 當執行到Glide.with();方法時,目前的Activity已經銷毀了,是以才導緻的這個問題。
我們盡量不要再非主線程裡面使用Glide加載圖檔,這樣容易導緻抛出如You cannot start a load for a destroyed activity的異常,如果有需求的話,有一種解決方案是直接傳入Application對象,這樣就不會有這個問題了,但是使用Application對象會導緻Glide加載圖檔的生命周期變長,當Activity已經銷毀時,還在繼續的加載圖檔,這樣做會浪費很多的資源,是以我們還是簡單的封裝一個Glide加載的工具類來解決這個問題比較好。
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.Context;
import android.os.Build;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
/**
* Glide 加載 簡單判空封裝 防止異步加載資料時調用Glide 抛出異常
* Created by Li_Xavier on 2017/6/20 0020.
*/
public class GlideLoadUtils {
private String TAG = "ImageLoader";
/**
* 借助内部類 實作線程安全的單例模式
* 屬于懶漢式單例,因為Java機制規定,内部類SingletonHolder隻有在getInstance()
* 方法第一次調用的時候才會被加載(實作了lazy),而且其加載過程是線程安全的。
* 内部類加載的時候執行個體化一次instance。
*/
public GlideLoadUtils() {
}
private static class GlideLoadUtilsHolder {
private final static GlideLoadUtils INSTANCE = new GlideLoadUtils();
}
public static GlideLoadUtils getInstance() {
return GlideLoadUtilsHolder.INSTANCE;
}
/**
* Glide 加載 簡單判空封裝 防止異步加載資料時調用Glide 抛出異常
*
* @param context
* @param url 加載圖檔的url位址 String
* @param imageView 加載圖檔的ImageView 控件
* @param default_image 圖檔展示錯誤的本地圖檔 id
*/
public void glideLoad(Context context, String url, ImageView imageView, int default_image) {
if (context != null) {
Glide.with(context).load(url).centerCrop().error(default_image).crossFade
().into(imageView);
} else {
Log.i(TAG, "Picture loading failed,context is null");
}
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public void glideLoad(Activity activity, String url, ImageView imageView, int default_image) {
if (!activity.isDestroyed()) {
Glide.with(activity).load(url).centerCrop().error(default_image).crossFade
().into(imageView);
} else {
Log.i(TAG, "Picture loading failed,activity is Destroyed");
}
}
public void glideLoad(Fragment fragment, String url, ImageView imageView, int default_image) {
if (fragment != null && fragment.getActivity() != null) {
Glide.with(fragment).load(url).centerCrop().error(default_image).crossFade
().into(imageView);
} else {
Log.i(TAG, "Picture loading failed,fragment is null");
}
}
public void glideLoad(android.app.Fragment fragment, String url, ImageView imageView, int default_image) {
if (fragment != null && fragment.getActivity() != null) {
Glide.with(fragment).load(url).centerCrop().error(default_image).crossFade
().into(imageView);
} else {
Log.i(TAG, "Picture loading failed,android.app.Fragment is null");
}
}
}
這隻是我針對項目中碰到的異常做的簡單分析,如果你想要詳細的研究下Glide的源碼 請參考郭霖大大的部落格:http://blog.csdn.net/guolin_blog/article/details/53939176