8

I am following up image cashing tutorial using Volley on Android developers, I am having problem with requesting an image request and caching it, I guess because of the singelton that I created (copied from the tutorial).

My Eclipse is giving error in the getInstance(this) because this is context and I am requesting an image I guess.

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);

This is singleton:

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;
    }
}

I am able to get the image and display it in such way , but I need to cache it , so I guess it to add it in a request right ? .. any help ?

mNetworkImageView = (NetworkImageView) findViewById(R.id.ImageView);
mImageLoader = MySingleton.getInstance(this).getImageLoader();
mNetworkImageView.setImageUrl(IMAGE_URL, mImageLoader);
hata
  • 11,633
  • 6
  • 46
  • 69
Moudiz
  • 7,211
  • 22
  • 78
  • 156
  • 1
    Java's basics: what `this` means, how use outer class inside anonymous interface implementation – Selvin Aug 12 '15 at 18:08
  • @Selvin I thought it refers to the context of the activity, as this activity. anyway i still dont understand how to solve my above problem, I guess you dont know how to use volley right? – Moudiz Aug 12 '15 at 18:14
  • I know how to use volley, but you don't know java... Also what is the point of getting singleton instance when in onResponse you already have the bitmap? – Selvin Aug 12 '15 at 18:15
  • @Selvin I am learning java , now I am googling about `this` to update my knowledge about java, and if you know volley can you help me with my question ? – Moudiz Aug 12 '15 at 18:18
  • well thats what it described in the tutorial , I mentioned in my question without the onResponse its working, so my the singleton work I should remove the instance ? @Selvin – Moudiz Aug 12 '15 at 18:23
  • You should put the logcat (error message) for more information. – BNK Aug 13 '15 at 09:57
  • @ChungPham I wasnt able to run the app because I had syntax error , the error as described in the question is here mImageLoader = MySingleton.getInstance(this).getImageLoader(); due to instace, all i want is a way to send this mNetworkImageView = (NetworkImageView) findViewById(R.id.ImageView); mImageLoader = MySingleton.getInstance(this).getImageLoader(); mNetworkImageView.setImageUrl(IMAGE_URL, mImageLoader); as request and cach it .. can you help me with that ? – Moudiz Aug 13 '15 at 12:10
  • I have just added my answer. – BNK Aug 13 '15 at 14:30
  • @Moudiz In the above code you actually add `ImageCache` at `new ImageLoader.ImageCache() { ... }` in `MySingleton` class. – hata Aug 13 '15 at 15:29
  • @hata so no need for a request because the singleton ? – Moudiz Aug 13 '15 at 15:42
  • @Moudiz The instance (**`mImageLoader`**) you get at `mImageLoader = MySingleton.getInstance(this).getImageLoader();` has already been convined with ImageCache in `MySingleton`. There is no need for additional operation for enabling caching. – hata Aug 13 '15 at 15:54
  • @hata ok yes I understand you , but in the android tutorial they said I need to do a [request](https://developer.android.com/intl/zh-cn/training/volley/request.html).. my question is in the imageloader I didnt hadn't to do a request, I was able to get the image without a request . understand me now ? – Moudiz Aug 13 '15 at 16:06
  • @hata btw a stupid question, but how does the cash work ? I assigned new ImageLoader.ImageCache() in singleton , I opened the app and I saw the image , when I closed the app the image diseapered, shouldnt the image stay because its cached ? – Moudiz Aug 13 '15 at 16:09
  • @Moudiz When you `setImageUrl` on `mNetworkImageView`, the `imageLoader` does `ImageRequest` internally. – hata Aug 13 '15 at 16:24
  • @Moudiz `ImageCache` is **L1 Cache**. That means it resides only during the app is launching. Volley also has internal L2 Cache (`DiskBasedCache`). It's size is 5MB. – hata Aug 13 '15 at 16:36
  • @hata I was doing some google about `DiskBasedCache`but I couldnt find anything helpful tutorial or examples about it especially there was some [performance problems](http://stackoverflow.com/questions/20916478/performance-issue-with-volleys-diskbasedcache) , your informations and help is really apreciated – Moudiz Aug 13 '15 at 19:42
  • @Moudiz You should read the source code of Volley if you are interested in `DiskBasedCache`. And such discussions are beyond commenting. If you have further questions, please post new questions. – hata Aug 14 '15 at 04:12
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/86954/discussion-between-moudiz-and-hata). – Moudiz Aug 14 '15 at 05:56

3 Answers3

5

This is my working sample code. Hope this help:

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>
BNK
  • 23,994
  • 8
  • 77
  • 87
  • I tried your example but when I close the app then open it , the image disapear, how then the image should be cached ? – Moudiz Aug 13 '15 at 16:03
  • what if I want to add DiskBasedCache? can you please add it in your example ? – Moudiz Aug 13 '15 at 19:43
  • I think you can find more about caching [here](http://stackoverflow.com/questions/26193501/volley-diskbasedcache-throws-filenotfoundexception). Try increase your cache size. Goodluck! – BNK Aug 13 '15 at 23:46
  • Hi! I updated my answer. Look at `Volley.newRequestQueue(mContext.getApplicationContext(), 10 * 1024 * 1024);`, this is for caching – BNK Aug 17 '15 at 06:43
  • shoudlnt i add diskcache ? and now I am trying to show the images in arraylist adapter , do you a ready simple example that you can help me with ? – Moudiz Aug 17 '15 at 08:02
  • That's diskcache, you can press Ctrl-B at `newRequestQueue` to view more details (which will be `newRequestQueue(Context context, int maxDiskCacheBytes)`) – BNK Aug 17 '15 at 08:03
  • oh nice ill check that thanks a alot , I have another question I was following this [tutoria android-custom-listview-with-image-and-text-using-volleyl](http://www.androidhive.info/2014/07/android-custom-listview-with-image-and-text-using-volley/) and it worked good. but I am have problem implementing in his code the diskcache , do u have a simple example with your code to display the imageloader in a listview ? – Moudiz Aug 17 '15 at 08:06
  • ok thanks anyway , tonight Ill post what I have already done in my code , and If you can help me that would be great :d – Moudiz Aug 17 '15 at 08:10
  • Try the above newRequestQueue for your listview example and check if it caches or not. – BNK Aug 17 '15 at 09:42
1

You´r in the wrong Context .. init a class member

private final Context ctx = this; 

and than use ctx inside onResponse

mImageLoader = MySingleton.getInstance(ctx).getImageLoader();
Alexander Sidikov Pfeif
  • 2,418
  • 1
  • 20
  • 35
1

One optimization suggestion for the getInstance method is to use double checked locking as it only synchronizes in case the instance is actually null:

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;
}

More information on singletons and the different ways of instantiating them can be found here.

The volatile keyword ensures, that the field is immediately seen by all threads and is not cached thread-locally. If the getInstance did not require a Context, there are other method explained in the link that would be more suitable than double checked locking.

AgentKnopf
  • 4,295
  • 7
  • 45
  • 81