{"id":13989183,"url":"https://github.com/GCX-HCI/tray","last_synced_at":"2025-07-22T10:31:31.204Z","repository":{"id":26895788,"uuid":"30357183","full_name":"GCX-HCI/tray","owner":"GCX-HCI","description":"a SharedPreferences replacement for Android with multiprocess support","archived":true,"fork":false,"pushed_at":"2021-03-16T13:37:45.000Z","size":708,"stargazers_count":2284,"open_issues_count":36,"forks_count":274,"subscribers_count":94,"default_branch":"master","last_synced_at":"2025-06-10T11:12:07.403Z","etag":null,"topics":["android","multiprocess","sharedpreferences"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/GCX-HCI.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-02-05T13:39:50.000Z","updated_at":"2025-05-05T12:29:44.000Z","dependencies_parsed_at":"2022-09-05T06:31:18.671Z","dependency_job_id":null,"html_url":"https://github.com/GCX-HCI/tray","commit_stats":null,"previous_names":["grandcentrix/tray"],"tags_count":10,"template":false,"template_full_name":null,"purl":"pkg:github/GCX-HCI/tray","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GCX-HCI%2Ftray","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GCX-HCI%2Ftray/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GCX-HCI%2Ftray/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GCX-HCI%2Ftray/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/GCX-HCI","download_url":"https://codeload.github.com/GCX-HCI/tray/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/GCX-HCI%2Ftray/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266475112,"owners_count":23934884,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-07-22T02:00:09.085Z","response_time":66,"last_error":null,"robots_txt_status":null,"robots_txt_updated_at":null,"robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","multiprocess","sharedpreferences"],"created_at":"2024-08-09T13:01:33.135Z","updated_at":"2025-07-22T10:31:30.572Z","avatar_url":"https://github.com/GCX-HCI.png","language":"Java","funding_links":[],"categories":["Java"],"sub_categories":[],"readme":"# DEPRECATED - no longer actively maintained\n\n# Tray - a SharedPreferences replacement for Android\n\n[![Build Status](https://travis-ci.org/grandcentrix/tray.svg?branch=master)](https://travis-ci.org/grandcentrix/tray) [![License](https://img.shields.io/badge/license-Apache%202-green.svg?style=flat)](https://github.com/grandcentrix/tray/blob/master/LICENSE.txt)\n\nIf you have read the documentation of the [`SharedPreferences`](http://developer.android.com/reference/android/content/SharedPreferences.html) you might have seen one of these warnings:\n\n\u003eNote: This class does not support use across multiple processes.\n\nGoogle even deprecated the multiprocess support because it never worked relieable\n\n[![](https://cloud.githubusercontent.com/assets/1096485/9793296/110575d2-57e5-11e5-9728-34d3597771b8.png)](http://developer.android.com/reference/android/content/Context.html#MODE_MULTI_PROCESS)\n\nTray is this mentioned _explicit cross-process data management approach_ powered by a [`ContentProvider`](http://developer.android.com/reference/android/content/ContentProvider.html). 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.\n\n## Features\n\n- **works multiprocess**\n- stores simple data types as key value pairs\n- automatically saves metadata for each entry (created, last updated, ...)\n- manage your Preferences in modules [TrayPreference](https://github.com/grandcentrix/tray/blob/master/library/src/main/java/net/grandcentrix/tray/TrayPreferences.java)\n- Delete single modules, all modules, or [all modules except some very important ones](https://github.com/grandcentrix/tray/blob/master/library/src/main/java/net/grandcentrix/tray/Tray.java#L79)\n- update and migrate your data from one app version to next one with versioned Preferences and a [`onUpgrade()`](https://github.com/grandcentrix/tray/blob/14325e182e225e668218fc539f5de0c9b9e524e7/library/src/main/java/net/grandcentrix/tray/core/Preferences.java#L196) method\n- Migrate your current data stored in the SharedPreferences to Tray with [`SharedPreferencesImport`](https://github.com/grandcentrix/tray/blob/master/library/src/main/java/net/grandcentrix/tray/core/SharedPreferencesImport.java)\n- **tray is 100% unit tested!**\n- 0 lint warnings/errors\n- Android 6.0 [Auto Backup for Apps](https://developer.android.com/guide/topics/data/autobackup.html) support! [Read more in the wiki](https://github.com/grandcentrix/tray/wiki/Android-M-Auto-Backup-for-Apps-support)\n\n## Usage\n\nSimple tutorial how to use Tray in your project instead of the SharedPreferences\n\n### Save and read preferences\n\n```java\n// create a preference accessor. This is for global app preferences.\nfinal AppPreferences appPreferences = new AppPreferences(getContext()); // this Preference comes for free from the library\n// save a key value pair\nappPreferences.put(\"key\", \"lorem ipsum\");\n\n// read the value for your key. the second parameter is a fallback (optional otherwise throws)\nfinal String value = appPreferences.getString(\"key\", \"default\");\nLog.v(TAG, \"value: \" + value); // value: lorem ipsum\n\n// read a key that isn't saved. returns the default (or throws without default)\nfinal String defaultValue = appPreferences.getString(\"key2\", \"default\");\nLog.v(TAG, \"value: \" + defaultValue); // value: default\n```\n\nNo `Editor`, no `commit()` or `apply()` :wink:\n\n### Create your own preference module\n\nIt'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.\n\n```java\n// create a preference accessor for a module\npublic class MyModulePreference extends TrayPreferences {\n\n    public static String KEY_IS_FIRST_LAUNCH = \"first_launch\";\n\n    public MyModulePreference(final Context context) {\n        super(context, \"myModule\", 1);\n    }\n}\n```\n\n```java\n// accessing the preferences for my own module\nfinal MyModulePreference myModulePreference = new MyModulePreference(getContext());\nmyModulePreference.put(MyModulePreference.KEY_IS_FIRST_LAUNCH, false);\n```\n\nSee the [sample project](https://github.com/grandcentrix/tray/tree/master/sample) for more\n\nLike the Android [`SQLiteOpenHelper`](http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html) a `TrayPreference` lets you implement methods to handle versioning.\n\n```java\npublic class MyModulePreference extends TrayPreferences {\n\n    public MyModulePreference(final Context context) {\n        super(context, \"myModule\", 1);\n    }\n\n    @Override\n    protected void onCreate(final int initialVersion) {\n        super.onCreate(initialVersion);\n    }\n\n    @Override\n    protected void onUpgrade(final int oldVersion, final int newVersion) {\n        super.onUpgrade(oldVersion, newVersion);\n    }\n\n    @Override\n    protected void onDowngrade(final int oldVersion, final int newVersion) {\n        super.onDowngrade(oldVersion, newVersion);\n    }\n}\n\n```\n\n`// TOOD add clear sample`\n\n### Migrate from SharedPreferences to Tray\n\nTo migrate values from SharedPreferences you have to create you own preference module. This module will be now store all of your SharedPreferences values.\n\n```java\npublic class ImportPreferences extends TrayPreferences {\n\n    // The SharedPreferences name\n    private static final String SHARED_PREFERENCES_FILE_NAME = \"PREF_NAME\";\n    \n    // The key inside the SHARED_PREFERENCES_NAME\n    private static final String KEY_FIRST_LAUNCH = \"KEY_FIRST_LAUNCH\";\n    \n    // The new key for this module\n    private static final String KEY_FIRST_LAUNCH_TRAY = \"KEY_FIRST_LAUNCH_TRAY\";\n    \n    public ImportPreferences(@NonNull Context context) {\n        super(context, \"myImportModule\", 1);\n    }    \n    \n    // Called only once when the module was created\n    @Override\n    protected void onCreate(int initialVersion) {\n        super.onCreate(initialVersion);\n            \n        // Create a SharedPreferencesImport object\n        SharedPreferencesImport importPref = \n            new SharedPreferencesImport(getContext(), \n                SHARED_PREFERENCES_FILE_NAME, KEY_FIRST_LAUNCH, KEY_FIRST_LAUNCH_TRAY);\n            \n        // Finally migrate it\n        migrate(importPref);\n    }\n}\n```\n\n## Getting Started\n\n##### Add Tray to your project\n\n###### GitHub Packages\n\n```gradle\n\nrepositories {\n    maven {\n        url = uri(\"https://maven.pkg.github.com/GCX-HCI/tray\")\n    }\n}\n\ndependencies {\n    implementation \"net.grandcentrix.tray:tray:0.12.0\"\n}\n\n```\n\n###### JCenter (deprecated)\n\n```gradle\n\nrepositories {\n    jcenter()\n}\n\ndependencies {\n    implementation \"net.grandcentrix.tray:tray:0.12.0\"\n}\n\n```\n\nMore on the `ContentProvider` configuration can be found in the [wiki](https://github.com/grandcentrix/tray/wiki/Custom-Authority)\n\n## Testcoverage 100%\n\nTray has 100% test coverage and we'll try to keep it at that level for stable releases.\n\nYou 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:\n\n![coverage report](https://cloud.githubusercontent.com/assets/1096485/9990484/fe61888c-6061-11e5-890d-a76f1ef60304.png)\n\nYou can check the coverage report at [codecov.io](https://codecov.io/github/grandcentrix/tray?branch=master)\n\nThose ~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.\n\n\n## Build state\n[![Build Status](https://travis-ci.org/grandcentrix/tray.svg?branch=master)](https://travis-ci.org/grandcentrix/tray)\n\n[![codecov.io](http://codecov.io/github/grandcentrix/tray/branch.svg?branch=master)](https://codecov.io/github/grandcentrix/tray?branch=master)\n\n## ContentProvider is overkill\n\nAt first, it was the simplest way to use IPC with [`Binder`](http://developer.android.com/reference/android/os/Binder.html) 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](https://github.com/grandcentrix/tray/issues/28#issuecomment-108282253)\n\nIf you have found a better solution implement the [`TrayStorage`](https://github.com/grandcentrix/tray/blob/14325e182e225e668218fc539f5de0c9b9e524e7/library/src/main/java/net/grandcentrix/tray/core/TrayStorage.java) and contribute to this project! We would appreciate it.\n\nThat 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.\n\n## Missing Features\n\nTray is ready to use without showblockers! But here are some nice to have features for the future:\n- Reactive wrapper to observe values \n- no support to save `Set\u003cString\u003e`. Is someone using this?\n- more metadata fields: (i.e. app version code/name)\n\n## Roadmap\n\n- performance tests\n- memory cache for based on contentobservers\n- prevent app crashes due to database errors\n- rx wrapper for changes\n- save additional data types (`Set\u003cString\u003e`, `byte[]`)\n\n## Versions\n\n##### Version 0.11.1 `07.02.17`\n\n- preference key cannot be empty #84\n- `clearBut(TrayPreference)` -\u003e `clearBut(AbstractTrayPreference)` #89\n\n##### Version 0.11.0 `07.09.16`\n- all accessor methods return `boolean` indicating the success of i.e. `put`, `remove`. They will never again throw an error. #69\n- new `contains()` method #74\n\n##### Version 0.10.0 `31.05.16`\n- All features and changes of the 1.0.0-rc preview builds\n- #65 Fix deletion of non string migrated shared preferences.\n\n\u003e##### Version 1.0.0 preview - postponed until the memory cache is ready\n\n\u003e###### 1.0.0-rc3 `05.11.15`\n- hotfix for listener on Android 6.0 which has caused a infinity loop #55\n- the sample project includes now a way to test the multi process support compared to the `SharedPreferences`\n- removed unnecessary write operation for every version check #54\n\n\u003e###### 1.0.0-rc2 `24.09.15`\n- added logging for all data changing methods. Enable via `adb shell setprop log.tag.Tray VERBOSE`\n\n\u003e###### 1.0.0-rc1 `21.09.15`\n- **Android M Auto Backup feature support** (see the [Documentation](https://github.com/grandcentrix/tray/wiki/Android-M-Auto-Backup-for-Apps-support))\n    - split up database for *user* and *device* specific data (device specific data can now be excluded from the auto backup)\n    - `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`\n- **New methods and changes**\n    - `PreferenceAccessor#wipe()` clears the preference data and it's internal data (version)\n    - `TrayPreferences#annexModule(String name)` imports a module by name and wipes it afterwards. This allows renaming of preferences without losing data\n    - `AbstractTrayPreference#annex(ModularizedStorage\u003cTrayItem\u003e)` allows a storage to import another storage, wipes the imported afterwards\n    - `Preference` `#onCreate(...)` and `#onUpgrade(...)` aren't abstract anymore because they don't require an implementation\n- **Deprecations** (will be removed soon)\n    - `TrayAppPreferences` is now deprecated. Use `AppPreferences` instead (renaming)\n    - `TrayModulePreferences` is now deprecated. Use `TrayPreferences` instead to extend from for your own Preferences\n- **Internal structure**\n    - new package structure. merged packages `accessor`, `migration` and `storage` into `core`\n    - package `provider` contains a `TrayStorage` implementation with a `ContentProvider`. Is easy exchangeable with another `TrayStorage` implementation\n    - `ModularizedTrayPreference` is now called `AbstractTrayPreference`\n    - `ModularizedStorage` was renamed to `TrayStorage`\n\n\n##### Version 0.9.2 `02.06.15`\n- `getContext()` is working in `TrayModulePreference#onCreate`\n\n##### Version 0.9.1 `18.05.15`\n- saving `null` with `mPref.put(KEY, null)` works now\n- access to preference with throwing methods instead of default value (throws ItemNotFoundException). Example: `mPref.getString(KEY);` instead of `mPref.getString(KEY, \"defaultValue\");`\n- WrongTypeException when accessing a preference with a different type and the data isn't parsable. Float (`10.1f`) -\u003e String works, String (`\"10.1\"`) -\u003e Float works, String (`\"test\"`) -\u003e Float throws!\n- javadoc in now included in aar\n\n##### Version 0.9 `27.04.15`\n- initial public release\n\n##### Version 0.2 - 0.8\n- Refactoring\n- 100% Testing\n- Bugfixing\n\n##### Version 0.1 `17.09.14`\n- first working prototype\n\n\n## Contributors\n\n- Pascal Welsch - https://github.com/passsy\n- Jannis Veerkamp - https://github.com/jannisveerkamp\n\n# License\n\n```\nCopyright 2015 grandcentrix GmbH\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGCX-HCI%2Ftray","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FGCX-HCI%2Ftray","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FGCX-HCI%2Ftray/lists"}