天天看點

Glide出現You cannot start a load for a destroyed activity異常的問題

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出現You cannot start a load for a destroyed 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

繼續閱讀