Giter Club home page Giter Club logo

tray's Introduction

DEPRECATED - no longer actively maintained

Tray - a SharedPreferences replacement for Android

Build Status License

If you have read the documentation of the SharedPreferences you might have seen one of these warnings:

Note: This class does not support use across multiple processes.

Google even deprecated the multiprocess support because it never worked relieable

Tray is this mentioned explicit cross-process data management approach powered by a ContentProvider. Tray also provides an advanced API which makes it super easy to access and maintain your data with upgrade and migrate mechanisms. Welcome to SharedPreferences 2.0 aka Tray.

Features

Usage

Simple tutorial how to use Tray in your project instead of the SharedPreferences

Save and read preferences

// create a preference accessor. This is for global app preferences.
final AppPreferences appPreferences = new AppPreferences(getContext()); // this Preference comes for free from the library
// save a key value pair
appPreferences.put("key", "lorem ipsum");

// read the value for your key. the second parameter is a fallback (optional otherwise throws)
final String value = appPreferences.getString("key", "default");
Log.v(TAG, "value: " + value); // value: lorem ipsum

// read a key that isn't saved. returns the default (or throws without default)
final String defaultValue = appPreferences.getString("key2", "default");
Log.v(TAG, "value: " + defaultValue); // value: default

No Editor, no commit() or apply() 😉

Create your own preference module

It's recommended to bundle preferences in groups, so called modules instead of putting everything in one global module. If you were using SharedPreferences before, you might have used different files to group your preferences. Extending the TrayModulePreferences and put all Keys inside this class is a recommended way to keep your code clean.

// create a preference accessor for a module
public class MyModulePreference extends TrayPreferences {

    public static String KEY_IS_FIRST_LAUNCH = "first_launch";

    public MyModulePreference(final Context context) {
        super(context, "myModule", 1);
    }
}
// accessing the preferences for my own module
final MyModulePreference myModulePreference = new MyModulePreference(getContext());
myModulePreference.put(MyModulePreference.KEY_IS_FIRST_LAUNCH, false);

See the sample project for more

Like the Android SQLiteOpenHelper a TrayPreference lets you implement methods to handle versioning.

public class MyModulePreference extends TrayPreferences {

    public MyModulePreference(final Context context) {
        super(context, "myModule", 1);
    }

    @Override
    protected void onCreate(final int initialVersion) {
        super.onCreate(initialVersion);
    }

    @Override
    protected void onUpgrade(final int oldVersion, final int newVersion) {
        super.onUpgrade(oldVersion, newVersion);
    }

    @Override
    protected void onDowngrade(final int oldVersion, final int newVersion) {
        super.onDowngrade(oldVersion, newVersion);
    }
}

// TOOD add clear sample

Migrate from SharedPreferences to Tray

To migrate values from SharedPreferences you have to create you own preference module. This module will be now store all of your SharedPreferences values.

public class ImportPreferences extends TrayPreferences {

    // The SharedPreferences name
    private static final String SHARED_PREFERENCES_FILE_NAME = "PREF_NAME";
    
    // The key inside the SHARED_PREFERENCES_NAME
    private static final String KEY_FIRST_LAUNCH = "KEY_FIRST_LAUNCH";
    
    // The new key for this module
    private static final String KEY_FIRST_LAUNCH_TRAY = "KEY_FIRST_LAUNCH_TRAY";
    
    public ImportPreferences(@NonNull Context context) {
        super(context, "myImportModule", 1);
    }    
    
    // Called only once when the module was created
    @Override
    protected void onCreate(int initialVersion) {
        super.onCreate(initialVersion);
            
        // Create a SharedPreferencesImport object
        SharedPreferencesImport importPref = 
            new SharedPreferencesImport(getContext(), 
                SHARED_PREFERENCES_FILE_NAME, KEY_FIRST_LAUNCH, KEY_FIRST_LAUNCH_TRAY);
            
        // Finally migrate it
        migrate(importPref);
    }
}

Getting Started

Add Tray to your project
GitHub Packages
repositories {
    maven {
        url = uri("https://maven.pkg.github.com/GCX-HCI/tray")
    }
}

dependencies {
    implementation "net.grandcentrix.tray:tray:0.12.0"
}
JCenter (deprecated)
repositories {
    jcenter()
}

dependencies {
    implementation "net.grandcentrix.tray:tray:0.12.0"
}

More on the ContentProvider configuration can be found in the wiki

Testcoverage 100%

Tray has 100% test coverage and we'll try to keep it at that level for stable releases.

You can run the coverage report with ./gradlew createDebugCoverageReport. You'll find the output in library/build/outputs/coverage/debug/index.html which looks like this:

coverage report

You can check the coverage report at codecov.io

Those ~170 tests will help us indicate bugs in the future before we publish them. Don't think the code is 100% bug free based on the test coverage.

Build state

Build Status

codecov.io

ContentProvider is overkill

At first, it was the simplest way to use IPC with Binder to solve the multiprocess problem. Using the ContentProvider with a database turned out to be very handy when it comes to save metadata. We thought about replacing the database with the real SharedPreferences to boost the performance (the SharedPreferences do not access the disk for every read/write action which causes the multiprocess problem btw) but the metadata seemed to be more valuable to us. see more informations

If you have found a better solution implement the TrayStorage and contribute to this project! We would appreciate it.

That said, yes the performance isn't as good as the SharedPreferences. But the performance is good enough to save/access single key value pairs synchron. If you want to save more you should think about a simple database.

Missing Features

Tray is ready to use without showblockers! But here are some nice to have features for the future:

  • Reactive wrapper to observe values
  • no support to save Set<String>. Is someone using this?
  • more metadata fields: (i.e. app version code/name)

Roadmap

  • performance tests
  • memory cache for based on contentobservers
  • prevent app crashes due to database errors
  • rx wrapper for changes
  • save additional data types (Set<String>, byte[])

