[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-4318":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":16,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":21,"hasPages":21,"topics":23,"createdAt":10,"pushedAt":10,"updatedAt":24,"readmeContent":25,"aiSummary":26,"trendingCount":16,"starSnapshotCount":16,"syncStatus":27,"lastSyncTime":28,"discoverSource":29},4318,"ion","koush\u002Fion","koush","Android Asynchronous Networking and Image Loading","",null,"Java",6252,1021,323,329,0,1,3,65.83,"Other",false,"master",[],"2026-06-12 04:00:22","*Android Asynchronous Networking and Image Loading*\n\n![](ion-sample\u002Fion-sample.png)\n\n#### Download\n * [Maven](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#get-ion)\n * [Git](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#get-ion)\n\n#### Features\n * [Kotlin coroutine\u002Fsuspend support](https:\u002F\u002Fgithub.com\u002Fkoush\u002FAndroidAsync\u002Fblob\u002Fmaster\u002FAndroidAsync-Kotlin\u002FREADME.md)\n * Asynchronously download:\n   * [Images](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#load-an-image-into-an-imageview) into ImageViews or Bitmaps (animated GIFs supported too)\n   * [JSON](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#get-json) (via [Gson](https:\u002F\u002Fcode.google.com\u002Fp\u002Fgoogle-gson\u002F))\n   * Strings\n   * [Files](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#download-a-file-with-a-progress-bar)\n   * Java types using [Gson](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#seamlessly-use-your-own-java-classes-with-gson)\n * Easy to use Fluent API designed for Android\n   * Automatically cancels operations when the calling Activity finishes\n   * Manages invocation back onto the UI thread\n   * All operations return a [Future](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#futures) and [can be cancelled](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#cancelling-requests)\n * HTTP POST\u002FPUT:\n   * text\u002Fplain\n   * application\u002Fjson - both [JsonObject](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#post-json-and-read-json) and [POJO](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#seamlessly-use-your-own-java-classes-with-gson)\n   * [application\u002Fx-www-form-urlencoded](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#post-applicationx-www-form-urlencoded-and-read-a-string)\n   * [multipart\u002Fform-data](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#post-multipartform-data-and-read-json-with-an-upload-progress-bar)\n * Transparent usage of HTTP features and optimizations:\n   * SPDY and HTTP\u002F2\n   * Caching\n   * Gzip\u002FDeflate Compression\n   * Connection pooling\u002Freuse via HTTP Connection: keep-alive\n   * Uses the best\u002Fstablest connection from a server if it has multiple IP addresses\n   * Cookies\n * [View received headers](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#viewing-received-headers)\n * [Grouping and cancellation of requests](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#request-groups)\n * [Download progress callbacks](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#download-a-file-with-a-progress-bar)\n * Supports file:\u002F, http(s):\u002F, and content:\u002F URIs\n * Request level [logging and profiling](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#logging)\n * [Support for proxy servers](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion#proxy-servers-like-charles-proxy) like [Charles Proxy](http:\u002F\u002Fwww.charlesproxy.com\u002F) to do request analysis\n * Based on [NIO](http:\u002F\u002Fen.wikipedia.org\u002Fwiki\u002FNew_I\u002FO) and [AndroidAsync](https:\u002F\u002Fgithub.com\u002Fkoush\u002FAndroidAsync)\n * Ability to use [self signed SSL certificates](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion\u002Fissues\u002F3)\n\n#### Samples\n\nThe included documented [ion-sample](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion\u002Ftree\u002Fmaster\u002Fion-sample) project includes some samples that demo common Android network operations:\n\n * [Twitter Client Sample](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion\u002Fblob\u002Fmaster\u002Fion-sample\u002Fsrc\u002Fcom\u002Fkoushikdutta\u002Fion\u002Fsample\u002FTwitter.java)\n   * Download JSON from a server (twitter feed)\n   * Populate a ListView Adapter and fetch more data as you scroll to the end\n   * Put images from a URLs into ImageViews (twitter profile pictures)\n * File Download with [Progress Bar Sample](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion\u002Fblob\u002Fmaster\u002Fion-sample\u002Fsrc\u002Fcom\u002Fkoushikdutta\u002Fion\u002Fsample\u002FProgressBarDownload.java)\n * Get JSON and show images with the [Image Search Sample](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion\u002Fblob\u002Fmaster\u002Fion-sample\u002Fsrc\u002Fcom\u002Fkoushikdutta\u002Fion\u002Fsample\u002FImageSearch.java)\n\n#### More Examples\n\nLooking for more? Check out the examples below that demonstrate some other common scenarios. You can also take a look\nat 30+ ion unit tests in the [ion-test](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion\u002Ftree\u002Fmaster\u002Fion\u002Ftest\u002Fsrc\u002Fcom\u002Fkoushikdutta\u002Fion\u002Ftest).\n\n#### Get JSON\n\n```java\nIon.with(context)\n.load(\"http:\u002F\u002Fexample.com\u002Fthing.json\")\n.asJsonObject()\n.setCallback(new FutureCallback\u003CJsonObject>() {\n   @Override\n    public void onCompleted(Exception e, JsonObject result) {\n        \u002F\u002F do stuff with the result or error\n    }\n});\n```\n\n#### Post JSON and read JSON\n\n```java\nJsonObject json = new JsonObject();\njson.addProperty(\"foo\", \"bar\");\n\nIon.with(context)\n.load(\"http:\u002F\u002Fexample.com\u002Fpost\")\n.setJsonObjectBody(json)\n.asJsonObject()\n.setCallback(new FutureCallback\u003CJsonObject>() {\n   @Override\n    public void onCompleted(Exception e, JsonObject result) {\n        \u002F\u002F do stuff with the result or error\n    }\n});\n```\n\n#### Post application\u002Fx-www-form-urlencoded and read a String\n\n```java\nIon.with(getContext())\n.load(\"https:\u002F\u002Fkoush.clockworkmod.com\u002Ftest\u002Fecho\")\n.setBodyParameter(\"goop\", \"noop\")\n.setBodyParameter(\"foo\", \"bar\")\n.asString()\n.setCallback(...)\n```\n\n#### Post multipart\u002Fform-data and read JSON with an upload progress bar\n\n```java\nIon.with(getContext())\n.load(\"https:\u002F\u002Fkoush.clockworkmod.com\u002Ftest\u002Fecho\")\n.uploadProgressBar(uploadProgressBar)\n.setMultipartParameter(\"goop\", \"noop\")\n.setMultipartFile(\"archive\", \"application\u002Fzip\", new File(\"\u002Fsdcard\u002Ffilename.zip\"))\n.asJsonObject()\n.setCallback(...)\n```\n\n#### Download a File with a progress bar\n\n```java\nIon.with(context)\n.load(\"http:\u002F\u002Fexample.com\u002Freally-big-file.zip\")\n\u002F\u002F have a ProgressBar get updated automatically with the percent\n.progressBar(progressBar)\n\u002F\u002F and a ProgressDialog\n.progressDialog(progressDialog)\n\u002F\u002F can also use a custom callback\n.progress(new ProgressCallback() {@Override\n   public void onProgress(long downloaded, long total) {\n       System.out.println(\"\" + downloaded + \" \u002F \" + total);\n   }\n})\n.write(new File(\"\u002Fsdcard\u002Freally-big-file.zip\"))\n.setCallback(new FutureCallback\u003CFile>() {\n   @Override\n    public void onCompleted(Exception e, File file) {\n        \u002F\u002F download done...\n        \u002F\u002F do stuff with the File or error\n    }\n});\n```\n\n#### Setting Headers\n\n```java\nIon.with(context)\n.load(\"http:\u002F\u002Fexample.com\u002Ftest.txt\")\n\u002F\u002F set the header\n.setHeader(\"foo\", \"bar\")\n.asString()\n.setCallback(...)\n```\n\n#### Load an image into an ImageView\n\n```java\n\u002F\u002F This is the \"long\" way to do build an ImageView request... it allows you to set headers, etc.\nIon.with(context)\n.load(\"http:\u002F\u002Fexample.com\u002Fimage.png\")\n.withBitmap()\n.placeholder(R.drawable.placeholder_image)\n.error(R.drawable.error_image)\n.animateLoad(spinAnimation)\n.animateIn(fadeInAnimation)\n.intoImageView(imageView);\n\n\u002F\u002F but for brevity, use the ImageView specific builder...\nIon.with(imageView)\n.placeholder(R.drawable.placeholder_image)\n.error(R.drawable.error_image)\n.animateLoad(spinAnimation)\n.animateIn(fadeInAnimation)\n.load(\"http:\u002F\u002Fexample.com\u002Fimage.png\");\n```\n\nThe Ion Image load API has the following features:\n * Disk and memory caching\n * Bitmaps are held via weak references so memory is managed very efficiently\n * ListView Adapter recycling support\n * Bitmap transformations via the .transform(Transform)\n * Animate loading and loaded ImageView states\n * [DeepZoom](http:\u002F\u002Fwww.youtube.com\u002Fwatch?v=yIMltNEAKZY) for extremely large images\n\n#### Futures\n\n_All_ operations return a custom [Future](http:\u002F\u002Fdeveloper.android.com\u002Freference\u002Fjava\u002Futil\u002Fconcurrent\u002FFuture.html) that allows\nyou to specify a callback that runs on completion.\n\n```java\npublic interface Future\u003CT> extends Cancellable, java.util.concurrent.Future\u003CT> {\n    \u002F**\n     * Set a callback to be invoked when this Future completes.\n     * @param callback\n     * @return\n     *\u002F\n    public Future\u003CT> setCallback(FutureCallback\u003CT> callback);\n}\n\nFuture\u003CString> string = Ion.with(context)\n.load(\"http:\u002F\u002Fexample.com\u002Fstring.txt\")\n.asString();\n\nFuture\u003CJsonObject> json = Ion.with(context)\n.load(\"http:\u002F\u002Fexample.com\u002Fjson.json\")\n.asJsonObject();\n\nFuture\u003CFile> file = Ion.with(context)\n.load(\"http:\u002F\u002Fexample.com\u002Ffile.zip\")\n.write(new File(\"\u002Fsdcard\u002Ffile.zip\"));\n\nFuture\u003CBitmap> bitmap = Ion.with(context)\n.load(\"http:\u002F\u002Fexample.com\u002Fimage.png\")\n.intoImageView(imageView);\n```\n\n#### Cancelling Requests\n\nFutures can be cancelled by calling .cancel():\n\n```java\nbitmap.cancel();\njson.cancel();\n```\n\n#### Blocking on Requests\n\nThough you should try to use callbacks for handling requests whenever possible, blocking on requests is possible too.\nAll Futures have a Future\u003CT>.get() method that waits for the result of the request, by blocking if necessary.\n\n```java\nJsonObject json = Ion.with(context)\n.load(\"http:\u002F\u002Fexample.com\u002Fthing.json\").asJsonObject().get();\n```\n\n#### Seamlessly use your own Java classes with [Gson](https:\u002F\u002Fcode.google.com\u002Fp\u002Fgoogle-gson\u002F)\n\n```java\npublic static class Tweet {\n    public String id;\n    public String text;\n    public String photo;\n}\n\npublic void getTweets() throws Exception {\n    Ion.with(context)\n    .load(\"http:\u002F\u002Fexample.com\u002Fapi\u002Ftweets\")\n    .as(new TypeToken\u003CList\u003CTweet>>(){})\n    .setCallback(new FutureCallback\u003CList\u003CTweet>>() {\n       @Override\n        public void onCompleted(Exception e, List\u003CTweet> tweets) {\n          \u002F\u002F chirp chirp\n        }\n    });\n}\n```\n\n#### Logging\n\nWondering why your app is slow? Ion lets you do both global and request level logging.\n\nTo enable it globally:\n\n```java\nIon.getDefault(getContext()).configure().setLogging(\"MyLogs\", Log.DEBUG);\n```\n\nOr to enable it on just a single request:\n\n```java\nIon.with(context)\n.load(\"http:\u002F\u002Fexample.com\u002Fthing.json\")\n.setLogging(\"MyLogs\", Log.DEBUG)\n.asJsonObject();\n```\n\nLog entries will look like this:\n\n```\nD\u002FMyLogs(23153): (0 ms) http:\u002F\u002Fexample.com\u002Fthing.json: Executing request.\nD\u002FMyLogs(23153): (106 ms) http:\u002F\u002Fexample.com\u002Fthing.json: Connecting socket\nD\u002FMyLogs(23153): (2985 ms) http:\u002F\u002Fexample.com\u002Fthing.json: Response is not cacheable\nD\u002FMyLogs(23153): (3003 ms) http:\u002F\u002Fexample.com\u002Fthing.json: Connection successful\n```\n\n#### Request Groups\n\nBy default, Ion automatically places all requests into a group with all the other requests\ncreated by that Activity or Service. Using the cancelAll(Activity) call, all requests\nstill pending can be easily cancelled:\n\n```java\nFuture\u003CJsonObject> json1 = Ion.with(activity, \"http:\u002F\u002Fexample.com\u002Ftest.json\").asJsonObject();\nFuture\u003CJsonObject> json2 = Ion.with(activity, \"http:\u002F\u002Fexample.com\u002Ftest2.json\").asJsonObject();\n\n\u002F\u002F later... in activity.onStop\n@Override\nprotected void onStop() {\n    Ion.getDefault(activity).cancelAll(activity);\n    super.onStop();\n}\n```\n\nIon also lets you tag your requests into groups to allow for easy cancellation of requests in that group later:\n\n```java\nObject jsonGroup = new Object();\nObject imageGroup = new Object();\n\nFuture\u003CJsonObject> json1 = Ion.with(activity)\n.load(\"http:\u002F\u002Fexample.com\u002Ftest.json\")\n\u002F\u002F tag in a custom group\n.group(jsonGroup)\n.asJsonObject();\n\nFuture\u003CJsonObject> json2 = Ion.with(activity)\n.load(\"http:\u002F\u002Fexample.com\u002Ftest2.json\")\n\u002F\u002F use the same custom group as the other json request\n.group(jsonGroup)\n.asJsonObject();\n\nFuture\u003CBitmap> image1 = Ion.with(activity)\n.load(\"http:\u002F\u002Fexample.com\u002Ftest.png\")\n\u002F\u002F for this image request, use a different group for images\n.group(imageGroup)\n.intoImageView(imageView1);\n\nFuture\u003CBitmap> image2 = Ion.with(activity)\n.load(\"http:\u002F\u002Fexample.com\u002Ftest2.png\")\n\u002F\u002F same imageGroup as before\n.group(imageGroup)\n.intoImageView(imageView2);\n\n\u002F\u002F later... to cancel only image downloads:\nIon.getDefault(activity).cancelAll(imageGroup);\n```\n\n#### Proxy Servers (like Charles Proxy)\n\nProxy server settings can be enabled all Ion requests, or on a per request basis:\n\n```java\n\u002F\u002F proxy all requests\nIon.getDefault(context).configure().proxy(\"mycomputer\", 8888);\n\n\u002F\u002F or... to proxy specific requests\nIon.with(context)\n.load(\"http:\u002F\u002Fexample.com\u002Fproxied.html\")\n.proxy(\"mycomputer\", 8888)\n.getString();\n```\n\nUsing Charles Proxy on your desktop computer in conjunction with request proxying will prove invaluable for debugging!\n\n![](ion-sample\u002Fcharles.png)\n\n#### Viewing Received Headers\n\nIon operations return a [ResponseFuture](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion\u002Fblob\u002Fmaster\u002Fion\u002Fsrc\u002Fcom\u002Fkoushikdutta\u002Fion\u002Ffuture\u002FResponseFuture.java),\nwhich grant access to response properties via the [Response object](https:\u002F\u002Fgithub.com\u002Fkoush\u002Fion\u002Fblob\u002Fmaster\u002Fion\u002Fsrc\u002Fcom\u002Fkoushikdutta\u002Fion\u002FResponse.java).\nThe Response object contains the headers, as well as the result:\n\n```java\nIon.with(getContext())\n.load(\"http:\u002F\u002Fexample.com\u002Ftest.txt\")\n.asString()\n.withResponse()\n.setCallback(new FutureCallback\u003CResponse\u003CString>>() {\n    @Override\n    public void onCompleted(Exception e, Response\u003CString> result) {\n        \u002F\u002F print the response code, ie, 200\n        System.out.println(result.getHeaders().code());\n        \u002F\u002F print the String that was downloaded\n        System.out.println(result.getResult());\n    }\n});\n```\n\n\n#### Get Ion\n\n##### Maven\n```xml\n\u003Cdependency>\n   \u003CgroupId>com.koushikdutta.ion\u003C\u002FgroupId>\n   \u003CartifactId>ion\u003C\u002FartifactId>\n   \u003Cversion>(insert latest version)\u003C\u002Fversion>\n\u003C\u002Fdependency>\n```\n\n##### Gradle\n```groovy\ndependencies {\n    compile 'com.koushikdutta.ion:ion:(insert latest version)'\n}\n````\n\n##### Local Checkout (with [AndroidAsync](https:\u002F\u002Fgithub.com\u002Fkoush\u002FAndroidAsync) dependency)\n```\ngit clone git:\u002F\u002Fgithub.com\u002Fkoush\u002FAndroidAsync.git\ngit clone git:\u002F\u002Fgithub.com\u002Fkoush\u002Fion.git\ncd ion\u002Fion\nant -Dsdk.dir=$ANDROID_HOME release install\n```\nJars are at\n * ion\u002Fion\u002Fbin\u002Fclasses.jar\n * AndroidAsync\u002FAndroidAsync\u002Fbin\u002Fclasses.jar\n\n#### Hack in Eclipse\n```\ngit clone git:\u002F\u002Fgithub.com\u002Fkoush\u002FAndroidAsync.git\ngit clone git:\u002F\u002Fgithub.com\u002Fkoush\u002Fion.git\n```\n* Import the project from AndroidAsync\u002FAndroidAsync into your workspace\n* Import all the ion projects (ion\u002Fion, ion\u002Fion-sample) into your workspace.\n\n#### Projects using ion\n\nThere's hundreds of apps using ion. Feel free to contact me or submit a pull request to add yours to this list.\n\n* [AllCast](https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.koushikdutta.cast)\n* [Helium](https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.koushikdutta.backup)\n* [Repost](https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.dodgingpixels.repost)\n* [Cloupload](https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=de.gidix.cloupload)\n* [Binge](https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.stfleurs.binge)\n* [PictureCast](https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.unstableapps.picturecast.app)\n* [Eventius](https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.eventius.android)\n* [Plume](https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.levelup.touiteur)\n* [GameRaven](https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.ioabsoftware.gameraven)\n* [See You There](https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=com.maps.wearat&hl=en)\n* [Doogles](https:\u002F\u002Fplay.google.com\u002Fstore\u002Fapps\u002Fdetails?id=io.dooglesapp)\n","koush\u002Fion 是一个专为 Android 平台设计的异步网络请求和图片加载库。它支持异步下载图片、JSON、字符串以及文件，并且能够无缝集成 Gson 进行 Java 类型的数据解析。该库提供了简洁易用的 Fluent API，自动管理 UI 线程回调与 Activity 生命周期相关的请求取消操作。此外，ion 还具备 HTTP\u002F2、SPDY 支持、缓存机制、压缩处理等高级特性，同时支持自定义 SSL 证书及代理服务器配置。适用于需要高效稳定的网络通信及图片加载功能的 Android 应用开发场景。",2,"2026-06-11 02:59:37","top_language"]