{"id":15538326,"url":"https://github.com/ali77gh/easydataandroid","last_synced_at":"2025-10-30T05:17:53.093Z","repository":{"id":150701771,"uuid":"145431362","full_name":"ali77gh/EasyDataAndroid","owner":"ali77gh","description":"all you need for working with files and database focused on being easy to use","archived":false,"fork":false,"pushed_at":"2021-08-01T14:11:44.000Z","size":7398,"stargazers_count":25,"open_issues_count":2,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-10-09T12:22:38.834Z","etag":null,"topics":["android","async","bitmap","dao","database","password-store","sqlite","sqlite3"],"latest_commit_sha":null,"homepage":"","language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ali77gh.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-08-20T14:51:07.000Z","updated_at":"2023-10-12T18:19:37.000Z","dependencies_parsed_at":null,"dependency_job_id":"56704a5d-292e-4e6c-86a7-3385fd2b5f71","html_url":"https://github.com/ali77gh/EasyDataAndroid","commit_stats":null,"previous_names":[],"tags_count":15,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ali77gh%2FEasyDataAndroid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ali77gh%2FEasyDataAndroid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ali77gh%2FEasyDataAndroid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ali77gh%2FEasyDataAndroid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ali77gh","download_url":"https://codeload.github.com/ali77gh/EasyDataAndroid/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250458362,"owners_count":21433860,"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","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","async","bitmap","dao","database","password-store","sqlite","sqlite3"],"created_at":"2024-10-02T12:03:12.876Z","updated_at":"2025-10-30T05:17:52.983Z","avatar_url":"https://github.com/ali77gh.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# EasyDataAndroid\n\n\u003ccenter\u003e\u003cimg src=\"./logo.png\" width=200 height=100\u003e\u003cbr\u003e\u003c/center\u003e\n\n[![](https://www.jitpack.io/v/ali77gh/EasyDataAndroid.svg)](https://www.jitpack.io/#ali77gh/EasyDataAndroid) \u003cbr\u003e\n\n\nPainless android library for working with files and database in easiest way as possible.\u003cbr\u003e\nSqlite ORM + handy tools for saving and restoring any kind of data.\u003cbr\u003e\n\n\u003cbr\u003e\n\n# Features\n1. No SQL knowledge needed. ✅\n2. Easy to setup and no bloody🩸 compile time code generations. ✅\n3. Supports nested objects and arrays. ✅\n4. Easy migration with no pain while app updates and schema changes. ✅\n5. Most complex queries are possible and easy to write with simple functions. ✅\n6. Async IO and Multi-Threading supported. ✅\n7. Kotlin programming language recommended. ✅\n8. Supports old android versions (min sdk 15) ✅ \n\n\u003cbr\u003e\u003cbr\u003e\n\n# Documentation\n1. [Installation](#gradle)\n2. ORM\n   1. [Setup your model](#setup-model)\n   2. [Simple CRUD](#simple-crud)\n   3. [More complex queries (with high order functions 😉)](#queries)\n   4. [FastTable (caching system) x10 speed ✈️](#fasttable)\n   5. [Nested object or array (no problem)](#nest)\n   6. [Migration (no problem)](#migration)\n   7. [IORun (handy multi-threading and AsyncIO tool)](#iorun)\n   8. [Performance](#performance)\n   9. [Under the hood](#under-the-hood)\n   10. [examples](./app/src/main/java/com/ali77gh/easydataexample)\n3. [Settings data save and load best practice (without ORM setup)](./app/src/main/java/com/ali77gh/easydataexample/Settings.kt)\n4. [SafeBox](#safebox)\n5. [Working with files](#working-with-files)\n   1. [BitmapDAO](#bitmapdao)\n   2. [ByteDAO](#bytedao)\n   3. [ObjectDAO](#objectdao)\n   4. [StringDAO](#stringdao)\n6. [License](#license)\n\n# gradle\nAdd it in your root build.gradle at the end of repositories:\n```groovy\nallprojects {\n\trepositories {\n\t\t// add this line\n\t\tmaven { url 'https://www.jitpack.io' }\n\t}\n}\n```\nStep 2. Add the dependency (in your app build.gradle):\n```groovy\ndependencies {\n\t// add this line\n\timplementation 'com.github.ali77gh:EasyDataAndroid:3.2.2'\n}\n```\n\n# Setup model\n\u003cu\u003eStep 1:\u003c/u\u003e all you need to do is implement \"Model\" interface\u003cbr\u003e\nnote: you need to have \"id\" field this will act like unique primary key\n```kotlin\nclass User(\n        override var id: String,\n        var hashPass: String,\n        var name: String,\n        var age :Int,\n        var role:String,\n        var money:Int,\n) : Model\n```\n\u003c/br\u003e\n\n\u003cu\u003eStep 2:\u003c/u\u003e Make a Table class of your model that is extending EasyTable (you can also put in inside User.kt if you want)\n```kotlin\nclass UserTable(context: Context) :\n       EasyTable\u003cUser\u003e(context, User::class.java,autoSetId = false){\n\n\t// your custom query here\n}\n// no need to put any code inside this class\n// anyway we will put some custom queries inside of this class later\n```\nautoSetId: this will generate random UUID while you insert a row (set it false if you have plan for id yourself or you are getting rows from server that already have id)\n\u003cbr\u003e\u003cbr\u003e\nThat's it 😉 now you are ready to go. \n\n\u003cbr\u003e\n\n# Simple CRUD\n\nMake table class instance:\n```kotlin\nval users = UserTable(context)\n```\n\nInsert:\n```kotlin\nval user = User(...)\nusers.insert(user)\n```\n\nRead:\n```kotlin\nval user = users.getById(\"id\")\n// you can also loop on users\nfor (user in users){\n\t//do something with user\n}\n// note: its not loading all users on ram (check out under the hood part for more information)\n// see complex queries part for More reading samples \n```\n\nUpdate:\n```kotlin\nuser.name = \"alireza\"\nusers.update(user) // it find user row by id and apply changes\n```\n\nDelete:\n```kotlin\n// delete one row by id\nusers.delete(user.id)\n\n// delete many rows with where statement\nusers.deleteWhere { it.name==\"ali\" } \n```\n\nI recommend you to write singleton (optional)\n```kotlin\n// repo singleton\ncompanion object {\n    private var repo: UserTable? = null\n    fun getRepo(context: Context): UserTable {\n        if (repo ==null) repo = UserTable(context)\n        return repo!!\n    }\n}\n// ------\n// then you can get table class like this:\nvar users = UserTable.getRepo(context)\n```\n\n\u003cb\u003eImportant warning ☢ ️: don't forget to star ⭐ this repo  \u003c/b\u003e\n\u003cbr\u003e\n\n# Queries\nNow its time to come back to Table class and add some queries.\u003cbr\u003e\nNote: You can write this queries right after users object too. but putting your queries here in Table class make your code cleaner. \u003cbr\u003e\nCheckout samples:\n\n```kotlin\n    class UserTable(context: Context) :\n        EasyTable\u003cUser\u003e(context, User::class.java, autoSetId = false) {\n\n   // custom queries here.\n\n   // you can access to all of standard high order functions on lists.\n   fun getByName(name: String) = filter { it.name == name }\n\n   // you can use 'get' keyword when you have no input param for query.\n   val admins get() = filter { it.role == \"admin\" }\n\n   // query can return any type (here is boolean).\n   fun isAdmin(id: String) = any { it.id == id \u0026\u0026 it.role == \"admin\" }\n\n   fun isUnderAge(id: String) = any { it.id == id \u0026\u0026 it.age \u003c 18 }\n\n   // you can write almost every queries in one line.\n   val top5Riches get() = sortedByDescending { it.money }.subList(0, 5)\n\n   // but you are free to write multi line query and use variables if you want. \n   val top5Riches\n      get() {\n         val sorted = sortedByDescending { it.money }\n         val top5 = sorted.subList(0, 5)\n         return top5\n      }\n\n\n   // here is a check password and i have nothing to say ;)\n   fun checkPassword(id: String, hashPass: String) = getById(id)!!.hashPass == hashPass\n\n\n   // lets delete some rows with where statement\n   fun removeUnderAges() = deleteWhere { it.age \u003c 18 }\n\n\n   // update all\n   // 'it' object is before update and what you return will replace with old one\n   fun increaseAges1() = updateAll {\n      it.age++\n      return@updateAll it\n   }\n\n   // same query in other style\n   fun increaseAges2() = updateAll { it.age++;it }\n\n   // again same query in other style (you are free to write query in your way)\n   fun increaseAges3() = updateAll { it.apply { age++ } }\n\n   // lets do some update with where statement \n   // first function returns boolean and 'true' means this row should update\n   // second function effects on that row (same in updateAll function)\n   fun increaseRoleOfAlis() = updateWhere({ it.name == \"ali\" }, { it.role = \"admin\";it })\n\n   // IORun will run your code in other thread and pass result back to UI thread with callback\n   // you can use it in other replaces too (not limited to writing queries)\n   fun asyncGetByName(name: String, cb: (user: User) -\u003e Unit) = IORun({ filter { it.name == name }[0] }, cb)\n\n}\n\n```\n\n\u003cbr\u003e\n\n# FastTable \n\nThis is a caching system and will speed up your read queries.\u003cbr\u003e\nif you want to enable FastTable all you have to do is extending FastTable instead EasyTable:\n\n```kotlin\nclass UserTable(context: Context) :\n       FastTable\u003cUser\u003e(context, User::class.java,autoSetId = false)\n```\n\nYou have exact same functionality but faster read queries [see performance part](#performance)\u003cbr\u003e\nMemory Usage: FastTable use 2MB more RAM with 10,000 of User object that we shows in this documentation.\n\n\u003cbr\u003e\n\n# Nest\nYou can have a field in type of list or custom class or list of custom class or even more nest levels. \u003cbr\u003e\ncheckout this:\n```kotlin\nclass Loc(val lat:Double,val lng:Double)\nclass User(\n        override var id: String,\n        var hashPass: String,\n        var name: String,\n        var age :Int,\n        var role:String,\n        var money:Int,\n        var marks :List\u003cFloat\u003e,\n        var locations: List\u003cLoc\u003e\n) : Model\n```\nhandy? right?\n\u003cbr\u003e\n\n# Migration\n\nNo problem.\u003cbr\u003e\nJust add field (nullable) and everything will work as well.\u003cbr\u003e\nWarning: lets say you insert a rowA to table and then you change schema by adding fieldX. then fieldX of rowA will be null\u003cbr\u003e\nRecommended way to handle this:\u003cbr\u003e\nadd init{} function in your model and set a default value for fieldX if is null.\n\n\u003cbr\u003e\n\n# IORun\nIts easier way to run a code in other thread and pass result to Main thread (UI thread).\u003cbr\u003e\nCheckout this:\n```kotlin\nIORun({\n\t//do heavy process or IO operation\n\treturn result\n} , { result -\u003e\n\t// this will run in ui thread\n\t// and you can do things with result object\n\t// object can have any type based on what you return in first function\n})\n```\n\n\u003cbr\u003e\n\n# Performance\nIts for 10,000 record on android virtualbox on my dual core laptop.\u003cbr\u003e\n[See running code of this test](./app/src/main/java/com/ali77gh/easydataexample/TestActivity.kt) \u003cbr\u003e\n\u003cimg src=\"./performance/EasyTable.png\"\u003e\u003cbr\u003e\n\u003cimg src=\"./performance/FastTable.png\"\u003e\n\nFastTable use 2MB more RAM with 10,000 of User object that we shows in this documentation\n\n\u003cbr\u003e\n\n# Under the hood\n\nThis library use sqlite and json to saving and restoring records.\u003cbr\u003e\n[KeyValDb](./easydata/src/main/java/com/ali77gh/easydata/sqlite/KeyValDb.kt) class handle all SQL stuff.\u003cbr\u003e\n[EasyTable](./easydata/src/main/java/com/ali77gh/easydata/sqlite/EasyTable.kt) class is extending KeyValDb and have json serialize and deserialize and generic things and also iteration stuff that provides high order functions on sqlite cursor.\u003cbr\u003e\n[FastTable](./easydata/src/main/java/com/ali77gh/easydata/sqlite/FastTable.kt) class is extending EasyTable and have some caching stuff for better read speed. see [performance](#performance)\n\n\u003cbr\u003e\n\n# SafeBox\nThis class used for saving password and other sensitive data.\u003cbr\u003e\nThis class actually encrypt and saves data in app local storage.\u003cbr\u003e\nCheckout this:\n```kotlin\n// This functions generates key with device unique id\n// this line is optional and you can use any string as a key \nval key = DeviceKeyGenerator.Generate(this)\n\n// Safe box instance\nval safeBox = SafeBox(this, key)\n\nsafeBox.save(\"password\", \"secret\")\nvar readed = safeBox.load(\"password\")\n```\n\n\u003cbr\u003e\n\n# Working with files\nThis tools provide \u003cb\u003e Read , Write , Delete \u003c/b\u003e for you with \u003cb\u003e Sync or Async \u003c/b\u003e mode on \u003cb\u003eExternal , Local , Cache \u003c/b\u003e Storage \u003cbr\u003e\u003cbr\u003e\nRootModes : LOCAL , EXTERNAL , CACHE \u003cbr\u003e\nLOCAL: app private storage (user and other apps can not access)\u003cbr\u003e\nEXTERNAL: root of public storage (user and other apps can access)\u003cbr\u003e\nCACHE: files in this mode will be removed by cleaner apps\u003cbr\u003e\n\u003cbr\u003e\n\n# BitmapDAO\n```kotlin\n// instance\nval bitmapDAO = BitmapDAO(this, RootMode.LOCAL) // see RootMode in Working with files section\n// simple save\nbitmapDAO.save(\"testBitmap.bmp\",bitmap)\n// save with 50% of quality\nbitmapDAO.save(\"testBitmap50.bmp\",bitmapForTest,50)\n// resize and save with given width and height\nbitmapDAO.save(\"testBitmap10px.bmp\",bitmapForTest, width=10, height=10)\n// async save\nbitmapDAO.saveAsync(\"testBitmapAsync.bmp\", bitmapForTest, callback = {\n    // done!\n})\n// simple load\nval bitmap = bitmapDAO.load(\"testBitmap.bmp\")\n// async load \nbitmapDAO.load(\"testBitmap.bmp\",{ bitmap-\u003e\n \n})\n```\n\n\u003cbr\u003e\n\n\n# BytesDAO\n```kotlin\n// instance\nval bytesDAO = BytesDAO(this, RootMode.LOCAL) // see RootMode in Working with files section\n// simple save\nbytesDAO.save(\"test\", byteArray)\n// async save\nbytesDAO.saveAsync(\"test\", byteArray,callback = {\n    // done!\n})\n// simple load\nval byteArray = bytesDAO.load(\"test\")\n// async load \nbytesDAO.load(\"test\", { bytes -\u003e\n\n})\n```\n\n\u003cbr\u003e\n\n# StringDAO\n\n```kotlin\n// instance\nval stringDAO = StringDAO(this, RootMode.LOCAL) // see RootMode in Working with files section\n// simple save\nstringDAO.save(\"test\", \"your string here\")\n// async save\nstringDAO.saveAsync(\"test\", yourString, callback = {\n   // done!\n})\n// simple load\nval yourString = stringDAO.load(\"test\")\n// async load \nstringDAO.load(\"test\", { bytes -\u003e\n\n})\n```\n\n\u003cbr\u003e\n\n\n# ObjectDAO\n\n```kotlin\n// instance\nval objectDAO = ObjectDAO(this, RootMode.LOCAL) // see RootMode in Working with files section\n// simple save\nobjectDAO.save(\"test\", obj)\n// async save\nobjectDAO.saveAsync(\"test\", obj, callback = {\n   // done!\n})\n// simple load\nval user = objectDAO.load(\"test\",User::class.java) as User\n// async load \nobjectDAO.load(\"test\", { obj -\u003e\n\n})\n```\n\n\u003cbr\u003e\n\n# License\n[MIT](https://github.com/ali77gh/EasyDataAndroid/blob/master/LICENSE)","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fali77gh%2Feasydataandroid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fali77gh%2Feasydataandroid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fali77gh%2Feasydataandroid/lists"}