Coder Social home page Coder Social logo

anupcowkur / reservoir Goto Github PK

View Code? Open in Web Editor NEW
665.0 29.0 76.0 696 KB

Android library to easily serialize and cache your objects to disk using key/value pairs.

License: MIT License

Java 100.00%
java rxjava cache android-library android gson disklrucache async reservoir serialization asynchronous

reservoir's Introduction

Deprecated

This project is no longer maintained. No new issues or pull requests will be accepted. You can still use the source or fork the project to suit your needs.

Reservoir

Reservoir is a simple library for Android that allows you to easily serialize and cache your objects to disk using key/value pairs.

Download

Including in your project

Add the jcenter repository to your gradle build file if it's not already present:

repositories {
    jcenter()
}

Next, add Reservoir as a dependency:

dependencies {
    compile 'com.anupcowkur:reservoir:3.1.0'
}

Usage

Initialize

Reservoir uses the internal cache storage allocated to your app. Before you can do anything, you need to initialize Reservoir with the cache size.

try {
    Reservoir.init(this, 2048); //in bytes
} catch (IOException e) {
        //failure
}

If you want to pass in a custom GSON instance for whatever reason, you can do that too:

try {
    Reservoir.init(this, 2048, myGsonInstance);
} catch (IOException e) {
        //failure
}

The best place to do this initialization would be in your application's onCreate() method.

Since this library depends directly on DiskLruCache, you can refer that project for more info on the maximum size you can allocate etc.

Put stuff

You can put objects into Reservoir synchronously or asynchronously.

Async put will you give you a callback on completion:

//Put a simple object
Reservoir.putAsync("myKey", myObject, new ReservoirPutCallback() {
            @Override
            public void onSuccess() {
                //success
            }

            @Override
            public void onFailure(Exception e) {
                //error
            }
        });


//Put collection
List<String> strings = new ArrayList<String>();
strings.add("one");
strings.add("two");
strings.add("three");
Reservoir.putAsync("myKey", strings, new ReservoirPutCallback() {
            @Override
            public void onSuccess() {
                //success
            }

            @Override
            public void onFailure(Exception e) {
                //error
            }
        });        

synchronous put:

//Put a simple object
try {
    Reservoir.put("myKey", myObject);
} catch (IOException e) {
    //failure;
}

//Put collection
List<String> strings = new ArrayList<String>();
strings.add("one");
strings.add("two");
strings.add("three");
try {
    Reservoir.put("myKey", strings);
} catch (IOException e) {
    //failure;
}

Async put uses the standard AsyncTask provided by the Android framework.

Get Stuff

You can get stuff out of Reservoir synchronously or asynchronously as well.

Async get will give you a callback on completion:

//Get a simple object
Reservoir.getAsync("myKey", MyClass.class, new ReservoirGetCallback<MyClass>() {
            @Override
            public void onSuccess(MyClass myObject) {
                //success
            }

            @Override
            public void onFailure(Exception e) {
                //error
            }
        });

//Get collection
Type resultType = new TypeToken<List<String>>() {}.getType();
Reservoir.getAsync("myKey", resultType, new ReservoirGetCallback<List<String>>() {
            @Override
            public void onSuccess(List<String> strings) {
                //success
            }

            @Override
            public void onFailure(Exception e) {
                //error
            }
        });        

synchronous get:

//Get a simple object
try {
    Reservoir.get("myKey", MyClass.class);
} catch (IOException e) {
        //failure
}

//Get collection
Type resultType = new TypeToken<List<String>>() {}.getType();
try {
    Reservoir.get("myKey", resultType);
} catch (IOException e) {
        //failure
}

Check for existence

If you wish to know whether an object exists for the given key, you can use:

try {
    boolean objectExists = Reservoir.contains("myKey");
} catch (IOException e) {}

Delete Stuff

deleting stuff can also be synchronous or asynchronous.

Async delete will give you a callback on completion:

Reservoir.deleteAsync("myKey", new ReservoirDeleteCallback() {
            @Override
            public void onSuccess(MyClass myObject) {
                //success
            }

            @Override
            public void onFailure(Exception e) {
                //error
            }
        });