Versions

Version 0.11.1 07.02.17
  • preference key cannot be empty #84
  • clearBut(TrayPreference) -> clearBut(AbstractTrayPreference) #89
Version 0.11.0 07.09.16
  • all accessor methods return boolean indicating the success of i.e. put, remove. They will never again throw an error. #69
  • new contains() method #74
Version 0.10.0 31.05.16
  • All features and changes of the 1.0.0-rc preview builds
  • #65 Fix deletion of non string migrated shared preferences.
Version 1.0.0 preview - postponed until the memory cache is ready
1.0.0-rc3 05.11.15
  • hotfix for listener on Android 6.0 which has caused a infinity loop #55
  • the sample project includes now a way to test the multi process support compared to the SharedPreferences
  • removed unnecessary write operation for every version check #54
1.0.0-rc2 24.09.15
  • added logging for all data changing methods. Enable via adb shell setprop log.tag.Tray VERBOSE
1.0.0-rc1 21.09.15
  • Android M Auto Backup feature support (see the Documentation)
    • split up database for user and device specific data (device specific data can now be excluded from the auto backup)
    • TrayPreferences has now an optional 3. constructor parameter TrayStorage.Type, USER or DEVICE indicating the internal database (required for Android M Auto Backup). Default is USER
  • New methods and changes
    • PreferenceAccessor#wipe() clears the preference data and it's internal data (version)
    • TrayPreferences#annexModule(String name) imports a module by name and wipes it afterwards. This allows renaming of preferences without losing data
    • AbstractTrayPreference#annex(ModularizedStorage<TrayItem>) allows a storage to import another storage, wipes the imported afterwards
    • Preference #onCreate(...) and #onUpgrade(...) aren't abstract anymore because they don't require an implementation
  • Deprecations (will be removed soon)
    • TrayAppPreferences is now deprecated. Use AppPreferences instead (renaming)
    • TrayModulePreferences is now deprecated. Use TrayPreferences instead to extend from for your own Preferences
  • Internal structure
    • new package structure. merged packages accessor, migration and storage into core
    • package provider contains a TrayStorage implementation with a ContentProvider. Is easy exchangeable with another TrayStorage implementation
    • ModularizedTrayPreference is now called AbstractTrayPreference
    • ModularizedStorage was renamed to TrayStorage
Version 0.9.2 02.06.15
  • getContext() is working in TrayModulePreference#onCreate
Version 0.9.1 18.05.15
  • saving null with mPref.put(KEY, null) works now
  • access to preference with throwing methods instead of default value (throws ItemNotFoundException). Example: mPref.getString(KEY); instead of mPref.getString(KEY, "defaultValue");
  • WrongTypeException when accessing a preference with a different type and the data isn't parsable. Float (10.1f) -> String works, String ("10.1") -> Float works, String ("test") -> Float throws!
  • javadoc in now included in aar
Version 0.9 27.04.15
  • initial public release
Version 0.2 - 0.8
  • Refactoring
  • 100% Testing
  • Bugfixing
Version 0.1 17.09.14
  • first working prototype

Contributors

License

Copyright 2015 grandcentrix GmbH

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

tray's People

Contributors

earlofego avatar eyedol avatar hegazy avatar jannisveerkamp avatar maqnouch avatar martinotten avatar passsy avatar stefma avatar twogood 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  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

tray's Issues

save Set<String> Is someone using this? -> YES PLEASE!

Hi,

In the "Missing Features" section you ask if someone wants to save Set - yes, I do!

Well, actually more saving a Map or an ArrayList<Map<String, Object>> - that's what I work with mostly. So I would very often need to save something like this as the value to some key:

[
   {
      "key": "value",
      "key2": 222,
      "key3": {"sub_key":"sub_value", "sub_key2":23232},
      "key4": [1,2,3,4],
      "key5": "etc"
   }
]

I've had to use a combination of Base64OutputStream, ByteArrayOutputStream and ObjectOutputStream to save objects to shared prefs. :\

"could not access stored data with uri content" error

My application is the only launcher of the android firmware.I got the follow error:
The TrayPreferences is initialized in MyApplication.onCreate

