Android Volley 中使用 getInstance(this) 添加 ImageLoader 出错

8

我正在按照安卓开发者网站上的Volley图片缓存教程进行学习,但在请求和缓存图片时遇到了问题,我猜想是因为我从教程中复制的单例模式。

我的Eclipse在getInstance(this)处报错,我猜想是因为这是上下文对象而我正在请求图片。

ImageRequest request = new ImageRequest(
    url,
    new Response.Listener<Bitmap>() {
        @Override
        public void onResponse(Bitmap bitmap) {
            mNetworkImageView = (NetworkImageView) findViewById(R.id.ImageView);
            mImageLoader = MySingleton.getInstance(this).getImageLoader();
            mNetworkImageView.setImageUrl(IMAGE_URL, mImageLoader);
        //  mImageLoader = MySingleton.getInstance(this).getImageLoader();
        //  mImageLoader.get(IMAGE_URL, ImageLoader.getImageListener(mImageView,
        //      R.drawable.ic_launcher, R.drawable.ic_launcher));
            }
        },
    0,
    0,
    null,
    new Response.ErrorListener() {
        public void onErrorResponse(VolleyError error) {
        //  mImageView.setImageResource(R.drawable.ic_launcher);
        }
    });
MySingleton.getInstance(this).addToRequestQueue(request);

这是单例模式:
package com.example.p;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;

import android.content.Context;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;

public class MySingleton {
    private static MySingleton mInstance;
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;
    private static Context mCtx;

    private MySingleton(Context context) {
        mCtx = context;
        mRequestQueue = getRequestQueue();

        mImageLoader = new ImageLoader(
            mRequestQueue,
            new ImageLoader.ImageCache() {
                private final LruCache<String, Bitmap>
                    cache = new LruCache<String, Bitmap>(20);

                @Override
                public Bitmap getBitmap(String url) {
                    return cache.get(url);
                }

                @Override
                public void putBitmap(String url, Bitmap bitmap) {
                    cache.put(url, bitmap);
                }
            });
    }

    public static synchronized MySingleton getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new MySingleton(context);
        }
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            // getApplicationContext() is key, it keeps you from leaking the
            // Activity or BroadcastReceiver if someone passes one in.
            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
        }
        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req) {
        getRequestQueue().add(req);
    }

    public ImageLoader getImageLoader() {
        return mImageLoader;
    }
}

我能够获取图片并以某种方式显示它,但我需要缓存它,所以我猜想将其添加到请求中,对吗?是否有帮助?
mNetworkImageView = (NetworkImageView) findViewById(R.id.ImageView);
mImageLoader = MySingleton.getInstance(this).getImageLoader();
mNetworkImageView.setImageUrl(IMAGE_URL, mImageLoader);

1
Java的基础知识:this的含义,如何在匿名接口实现中使用外部类 - Selvin
@Selvin 我认为它指的是活动的上下文,就像这个活动。无论如何,我仍然不明白如何解决我上面的问题,我猜你不知道如何使用Volley对吧? - Moudiz
我知道如何使用Volley,但你不懂Java...另外,在onResponse中已经有位图了,获取单例实例的意义是什么? - Selvin
好的,这就是教程中描述的内容,我在我的问题中提到了没有onResponse它可以工作,所以为了使单例模式起作用,我应该删除实例吗?@Selvin - Moudiz
@hata 我在谷歌上搜索了一些关于DiskBasedCache的资料,但是我没有找到任何有用的教程或者示例,尤其是在一些性能问题方面。非常感谢您提供的信息和帮助。 - Moudiz
显示剩余13条评论
3个回答

5

以下是我的工作示例代码,希望能够帮助你:

MainActivity.java文件内容:

import ...

public class MainActivity extends Activity {

    final Context mContext = this;   

    @Override

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        NetworkImageView mNetworkImageView = (NetworkImageView) findViewById(R.id.networkImageView);

        String mUrl = "http://192.168.0.100/api/getimage";
        mNetworkImageView.setImageUrl(mUrl, VolleySingleton.getInstance(mContext).getImageLoader());       
    }

    ...  

}

VolleySingleton.java:

public class VolleySingleton {
    private static VolleySingleton mInstance;
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;
    private static Context mContext;

    private VolleySingleton(Context context) {
        mContext = context;
        mRequestQueue = getRequestQueue();

        mImageLoader = new ImageLoader(mRequestQueue,
                new ImageLoader.ImageCache() {
                    private final LruCache<String, Bitmap>
                            cache = new LruCache<>(20);

                    @Override
                    public Bitmap getBitmap(String url) {
                        return cache.get(url);
                    }

                    @Override
                    public void putBitmap(String url, Bitmap bitmap) {
                        cache.put(url, bitmap);
                    }
                });
    }

    public static synchronized VolleySingleton getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new VolleySingleton(context);
        }
        return mInstance;
    }

    private RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            // getApplicationContext() is key, it keeps you from leaking the
            // Activity or BroadcastReceiver if someone passes one in.
            mRequestQueue = Volley.newRequestQueue(mContext.getApplicationContext(), 10 * 1024 * 1024); // this for caching
        }
        return mRequestQueue;
    }

    public <T> void addToRequestQueue(Request<T> req) {
        getRequestQueue().add(req);
    }

    public ImageLoader getImageLoader() {
        return mImageLoader;
    }   
}

activity_main.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    tools:context=".MainActivity">       

        <com.android.volley.toolbox.NetworkImageView
            android:id="@+id/networkImageView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />       

</LinearLayout>

AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.volleyapp" >

    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>        
    </application>

</manifest>

我尝试了你的示例,但当我关闭应用程序然后再打开它时,图像就消失了,那么该如何缓存图像呢? - Moudiz
如果我想添加DiskBasedCache怎么办?你能否在你的示例中添加它? - Moudiz
我认为你可以在这里找到更多关于缓存的信息:http://stackoverflow.com/questions/26193501/volley-diskbasedcache-throws-filenotfoundexception。尝试增加你的缓存大小。祝你好运! - BNK
嗨!我更新了我的答案。看一下 Volley.newRequestQueue(mContext.getApplicationContext(), 10 * 1024 * 1024);,这是用于缓存的。 - BNK
尝试在您的列表示例中使用上述 newRequestQueue,并检查它是否缓存。 - BNK
显示剩余4条评论

1
getInstance方法的一个优化建议是使用双重检查锁定,因为它只在实例实际为空时才同步:
private volatile static VolleySingleton self;

public static VolleySingleton getInstance(Context context) {
    if (self == null) {
        //Using double checked locking
        synchronized (VolleySingleton.class) {
            if (self == null) {
                //Using app context prevents leaking of activity context
                self = new VolleySingleton(context.getApplicationContext());
            }
        }
    }
    return self;
}

关于单例模式及其不同实例化方式的更多信息可以在这里找到。

volatile关键字确保该字段立即被所有线程看到,并且不会被线程本地缓存。如果getInstance方法不需要上下文,则链接中解释的其他方法比双重检查锁更合适。


1

您在错误的上下文中..初始化一个类成员

private final Context ctx = this; 

然后在onResponse中使用ctx。
mImageLoader = MySingleton.getInstance(ctx).getImageLoader();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接