synchronous delete:

try {
    Reservoir.delete("myKey");
} catch (IOException e) {
        //failure
}

Clearing the cache

You can clear the entire cache at once if you want.

asynchronous clear:

Reservoir.clearAsync(new ReservoirClearCallback() {
            @Override
            public void onSuccess() {
                try {
                    assertEquals(0, Reservoir.bytesUsed());
                } catch (Exception e) {
                   
                }
            }

            @Override
            public void onFailure(Exception e) {
                
            }
        });

synchronous clear:

try {
    Reservoir.clear();
} catch (IOException e) {
        //failure
}

RxJava

Reservoir is down with RxJava! All the async methods have RxJava variants that return observables. These observables are scheduled on a background thread and observed on the main thread by default (you can change this easily by assigning your own schedulers and observers to the returned observable).

First, you'll need to add RxJava dependency to your app since Reservoir does not come bundled with it:

compile 'io.reactivex:rxandroid:<rxandroid-version>' - tested with v1.2.1
compile 'io.reactivex:rxjava:<rxjava-version>' - tested with v1.1.6

Then you can use the RxJava variants of all the regular Reservoir methods.

put:

//Put a simple object
Reservoir.putUsingObservable("myKey", myObject) returns Observable<Boolean>

//Put collection
List<String> strings = new ArrayList<String>();
strings.add("one");
strings.add("two");
strings.add("three");
Reservoir.putUsingObservable("myKey", strings) returns Observable<Boolean>

get:

//Get a simple object
Reservoir.getUsingObservable("myKey", MyClass.class) returns Observable<MyClass>

//Get collection
//Note : Rx observables return items one at a time. So even if you put in a complete collection, the items in the collection will be returned 
//one by one by the observable.
Type collectionType = new TypeToken<List<String>>() {}.getType();
Reservoir.getUsingObservable("myKey", String.class, collectionType) returns Observable<String>

delete:

Reservoir.deleteUsingObservable("myKey") returns Observable<Boolean>

clear:

Reservoir.clearUsingObservable() returns Observable<Boolean>

If you'd like to see examples of using these observables, check out the tests in the sample application.

FAQs

What kind of objects can I add to Reservoir?

Anything that GSON can serialize.

What happens if my cache size is exceeded?

Older objects will be removed in a LRU (Least Recently Used) order.

Can I use this a SharedPreferences replacement?

NO! This is a cache. You should store stuff in here that is good to have around, but you wouldn't mind if they were to be removed. SharedPreferences are meant to store user preferences which is not something you want to lose.

Sample

Check out the sample application tests for complete examples of API usage.

Contributing

Contributions welcome via Github pull requests.

Credits

Reservoir is just a tiny little convenience wrapper around the following fantastic projects:

License

This project is licensed under the MIT License. Please refer the License.txt file.

reservoir's People

Contributors

anupcowkur avatar ravidsrk avatar robertoestivill avatar s0h4m avatar synapticvoid avatar trevor-e avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

reservoir's Issues

Enforce init call

In the current implementation, performing an operation without calling init leads to an NullPointerException without specifying the cause. This should be made more explicit.

Support for expiration date

Hi,

this is already a nice implementation for a cache. But I have a feature request:
It would be nice to be able to specify an expiration time. When getting an item from the cache it would first check if the item exists and if it is expired. A Method should be passed to the get-request which will be executed to retrieve the new items which will be saved to cache after returning.
-> Observable rxObject = cache.get(, <FETCH_DATA>, <MAX_AGE>)
T: The type of the item
KEY: The key of the item
FETCH_DATA: A class that returns a list of T or a single T which is executed asynchronously

That's just a raw idea... but it would be very nice.

best regards
Tobias

putReservoir success, getReservoir failure

I have a strange behavior. I have called WS with okhttp + retrofit and I have retrieved POJO. Then I want save these objects with Reservoir. My method putReservoir goes in onSuccess but getReservoir goes in onFailure. In both side, I'm using the same object.

As a consequence, I have another question ==> Where are data on the phone ?