08-12 14:52:14.283 E/ActivityThread( 1462): Failed to find provider info for com.testtay.user_preference
08-12 14:52:14.293 E/CrashHandler( 1462): encountered a fatal error
08-12 14:52:14.293 E/CrashHandler( 1462): java.lang.RuntimeException: Unable to create application com.testtay.application.MyApplication: java.lang.IllegalStateException: could not access stored data with uri content://com.testtay.user_preference/internal_preferences/group/version?backup=true. Is the provider registered in the manifest of your application?
08-12 14:52:14.293 E/CrashHandler( 1462):       at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4347)
08-12 14:52:14.293 E/CrashHandler( 1462):       at android.app.ActivityThread.access$1500(ActivityThread.java:135)
08-12 14:52:14.293 E/CrashHandler( 1462):       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1256)
08-12 14:52:14.293 E/CrashHandler( 1462):       at android.os.Handler.dispatchMessage(Handler.java:102)
08-12 14:52:14.293 E/CrashHandler( 1462):       at android.os.Looper.loop(Looper.java:136)
08-12 14:52:14.293 E/CrashHandler( 1462):       at android.app.ActivityThread.main(ActivityThread.java:5017)
08-12 14:52:14.293 E/CrashHandler( 1462):       at java.lang.reflect.Method.invokeNative(Native Method)
08-12 14:52:14.293 E/CrashHandler( 1462):       at java.lang.reflect.Method.invoke(Method.java:515)
08-12 14:52:14.293 E/CrashHandler( 1462):       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:788)
08-12 14:52:14.293 E/CrashHandler( 1462):       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:604)
08-12 14:52:14.293 E/CrashHandler( 1462):       at dalvik.system.NativeStart.main(Native Method)
08-12 14:52:14.293 E/CrashHandler( 1462): Caused by: java.lang.IllegalStateException: could not access stored data with uri content://com.testtay.user_preference/internal_preferences/group/version?backup=true. Is the provider registered in the manifest of your application?
08-12 14:52:14.293 E/CrashHandler( 1462):       at net.grandcentrix.tray.provider.TrayProviderHelper.queryProvider(TrayProviderHelper.java:145)
08-12 14:52:14.293 E/CrashHandler( 1462):       at net.grandcentrix.tray.provider.ContentProviderStorage.getVersion(ContentProviderStorage.java:214)
08-12 14:52:14.293 E/CrashHandler( 1462):       at net.grandcentrix.tray.core.Preferences.changeVersion(Preferences.java:224)
08-12 14:52:14.293 E/CrashHandler( 1462):       at net.grandcentrix.tray.core.Preferences.<init>(Preferences.java:51)
08-12 14:52:14.293 E/CrashHandler( 1462):       at net.grandcentrix.tray.core.AbstractTrayPreference.<init>(AbstractTrayPreference.java:31)
08-12 14:52:14.293 E/CrashHandler( 1462):       at net.grandcentrix.tray.TrayPreferences.<init>(TrayPreferences.java:43)
08-12 14:52:14.293 E/CrashHandler( 1462):       at net.grandcentrix.tray.TrayPreferences.<init>(TrayPreferences.java:48)
08-12 14:52:14.293 E/CrashHandler( 1462):       at com.testtay.common.preferences.CacheTrayPreferences.<init>(CacheTrayPreferences.java:24)
08-12 14:52:14.293 E/CrashHandler( 1462):       at com.testtay.common.preferences.UserPreferences.<init>(UserPreferences.java:34)
08-12 14:52:14.293 E/CrashHandler( 1462):       at com.testtay.common.preferences.UserPreferences.init(UserPreferences.java:21)
08-12 14:52:14.293 E/CrashHandler( 1462):       at com.testtay.utils.ProcessUtils.initCommon(ProcessUtils.java:149)
08-12 14:52:14.293 E/CrashHandler( 1462):       at com.testtay.application.process.MainProcessApp.create(MainProcessApp.java:64)
08-12 14:52:14.293 E/CrashHandler( 1462):       at com.testtay.application.MyApplication.onCreate(MyApplication.java:50)
08-12 14:52:14.293 E/CrashHandler( 1462):       at android.app.Instrumentation.callApplicationOnCreate(Instrumentation.java:1021)
08-12 14:52:14.293 E/CrashHandler( 1462):       at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4344)
08-12 14:52:14.293 E/CrashHandler( 1462):       ... 10 more

SecurityException

Do you know why this security exception occur?
I am accessing it in my own process.

I am keeping tray provider like below for proguard obfuscation

-keep class net.grandcentrix.tray.provider.** {*;}
03-25 15:02:57.107 W 22864    22969    System.err:            net.grandcentrix.tray.a.f: Hard error accessing the ContentProvider 
03-25 15:02:57.107 W 22864    22969    System.err:            	at net.grandcentrix.tray.provider.TrayProviderHelper.queryProvider(SourceFile:170) 
03-25 15:02:57.107 W 22864    22969    System.err:            	at net.grandcentrix.tray.provider.ContentProviderStorage.getVersion(SourceFile:216) 
03-25 15:02:57.107 W 22864    22969    System.err:            	at net.grandcentrix.tray.a.e.a(SourceFile:257) 
03-25 15:02:57.107 W 22864    22969    System.err:            	at net.grandcentrix.tray.a.e.a(SourceFile:291) 
03-25 15:02:57.107 W 22864    22969    System.err:            	at net.grandcentrix.tray.a.e.<init>(SourceFile:57) 
03-25 15:02:57.107 W 22864    22969    System.err:            	at net.grandcentrix.tray.a.a.<init>(SourceFile:31) 
03-25 15:02:57.107 W 22864    22969    System.err:            	at net.grandcentrix.tray.c.<init>(SourceFile:43) 
03-25 15:02:57.107 W 22864    22969    System.err:            	at net.grandcentrix.tray.c.<init>(SourceFile:48) 
03-25 15:02:57.108 W 22864    22969    System.err:            Caused by: java.lang.SecurityException: Permission Denial: reading net.grandcentrix.tray.provider.TrayContentProvider uri content://com.xxxxxxx.tray/internal_preferences/THEME/version?backup=true from pid=0, uid=1000 requires the provider be exported, or grantUriPermission() 
03-25 15:02:57.109 W 22864    22969    System.err:            	at net.grandcentrix.tray.provider.TrayProviderHelper.queryProvider(SourceFile:168) 
03-25 15:02:57.113 W 22864    22969    System.err:            net.grandcentrix.tray.a.f: Hard error accessing the ContentProvider 
03-25 15:02:57.113 W 22864    22969    System.err:            	at net.grandcentrix.tray.provider.TrayProviderHelper.queryProvider(SourceFile:170) 
03-25 15:02:57.113 W 22864    22969    System.err:            	at net.grandcentrix.tray.provider.ContentProviderStorage.getVersion(SourceFile:216) 
03-25 15:02:57.113 W 22864    22969    System.err:            	at net.grandcentrix.tray.a.e.a(SourceFile:257) 
03-25 15:02:57.114 W 22864    22969    System.err:            	at net.grandcentrix.tray.a.e.a(SourceFile:291) 
03-25 15:02:57.114 W 22864    22969    System.err:            	at net.grandcentrix.tray.a.e.b(SourceFile:130) 
03-25 15:02:57.114 W 22864    22969    System.err:            Caused by: java.lang.SecurityException: Permission Denial: reading net.grandcentrix.tray.provider.TrayContentProvider uri content://com.xxxxxxxx.tray/internal_preferences/THEME/version?backup=true from pid=0, uid=1000 requires the provider be exported, or grantUriPermission() 
03-25 15:02:57.115 W 22864    22969    System.err:            	at net.grandcentrix.tray.provider.TrayProviderHelper.queryProvider(SourceFile:168) 

