按下后退按钮时异步任务崩溃。

时间:2013-01-15 04:30:53

标签: android android-asynctask

暂停/删除调用片段时,异步任务会发生什么?我假设任务完成但是有人可以确认这一点很棒。我试图找到一个修复我的崩溃。

我有一个异步任务,它是一个递归图像加载器。图像加载器是它为其加载图像的listFragment的内部类。除非在完成加载所有图像之前按下后退按钮,否则图像加载器工作正常。当我在所有图像完成之前按下后退按钮加载应用程序崩溃。我觉得图像加载器失去了对适配器的访问权限,但我不确定我是否正确地攻击了这个问题。我不确定如何在异步任务中修复此问题。如果有人可以帮助我。甚至反驳了一个想法,让我的思维方式不同。

任何帮助都会很棒。谢谢你的时间,甚至看。

ImageLoader的

private class ImageLoaderTask extends
        AsyncTask<HashMap<String, Object>, Void, HashMap<String, Object>> {

    @Override
    protected HashMap<String, Object> doInBackground(
            HashMap<String, Object>... hm) {
        InputStream in = null;

        String url = (String) hm[0].get("image");
        int pos = (Integer) hm[0].get("pos");
        url = "http://kzfr.org/u/img/small/" + url;
        URL mUrl;

        try {
            mUrl = new URL(url);
            URLConnection urlConnect = (URLConnection) mUrl
                    .openConnection();
            in = urlConnect.getInputStream();

            File cacheDirectory = getSherlockActivity().getBaseContext()
                    .getCacheDir();

            File tmp = new File(cacheDirectory.getPath() + "/kzfr_small"
                    + pos + ".png");

            FileOutputStream fOut = new FileOutputStream(tmp);

            Bitmap b = BitmapFactory.decodeStream(in);
            b.compress(Bitmap.CompressFormat.PNG, 100, fOut);
            fOut.flush();
            fOut.close();

            HashMap<String, Object> hmBmp = new HashMap<String, Object>();
            hmBmp.put("blank_start", tmp.getPath());
            hmBmp.put("pos", pos);
            return hmBmp;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    protected void onPostExecute(HashMap<String, Object> result) {
        // this is the img path that leads to a local image that was just
        // saved on the phone.
        String imgPath = (String) result.get("blank_start");
        // the position of the image in the listview
        int position = (Integer) result.get("pos");
        // local adapter
        SimpleAdapter adapter = (SimpleAdapter) getListAdapter();
        // hashmap entry from the local adapter at the position of the image
        // that we just loaded.
        HashMap<String, Object> hm = (HashMap<String, Object>) adapter
                .getItem(position);
        // replace the actual blank image with the image path that we just
        // loaded
        hm.put("blank_start", imgPath);
        // update listview
        adapter.notifyDataSetChanged();
        // we are done with the current position increment position and move
        // on.
        position++;
        // this while loop finds the next position in the adapter that does
        // not have false for an image path. False indicates that there is
        // no image and i should load a default image.
        while (position < adapter.getCount()) {
            hm = (HashMap<String, Object>) adapter.getItem(position);
            imgPath = (String) hm.get("image");

            if (!imgPath.equalsIgnoreCase("false")) {
                break;
            }
            position++;
        }

        // checks to make sure that the position is not out of bounds on the
        // adapter. If in bounds then there might be more images to load.
        // Start a new ImageLoader. This is a recursive Class.
        if (position < adapter.getCount()) {
            ImageLoaderTask imageLoader = new ImageLoaderTask();

            hm.put("pos", (position));
            imageLoader.execute(hm);
        }

    }

栈跟踪

01-14 20:09:28.752: E/AndroidRuntime(6069): FATAL EXCEPTION: main
01-14 20:09:28.752: E/AndroidRuntime(6069): java.lang.NullPointerException
01-14 20:09:28.752: E/AndroidRuntime(6069):     at com.appdomum.doublea.ListFrag$ImageLoaderTask.onPostExecute(ListFrag.java:272)
01-14 20:09:28.752: E/AndroidRuntime(6069):     at com.appdomum.doublea.ListFrag$ImageLoaderTask.onPostExecute(ListFrag.java:1)
01-14 20:09:28.752: E/AndroidRuntime(6069):     at android.os.AsyncTask.finish(AsyncTask.java:417)
01-14 20:09:28.752: E/AndroidRuntime(6069):     at android.os.AsyncTask.access$300(AsyncTask.java:127)
01-14 20:09:28.752: E/AndroidRuntime(6069):     at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429)
01-14 20:09:28.752: E/AndroidRuntime(6069):     at android.os.Handler.dispatchMessage(Handler.java:99)
01-14 20:09:28.752: E/AndroidRuntime(6069):     at android.os.Looper.loop(Looper.java:130)
01-14 20:09:28.752: E/AndroidRuntime(6069):     at android.app.ActivityThread.main(ActivityThread.java:3806)
01-14 20:09:28.752: E/AndroidRuntime(6069):     at java.lang.reflect.Method.invokeNative(Native Method)
01-14 20:09:28.752: E/AndroidRuntime(6069):     at java.lang.reflect.Method.invoke(Method.java:507)
01-14 20:09:28.752: E/AndroidRuntime(6069):     at     com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
01-14 20:09:28.752: E/AndroidRuntime(6069):     at   com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
01-14 20:09:28.752: E/AndroidRuntime(6069):     at dalvik.system.NativeStart.main(Native Method)

2 个答案:

答案 0 :(得分:4)

我认为你现在正走在正确的轨道上。

当编写自定义AsyncTask许多开发人员忘记时,有必要处理任务取消。简单地取消任务并不意味着它的代码不会在(例如)Activity / Fragment资源上执行,这些资源可能会在当时被释放。这通常在以下步骤中完成:

  1. 创建任务后,保存对它的引用。
  2. Activity / Fragment完成后取消所有正在运行的任务。这通常在Activity.onDestroy() / Fragment.onDestroy()中完成。删除对已取消任务的所有引用。
  3. 在任务代码中:
    1. doInBackground()中定期检查isCancelled()是否onCancelled()。如果是这样,整理并中止进一步的执行。
    2. 最终在onPostExecute()处理取消(在UI线程上运行)。
    3. null中执行已取消任务的任何处理(如果任务已取消,则执行并带有{{1}}结果。

答案 1 :(得分:1)

  

暂停/删除调用片段时,异步任务会发生什么?我假设任务完成但是有人可以确认这一点很棒。

这是不正确的;通过取消您在后台线程中所做的工作,框架不会为您完成工作。调用onPause()onResume()等回调的原因是通知您的应用 需要清理和取消任何非正常运行的工作在一个长期存在的组件中,如Service

在这种情况下,如果您不取消该任务则继续运行。由于此类被定义为非静态内部类,因此它还意味着您已创建临时内存泄漏,因为AsyncTask持有对Fragment的隐式引用,并且因为该任务已附加对于正在运行的线程,即使在onDestroy()之后,也不会对对象(或其成员)进行垃圾回收。

您需要以检查取消标记的方式实施AsyncTask,您可以手动取消onPause()或类似回调中的工作。