A/libc: Fatal signal 11 (SIGSEGV)

Hi Guys, I getting this issue with the lib

A/libc: Fatal signal 11 (SIGSEGV), code 1, fault addr 0x81bb2c25 in tid 27598

do you know whats the cause?

Regards

A question

Can add the option to delete a value by key?

I think it would be easy for you

If not I can't use the library ;)

Thanks,

Remove .jar dependencies

Loving the functionality of this library, but the fact that you're including the dependencies as .jar files as opposed to proper dependencies in your .pom or build.gradle is resulting in errors like;

com.android.dex.DexException: Multiple dex files define Lcom/jakewharton/disklrucache/DiskLruCache$1;

You should switch to loading dependencies through

compile 'com.jakewharton:disklrucache:2.0.2'
compile 'com.google.code.gson:gson:2.2.2'
compile 'commons-io:commons-io:2.4'

null object reference!

Thanks for the great library, and here what I am getting

Attempt to invoke virtual method 'java.lang.String com.anupcowkur.reservoir.SimpleDiskCache$StringEntry.getString()' on a null object reference

Any help please?

rx.java seems not optionable - `Failed resolution of rx/Observable$onSubscribe` error

Hi,
I added Reservoir to my project where I already use rxJava2. So I try to add Reservoir as dependency without rxJava reference.

Into my gradle file I have only:

compile 'com.anupcowkur:reservoir:3.1.0'

but I give this error:

Rejecting re-init on previously-failed class java.lang.Class<com.anupcowkur.reservoir.Reservoir$1>: java.lang.NoClassDefFoundError: Failed resolution of: Lrx/Observable$OnSubscribe;

I don't really want to add rxJava1 dependency to avoid a mess with rxJava2 imports.

Any suggestions?

Issue in put function

Hi,

To make it work I had to put UTF-8 in value.getBytes, since in my language I use accents. If the charsetname is not defined the result string of getbytes can be chopped

Cheers.

private void put(String key, String value, Map<String, ? extends Serializable> annotations)
        throws
        IOException {
    OutputStream cos = null;
    try {
        cos = openStream(key, annotations);
        cos.write(value.getBytes(HTTP.UTF_8));
    } finally {
        if (cos != null)
            cos.close();
    }

}

Proguard Rules

What Proguard Rules should I put in my proguard-rules.pro file?

Can we put multiple keys into cache?

I want to cache the data with multiple keys and retrieve them. Like for example, first I want to save some data with key A and after that some data with key B and so on. And then retrieve that data based on the keys. Is that possible??

Failed to invoke protected java.text.DateFormat() with no args

I don't understand this error, the piece below logs the error

  Reservoir.getAsync("eventsData", resultType, new ReservoirGetCallback<List<EventsDataList>>(){
             @Override
             public void onSuccess(List<EventsDataList> events) {
                 //success
                 mAdapter.addAll(events);
             }

             @Override
             public void onFailure(Exception e) {
                 //error
                 Log.d("ErrorLog", e.getMessage()); //This is where i get the error 
             }
  });

Error:Execution failed for task ':mobile:dexDebug'

When i try to run my project with your library i got this error every time:

Error:Execution failed for task ':mobile:dexDebug'.

com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/Library/Java/JavaVirtualMachines/jdk1.8.0_45.jdk/Contents/Home/bin/java'' finished with non-zero exit value 2

This is my Gradle dependencies:

compile 'com.google.android.gms:play-services:7.5.0'
compile 'com.google.code.findbugs:annotations:3.0.0'
compile 'com.google.code.gson:gson:2.3.1'
compile 'com.github.androidquery:androidquery:0.26.9'
compile 'com.squareup.retrofit:retrofit:1.9.0'
compile 'de.greenrobot:eventbus:2.4.0'
compile 'org.androidannotations:androidannotations-api:3.2'
compile 'com.android.support:recyclerview-v7:21.0.0'
compile "com.daimajia.swipelayout:library:1.2.0@aar"
compile 'de.hdodenhof:circleimageview:1.3.0'
compile 'com.squareup.okhttp:okhttp:2.4.0'
// compile 'com.anupcowkur:reservoir:2.0'
// Design Support Library
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.android.support:support-v4:22.2.0'
compile 'com.android.support:design:22.2.0'