not to use resValue

Hi,
In Android Studio,we can use resValue to define authority in provider,but how to define authority in eclipse environment for multi-apps?

java.lang.IllegalStateException

java.lang.IllegalStateException: could not access stored data with uri content://com.taobao.qianniu.tray/internal_preferences/qianniu/version. Is the provider registered in the manifest of your application?
at net.grandcentrix.tray.provider.TrayProviderHelper.queryProvider(SourceFile:196)
at net.grandcentrix.tray.storage.TrayStorage.getVersion(SourceFile:85)
at net.grandcentrix.tray.accessor.Preference.changeVersion(SourceFile:158)
at net.grandcentrix.tray.accessor.Preference.(SourceFile:52)
at net.grandcentrix.tray.accessor.TrayPreference.(SourceFile:33)
at net.grandcentrix.tray.TrayModulePreferences.(SourceFile:40)

MediaPad X1 7.0 11
PE-TL00M 7
Coolpad 9150W 6
HUAWEI P7-L07 6
PE-TL20 6
Che2-UL00 6
G621-TL00 5
vivo X5Pro D 5
Coolpad 8675-A 4
SM-N900

please reduce the minSdkVersion <=10

Dear grandcentrix GmbH,
I like this library , it make sharedPreference so easy to use....
but it need minApi is 15 , it's wired , this limited many customer requirements,
because many shopping apps do not want higher api level , that means lost costom.....
so when I use it I must import the library moudle ,then set the minSdkVersion to 10 ,
but I think many developers do not know that the library's api-level could be reduced , maybe it will lost many follows cause this...
So would please reduce it to fits much more app ?

Best Regards.

Contains method

Currently it is not possible to check if a key exist in Tray.
We have to do something like:

       try {
            return mPreference.getBoolean(key)
        } catch (ItemNotFoundException e) {
            return false;
        }

It would be great if we have a method like contains(key) to check if a key exists.

Caused by: java.lang.IllegalArgumentException: Query is not supported for Uri:

Caused by: java.lang.IllegalArgumentException: Query is not supported for Uri: content://com.sankuai.meituan.dispatch.crowdsource.tray/preferences/com.sankuai.meituan.xxxxx/xxxxxxxe.mark?backup=true
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:167)
at android.database.DatabaseUtils.readExceptionFromParcel(DatabaseUtils.java:137)
at android.content.ContentProviderProxy.query(ContentProviderNative.java:413)
at android.content.ContentResolver.query(ContentResolver.java:461)
at android.content.ContentResolver.query(ContentResolver.java:404)
at net.grandcentrix.tray.provider.TrayProviderHelper.a(ProGuard:141)
at net.grandcentrix.tray.provider.ContentProviderStorage.a(ProGuard:2172)
at net.grandcentrix.tray.core.Preferences.a(ProGuard:68)
at com.meituan.xxx.util.SPUtil.a(ProGuard:1118)
at com.meituan.xxx.util.SPUtil.a(ProGuard:25)

Content provider

Since this is based on content provider, is Tray required to declare in manifest as Content Provider does with tag? My concern is usability across apps.

MODE_MULTI_PROCESS is deprecated (API 23). Recommendation ContentProvider

This constant was deprecated in API level 23.
MODE_MULTI_PROCESS does not work reliably in some versions of Android, and furthermore does not provide any mechanism for reconciling concurrent modifications across processes. Applications should not attempt to use it. Instead, they should use an explicit cross-process data management approach such as ContentProvider.

Tray is an accepted (recommended) solution from the Android documentation. Not mentioned there of cause, but a good alternative.

screen shot 2015-09-10 at 5 10 46 pm

Support for Set<String>

I see your library and i need to replace my shared prefs because my prefs is caching in my service and i dont want it , so Context.MODE_MULTI_PROCESS is deprecated.

Actually i can use this library for now but i need to store Set this is so important for me , can you add this support please !

  • Set. Is someone using this? ( Yes there is ghost developers using it :) )

error SQLiteConstraintException on emulator with Android N

