{"id":13428852,"url":"https://github.com/mcxiaoke/kotlin-koi","last_synced_at":"2025-04-05T14:03:02.987Z","repository":{"id":39618136,"uuid":"50421113","full_name":"mcxiaoke/kotlin-koi","owner":"mcxiaoke","description":"Koi, a lightweight kotlin library for Android Development.","archived":false,"fork":false,"pushed_at":"2017-08-25T09:22:36.000Z","size":210,"stargazers_count":515,"open_issues_count":4,"forks_count":54,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-03-29T13:03:29.335Z","etag":null,"topics":["kotlin","kotlin-android","kotlin-library"],"latest_commit_sha":null,"homepage":"https://github.com/mcxiaoke/kotlin-koi","language":"Kotlin","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/mcxiaoke.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}},"created_at":"2016-01-26T10:19:24.000Z","updated_at":"2025-01-24T05:09:52.000Z","dependencies_parsed_at":"2022-09-13T15:10:22.528Z","dependency_job_id":null,"html_url":"https://github.com/mcxiaoke/kotlin-koi","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcxiaoke%2Fkotlin-koi","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcxiaoke%2Fkotlin-koi/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcxiaoke%2Fkotlin-koi/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mcxiaoke%2Fkotlin-koi/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mcxiaoke","download_url":"https://codeload.github.com/mcxiaoke/kotlin-koi/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247345848,"owners_count":20924102,"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":["kotlin","kotlin-android","kotlin-library"],"created_at":"2024-07-31T01:01:06.857Z","updated_at":"2025-04-05T14:03:02.966Z","avatar_url":"https://github.com/mcxiaoke.png","language":"Kotlin","funding_links":[],"categories":["Libraries","Kotlin","\u003ca name=\"utility\"\u003e\u003c/a\u003eUtility \u003csup\u003e[Back ⇈](#contents)\u003c/sup\u003e","开源库"],"sub_categories":["扩展"],"readme":"# Koi - A lightweight Kotlin library for Android\n\n**Koi** include many useful extensions and functions, they can help reducing the boilerplate code in Android applications. Specifically, **Koi** include a powerful extension function named [**asyncSafe**](#async-functions).\n\n## Gradle\n\nLatest Version: [![Maven Central](https://img.shields.io/badge/2017.08.25-com.mcxiaoke.koi:core:0.5.5-brightgreen.svg)](http://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.mcxiaoke.koi%22) Compiled with Kotlin 1.1.4.\n\n```gradle\ncompile 'com.mcxiaoke.koi:core:0.5.5' // useful extensions (only ~100k)\ncompile 'com.mcxiaoke.koi:async:0.5.5' // async functions (only ~70k)\n```\n\n## Usage\n\n\u003c!-- toc --\u003e\n  * [Context Extensions](#context-extensions)\n    * [Activity Functions](#activity-functions)\n    * [Fragment Functions](#fragment-functions)\n    * [Easy to use Toast](#easy-to-use-toast)\n    * [Easy to Inflate Layout](#easy-to-inflate-layout)\n    * [Useful Functions](#useful-functions)\n    * [Easy to create Intent](#easy-to-create-intent)\n    * [Easy to Start Activity](#easy-to-start-activity)\n    * [Easy to Start Service](#easy-to-start-service)\n    * [Network State](#network-state)\n    * [Notification Builder](#notification-builder)\n    * [Package Functions](#package-functions)\n    * [System Service](#system-service)\n    * [Easy to Log](#easy-to-log)\n  * [View Extensions](#view-extensions)\n    * [View Listeners 1](#view-listeners-1)\n    * [View Listeners 2](#view-listeners-2)\n    * [ListView Listeners](#listview-listeners)\n    * [View Utils](#view-utils)\n  * [Adapter Extensions](#adapter-extensions)\n    * [Easy to Create Adapter](#easy-to-create-adapter)\n  * [Bundle Extensions](#bundle-extensions)\n    * [Bundle Builder](#bundle-builder)\n  * [Parcelable Extensions](#parcelable-extensions)\n    * [Easy to create Parcelable](#easy-to-create-parcelable)\n  * [Collection Extensions](#collection-extensions)\n    * [Collection to String](#collection-to-string)\n    * [Map to String](#map-to-string)\n    * [List Append](#list-append)\n  * [Database Extensions](#database-extensions)\n    * [Easy to get Cursor Value](#easy-to-get-cursor-value)\n    * [Easy to convert Cursor to Model](#easy-to-convert-cursor-to-model)\n    * [Easy to use Transaction](#easy-to-use-transaction)\n  * [IO Extensions](#io-extensions)\n    * [Easy to close Stream](#easy-to-close-stream)\n    * [Stream doSafe Function](#stream-dosafe-function)\n    * [readString/readList](#readstringreadlist)\n    * [readString/readList using doSafe](#readstringreadlist-using-dosafe)\n    * [writeString/writeList using doSafe](#writestringwritelist-using-dosafe)\n    * [File Read and Write](#file-read-and-write)\n  * [Handler Extensions](#handler-extensions)\n    * [Easy to use Handler](#easy-to-use-handler)\n  * [Other Extensions](#other-extensions)\n    * [Date Functions](#date-functions)\n    * [Number Functions](#number-functions)\n    * [String Functions](#string-functions)\n    * [Crypto Functions](#crypto-functions)\n    * [Check API Level](#check-api-level)\n    * [Device Functions](#device-functions)\n    * [Preconditions](#preconditions)\n  * [Thread Functions](#thread-functions)\n    * [Create Thread Pool](#create-thread-pool)\n    * [Main Thread Functions](#main-thread-functions)\n    * [Context Check](#context-check)\n    * [Safe Functions](#safe-functions)\n  * [Async Functions](#async-functions)\n    * [With Context Check 1](#with-context-check-1)\n    * [With Context Check 2](#with-context-check-2)\n    * [Without Context Check](#without-context-check)\n    * [With Delay](#with-delay)\n    * [Custom Executor](#custom-executor)\n\n\u003c!-- toc stop --\u003e\n\n\n### Context Extensions\n\n#### Activity Functions\n```kotlin\n// available for Activity\nfun activityExtensions() {\n    val act = getActivity() // Activity\n    act.restart() // restart Activity\n    val app = act.getApp() // Application\n    val app2 = act.application  // Application\n    // Activity.find()\n    // Fragment.find()\n    // View.find()\n    val textView = act.find\u003cTextView\u003e(android.R.id.text1)\n}\n```\n\n#### Fragment Functions\n```kotlin\n// available for Fragment\nfun fragmentExtensions() {\n    val act = activity // Activity\n    val app = getApp() // Application\n    val textView = find\u003cTextView\u003e(android.R.id.text1) // view.findViewById\n    val imageView = find\u003cTextView\u003e(android.R.id.icon1) // view.findViewById\n    }\n```\n\n#### Easy to use Toast\n```kotlin\n// available for Context\nfun toastExtensions() {\n    // available in Activity/Fragment/Service/Context\n    toast(R.string.app_name)\n    toast(\"this is a toast\")\n    longToast(R.string.app_name)\n    longToast(\"this is a long toast\")\n}\n```\n\n#### Easy to Inflate Layout\n```kotlin\n    // available for Context\n    fun inflateLayout() {\n        val view1 = inflate(R.layout.activity_main)\n        val viewGroup = view1 as ViewGroup\n        val view2 = inflate(android.R.layout.activity_list_item, viewGroup, false)\n    }\n```\n\n#### Useful Functions\n```kotlin\n// available for Context\nfun miscExtensions() {\n    val hasCamera = hasCamera()\n\n    mediaScan(Uri.parse(\"file:///sdcard/Pictures/koi/cat.png\"))\n    addToMediaStore(File(\"/sdcard/Pictures/koi/cat.png\"))\n\n    val batteryStatusIntent = getBatteryStatus()\n    val colorValue = getResourceValue(android.R.color.darker_gray)\n}\n```\n\n#### Easy to create Intent\n```kotlin\n// available for Context\nfun intentExtensions() {\n    val extras = Bundle { putString(\"key\", \"value\") }\n    val intent1 = newIntent\u003cMainActivity\u003e()\n    val intent2 = newIntent\u003cMainActivity\u003e(Intent.FLAG_ACTIVITY_NEW_TASK, extras)\n}\n```\n\n#### Easy to Start Activity\n```kotlin\n// available for Activity\nfun startActivityExtensions() {\n    startActivity\u003cMainActivity\u003e()\n    // equal to\n    startActivity(Intent(this, MainActivity::class.java))\n\n    startActivity\u003cMainActivity\u003e(Intent.FLAG_ACTIVITY_SINGLE_TOP, Bundle())\n    startActivity\u003cMainActivity\u003e(Bundle())\n\n    startActivityForResult\u003cMainActivity\u003e(100)\n    startActivityForResult\u003cMainActivity\u003e(Bundle(), 100)\n    startActivityForResult\u003cMainActivity\u003e(200, Intent.FLAG_ACTIVITY_CLEAR_TOP)\n}\n```\n\n#### Easy to Start Service\n```kotlin\n// available for Context\nfun startServiceExtensions() {\n    startService\u003cBackgroundService\u003e()\n    startService\u003cBackgroundService\u003e(Bundle())\n}\n```\n\n#### Network State\n```kotlin\n// available for Context\nfun networkExtensions() {\n    val name = networkTypeName()\n    val operator = networkOperator()\n    val type = networkType()\n    val wifi = isWifi()\n    val mobile = isMobile()\n    val connected = isConnected()\n}\n```\n\n#### Notification Builder\n```kotlin\n// available for Context\nfun notificationExtensions() {\n    // easy way using Notification.Builder\n    val notification = newNotification() {\n        this.setColor(0x0099cc)\n                .setAutoCancel(true)\n                .setContentTitle(\"Notification Title\")\n                .setContentText(\"Notification Message Text\")\n                .setDefaults(0)\n                .setGroup(\"koi\")\n                .setVibrate(longArrayOf(1, 0, 0, 1))\n                .setSubText(\"this is a sub title\")\n                .setSmallIcon(android.R.drawable.ic_dialog_info)\n                .setLargeIcon(null)\n    }\n}\n```\n\n#### Package Functions\n```kotlin\n// available for Context\nfun packageExtensions() {\n    val isYoutubeInstalled = isAppInstalled(\"com.google.android.youtube\")\n    val isMainProcess = isMainProcess()\n    val disabled = isComponentDisabled(MainActivity::class.java)\n    enableComponent(MainActivity::class.java)\n\n    val sig = getPackageSignature()\n    val sigString = getSignature()\n    println(dumpSignature())\n}\n```\n\n#### System Service\n```kotlin\n// available for Context\n// easy way to get system service, no cast\nfun systemServices() {\n    val wm = getWindowService()\n    val tm = getTelephonyManager()\n    val nm = getNotificationManager()\n    val cm = getConnectivityManager()\n    val am = getAccountManager()\n    val acm = getActivityManager()\n    val alm = getAlarmManager()\n    val imm = getInputMethodManager()\n    val inflater = getLayoutService()\n    val lm = getLocationManager()\n    val wifi = getWifiManager()\n}\n```\n\n#### Easy to Log\n```kotlin\n// available for Context\nfun logExtensions() {\n    KoiConfig.logEnabled = true //default is false\n    // true == Log.VERBOSE\n    // false == Log.ASSERT\n    // optional\n    KoiConfig.logLevel = Log.VERBOSE // default is Log.ASSERT\n    //\n\n    logv(\"log functions available in Context\")  //Log.v\n    logd(\"log functions available in Context\")  //Log.d\n    loge(\"log functions available in Context\")   //Log.e\n\n    // support lazy evaluated message\n    logv { \"lazy eval message lambda\" }  //Log.v\n    logw { \"lazy eval message lambda\" }  //Log.w\n}\n```\n\n### View Extensions\n\n#### View Listeners 1\n```kotlin\nfun viewListeners1() {\n    val view = View(this)\n    // View.OnClickListener\n    view.onClick { print(\"view clicked\") }\n    // View.OnLongClickListener\n    view.onLongClick { print(\"view long clicked\");false }\n    // View.OnKeyListener\n    view.onKeyEvent { view, keyCode, event -\u003e\n        print(\"keyEvent: action:${event.action} code:$keyCode\")\n        false\n    }\n    // View.OnTouchListener\n    view.onTouchEvent { view, event -\u003e\n        when (event.actionMasked) {\n            MotionEvent.ACTION_DOWN -\u003e print(\"touch down\")\n            MotionEvent.ACTION_UP -\u003e print(\"touch up\")\n            MotionEvent.ACTION_MOVE -\u003e print(\"touch move\")\n        }\n        false\n    }\n    // View.OnFocusChangeListener\n    view.onFocusChange { view, hasFocus -\u003e\n        print(\"focus changed = $hasFocus\")\n    }\n}\n```\n\n#### View Listeners 2\n```kotlin\nfun viewListeners2() {\n    // TextWatcher\n    val editText = EditText(this)\n    editText.onTextChange { text, start, before, count -\u003e\n        print(\"text changed: $text\")\n    }\n\n    // OnCheckedChangeListener\n    val checkBox = CheckBox(this)\n    checkBox.onCheckedChanged { button, isChecked -\u003e\n        print(\"CheckBox value changed:$isChecked\")\n    }\n\n    // OnSeekBarChangeListener\n    val seekBar = SeekBar(this)\n    seekBar.onProgressChanged { seekBar, progress, fromUser -\u003e\n        print(\"seekBar progress: $progress\")\n    }\n}\n````\n\n#### ListView Listeners\n```kotlin\nfun listViewListeners() {\n    val listView = ListView(this)\n    // OnItemClickListener\n    listView.onItemClick { parent, view, position, id -\u003e\n        print(\"onItemClick: position=$position\")\n    }\n    // OnScrollListener\n    listView.onScrollChanged { view, scrollState -\u003e\n        print(\"scroll state changed\")\n    }\n}\n```\n\n#### View Utils\n```kotlin\n// available for View\nfun viewSample() {\n    val w = dm.widthPixels\n    val h = dm.heightPixels\n    val v1 = 32.5f\n    val dp1 = v1.pxToDp()\n    val v2 = 24f\n    val px1 = v2.dpToPx()\n    val dp2 = pxToDp(56)\n    val px2 = dpToPx(32)\n    val dp3 = 72.pxToDp()\n    val px3 = 48.dpToPx()\n\n    hideSoftKeyboard()\n    val editText = EditText(context)\n    editText.showSoftKeyboard()\n    editText.toggleSoftKeyboard()\n}\n```\n\n### Adapter Extensions\n\n#### Easy to Create Adapter\n```kotlin\n// easy way to create array adapter\nfun adapterFunctions() {\n    listView.adapter = quickAdapterOf(\n            android.R.layout.simple_list_item_2,\n            (1..100).map { \"List Item No.$it\" })\n    { binder, data -\u003e\n        binder.setText(android.R.id.text1, data)\n        binder.setText(android.R.id.text2, \"Index: ${binder.position}\")\n    }\n\n    val adapter2 = quickAdapterOf\u003cString\u003e(android.R.layout.simple_list_item_1) {\n        binder, data -\u003e\n        binder.setText(android.R.id.text1, data)\n    }\n    adapter2.addAll(listOf(\"Cat\", \"Dog\", \"Rabbit\"))\n\n    val adapter3 = quickAdapterOf\u003cInt\u003e(android.R.layout.simple_list_item_1,\n            arrayOf(1, 2, 3, 4, 5, 6)) {\n        binder, data -\u003e\n        binder.setText(android.R.id.text1, \"Item Number: $data\")\n    }\n\n    val adapter4 = quickAdapterOf\u003cInt\u003e(android.R.layout.simple_list_item_1,\n            setOf(22, 33, 4, 5, 6, 8, 8, 8)) {\n        binder, data -\u003e\n        binder.setText(android.R.id.text1, \"Item Number: $data\")\n    }\n}\n```\n\n### Bundle Extensions\n\n#### Bundle Builder\n```kotlin\n// available in any where\nfun bundleExtension() {\n    // easy way to create bundle\n    val bundle = Bundle {\n        putString(\"key\", \"value\")\n        putInt(\"int\", 12345)\n        putBoolean(\"boolean\", false)\n        putIntArray(\"intArray\", intArrayOf(1, 2, 3, 4, 5))\n        putStringArrayList(\"strings\", arrayListOf(\"Hello\", \"World\", \"Cat\"))\n    }\n\n    // equal to using with\n    val bundle2 = Bundle()\n    with(bundle2) {\n        putString(\"key\", \"value\")\n        putInt(\"int\", 12345)\n        putBoolean(\"boolean\", false)\n        putIntArray(\"intArray\", intArrayOf(1, 2, 3, 4, 5))\n        putStringArrayList(\"strings\", arrayListOf(\"Hello\", \"World\", \"Cat\"))\n    }\n}\n```\n\n### Parcelable Extensions\n\n#### Easy to create Parcelable\n```kotlin\n// easy way to create Android Parcelable class\ndata class Person(val name: String, val age: Int) : Parcelable {\n    override fun writeToParcel(dest: Parcel, flags: Int) {\n        dest.writeString(name)\n        dest.writeInt(age)\n    }\n\n    override fun describeContents(): Int = 0\n    protected constructor(p: Parcel) : this(name = p.readString(), age = p.readInt()) {}\n    companion object {\n        // using createParcel\n        @JvmField val CREATOR = createParcel { Person(it) }\n    }\n}\n```\n\n### Collection Extensions\n\n#### Collection to String\n```kotlin\nfun collectionToString() {\n    val pets = listOf\u003cString\u003e(\"Cat\", \"Dog\", \"Rabbit\", \"Fish\")\n    // list to string, delimiter is space\n    val string1 = pets.asString(delim = \" \") // \"Cat Dog Rabbit Fish\"\n    // default delimiter is comma\n    val string2 = pets.asString() // \"Cat,Dog,Rabbit,Fish\"\n    val numbers = arrayOf(2016, 2, 2, 20, 57, 40)\n    // array to string, default delimiter is comma\n    val string3 = numbers.asString() // \"2016,2,2,20,57,40\"\n    // array to string, delimiter is -\n    val string4 = numbers.asString(delim = \"-\") // 2016-2-2-20-57-40\n    // using Kotlin stdlib\n    val s1 = pets.joinToString()\n    val s2 = numbers.joinToString(separator = \"-\", prefix = \"\u003c\", postfix = \"\u003e\")\n}\n```\n\n#### Map to String\n```kotlin\nfun mapToString() {\n    val map = mapOf\u003cString, Int\u003e(\n            \"John\" to 30,\n            \"Smith\" to 50,\n            \"Alice\" to 22\n    )\n    // default delimiter is ,\n    val string1 = map.asString() // \"John=30,Smith=50,Alice=22\"\n    // using delimiter /\n    val string2 = map.asString(delim = \"/\") // \"John=30/Smith=50/Alice=22\"\n    // using stdlib\n    map.asSequence().joinToString { \"${it.key}=${it.value}\" }\n}\n```\n\n#### List Append\n```kotlin\nfun appendAndPrepend() {\n    val numbers = (1..6).toArrayList()\n    println(numbers.joinToString()) // \"1, 2, 3, 4, 5, 6, 7\"\n    numbers.head() // .dropLast(1)\n    numbers.tail() //.drop(1)\n    val numbers2 = 100.appendTo(numbers) //\n    val numbers3 = 2016.prependTo(numbers)\n}\n```\n\n### Database Extensions\n\n#### Easy to get Cursor Value\n```kotlin\n// available for Cursor\nfun cursorValueExtensions() {\n    val cursor = this.writableDatabase.query(\"table\", null, null, null, null, null, null)\n    cursor.moveToFirst()\n    do {\n        val intVal = cursor.intValue(\"column-a\")\n        val stringVal = cursor.stringValue(\"column-b\")\n        val longVal = cursor.longValue(\"column-c\")\n        val booleanValue = cursor.booleanValue(\"column-d\")\n        val doubleValue = cursor.doubleValue(\"column-e\")\n        val floatValue = cursor.floatValue(\"column-f\")\n\n        // no need to do like this, so verbose\n        cursor.getInt(cursor.getColumnIndexOrThrow(\"column-a\"))\n        cursor.getString(cursor.getColumnIndexOrThrow(\"column-b\"))\n    } while (cursor.moveToNext())\n}\n```\n\n#### Easy to convert Cursor to Model\n```kotlin\n// available for Cursor\n// transform cursor to model object\nfun cursorToModels() {\n    val where = \" age\u003e? \"\n    val whereArgs = arrayOf(\"20\")\n    val cursor = this.readableDatabase.query(\"users\", null, where, whereArgs, null, null, null)\n    val users1 = cursor.map {\n        UserInfo(\n                stringValue(\"name\"),\n                intValue(\"age\"),\n                stringValue(\"bio\"),\n                booleanValue(\"pet_flag\"))\n    }\n\n    // or using mapAndClose\n    val users2 = cursor.mapAndClose {\n        UserInfo(\n                stringValue(\"name\"),\n                intValue(\"age\"),\n                stringValue(\"bio\"),\n                booleanValue(\"pet_flag\"))\n    }\n\n    // or using Cursor?mapTo(collection, transform())\n}\n```\n\n#### Easy to use Transaction\n```kotlin\n// available for SQLiteDatabase and SQLiteOpenHelper\n// auto apply transaction to db operations\nfun inTransaction() {\n    val db = this.writableDatabase\n    val values = ContentValues()\n\n    // or db.transaction\n    transaction {\n        db.execSQL(\"insert into users (?,?,?) (1,2,3)\")\n        db.insert(\"users\", null, values)\n    }\n    // equal to\n    db.beginTransaction()\n    try {\n        db.execSQL(\"insert into users (?,?,?) (1,2,3)\")\n        db.insert(\"users\", null, values)\n        db.setTransactionSuccessful()\n    } finally {\n        db.endTransaction()\n    }\n}\n```\n\n### IO Extensions\n\n#### Easy to close Stream\n```kotlin\n// available for Closeable\nfun closeableSample() {\n    val input = FileInputStream(File(\"readme.txt\"))\n    try {\n        val string = input.readString(\"UTF-8\")\n    } catch(e: IOException) {\n        e.printStackTrace()\n    } finally {\n        input.closeQuietly()\n    }\n}\n```\n\n#### Stream doSafe Function\n```kotlin\n// simple way, equal to closeableSample\n// InputStream.doSafe{}\nfun doSafeSample() {\n    val input = FileInputStream(File(\"readme.txt\"))\n    input.doSafe {\n        val string = readString(\"UTF-8\")\n    }\n}\n```\n\n####  readString/readList\n```kotlin\n// available for InputStream/Reader\nfun readStringAndList1() {\n    val input = FileInputStream(File(\"readme.txt\"))\n    try {\n        val reader = input.reader(Encoding.CHARSET_UTF_8)\n\n        val string1 = input.readString(Encoding.UTF_8)\n        val string2 = input.readString(Encoding.CHARSET_UTF_8)\n\n        val list1 = input.readList()\n        val list2 = input.readList(Encoding.CHARSET_UTF_8)\n\n    } catch(e: IOException) {\n\n    } finally {\n        input.closeQuietly()\n    }\n}\n```\n\n#### readString/readList using doSafe\n```kotlin\n// available for InputStream/Reader\n//equal to readStringAndList1\nfun readStringAndList2() {\n    val input = FileInputStream(File(\"readme.txt\"))\n    input.doSafe {\n        val reader = reader(Encoding.CHARSET_UTF_8)\n\n        val string1 = readString(Encoding.UTF_8)\n        val string2 = readString(Encoding.CHARSET_UTF_8)\n\n        val list1 = readList()\n        val list2 = readList(Encoding.CHARSET_UTF_8)\n    }\n}\n```\n\n#### writeString/writeList using doSafe\n```kotlin\nfun writeStringAndList() {\n    val output = FileOutputStream(\"output.txt\")\n    output.doSafe {\n        output.writeString(\"hello, world\")\n        output.writeString(\"Alic's Adventures in Wonderland\", charset = Encoding.CHARSET_UTF_8)\n\n        val list1 = listOf\u003cInt\u003e(1, 2, 3, 4, 5)\n        val list2 = (1..8).map { \"Item No.$it\" }\n        output.writeList(list1, charset = Encoding.CHARSET_UTF_8)\n        output.writeList(list2)\n    }\n}\n```\n\n#### File Read and Write\n```kotlin\nfun fileReadWrite() {\n    val directory = File(\"/Users/koi/workspace\")\n    val file = File(\"some.txt\")\n\n    val text1 = file.readText()\n    val text2 = file.readString(Encoding.CHARSET_UTF_8)\n    val list1 = file.readList()\n    val list2 = file.readLines(Encoding.CHARSET_UTF_8)\n\n    file.writeText(\"hello, world\")\n    file.writeList(list1)\n    file.writeList(list2, Encoding.CHARSET_UTF_8)\n\n    val v1 = file.relativeToOrNull(directory)\n    val v2 = file.toRelativeString(directory)\n\n    // clean files in directory\n    directory.clean()\n\n\n    val file1 = File(\"a.txt\")\n    val file2 = File(\"b.txt\")\n    file1.copyTo(file2, overwrite = false)\n}\n```\n\n### Handler Extensions\n\n#### Easy to use Handler\n```kotlin\n// available for Handler\n// short name for functions\nfun handlerFunctions() {\n    val handler = Handler()\n    handler.atNow { print(\"perform action now\") }\n    // equal to\n    handler.post { print(\"perform action now\") }\n\n    handler.atFront { print(\"perform action at first\") }\n    // equal to\n    handler.postAtFrontOfQueue { print(\"perform action at first\") }\n\n    handler.atTime(timestamp() + 5000, { print(\"perform action after 5s\") })\n    // equal to\n    handler.postAtTime({ print(\"perform action after 5s\") }, 5000)\n\n    handler.delayed(3000, { print(\"perform action after 5s\") })\n    // equal to\n    handler.postDelayed({ print(\"perform action after 5s\") }, 3000)\n}\n```\n\n### Other Extensions\n\n#### Date Functions\n```kotlin\n// available in any where\nfun dateSample() {\n    val nowString = dateNow()\n    val date = dateParse(\"2016-02-02 20:30:45\")\n    val dateStr1 = date.asString()\n    val dateStr2 = date.asString(SimpleDateFormat(\"yyyyMMdd.HHmmss\"))\n    val dateStr3 = date.asString(\"yyyy-MM-dd-HH-mm-ss\")\n\n    // easy way to get timestamp\n    val timestamp1 = timestamp()\n    // equal to\n    val timestamp2 = System.currentTimeMillis()\n    val dateStr4 = timestamp1.asDateString()\n}\n```\n\n#### Number Functions\n```kotlin\nfun numberExtensions() {\n    val number = 179325344324902187L\n    println(number.readableByteCount())\n\n    val bytes = byteArrayOf(1, 7, 0, 8, 9, 4, 125)\n    println(bytes.hexString())\n}\n```\n\n#### String Functions\n```kotlin\n// available for String\nfun stringExtensions() {\n    val string = \"hello, little cat!\"\n    val quotedString = string.quote()\n    val isBlank = string.isBlank()\n    val hexBytes = string.toHexBytes()\n    val s1 = string.trimAllWhitespace()\n    val c = string.containsWhitespace()\n\n    val url = \"https://github.com/mcxiaoke/kotlin-koi?year=2016\u0026encoding=utf8\u0026a=b#changelog\"\n    val urlNoQuery = url.withoutQuery()\n\n    val isNameSafe = url.isNameSafe()\n    val fileName = url.toSafeFileName()\n    val queries = url.toQueries()\n\n    val path = \"/Users/koi/workspace/String.kt\"\n    val baseName = path.fileNameWithoutExtension()\n    val extension = path.fileExtension()\n    val name = path.fileName()\n}\n```\n\n#### Crypto Functions\n```kotlin\n// available in any where\nfun cryptoFunctions() {\n    val md5 = HASH.md5(\"hello, world\")\n    val sha1 = HASH.sha1(\"hello, world\")\n    val sha256 = HASH.sha256(\"hello, world\")\n}\n```\n\n#### Check API Level\n```kotlin\n// available in any where\nfun apiLevelFunctions() {\n    // Build.VERSION.SDK_INT\n    val v = currentVersion()\n    val ics = icsOrNewer()\n    val kk = kitkatOrNewer()\n    val bkk = beforeKitkat()\n    val lol = lollipopOrNewer()\n    val mar = marshmallowOrNewer()\n}\n```\n\n#### Device Functions\n```kotlin\n// available in any where\nfun deviceSample() {\n    val a = isLargeHeap\n    val b = noSdcard()\n    val c = noFreeSpace(needSize = 10 * 1024 * 1024L)\n    val d = freeSpace()\n}\n```\n\n#### Preconditions\n```kotlin\n// available in any where\n// null and empty check\nfun preconditions() {\n    throwIfEmpty(listOf(), \"collection is null or empty\")\n    throwIfNull(null, \"object is null\")\n    throwIfTrue(currentVersion() == 10, \"result is true\")\n    throwIfFalse(currentVersion() \u003c 4, \"result is false\")\n}\n```\n\n### Thread Functions\n\n#### Create Thread Pool\n```kotlin\n// available in any where\nfun executorFunctions() {\n    // global main handler\n    val uiHandler1 = CoreExecutor.mainHandler\n    // or using this function\n    val uiHandler2 = koiHandler()\n\n    // global executor service\n    val executor = CoreExecutor.executor\n    // or using this function\n    val executor2 = koiExecutor()\n\n    // create thread pool functions\n    val pool1 = newCachedThreadPool(\"cached\")\n    val pool2 = newFixedThreadPool(\"fixed\", 4)\n    val pool3 = newSingleThreadExecutor(\"single\")\n}\n```\n\n#### Main Thread Functions\n```kotlin\n// available in any where\nfun mainThreadFunctions() {\n    //check current thread\n    // call from any where\n    val isMain = isMainThread()\n\n    // execute in main thread\n    mainThread {\n        print(\"${(1..8).asSequence().joinToString()}\")\n    }\n\n    // delay execute in main thread\n    mainThreadDelay(3000) {\n        print(\"execute after 3000 ms\")\n    }\n}\n```\n\n#### Context Check\n```kotlin\n\n    // isContextAlive function impl\n    fun \u003cT\u003e isContextAlive(context: T?): Boolean {\n        return when (context) {\n            null -\u003e false\n            is Activity -\u003e !context.isFinishing\n            is Fragment -\u003e context.isAdded\n            is android.support.v4.app.Fragment -\u003e context.isAdded\n            is Detachable -\u003e !context.isDetached()\n            else -\u003e true\n        }\n    }\n```\n\n#### Safe Functions\n```kotlin\n// available in any where\nfun safeFunctions() {\n    val context = this\n    // check Activity/Fragment lifecycle\n    val alive = isContextAlive(context)\n\n    fun func1() {\n        print(\"func1\")\n    }\n    // convert to safe function with context check\n    // internal using  isContextAlive\n    val safeFun1 = safeFunction(::func1)\n\n    // call function with context check\n    // internal using isContextAlive\n    safeExecute(::func1)\n\n    // direct use\n    safeExecute { print(\"func1\") }\n}\n```\n\n### Async Functions\n\n```kotlin\nclass AsyncFunctionsSample {\n    private val intVal = 1000\n    private var strVal: String? = null\n}\n```\n\n#### With Context Check 1\n```kotlin\n// async functions with context check\n// internal using isContextAlive\n// context alive:\n// !Activity.isFinishing\n// Fragment.isAdded\n// !Detachable.isDetached\n//\n// available in any where\n// using in Activity/Fragment better\nfun asyncSafeFunction1() {\n    // safe means context alive check\n    // async\n    asyncSafe {\n        print(\"action executed only if context alive \")\n        // if you want get caller context\n        // maybe null\n        val ctx = getCtx()\n        // you can also using outside variables\n        // not recommended\n        // if context is Activity or Fragment\n        // may cause memory leak\n        print(\"outside value, $intVal $strVal\")\n\n        // you can using mainThreadSafe here\n        // like a callback\n        mainThreadSafe {\n            // also with context alive check\n            // if context dead, not executed\n            print(\"code here executed in main thread\")\n        }\n        // if you don't want context check, using mainThread{}\n        mainThread {\n            // no context check\n            print(\"code here executed in main thread\")\n        }\n    }\n    // if your result or error is nullable\n    // using asyncSafe2, just as asyncSafe\n    // but type of result and error is T?, Throwable?\n}\n```\n\n#### With Context Check 2\n```kotlin\nfun asyncSafeFunction2() {\n\n    // async with callback\n    asyncSafe(\n            {\n                print(\"action executed in async thread\")\n                listOf\u003cInt\u003e(1, 2, 3, 4, 5)\n            },\n            { result, error -\u003e\n                // in main thread\n                print(\"callback executed in main thread\")\n            })\n}\n```\n\n```kotlin\nfun asyncSafeFunction3() {\n    // async with success/failure callback\n    asyncSafe(\n            {\n                print(\"action executed in async thread\")\n                \"this string is result of the action\"\n                // throw RuntimeException(\"action error\")\n            },\n            { result -\u003e\n                // if action success with no exception\n                print(\"success callback in main thread result:$result\")\n            },\n            { error -\u003e\n                // if action failed with exception\n                print(\"failure callback in main thread, error:$error\")\n            })\n}\n```\n\n#### Without Context Check\n```kotlin\n// if you don't want context check\n// using asyncUnsafe series functions\n// just replace asyncSafe with asyncUnsafe\nfun asyncUnsafeFunctions() {\n    // async\n    asyncUnsafe {\n        print(\"action executed with no context check \")\n        // may cause memory leak\n        print(\"outside value, $intVal $strVal\")\n\n        mainThread {\n            // no context check\n            print(\"code here executed in main thread\")\n        }\n    }\n}\n```\n\n#### Custom Executor\n```kotlin\n    val executor = Executors.newFixedThreadPool(4)\n    asyncSafe(executor) {\n        print(\"action executed in async thread\")\n        mainThreadSafe {\n            print(\"code here executed in main thread\")\n        }\n    }\n```\n\n#### With Delay\n```kotlin\n// async functions with delay\n// with context check\n// if context died, not executed\n// others just like asyncSafe\nfun asyncDelayFunctions() {\n    // usage see asyncSafe\n    asyncDelay(5000) {\n        print(\"action executed after 5000ms only if context alive \")\n\n        // you can using mainThreadSafe here\n        // like a callback\n        mainThreadSafe {\n            // also with context alive check\n            // if context dead, not executed\n            print(\"code here executed in main thread\")\n        }\n        // if you don't want context check, using mainThread{}\n        mainThread {\n            // no context check\n            print(\"code here executed in main thread\")\n        }\n    }\n}\n```\n\n### About Me\n\n#### Contacts\n\n* Blog: \u003chttp://blog.mcxiaoke.com\u003e\n* Github: \u003chttps://github.com/mcxiaoke\u003e\n* Email: [github@mcxiaoke.com](mailto:github@mcxiaoke.com)\n\n#### Projects\n\n* awesome-kotlin: \u003chttps://github.com/mcxiaoke/awesome-kotlin\u003e\n* Android-Next: \u003chttps://github.com/mcxiaoke/Android-Next\u003e\n* PackerNg: \u003chttps://github.com/mcxiaoke/packer-ng-plugin\u003e\n* gradle-packer-plugin: \u003chttps://github.com/mcxiaoke/gradle-packer-plugin\u003e\n* xBus: \u003chttps://github.com/mcxiaoke/xBus\u003e\n* ReacitveX Docs: \u003chttps://github.com/mcxiaoke/RxDocs\u003e\n* MQTT Translation: \u003chttps://github.com/mcxiaoke/mqtt\u003e\n* Fanfou App: \u003chttps://github.com/mcxiaoke/minicat\u003e\n* Fanfou Opensource: \u003chttps://github.com/mcxiaoke/fanfouapp-opensource\u003e\n* Volley Mirror: \u003chttps://github.com/mcxiaoke/android-volley\u003e\n\n------\n\n### License\n\n    Copyright 2015, 2016 Xiaoke Zhang\n\n    Licensed under the Apache License, Version 2.0 (the \"License\");\n    you may not use this file except in compliance with the License.\n    You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software\n    distributed under the License is distributed on an \"AS IS\" BASIS,\n    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n    See the License for the specific language governing permissions and\n    limitations under the License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcxiaoke%2Fkotlin-koi","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmcxiaoke%2Fkotlin-koi","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmcxiaoke%2Fkotlin-koi/lists"}