Delete all

It would be nice to add an option to delete everything.

Exposing `Type` parameter

I'm having some issues caching a list of objects. Apparently I'm not passing the correct Class to the Reservoir.get method when deserializing the list. I have confirmed that the write works just fine, and the Json is in the cache file.

I tried a few things that i would normally do when using Gson:

// In Gson
Type type = new TypeToken<List<MyObject>>() {}.getType());
List<MyObject> objects = gson.fromJson( jsonString, type );

// In Reservoir
Class clazz = new TypeToken<List<MyObject>>() {}.getType()).getClass();
List<MyObject> objects = Reservoir.get( "myKey", clazz );

As you can imagine the above doesn't work.
After trying a few other things, I noticed that while Gson api exposes the following methods:

 public <T> T fromJson(String json, Class<T> classOfT) 
 public <T> T fromJson(String json, Type typeOfT)  

But reservoir only exposes

public <T> T fromJson(String json, Class<T> classOfT) 

Would it be worth to overload the methods in the library to take Types too?
Does anyone knows a workaround that doesn't involved wrapping the list in another object ?

Thanks

NullPointerException in Reservoir.java:180

One of my customers is frequently seeing the following issue in a beta version of their app. We've seen over 60+ non-fatals over 3 separate instances affecting 10+ users in a beta test group of around 15

Trace:

java.lang.NullPointerException
       at com.anupcowkur.reservoir.Reservoir$GetTask.doInBackground(Reservoir.java:180)
       at com.anupcowkur.reservoir.Reservoir$GetTask.doInBackground(Reservoir.java:163)
       at android.os.AsyncTask$2.call(AsyncTask.java:287)
       at java.util.concurrent.FutureTask.run(FutureTask.java:234)
       at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
       at java.lang.Thread.run(Thread.java:856)

This happens at multiple locations in the app and has no specific trigger. Reservoir has been initiated in the application object like so:

Reservoir.init(this, 100000);

Seems the cache object might be null in Reservoir.java:180, let me know if you need any more information to reproduce the issue

Declares multiple JSON fields named id

The problem is kinda weird. When I try to store objects that have ID variables inside, I face this exception:

java.lang.IllegalArgumentException: class xxxxx.Article declares multiple JSON fields named id .....

How to solve this problem?

cannot access Observable class file for rx.Observable not found

this does not happen to putAsync. This only happens to getAsync.
My code:

final Type resultType = new TypeToken<List<Category>>() {
        }.getType();
        Reservoir.getAsync("categories", resultType, new ReservoirGetCallback<List<Category>>() {
            @Override
            public void onSuccess(List<Category> categories) {
                //success
                Log.i(TAG, "onSuccess");
            }

            @Override
            public void onFailure(Exception e) {
                //error
                Log.e(TAG, "onFailure", e);
                fetchCatsFromRemote(object);
            }
        });

Comparison with Retrofit using Okhttp

I was using your library in a project but then I started using retrofit and recently came across this post http://blog.denevell.org/android-okhttp-retrofit-using-cache.html , which implies okhttp response cache can be used along with retrofit to cache network requests and use them offline.
So can you highlight how reservoir is different than the above solution or can be it be used with retrofit in a better way. My use case is simple, I'm getting data from rest api using retrofit but the data changes rarely like once in a week. So which solution would work best for me?

Delete operation seems not to work

Reservoir.put("lol", "fsdfasdfa");
Reservoir.delete("lol");
Reservoir.contains("lol") returns true

Reservoir.put("lol", "fsdfasdfa");
Reservoir.clear()
Reservoir.contains("lol") returns false

GSON Error

I get this GSON error when using Reservoir.get.