04-28 08:37:18.840 8826-9078/*** E/SQLiteDatabase: Error inserting KEY=version MIGRATED_KEY=null UPDATED=1493368638631 CREATED=1493368638631 MODULE=cookie VALUE=1
android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed: TrayInternal.MODULE, TrayInternal.KEY (code 2067)
at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:782)
at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1472)
at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1343)
at net.grandcentrix.tray.provider.SqliteHelper.insertOrUpdate(SqliteHelper.java:148)
at net.grandcentrix.tray.provider.TrayContentProvider.insertOrUpdate(TrayContentProvider.java:206)
at net.grandcentrix.tray.provider.TrayContentProvider.insert(TrayContentProvider.java:187)
at android.content.ContentProvider$Transport.insert(ContentProvider.java:264)
at android.content.ContentResolver.insert(ContentResolver.java:1274)
at net.grandcentrix.tray.provider.TrayProviderHelper.persist(TrayProviderHelper.java:151)
at net.grandcentrix.tray.provider.TrayProviderHelper.persist(TrayProviderHelper.java:142)
at net.grandcentrix.tray.provider.ContentProviderStorage.setVersion(ContentProviderStorage.java:350)
at net.grandcentrix.tray.core.Preferences.changeVersion(Preferences.java:272)
at net.grandcentrix.tray.core.Preferences.isVersionChangeChecked(Preferences.java:292)
at net.grandcentrix.tray.core.Preferences.(Preferences.java:58)
at net.grandcentrix.tray.core.AbstractTrayPreference.(AbstractTrayPreference.java:31)
at net.grandcentrix.tray.TrayPreferences.(TrayPreferences.java:43)
at net.grandcentrix.tray.TrayPreferences.(TrayPreferences.java:48)
at ***.module.sp.CookiePreferences.(CookiePreferences.java:23)
at ***.util.FileTypeUtil.getCookies(FileTypeUtil.java:839)
at ***.util.Util.SendPhoneInfo(Util.java:386)
at .util.Util.access$000(Util.java:124)
at .util.Util$1.run(Util.java:371)
04-28 08:37:18.840 8826-9078/
W/Tray: Couldn't update or insert data. Uri: content://
.tray/internal_preferences/cookie/version?backup=true

Reference:

compile 'net.grandcentrix.tray:tray:0.11.1'

And code at "net.grandcentrix.tray.core.Preferences.changeVersion(Preferences.java:272)" is below:

getStorage().setVersion(newVersion);

Null is not inserted

Inserting null with insert(KEY, null) doesn't work at the moment. The only way to delete a value is to call remove(KEY).

can i use listener?

can i use listener like:
sharedPreferences.registerOnSharedPreferenceChangeListener(this)
?
thanks

Support use across multiple processess, how to use it?

Support use across multiple processess

I'm trying to understand how can I take advantage of it . Can you give a practical example ?

Is it simply writes data asynchronous?

what will be happen when the app is killed before the data has been written to disk?
Is it true 'commit' semantics?

Topics that do not fully understand:

Tray solves this problem with a ContentProvider based storage.

What are the benefits it gives to us? Why we want use ContentProvider? Whether it's sharing of resources between different applications ?

Works multiprocess

So I can save several values ​​in parallel?

automatically saves metadata for each entry (created, last updated, ...)

I do not understand? automatically saves metadata?

At first, it was the simpst way to use IPC with Binder to solve the multiprocess problem. Using the ContentProvider with a database turned out to be very handy when it comes to save metadata. We thought about replacing the database with the real SharedPreferences to boost the performance (the SharedPreferences do not access the disk for every read/write action which causes the multiprocess problem btw) but the metadata seemed to be more valuable to us.

Why we want use ContentProvider? Whether it's sharing of resources between different applications ?

changeVersion doesn't support multithreading. -> NullPointerException

Very thanks for your product,but i have got a NullPointerException while migrating data,can anybody give me some advice?

java.lang.NullPointerException:
Attempt to invoke virtual method 'java.lang.String java.lang.Object.toString()' on a null object reference
at net.grandcentrix.tray.core.SharedPreferencesImport.onPostMigrate(SharedPreferencesImport.java:79)
at net.grandcentrix.tray.core.SharedPreferencesImport.onPostMigrate(SharedPreferencesImport.java:38)
at net.grandcentrix.tray.core.Preferences.migrate(Preferences.java:125)
at ...module.sp.CookiePreferences.importSP(CookiePreferences.java:47)
at ..
.module.sp.CookiePreferences.onCreate(CookiePreferences.java:29)
at net.grandcentrix.tray.core.Preferences.changeVersion(Preferences.java:262)
at net.grandcentrix.tray.core.Preferences.isVersionChangeChecked(Preferences.java:292)
at net.grandcentrix.tray.core.Preferences.(Preferences.java:58)
at net.grandcentrix.tray.core.AbstractTrayPreference.(AbstractTrayPreference.java:31)
at net.grandcentrix.tray.TrayPreferences.(TrayPreferences.java:43)
at net.grandcentrix.tray.TrayPreferences.(TrayPreferences.java:48)
at ..*.module.sp.CookiePreferences.(CookiePreferences.java:23)

And below is code in CookiePreferences:

`public class CookiePreferences extends TrayPreferences {
private static final String TAG = CookiePreferences.class.getSimpleName();
public static final String MODULE = "cookie";
private static final String SP_NAME = "mycookie";
public CookiePreferences(Context context) {
super(context, MODULE, 1);
}

@Override
protected void onCreate(int initialVersion) {
    super.onCreate(initialVersion);
    importSP();
}

private void importSP() {
    final SharedPreferences sps = getContext().getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
    HashMap<String, Object> all = new HashMap<>(sps.getAll());

    Set<Map.Entry<String, Object>> set = all.entrySet();
    for (Map.Entry<String, Object> en : set) {
        String key = en.getKey();
        SharedPreferencesImport ip = new SharedPreferencesImport(getContext(), SP_NAME, key, key);
        migrate(ip);
    }
}

}`

Version:

compile 'net.grandcentrix.tray:tray:0.11.1'

happen CursorWindowAllocationException

Hello @passsy

I met a problem of CursorWindowAllocationException today.

Stack Trace

android.database.CursorWindowAllocationException
Cursor window allocation of 2048 kb failed.
java.lang.RuntimeException:Unable to resume activity {com.jiahe.qixin/com.jiahe.qixin.ui.MainActivity}: android.database.CursorWindowAllocationException: Cursor window allocation of 2048 kb failed.
android.app.ActivityThread.performResumeActivity(ActivityThread.java:2940)
......
cause by:
android.database.CursorWindowAllocationException:Cursor window allocation of 2048 kb failed.
android.database.CursorWindow.<init>(CursorWindow.java:108)
android.database.CursorWindow.<init>(CursorWindow.java:100)
android.database.AbstractWindowedCursor.clearOrCreateWindow(AbstractWindowedCursor.java:198)
android.database.sqlite.SQLiteCursor.clearOrCreateWindow(SQLiteCursor.java:301)
android.database.sqlite.SQLiteCursor.fillWindow(SQLiteCursor.java:139)
android.database.sqlite.SQLiteCursor.getCount(SQLiteCursor.java:133)
android.content.ContentResolver.query(ContentResolver.java:436)
android.content.ContentResolver.query(ContentResolver.java:360)
net.grandcentrix.tray.provider.TrayProviderHelper.java.util.List queryProvider(android.net.Uri)(TrayProviderHelper.java:192)
net.grandcentrix.tray.storage.TrayStorage.int getVersion()(TrayStorage.java:85)
net.grandcentrix.tray.accessor.Preference.void changeVersion(int)(Preference.java:158)
net.grandcentrix.tray.accessor.Preference.void <init>(net.grandcentrix.tray.storage.PreferenceStorage,int)(Preference.java:52)
net.grandcentrix.tray.accessor.TrayPreference.void <init>(net.grandcentrix.tray.storage.ModularizedStorage,int)(TrayPreference.java:33)
net.grandcentrix.tray.TrayModulePreferences.void <init>(android.content.Context,java.lang.String,int)(TrayModulePreferences.java:40)
com.XXX.YYY.ui.theme.ThemeSharePrefs.void <init>(android.content.Context,java.lang.String)(ThemeSharePrefs.java:20)
com.XXX.YYY.utils.PrefUtils.java.lang.String getThemeColorFromPreferece(android.content.Context)(PrefUtils.java:912)
com.XXX.YYY.ui.MainActivity.void onResume()(MainActivity.java:1137)
android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1209)
android.app.Activity.performResume(Activity.java:5450)
android.app.ActivityThread.performResumeActivity(ActivityThread.java:2925)
android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2969)
android.app.ActivityThread$H.handleMessage(ActivityThread.java:1371)
android.os.Handler.dispatchMessage(Handler.java:99)
android.os.Looper.loop(Looper.java:177)
android.app.ActivityThread.main(ActivityThread.java:5496)
java.lang.reflect.Method.invokeNative(Native Method)
java.lang.reflect.Method.invoke(Method.java:525)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1225)
com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1041)
dalvik.system.NativeStart.main(Native Method)

Values as InputStreams

Hi Tray team,

What do you think of adding the ability to read an input stream from a tray item and write to a tray item using an outputstream ?
(Ideally there would be no transformation of the binary stream to get higher speed levels).

Actually, it would help to serialize and parse Pojos encoded with whatever format devs want (I would use it for JSON, but protobuf would also work, etc..)

thanks for your efforts on this lib guys !

error ClassNotFoundException on GALAXY S3

FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to get provider net.grandcentrix.tray.provider.TrayContentProvider: java.lang.ClassNotFoundException: Didn't find class "net.grandcentrix.tray.provider.TrayContentProvider" on path: /data/app/com.yudong.fitnew-1.apk
at android.app.ActivityThread.installProvider(ActivityThread.java:5157)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:4755)
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4693)
at android.app.ActivityThread.access$1400(ActivityThread.java:160)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1377)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:176)
at android.app.ActivityThread.main(ActivityThread.java:5454)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1209)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1025)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.ClassNotFoundException: Didn't find class "net.grandcentrix.tray.provider.TrayContentProvider" on path: /data/app/com.yudong.fitnew-1.apk
at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:64)
at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
at android.app.ActivityThread.installProvider(ActivityThread.java:5142)
at android.app.ActivityThread.installContentProviders(ActivityThread.java:4755) 
at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4693) 
at android.app.ActivityThread.access$1400(ActivityThread.java:160) 
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1377) 
at android.os.Handler.dispatchMessage(Handler.java:99) 
at android.os.Looper.loop(Looper.java:176) 
at android.app.ActivityThread.main(ActivityThread.java:5454) 
at java.lang.reflect.Method.invokeNative(Native Method) 
at java.lang.reflect.Method.invoke(Method.java:525) 
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1209) 
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1025) 
at dalvik.system.NativeStart.main(Native Method) 

Missing translation (build error)

resValue "string", "tray__authority", "${applicationId}.tray"
causes missing translation error during exporting signed apk. How can I ignore this the elegant way?

There is no way to add translatable="false". I don't want to ignore all the missing translations either.

It's so strange because my second project doesn't have the problem at all.

use put in onUpgrade will call onUpgrade again

 protected void onUpgrade(int oldVersion, int newVersion) {
        switch (newVersion){
            case 2:
                put(CURRENT_DEVICES_ID, UUID.randomUUID().toString());
        }
    }

when put function call ,then will call onUpgrade again

Compiler complains passing TrayStorage.Type.USER

The following causes the compiler to complain; TrayStorage.Type.USER needs to be an int.

private class MyPreference extends TrayPreferences {
    public MyPreference(@NonNull final Context context) {
        super(context, "myModule", VERSION, TrayStorage.Type.USER);
    }
}

How to run it in specific process

I am having two processes in my app. Say I have two process com.example.appname.xx and another com.example.appname.yy . By default it is running in the process com.example.appname. How can I indicate it to run this in either com.example.appname.xx orcom.example.appname.yy.Thanks.

Can't initialise new AppPreferences(getContext())

Hi everyone, here asking for some help!

It crashed in the TrayContract.java

I get a nullpointerexpection, in this method
@nonnull
private static String getAuthority(@nonnull final Context context) {
return TextUtils.isEmpty(sTestAuthority) ?
context.getString(R.string.tray__authority) :
sTestAuthority;
}

I believe it can't get the context.getString(R.string.tray__authority) in here.

  • I set the the tray__authority and I cleaned the project.
  • I set one different value for every flavour, based on the the ApplicationId.
  • if I do context.getString(R.string.tray__authority) I get the right value.
  • It's the same context has the one I gave to your AppPreferences.

I don't understand the issue, can somebody help?

And found a big bug

Different projects in the same project, using the tray to INSTALL_FAILED_CONFLICTING_PROVIDER problems in API = 19

Found a crash about the sqlite

JNI: CheckJNI is off; workarounds are off; pins=0; globals=269
DALVIK THREADS:
(mutexes: tll=0 tsl=0 tscl=0 ghl=0)
"main" prio=5 tid=1 NATIVE
  | group="main" sCount=1 dsCount=0 obj=0x41db7d08 self=0x415cd008
  | sysTid=10032 nice=-11 sched=0/0 cgrp=apps handle=1074487636
  | state=D schedstat=( 0 0 0 ) utm=88 stm=33 core=0
  #00  pc  0002035c  pwrite64 LINE:libc.so
  #01  pc  0000c3b7  /system/lib/libsqlite.so
  #02  pc  0001ccb5  /system/lib/libsqlite.so
  #03  pc  000312bb  /system/lib/libsqlite.so
  #04  pc  00035ecd  /system/lib/libsqlite.so
  #05  pc  0003608f  /system/lib/libsqlite.so
  #06  pc  00036b51  /system/lib/libsqlite.so
  #07  pc  000537cb  /system/lib/libsqlite.so
  #08  pc  000417f3  sqlite3_step LINE:libsqlite.so
  #09  pc  0006492d  /system/lib/libandroid_runtime.so
  #10  pc  00064989  /system/lib/libandroid_runtime.so
  #11  pc  0001dd4c  dvmPlatformInvoke LINE:libdvm.so
  #12  pc  0004dfc7  /system/lib/libdvm.so
  #13  pc  00027160  /system/lib/libdvm.so
  #14  pc  0002e0a8  dvmMterpStd LINE:libdvm.so
  #15  pc  0002b754  /system/lib/libdvm.so
  #16  pc  000603bb  /system/lib/libdvm.so
  #17  pc  000603df  /system/lib/libdvm.so
  #18  pc  0006b999  dvmInitClass LINE:libdvm.so
  #19  pc  0006c469  dvmResolveStaticField LINE:libdvm.so
  #20  pc  00022574  dvmAsmSisterStart LINE:libdvm.so
  #21  pc  0002e0a8  dvmMterpStd LINE:libdvm.so
  #22  pc  0002b754  /system/lib/libdvm.so
  #23  pc  0006069d  /system/lib/libdvm.so
  #24  pc  000685b3  /system/lib/libdvm.so
  #25  pc  00027160  /system/lib/libdvm.so
  #26  pc  0002e0a8  dvmMterpStd LINE:libdvm.so
  #27  pc  0002b754  /system/lib/libdvm.so
  #28  pc  000603bb  /system/lib/libdvm.so
  #29  pc  00049c17  /system/lib/libdvm.so
  #30  pc  00051a37  /system/lib/libandroid_runtime.so
  #31  pc  00052fcb  /system/lib/libandroid_runtime.so
  at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
  at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:972)
  at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
  at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
  at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1603)
  at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1473)
  at com.tray.provider.SqliteHelper.insertOrUpdate(ProGuard:148)
  at com.tray.provider.TrayContentProvider.insertOrUpdate(ProGuard:1206)
                                           insert
  at android.content.ContentProvider$Transport.insert(ContentProvider.java:220)
  at android.content.ContentResolver.insert(ContentResolver.java:1195)
  at com.tray.provider.TrayProviderHelper.persist(ProGuard:149)
  at com.tray.provider.ContentProviderStorage.com.tray.provider.TrayProviderHelper.persist(ProGuard:13140)
                                              setVersion
  at com.tray.core.Preferences.changeVersion(ProGuard:270)
  at com.tray.core.Preferences.isVersionChangeChecked(ProGuard:290)
  at com.tray.core.Preferences.<init>(ProGuard:56)
  at com.tray.core.AbstractTrayPreference.<init>(ProGuard:32)
  at com.tray.TrayPreferences.<init>(ProGuard:42)
  at com.tray.TrayPreferences.<init>(ProGuard:47)

crash on android 2.3

util.SqliteHelper uses a method (android.database.DatabaseUtils.queryNumEntries) which is in api level 11, leading to crash on android 2.3 device.

TrayContentProvider does not found when using Gradle 1.5.0-beta2 & proguard enabled

Though I've just reported the issue in Google's issue tracker, I thought it would be good to also report here.

I just got following error when tried to use Android Gradle plugin 1.5.0-beta2 with proguard enabled.
without proguard, it works fine. but when I enable proguard, it starts crashing right after I launch the app.
With Gradle plugin 1.3.0, it works fine.

I'm using 1.0.0-rc3 of the tray.

java.lang.RuntimeException: Unable to get provider net.grandcentrix.tray.provider.TrayContentProvider: java.lang.ClassNotFoundException: Didn't find class "net.grandcentrix.tray.provider.TrayContentProvider" on path: DexPathList[[zip file "/data/app/net.yslibrary.omnitweety-1/base.apk"],nativeLibraryDirectories=[/data/app/net.yslibrary.omnitweety-1/lib/arm64, /data/app/net.yslibrary.omnitweety-1/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]
                                                     at android.app.ActivityThread.installProvider(ActivityThread.java:5156)
                                                     at android.app.ActivityThread.installContentProviders(ActivityThread.java:4748)
                                                     at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4688)
                                                     at android.app.ActivityThread.-wrap1(ActivityThread.java)
                                                     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1405)
                                                     at android.os.Handler.dispatchMessage(Handler.java:102)
                                                     at android.os.Looper.loop(Looper.java:148)
                                                     at android.app.ActivityThread.main(ActivityThread.java:5417)
                                                     at java.lang.reflect.Method.invoke(Native Method)
                                                     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
                                                     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
                                                  Caused by: java.lang.ClassNotFoundException: Didn't find class "net.grandcentrix.tray.provider.TrayContentProvider" on path: DexPathList[[zip file "/data/app/net.yslibrary.omnitweety-1/base.apk"],nativeLibraryDirectories=[/data/app/net.yslibrary.omnitweety-1/lib/arm64, /data/app/net.yslibrary.omnitweety-1/base.apk!/lib/arm64-v8a, /vendor/lib64, /system/lib64]]
                                                     at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
                                                     at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
                                                     at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
                                                     at android.app.ActivityThread.installProvider(ActivityThread.java:5141)
                                                     at android.app.ActivityThread.installContentProviders(ActivityThread.java:4748) 
                                                     at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4688) 
                                                     at android.app.ActivityThread.-wrap1(ActivityThread.java) 
                                                     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1405) 
                                                     at android.os.Handler.dispatchMessage(Handler.java:102) 
                                                     at android.os.Looper.loop(Looper.java:148) 
                                                     at android.app.ActivityThread.main(ActivityThread.java:5417) 
                                                     at java.lang.reflect.Method.invoke(Native Method) 
                                                     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
                                                     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 
                                                    Suppressed: java.lang.ClassNotFoundException: net.grandcentrix.tray.provider.TrayContentProvider
                                                     at java.lang.Class.classForName(Native Method)
                                                     at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
                                                     at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
                                                     at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
                                                            ... 12 more
                                                  Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available

Encryption wrapper class

Wrote an encryption wrapper class using Tray to replace the one i had been using for SharedPreferences. Maybe someone else will find it useful. Find it here https://github.com/draekko/securedtray

Thanks for the good work on Tray as it solved a problem i had with concurrent access to data with multiple processes & SharedPreferences. Saved me a ton of time writing a similar library from scratch.

android.database.sqlite.SQLiteReadOnlyDatabaseException: attempt to write a readonly database

I have the same problem.

android.database.sqlite.SQLiteReadOnlyDatabaseException: attempt to write a readonly database (code 1032)
at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount(Native Method)
at android.database.sqlite.SQLiteConnection.executeForChangedRowCount(SQLiteConnection.java:743)
at android.database.sqlite.SQLiteSession.executeForChangedRowCount(SQLiteSession.java:754)
at android.database.sqlite.SQLiteStatement.executeUpdateDelete(SQLiteStatement.java:64)
at android.database.sqlite.SQLiteDatabase.updateWithOnConflict(SQLiteDatabase.java:1606)
at android.database.sqlite.SQLiteDatabase.update(SQLiteDatabase.java:1552)
at net.grandcentrix.tray.provider.SqliteHelper.a(ProGuard:164)
at net.grandcentrix.tray.provider.TrayContentProvider.insert(ProGuard:179)
at android.content.ContentProvider$Transport.insert(ContentProvider.java:246)
at android.content.ContentResolver.insert(ContentResolver.java:1256)
at net.grandcentrix.tray.provider.TrayProviderHelper.a(ProGuard:128)
at net.grandcentrix.tray.provider.ContentProviderStorage.a(ProGuard:227)
at net.grandcentrix.tray.core.Preferences.b(ProGuard:123)
at com.meituan.banma.util.SPUtil.b(ProGuard:52)
at com.meituan.banma.model.AppPrefs.b(ProGuard:159)
at com.meituan.banma.model.UserModel.a(ProGuard:102)
at com.meituan.banma.model.UserModel$2.onResponse(ProGuard:85)
at com.meituan.banma.net.request.BaseRequest.a(ProGuard:104)
at com.meituan.banma.net.request.BaseRequest$1.onResponse(ProGuard:85)
at com.meituan.banma.net.request.BaseRequest$1.onResponse(ProGuard:82)
at com.meituan.banma.net.request.BaseRequest$InnerRequest.deliverResponse(ProGuard:212)
at com.meituan.banma.net.request.BaseRequest$InnerRequest.deliverResponse(ProGuard:189)
at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ProGuard:99)
at android.os.Handler.handleCallback(Handler.java:815)
at android.os.Handler.dispatchMessage(Handler.java:104)
at android.os.Looper.loop(Looper.java:230)
at android.app.ActivityThread.main(ActivityThread.java:5682)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:998)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:793)

How to solve?
I'm using 1.0.0-rc2 version.

Can I share data on multi-apps? How do it?

when i use Tray in two app,config different tray__authority,then i put a key in one app and get the key in another app.But the value it empty.
Can tray share data on multi-apps?

Accessing Outside of Container App

there is documentation or functionallity about how to use TrayPreferences with an external App?

something like:

public class AppPreferencesManager extends TrayPreferencesConnector {
    // setting up access:
    // like uri of Tray ContentProvider
}

so we can use

AppPreferencesManager  appPref;
appPref.put(key,value);
storedValue = appPref.get(key,value);

from any app outside of app/module container.

SharedPreference UI

Just an FYI letting you i created a new library that utilizes tray to implement sharedpreference UI. It also handles Set < String > using JSON and saves it as a string. You could probably use the same method in tray as well.

https://github.com/draekko-rand/traypreferences

Currently only tested on API 23. I've included a sample app. The project uses Android Studio 2.2

Android O Support

I think the new PreferenceDataStore interface may be of interest for this library. Classes that implement this interface can be used as the backend for SharedPreferences operations.

compatibility with API 9

uses-sdk:minSdkVersion 9 cannot be smaller than version 15 declared in library

I'm wondering can It support a lower version

Issue Registering Provider in Robolectric Tests

I'm trying to run Robolectric tests with code that uses Tray, but immediately get the following error for any classes that access AppPreferences:

java.lang.IllegalStateException: could not access stored data with uri content://com.example.tray/internal_preferences/com.example/version?backup=true. Is the provider registered in the manifest of your application?

Is there a suggested set up for unit testing classes that use Tray? I tried following the existing Tray library unit tests, but ran into issues since TrayContract is private scope.

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.