[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-7599":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":17,"stars7d":18,"stars30d":19,"stars90d":16,"forks30d":16,"starsTrendScore":20,"compositeScore":21,"rankGlobal":10,"rankLanguage":10,"license":22,"archived":23,"fork":23,"defaultBranch":24,"hasWiki":23,"hasPages":23,"topics":25,"createdAt":10,"pushedAt":10,"updatedAt":46,"readmeContent":47,"aiSummary":48,"trendingCount":16,"starSnapshotCount":16,"syncStatus":18,"lastSyncTime":49,"discoverSource":50},7599,"Fetch","tonyofrancis\u002FFetch","tonyofrancis","The best file downloader library for Android","https:\u002F\u002Fwww.meta.stackoverflow.com\u002Ftags\u002Ffetch2",null,"Kotlin",1812,357,38,119,0,1,2,5,3,59.16,"Apache License 2.0",false,"v3.0x",[26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45],"android","android-development","android-library","android-tv","app","application","downloader","downloadmanager","fast-networking","filedownloader","filemanager","gradle","http","httpurlconnection","internet","internet-of-things","mobile","networking","okhttp","retrofit","2026-06-12 04:00:34","[![Android Arsenal](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002FAndroid%20Arsenal-Android%20Networking-blue.svg?style=flat)](https:\u002F\u002Fandroid-arsenal.com\u002Fdetails\u002F1\u002F5196)\n[![License](https:\u002F\u002Fimg.shields.io\u002Fbadge\u002Flicense-Apache%202.0-blue.svg)](https:\u002F\u002Fgithub.com\u002Ftonyofrancis\u002FFetch\u002Fblob\u002Fmaster\u002FLICENSE)\n\n![ScreenShot](https:\u002F\u002Fgithub.com\u002Ftonyofrancis\u002FFetch\u002Fblob\u002Fv2\u002Ffull_logo.png)\n\nOverview\n--------\n\nFetch is a simple, powerful, customizable file download manager library for Android.\n\n![ScreenShot](https:\u002F\u002Fgithub.com\u002Ftonyofrancis\u002FFetch\u002Fblob\u002Fv2\u002Fscreenshot.png)\n\nFeatures\n--------\n\n* Simple and easy to use API.\n* Continuous downloading in the background.\n* Concurrent downloading support.\n* Ability to pause and resume downloads.\n* Set the priority of a download.\n* Network-specific downloading support.\n* Ability to retry failed downloads.\n* Ability to group downloads.\n* Easy progress and status tracking.\n* Download remaining time reporting (ETA).\n* Download speed reporting.\n* Save and Retrieve download information anytime.\n* Notification Support.\n* Storage Access Framework, Content Provider and URI support.\n* And more...\n\nPrerequisites\n-------------\n\nIf you are saving downloads outside of your application's sandbox, you will need to\nadd the following storage permissions to your application's manifest. For Android SDK version\n23(M) and above, you will also need to explicitly request these permissions from the user.\n\n```xml\n\u003Cuses-permission android:name=\"android.permission.WRITE_EXTERNAL_STORAGE\"\u002F>\n\u003Cuses-permission android:name=\"android.permission.READ_EXTERNAL_STORAGE\"\u002F>\n```\nAlso, as you are going to use Internet to download files. We need to add the Internet access permissions\nin the Manifest.\n\n```xml\n\u003Cuses-permission android:name=\"android.permission.INTERNET\"\u002F>\n```\n\nHow to use Fetch\n----------------\n\nUsing Fetch is easy! Just add the following to your project's `build.gradle` file. In newer Android\nStudio projects add this to `settings.gradle`\n```groovy\nallprojects {\n    repositories {\n        maven { url 'https:\u002F\u002Fjitpack.io' }\n    }\n}\n```\n\nAdd the Gradle dependency to your application's build.gradle file.\n```groovy\nimplementation \"com.github.tonyofrancis.Fetch:fetch2:3.4.1\"\n```\n\nNext, get an instance of Fetch and request a download.\n\n```java\npublic class TestActivity extends AppCompatActivity {\n\n    private Fetch fetch;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n\n FetchConfiguration fetchConfiguration = new FetchConfiguration.Builder(this)\n                .setDownloadConcurrentLimit(3)\n                .build();\n\n        fetch = Fetch.Impl.getInstance(fetchConfiguration);\n\n        String url = \"http:www.example.com\u002Ftest.txt\";\n        String file = \"\u002Fdownloads\u002Ftest.txt\";\n        \n        final Request request = new Request(url, file);\n        request.setPriority(Priority.HIGH);\n        request.setNetworkType(NetworkType.ALL);\n        request.addHeader(\"clientKey\", \"SD78DF93_3947&MVNGHE1WONG\");\n        \n        fetch.enqueue(request, updatedRequest -> {\n            \u002F\u002FRequest was successfully enqueued for download.\n        }, error -> {\n            \u002F\u002FAn error occurred enqueuing the request.\n        });\n\n    }\n}\n```\n\nTracking a download's progress and status is very easy with Fetch. \nSimply add a FetchListener to your Fetch instance, and the listener will be notified whenever a download's\nstatus or progress changes.\n\n```java\nFetchListener fetchListener = new FetchListener() {\n    @Override\n    public void onQueued(@NotNull Download download, boolean waitingOnNetwork) {\n        if (request.getId() == download.getId()) {\n            showDownloadInList(download);\n        }\n    }\n\n    @Override\n    public void onCompleted(@NotNull Download download) {\n\n    }\n\n    @Override\n    public void onError(@NotNull Download download) {\n        Error error = download.getError();\n    }\n\n    @Override\n    public void onProgress(@NotNull Download download, long etaInMilliSeconds, long downloadedBytesPerSecond) {\n        if (request.getId() == download.getId()) {\n            updateDownload(download, etaInMilliSeconds);\n        }\n        int progress = download.getProgress();\n    }\n\n    @Override\n    public void onPaused(@NotNull Download download) {\n\n    }\n\n    @Override\n    public void onResumed(@NotNull Download download) {\n\n    }\n\n    @Override\n    public void onCancelled(@NotNull Download download) {\n\n    }\n\n    @Override\n    public void onRemoved(@NotNull Download download) {\n\n    }\n\n    @Override\n    public void onDeleted(@NotNull Download download) {\n\n    }\n};\n\nfetch.addListener(fetchListener);\n\n\u002F\u002FRemove listener when done.\nfetch.removeListener(fetchListener);\n```\n\nFetch supports pausing and resuming downloads using the request's id.\nA request's id is a unique identifier that maps a request to a Fetch Download.\nA download returned by Fetch will have have an id that matches the request id that\nstarted the download.\n\n```java\nRequest request1 = new Request(url, file);\nRequest request2 = new Request(url2, file2);\n\nfetch.pause(request1.getId());\n\n...\n\nfetch.resume(request2.getId());\n\n```\n\nYou can query Fetch for download information in several ways.\n\n```java\n\u002F\u002FQuery all downloads\nfetch.getDownloads(new Func\u003CList\u003C? extends Download>>() {\n    @Override\n        public void call(List\u003C? extends Download> downloads) {\n            \u002F\u002FAccess all downloads here\n        }\n});\n\n\u002F\u002FGet all downloads with a status\nfetch.getDownloadsWithStatus(Status.DOWNLOADING, new Func\u003CList\u003C? extends Download>>() {\n    @Override\n        public void call(List\u003C? extends Download> downloads) {\n            \u002F\u002FAccess downloads that are downloading\n        }\n});\n\n\u002F\u002F You can also access grouped downloads\nint groupId = 52687447745;\nfetch.getDownloadsInGroup(groupId, new Func\u003CList\u003C? extends Download>>() {\n    @Override\n      public void call(List\u003C? extends Download> downloads) {\n              \u002F\u002FAccess grouped downloads\n      }\n});\n```\n\nWhen you are done with an instance of Fetch, simply release it.\n\n```java\n\u002F\u002Fdo work\n\nfetch.close();\n\n\u002F\u002Fdo more work\n```\n\nDownloaders\n----------------\n\nBy default Fetch uses the HttpUrlConnection client via the HttpUrlConnectionDownloader\nto download requests. Add the following Gradle dependency to your application's build.gradle\nto use the OkHttp Downloader instead. You can create your custom downloaders\nif necessary. See the Java docs for details.\n\n```groovy\nimplementation \"com.github.tonyofrancis.Fetch:fetch2okhttp:3.4.1\"\n```\n\nSet the OkHttp Downloader for Fetch to use.\n```java\nOkHttpClient okHttpClient = new OkHttpClient.Builder().build();\n\nFetchConfiguration fetchConfiguration = new FetchConfiguration.Builder(this)\n    .setDownloadConcurrentLimit(10)\n    .setHttpDownloader(new OkHttpDownloader(okHttpClient))\n    .build();\n\nFetch fetch = Fetch.Impl.getInstance(fetchConfiguration);\n```\n\nRxFetch\n----------------\n\nIf you would like to take advantage of RxJava2 features when using Fetch,\nadd the following gradle dependency to your application's build.gradle file.\n\n```groovy\nimplementation \"com.github.tonyofrancis.Fetch:fetch2rx:3.4.1\"\n```\n\nRxFetch makes it super easy to enqueue download requests and query downloads using rxJava2 functional methods.\n\n```java\nFetchConfiguration fetchConfiguration = new FetchConfiguration.Builder(this).build();\nRxfetch rxFetch = RxFetch.Impl.getInstance(fetchConfiguration);\n\nrxFetch.getDownloads()\n        .asFlowable()\n        .subscribe(new Consumer\u003CList\u003CDownload>>() {\n            @Override\n            public void accept(List\u003CDownload> downloads) throws Exception {\n                \u002F\u002FAccess results\n            }\n        }, new Consumer\u003CThrowable>() {\n            @Override\n            public void accept(Throwable throwable) throws Exception {\n                \u002F\u002FAn error occurred\n                final Error error = FetchErrorUtils.getErrorFromThrowable(throwable);\n            }\n        });\n```\n\nFetchFileServer\n----------------\n\nIntroducing the FetchFileServer. The FetchFileServer is a lightweight TCP File Server that acts like\nan HTTP file server designed specifically to share files between Android devices. You can host file resources\nwith the FetchFileServer on one device and have to Fetch download Files from the server\non another device. See the sample app for more information. Wiki on FetchFileServer will be\nadded in the coming days.\n\nStart using FetchFileServer by adding the gradle dependency to your application's build.gradle file.\n \n```groovy\nimplementation \"com.github.tonyofrancis.Fetch:fetch2fileserver:3.4.1\"\n```\n\nStart a FetchFileServer instance and add resource files that it can serve to connected clients.\n```java\npublic class TestActivity extends AppCompatActivity {\n\n    FetchFileServer fetchFileServer;\n    \n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        fetchFileServer = new FetchFileServer.Builder(this)\n                .build();\n        \n        fetchFileServer.start(); \u002F\u002Flisten for client connections\n\n        File file = new File(\"\u002Fdownloads\u002Ftestfile.txt\");\n        FileResource fileResource = new FileResource();\n        fileResource.setFile(file.getAbsolutePath());\n        fileResource.setLength(file.length());\n        fileResource.setName(\"testfile.txt\");\n        fileResource.setId(UUID.randomUUID().hashCode());\n        \n        fetchFileServer.addFileResource(fileResource);\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        fetchFileServer.shutDown(false);\n    }\n}\n```\n\nDownloading a file from a FetchFileServer using the Fetch is easy.\n\n```java\npublic class TestActivity extends AppCompatActivity {\n\n    Fetch fetch;\n\n    @Override\n    protected void onCreate(@Nullable Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        FetchConfiguration fetchConfiguration = new FetchConfiguration.Builder(this)\n                .setFileServerDownloader(new FetchFileServerDownloader()) \u002F\u002Fhave to set the file server downloader\n                .build();\n        fetch = Fetch.Impl.getInstance(fetchConfiguration);\n        fetch.addListener(fetchListener);\n\n        String file = \"\u002Fdownloads\u002Fsample.txt\";\n        String url = new FetchFileServerUrlBuilder()\n                .setHostInetAddress(\"127.0.0.1\", 6886) \u002F\u002Ffile server ip and port\n                .setFileResourceIdentifier(\"testfile.txt\") \u002F\u002Ffile resource name or id\n                .create();\n        Request request = new Request(url, file);\n        fetch.enqueue(request, request1 -> {\n            \u002F\u002FRequest enqueued for download\n        }, error -> {\n            \u002F\u002FError while enqueuing download\n        });\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        fetch.addListener(fetchListener);\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        fetch.removeListener(fetchListener);\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        fetch.close();\n    }\n\n    private FetchListener fetchListener = new AbstractFetchListener() {\n        @Override\n        public void onProgress(@NotNull Download download, long etaInMilliSeconds, long downloadedBytesPerSecond) {\n            super.onProgress(download, etaInMilliSeconds, downloadedBytesPerSecond);\n            Log.d(\"TestActivity\", \"Progress: \" + download.getProgress());\n        }\n\n        @Override\n        public void onError(@NotNull Download download) {\n            super.onError(download);\n            Log.d(\"TestActivity\", \"Error: \" + download.getError().toString());\n        }\n\n        @Override\n        public void onCompleted(@NotNull Download download) {\n            super.onCompleted(download);\n            Log.d(\"TestActivity\", \"Completed \");\n        }\n    };\n}\n```\n\nFetch1 Migration\n----------------\n\nMigrate downloads from Fetch1 to Fetch2 using the migration assistant. Add the following gradle dependency to your application's build.gradle file.\n\n```groovy\nimplementation \"com.github.tonyofrancis.Fetch:fetchmigrator:3.4.1\"\n```\n\nThen run the Migrator.\n\n```java\n        if (!didMigrationRun()) {\n            \u002F\u002FMigration has to run on a background thread\n            new Thread(new Runnable() {\n                @Override\n                public void run() {\n                    try {\n                        final List\u003CDownloadTransferPair> transferredDownloads = FetchMigrator.migrateFromV1toV2(getApplicationContext(), APP_FETCH_NAMESPACE);\n                        \u002F\u002FTODO: update external references of ids\n                        for (DownloadTransferPair transferredDownload : transferredDownloads) {\n                            Log.d(\"newDownload\", transferredDownload.getNewDownload().toString());\n                            Log.d(\"oldId in Fetch v1\", transferredDownload.getOldID() + \"\");\n                        }\n                        FetchMigrator.deleteFetchV1Database(getApplicationContext());\n                        setMigrationDidRun(true);\n                        \u002F\u002FSetup and Run Fetch2 after the migration\n                    } catch (SQLException e) {\n                        e.printStackTrace();\n                    }\n                }\n            }).start();\n        } else {\n            \u002F\u002FSetup and Run Fetch2  normally\n        }\n```\n\nContribute\n----------\n\nFetch can only get better if you make code contributions. Found a bug? Report it.\nHave a feature idea you'd love to see in Fetch? Contribute to the project!\n\nLicense\n-------\n\n```\nCopyright (C) 2017 Tonyo Francis.\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:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-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","Fetch 是一个适用于 Android 平台的强大文件下载管理库。它提供了简单易用的 API，支持后台连续下载、并发下载以及暂停和恢复下载等功能。此外，Fetch 还支持设置下载优先级、网络特定下载、失败重试、下载分组、进度和状态跟踪等特性，并且能够报告下载剩余时间和速度。该库还具备通知支持、存储访问框架兼容性及内容提供者与 URI 支持等功能。由于其丰富的功能集和良好的扩展性，Fetch 非常适合需要高效稳定文件下载能力的 Android 应用开发场景中使用。","2026-06-11 03:13:14","top_language"]