com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_OBJECT but was STRING at line 1 column 2 path $
08-16 22:26:38.087 18657-18944/ca.jeffrey.apodgallery W/System.err:     at com.google.gson.internal.bind.ReflectiveTypeAdapterFactory$Adapter.read(ReflectiveTypeAdapterFactory.java:224)
08-16 22:26:38.087 18657-18944/ca.jeffrey.apodgallery W/System.err:     at com.google.gson.Gson.fromJson(Gson.java:887)
08-16 22:26:38.087 18657-18944/ca.jeffrey.apodgallery W/System.err:     at com.google.gson.Gson.fromJson(Gson.java:852)
08-16 22:26:38.087 18657-18944/ca.jeffrey.apodgallery W/System.err:     at com.google.gson.Gson.fromJson(Gson.java:801)
08-16 22:26:38.087 18657-18944/ca.jeffrey.apodgallery W/System.err:     at com.google.gson.Gson.fromJson(Gson.java:773)
08-16 22:26:38.087 18657-18944/ca.jeffrey.apodgallery W/System.err:     at com.anupcowkur.reservoir.Reservoir.get(Reservoir.java:176)

Any chance for iOS ?

I really love using this library.
Is there any chance to make it for iOS Swift3 ? :)

Thank you again

The NPE in the SimpleDiskCache.writeMetadata(...)

I don't know if I did something wrong with the library, but I've got a new crash report from a user. Could you check it?

The stack trace:

java.lang.NullPointerException
       at com.anupcowkur.reservoir.SimpleDiskCache.writeMetadata(SimpleDiskCache.java:108)
       at com.anupcowkur.reservoir.SimpleDiskCache.openStream(SimpleDiskCache.java:73)
       at com.anupcowkur.reservoir.SimpleDiskCache.put(SimpleDiskCache.java:95)
       at com.anupcowkur.reservoir.SimpleDiskCache.put(SimpleDiskCache.java:83)
       at com.anupcowkur.reservoir.Reservoir.put(Reservoir.java:47)
      ...

Passing complex objects

Hi, i'm trying to use reservoir with an object extending another object but it gives me a 'NullpointerException' when i'm trying to get it from cache. So, is it a bug or it doesn't work with this kind of objects?

NPE in Reservoir.get(...) method.

Hello! I've got a strange NPE in Reservoir.get() method:

java.lang.NullPointerException
at com.anupcowkur.reservoir.Reservoir.get(Reservoir.java:75)
...

How is it possible if I always check a key and a value for null before saving in the Reservoir?

RxAndroid as a provided dependency

Currently RxAndroid is added as a 'compile' dependency in the library, this can be an overhead (it adds around 3555 methods to the overall method count) to projects and upstream libraries which do not use RxAndroid. Can we offer the RxJava dependency as a 'provided' dependency to work around this?

I'm also adding a Pull Request for reference, all the Espresso seem to pass.

Error using latest version of android studio 0.6.1

HI,

When compiling my project with your library, Android studio compares the manifests between the project and the library and if it finds an icon or label property in the library, it complaints and doesn't want to compile, as you can see below.

Can you modify and remove this parameters and release another version?

Thanks.

/** is also present at com.github.anupcowkur:reservoir:1.1.1:6:45 value=(@drawable/ic_launcher)
Suggestion: add 'tools:replace="icon"' to element at AndroidManifest.xml:34:5 to override
C:\Users\druidamix\saton\saton\src\main\AndroidManifest.xml
Error:(39, 9) Attribute application@icon value=(@drawable/saton) from AndroidManifest.xml:39:9
Error:(38, 9) Attribute application@label value=(SATON) from AndroidManifest.xml:38:9
is also present at com.github.anupcowkur:reservoir:1.1.1:6:82 value=(@string/app_name)
Suggestion: add 'tools:replace="label"' to element at AndroidManifest.xml:34:5 to override
Error:Execution failed for task ':saton:processDebugManifest'.

Manifest merger failed with multiple errors, see logs
Information:BUILD FAILED **/

What can be the causes for faliure for storing

I am planning to use this library to store Hash. The size of the Hash may he up to 10,000 or even more.

Can this library used to store Hash?

Also what can be the possible causes for the failure of storing any object?

Any help would be really Grateful.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.