基于上一篇文章实现了基本的okhttp请求基本用法: https://mp.csdn.net/editor/html/115055910
下面结合retrofit和rxJava实现文件上传下载, 带下载进度,.
基本介绍
. Retrofit是基于okhttp基础上进行高度封装或者包装的产物, 结合rxJava, 可以说是如鱼得水,天作之合, 可以很方便实现网络请求的同步异步切换, 摆脱了以前的AsyncTask和Handler机制. 直接链式写法实现网络切换;
package com.hulk.android.http.retrofit;
import android.text.TextUtils;
import android.util.Log;
import com.hulk.android.http.ok.OkHttpManager;
import com.hulk.android.http.ok.ProgressListener;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Retrofit 管理器
* @author: zhanghao
* @Time: 2021-03-04 16:00
*/
public class RetrofitManager {
private static final String TAG = "RetrofitManager";
Retrofit mRetrofit;
String mBaseUrl;
/**
* 浏览器模式,此模式 Ok http client 下不执行SSL证书等等验证,直接下载
*/
boolean browserMode = true;
/**
* 全局精度监听器, 与 OK http 绑定
*/
ProgressListener mProgressListener;
static RetrofitManager instance = new RetrofitManager();
public static RetrofitManager getInstance() {
return instance;
}
private RetrofitManager() {
}
public void setProgressListener(ProgressListener progressListener) {
this.mProgressListener = progressListener;
}
public void setBrowserMode(boolean browserMode) {
this.browserMode = browserMode;
}
public void setBaseUrl(String baseUrl) {
this.mBaseUrl = baseUrl;
}
public String getBaseUrl() {
return mBaseUrl;
}
public OkHttpClient getOkHttpClient() {
return getOkHttpClient(mProgressListener);
}
public OkHttpClient getOkHttpClient(ProgressListener listener) {
if (listener == null) {
Log.w(TAG, "getOkHttpClient: progress listener is null");
}
if (browserMode) {
return OkHttpManager.getBrowserDownloadClient(listener);
}
return OkHttpManager.getDownloadSslClient(listener);
}
public Retrofit getRetrofit() {
ensureRetrofit();
return mRetrofit;
}
public static Retrofit createRetrofit(String baseUrl, OkHttpClient okHttpClient) {
//可以增加其他转换器
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addConverterFactory(ToByteConvertFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
return retrofit;
}
public Retrofit createRetrofit(OkHttpClient okHttpClient) {
String baseUrl = getAvailableBaseUrl();
return createRetrofit(baseUrl, okHttpClient);
}
public String getAvailableBaseUrl() {
if (TextUtils.isEmpty(mBaseUrl)) {
mBaseUrl = "https://www.baidu.com";
}
return mBaseUrl;
}
private void initRetrofit() {
initRetrofit(getOkHttpClient());
}
public void initRetrofit(OkHttpClient okHttpClient) {
mRetrofit = createRetrofit(okHttpClient);
}
private void initRetrofit(ProgressListener progressListener) {
initRetrofit(getOkHttpClient(progressListener));
}
private void ensureRetrofit() {
if (mRetrofit == null) {
initRetrofit();
}
}
}
下载API
/**
* 下载API
* @author: zhanghao
* @Time: 2021-02-25 21:29
*/
public interface DownloadApiService {
/**
* 下载通用函数
* @param url
* @return
*/
@Streaming
@GET
Observable<ResponseBody> download(@Url String url);
}
package com.hulk.android.http.upload;
import com.hulk.android.http.conn.HttpResult;
import java.util.Map;
import io.reactivex.Observable;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import retrofit2.http.Body;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.PartMap;
import retrofit2.http.Url;
/**
* 上传文件接口
* @author: zhanghao
* @Time: 2021-02-26 10:41
*/
public interface UploadApiService {
/**
* 上传单个文本和单个文件
* @param textBody 文本数据: text 字段名称, 实际开发中在确定.
* @param fileBody 文件数据: file 为字段名称, test.png 为服务器上保存的文件名, 实际开发中在确定,
* @return
*/
@Multipart
@POST("v1/uploadImg")
Observable<HttpResult> uploadImg(@Part("text") RequestBody textBody, @Part("file\"; filename=\"test.png") RequestBody fileBody);
/**
* 上传多个文本和多个文件(传递map)
* @param textBodyMap 文本map
* @param fileBodyMap 文件map
* @return
*/
@Multipart
@POST("v1/uploadFiles")
Observable<HttpResult> upLoad(@PartMap Map<String, RequestBody> textBodyMap, @PartMap Map<String, RequestBody> fileBodyMap);
/**
* 上传多个文本和多个文件, 传递 MultipartBody:
* MultipartBody.Builder builder = new MultipartBody.Builder();
* //文本部分
* builder.addFormDataPart("title", "User comments");
* builder.addFormDataPart("content", "The retrofit+Rxjava is perfect");
*
* //文件部分
* File file1 = new File("hulk.jpg");
* RequestBody requestBody = RequestBody.create(MediaType.parse("image/jpg"), file1);
* // “image”为文件参数的参数名
* builder.addFormDataPart("image", file1.getName(), requestBody);
*
* File file2 = new File("hulk.mp4");
* RequestBody requestBody2 = RequestBody.create(MediaType.parse("video/mp4"), file2);
* // “video”为文件参数的参数名
* builder.addFormDataPart("video", file2.getName(), requestBody2);
*
* builder.setType(MultipartBody.FORM);
* MultipartBody multipartBody = builder.build();
*
* @param url 直接使用完整的 url
* @param multipartBody
* @return
*/
@POST
Observable<HttpResult> upLoad(@Url String url, @Body MultipartBody multipartBody);
}
RetrofitDownloader
package com.hulk.android.http.download;
import android.content.Context;
import android.os.Environment;
import android.text.TextUtils;
import com.hulk.android.http.content.FileHelper;
import com.hulk.android.log.Log;
import com.hulk.android.http.utils.HttpFileUtils;
import com.hulk.android.http.ok.OkHttpManager;
import com.hulk.android.http.ok.ProgressListener;
import com.hulk.android.http.retrofit.RetrofitManager;
import java.io.InputStream;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.functions.Consumer;
import io.reactivex.schedulers.Schedulers;
import okhttp3.OkHttpClient;
import okhttp3.ResponseBody;
import retrofit2.Retrofit;
/**
* retrofit下载器
* @author: zhanghao
* @Time: 2021-02-25 21:06
*/
public class RetrofitDownloader {
private static final String TAG = "RetrofitDownloader";
Context context;
String baseUrl;
Retrofit retrofit;
DownloadApiService downloadService;
/**
* 浏览器模式,此模式 Ok http client 下不执行SSL证书等等验证,直接下载
*/
boolean browserMode = true;
long contentLength = 0;
/**
* 全局精度监听器
*/
ProgressListener mProgressListener;
public RetrofitDownloader(Context context) {
this.context = context;
init();
}
public RetrofitDownloader(Context context, ProgressListener progressListener) {
this.context = context;
this.mProgressListener = progressListener;
init();
}
public RetrofitDownloader(Context context, String baseUrl, boolean browserMode, ProgressListener progressListener) {
this.context = context;
this.baseUrl = baseUrl;
this.browserMode = browserMode;
this.mProgressListener = progressListener;
init();
}
public RetrofitDownloader(Context context, Retrofit retrofit) {
this.context = context;
this.retrofit = retrofit;
}
private OkHttpClient getOkHttpClient() {
return getOkHttpClient(mProgressListener);
}
private OkHttpClient getOkHttpClient(ProgressListener listener) {
if (listener == null) {
Log.w(TAG, "getOkHttpClient: progress listener is null");
}
if (browserMode) {
return OkHttpManager.getBrowserDownloadClient(listener);
}
return OkHttpManager.getDownloadSslClient(listener);
}
public void init(OkHttpClient okHttpClient) {
downloadService = createDownloadApiService(okHttpClient);
}
public DownloadApiService createDownloadApiService(OkHttpClient client) {
String baseUrl = getAvailableBaseUrl();
return RetrofitManager.createRetrofit(baseUrl, client).create(DownloadApiService.class);
}
public String getAvailableBaseUrl() {
if (TextUtils.isEmpty(baseUrl)) {
//baseUrl = ByodApiManager.getBaseUrl(context);
baseUrl = "https://www.baidu.com";
}
return baseUrl;
}
private void init() {
init(getOkHttpClient());
}
private void init(ProgressListener progressListener) {
init(getOkHttpClient(progressListener));
}
private void ensureRetrofit() {
if (retrofit == null) {
init();
}
}
/**
* 下载
* <p>使用observer实现精度结果监听,更为常用先进
* @param url
* @param filePath
* @param observer
*/
public void download(@NonNull String url, final String filePath, final DownloadObserver observer) {
Log.i(TAG, "download2: url=" + url + ", filePath=" + filePath + ", observer=" + observer);
//通过Observable发起请求
ensureRetrofit();
Observable<ResponseBody> observable = downloadService.download(url);
observable
//指定网络请求在io后台线程中进行
.subscribeOn(Schedulers.io())
//指定doOnNext的操作在io后台线程进行
.observeOn(Schedulers.io())
.doOnNext(new Consumer<ResponseBody>() {
//doOnNext里的方法执行完毕,observer里的onNext、onError等方法才会执行。
@Override
public void accept(ResponseBody body) throws Exception {
//下载文件,通过body.byteStream()可以得到输入流, 保存到本地
InputStream inputStream = body.byteStream();
long contentLength = body.contentLength();
Log.i(TAG, "doOnNext.accept contentLength: " + contentLength);
FileDownloadImpl impl = new FileDownloadImpl(url, filePath, contentLength, observer.downloadListener);
FileHelper.writeDownloadFile(inputStream, filePath, impl);
}
})
//指定observer回调在UI主线程中进行
.observeOn(AndroidSchedulers.mainThread())
//发起请求,请求的结果先回调到doOnNext进行处理,再回调到observer中
.subscribe(observer);
}
public void download(@NonNull String url, final String filePath, final DownloadListener downloadListener) {
Log.i(TAG, "download2: url=" + url + ", filePath=" + filePath);
download(url, filePath, new DownloadObserver(url, filePath, downloadListener));
}
public static void test(Context context, String url) {
DownloadListener downloadListener = new DownloadListener() {
@Override
public void onStart(String remark) {
Log.w(TAG, "DownloadListener.onStart: " + remark);
}
@Override
public void onProgress(int progress) {
Log.w(TAG, "DownloadListener.onProgress: " + progress);
}
@Override
public void onFinished(String url, String filePath) {
Log.w(TAG, "DownloadListener.onFinished: " + url + ", filePath=" + filePath);
}
@Override
public void onFailure(Throwable throwable) {
Log.e(TAG, "onFailure: " + throwable);
}
};
RetrofitDownloader downloader = new RetrofitDownloader(context);
String filename = HttpFileUtils.getFileName(url);
String filePath = Environment.getExternalStorageDirectory() + "/Hulk/file/" + filename;
downloader.download(url, filePath, downloadListener);
}
public static void test(Context context) {
String url = "https://s.shouji.qihucdn.com/210307/1fa2b07cdca7a7f81f8c4eedff06499b/com.qihoo.appstore_300090095.apk?en=curpage%3D%26exp%3D1615781273%26from%3Dopenbox_channel_getUrl%26m2%3D%26ts%3D1615176473%26tok%3D30feb64608edefe4b78cbabe4a91d6f5%26v%3D%26f%3Dz.apk";
test(context, url);
}
}
package com.hulk.android.http.image;
import android.content.Context;
import android.graphics.Bitmap;
import android.text.TextUtils;
import android.util.Log;
import android.util.LruCache;
import android.widget.ImageView;
import com.hulk.android.http.download.DownloadListener;
import com.hulk.android.http.download.DownloadUtil;
import com.hulk.android.http.download.RetrofitDownloader;
/**
* 图片加载器
* @author: zhanghao
* @Time: 2021-03-04 20:09
*/
public class ImageLoader {
private static final String TAG = "ImageLoader";
private static class ImageLoaderHolder {
private static ImageLoader imageLoader = new ImageLoader();
public static ImageLoader get() {
return imageLoader;
}
}
private Context mContext = null;
private LruCache<String, Bitmap> mCache = ImageLoadHelper.sCache;
private ImageView mImageView;
private String mUrl = null;
private String mCacheFileName = null;
private ImageLoadCallback mCallback = null;
private int defaultImgResId = 0;
private int errorImgResId = 0;
private boolean needDownload = true;
private RetrofitDownloader mDownloader;
private String mFilename;
private ImageLoader() {
}
public static ImageLoader with(Context context) {
ImageLoader loader = ImageLoaderHolder.get();
loader.init(context);
return loader;
}
private void init(Context context) {
this.mContext = context;
}
public ImageLoader load(String url) {
this.mUrl = url;
return this;
}
public ImageLoader filePath(String filename) {
this.mFilename = filename;
return this;
}
public ImageLoader defaultImgResId(int defaultImgResId) {
this.defaultImgResId = defaultImgResId;
return this;
}
public ImageLoader errorImgResId(int errorImgResId) {
this.errorImgResId = errorImgResId;
return this;
}
public ImageLoader callback(ImageLoadCallback callback) {
this.mCallback = callback;
return this;
}
public ImageLoader cache(LruCache<String, Bitmap> cache) {
this.mCache = cache;
return this;
}
private ImageLoader needDownload(boolean needDownload) {
this.needDownload = needDownload;
return this;
}
public void into(ImageView imageView) {
this.mImageView = imageView;
boolean preloaded = preload();
if (preloaded) {
//Log.w(TAG, "into: preloaded.");
return;
}
//download();
download2();
}
private boolean canDownload() {
if (!needDownload) {
Log.w(TAG, "canDownload: Not need download.");
return false;
}
if (isDownloading()) {
Log.i(TAG, "canDownload: Downloading: " + mUrl);
return false;
}
return true;
}
private void download() {
if (!canDownload()) {
Log.w(TAG, "download: Can not download.");
return;
}
ImageLoadHelper.ensureCacheDir();
String filePath = getFilePath();
Log.i(TAG, "download: mUrl=" + mUrl + ", filePath=" + filePath);
DownloadUtil.download(mUrl, filePath, new DownloadImpl());
}
private void download2() {
if (!canDownload()) {
Log.w(TAG, "download2: Not need download.");
return;
}
if (mDownloader == null) {
mDownloader = new RetrofitDownloader(mContext);
}
ImageLoadHelper.ensureCacheDir();
String filePath = getFilePath();
Log.i(TAG, "download2: mUrl=" + mUrl + ", filePath=" + filePath);
mDownloader.download(mUrl, filePath, new DownloadImpl());
}
private boolean preload() {
String url = mUrl;
if (TextUtils.isEmpty(url)){
// 设置为默认图片
Log.w(TAG, "preload: set default Image for url is empty.");
setImageResource(errorImgResId);
onImageLoadFailed();
return true;
}
// 先从缓存中查找
Bitmap img = getCache(url);
if (img != null) {
setImageBitmap(img);
return true;
}
Bitmap bp = getImageFromLocal();
if (bp != null) {
setImageBitmap(bp);
putCache(mUrl, bp);
return true;
}
// 先设置为默认图片
setImageResource(defaultImgResId);
return false;
}
private void postDownLoad(String url, String filePath) {
if (mImageView == null) {
Log.i(TAG, "postDownLoad: mImageView is null");
return;
}
Object tag = mImageView.getTag();
if (tag instanceof String) {
url = (String) tag;
}
Bitmap image = getLocalImage(filePath);
if (image == null) {
// 设置为默认图片
Log.w(TAG, "onPostExecute: set default image for result is null");
setImageResource(errorImgResId);
onImageLoadFailed();
} else {
putCache(url, image);
setImageBitmap( image);
}
}
private void putCache(String url, Bitmap image) {
if (TextUtils.isEmpty(url) || image == null) {
return;
}
if (mCache != null) {
mCache.put(url, image);
}
}
private Bitmap getCache(String url) {
if (TextUtils.isEmpty(url)) {
return null;
}
if (mCache == null) {
return null;
}
Bitmap image = mCache.get(url);
return image;
}
private void setImageBitmap(Bitmap bitmap) {
if (bitmap != null && mImageView != null) {
mImageView.setImageBitmap(bitmap);
}
onImageLoadSuccess(bitmap);
}
private void setImageResource(int resId) {
if (resId > 0 && mImageView != null) {
mImageView.setImageResource(resId);
}
}
private void onImageLoadSuccess(Bitmap bitmap) {
if (mCallback != null){
mCallback.onImageLoadSuccess(mUrl, mImageView, bitmap);
}
}
private void onImageLoadFailed() {
if (mCallback != null){
mCallback.onImageLoadFailed(mUrl, mImageView);
}
}
private boolean isDownloading() {
return ImageLoadHelper.checkDownloading(mUrl);
}
private void removeDownloading() {
ImageLoadHelper.removeDownloading(mUrl);
}
private Bitmap getImageFromLocal() {
String filename = getFilename();
return ImageLoadHelper.getImageFromLocal(filename);
}
private Bitmap getLocalImage(String filePath) {
return ImageLoadHelper.getLocalImage(filePath);
}
private String getFilename() {
if (TextUtils.isEmpty(mFilename)) {
mFilename = ImageLoadHelper.getFileName(mUrl);
}
return mFilename;
}
private String getFilePath() {
String filename = getFilename();
return ImageLoadHelper.getCacheFilePath(filename);
}
private class DownloadImpl implements DownloadListener {
@Override
public void onStart(String remark) {
Log.w(TAG, "onStart: " + remark);
}
@Override
public void onProgress(int progress) {
Log.v(TAG, "onProgress: " + progress);
}
@Override
public void onFinished(String url, String filePath) {
Log.w(TAG, "onFinished: " + url + ",filePath: " + filePath);
postDownLoad(url, filePath);
}
@Override
public void onFailure(Throwable throwable) {
Log.e(TAG, "onFailure: " + throwable);
setImageResource(errorImgResId);
}
}
}
package com.android.hulk.androidutils;
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.hulk.android.http.download.DownloadListener;
import com.hulk.android.http.download.RetrofitDownloader;
import com.hulk.android.http.image.ImageLoadCallback;
import com.hulk.android.http.image.ImageLoadHelper;
import com.hulk.android.http.utils.HttpFileUtils;
import com.hulk.android.log.Log;
import com.hulk.android.log.RuntimeLog;
import com.hulk.util.file.PrintUtil;
import java.util.Arrays;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private static final String WRITE_EXTERNAL_STORAGE = Manifest.permission.WRITE_EXTERNAL_STORAGE;
private static final String READ_EXTERNAL_STORAGE = Manifest.permission.READ_EXTERNAL_STORAGE;
private static final String[] PERMISSIONS = {WRITE_EXTERNAL_STORAGE, READ_EXTERNAL_STORAGE};
private static final String IMAGE_URL = "https://pics5.baidu.com/feed/b8389b504fc2d56263ab63951219b5e776c66c3c.jpeg?token=e3f2b3a4806bb1a38913457e6ecb160a&s=1ECF985E4CDD0CC80687F3F10300801E";
private static final String IMAGE_URL2 = "https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fa3.att.hudong.com%2F65%2F38%2F16300534049378135355388981738.jpg&refer=http%3A%2F%2Fa3.att.hudong.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=jpeg?sec=1617537464&t=f6e8544f19d6eb754aaa057f125e4373";
private static final String IMAGE_URL3 = "https://gss0.baidu.com/70cFfyinKgQFm2e88IuM_a/forum/w%3d580/sign=9cdd7204e51190ef01fb92d7fe199df7/b13533fa828ba61eb13b6ac64334970a314e5905.jpg";
Context mContext;
ImageView img_view;
TextView msg_tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mContext = getApplicationContext();
setContentView(R.layout.activity_main2);
Log.w(TAG, "onCreate: ");
requestPermissionsIfNot();
img_view = findViewById(R.id.img_view);
msg_tv = findViewById(R.id.msg_tv);
Button download_file_view = findViewById(R.id.download_file_view);
if (download_file_view != null) {
download_file_view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
test(mContext);
}
});
}
testLoadImage();
}
private void testLoadImage() {
Button load_glide_view = findViewById(R.id.load_glide_view);
if (load_glide_view != null) {
load_glide_view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ImageLoadHelper.loadByGlide(mContext, img_view, IMAGE_URL, R.mipmap.app_default_icon);
}
});
}
Button load_task_view = findViewById(R.id.load_task_view);
if (load_task_view != null) {
load_task_view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ImageLoadHelper.loadByTask(mContext, img_view, IMAGE_URL2, R.mipmap.app_default_icon);
}
});
}
Button load_image_view = findViewById(R.id.load_image_view);
if (load_image_view != null) {
load_image_view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ImageLoadHelper.loadImage(mContext, img_view, IMAGE_URL3, R.mipmap.app_default_icon, new ImageLoadCallback() {
@Override
public void onImageLoadSuccess(String url, ImageView view, Bitmap bitmap) {
Log.w(TAG, "onImageLoadSuccess: " + view + ", bitmap=" + bitmap);
}
@Override
public void onImageLoadFailed(String url, ImageView view) {
Log.w(TAG, "onImageLoadSuccess: " + view);
}
});
}
});
}
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
}
@Override
protected void onStart() {
super.onStart();
Log.w(TAG, "onStart: ");
}
@Override
protected void onResume() {
super.onResume();
Log.w(TAG, "onResume: ");
RuntimeLog.flushAsync(TAG + ".onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.w(TAG, "onPause: ");
}
@Override
protected void onStop() {
super.onStop();
Log.w(TAG, "onStop: ");
}
@Override
protected void onDestroy() {
Log.w(TAG, "onDestroy: ");
RuntimeLog.flushAsync(TAG + ".onDestroy");
super.onDestroy();
}
protected void requestPermissionsIfNot() {
if (Build.VERSION.SDK_INT >= 23) {
if (checkSelfPermission(WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
Log.w(TAG, "requestPermissions: " + Arrays.toString(PERMISSIONS));
requestPermissions(PERMISSIONS, 1);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
Log.w(TAG, "onRequestPermissionsResult: requestCode=" + requestCode
+ ", permissions=" + Arrays.toString(permissions) + ", grantResults= " + grantResults);
}
protected void setMsg(String text, boolean append) {
if (append) {
msg_tv.append("\n" + text);
} else {
msg_tv.setText(text);
}
}
protected void setMsg(String text) {
setMsg(text, false);
}
protected void appendMsg(String text) {
setMsg(text, true);
}
public void test(Context context, String url) {
msg_tv.setTextColor(Color.BLACK);
setMsg("Downloading from url: " + url);
DownloadListener downloadListener = new DownloadListener() {
@Override
public void onStart(String remark) {
Log.w(TAG, "DownloadListener.onStart: " + remark);
appendMsg("onStart: " + remark);
}
@Override
public void onProgress(int progress) {
Log.w(TAG, "DownloadListener.onProgress: " + progress);
appendMsg("onProgress: " + progress);
}
@Override
public void onFinished(String url, String filePath) {
Log.w(TAG, "DownloadListener.onFinished: " + url + ", filePath=" + filePath);
msg_tv.append("onFinished: " + filePath);
}
@Override
public void onFailure(Throwable throwable) {
Log.e(TAG, "onFailure: " + throwable);
msg_tv.setTextColor(Color.RED);
appendMsg("onFailure: " + throwable + "\n" + PrintUtil.formatStackTrace("", throwable));
}
};
RetrofitDownloader downloader = new RetrofitDownloader(context);
String filename = "test.apk";
String filePath = Environment.getExternalStorageDirectory() + "/Hulk/file/" + filename;
downloader.download(url, filePath, downloadListener);
}
public void test(Context context) {
String url = "https://s.shouji.qihucdn.com/210307/1fa2b07cdca7a7f81f8c4eedff06499b/com.qihoo.appstore_300090095.apk?en=curpage%3D%26exp%3D1615781273%26from%3Dopenbox_channel_getUrl%26m2%3D%26ts%3D1615176473%26tok%3D30feb64608edefe4b78cbabe4a91d6f5%26v%3D%26f%3Dz.apk";
test(context, url);
}
}
Http请求结果 (对应服务器返回的json数据)
package com.hulk.android.http.conn;
/**
* Http请求结果 (对应服务器返回的json数据)
* <p> json:
* {"code":0,"msg":"Success","data":"This is response data"}
* 此处是举例返回数据, 具体的返回数据还更具真实数据确定
* @author: zhanghao
* @Time: 2021-03-04 17:35
*/
public class HttpResult<T> {
public int code = -1;
public String msg;
public String detail;
public Throwable error;
public T data = null;
//TODO 此处是举例返回数据, 具体的返回数据还更具真实数据确定
public HttpResult() {
}
public HttpResult(int code, String msg) {
this.code = code;
this.msg = msg;
}
public HttpResult(int code, String msg, String detail, Throwable error) {
this.code = code;
this.msg = msg;
this.detail = detail;
this.error = error;
}
public void setData(T data) {
this.data = data;
}
}
以上主要代码展示, 具体的代目可在参考源代码 https://download.csdn.net/download/zhanghao_Hulk/16020524 , 或者留言单独提供