OkGo源码分析


国际惯例, 从一个使用demo入手

OkGo.get(Urls.URL_DOWNLOAD)//
                .tag(this)//
                .headers("header1", "headerValue1")//
                .params("param1", "paramValue1")//
                .execute(new FileCallback("OkGo.apk") {
                    @Override
                    public void onBefore(BaseRequest request) {
                        btnFileDownload.setText("正在下载中");
                    }

                    @Override
                    public void onSuccess(File file, Call call, Response response) {
                        handleResponse(file, call, response);
                        btnFileDownload.setText("下载完成");
                    }

                    @Override
                    public void downloadProgress(long currentSize, long totalSize, float progress, long networkSpeed) {
                       
                    }

                    @Override
                    public void onError(Call call, @Nullable Response response, @Nullable Exception e) {
                        super.onError(call, response, e);
                        handleError(call, response);
                        btnFileDownload.setText("下载出错");
                    }
                });

1. 创建BaseRequest对象

baseRequest 对象是OkGo封装的一个Request基类,这里的GetRequest 是他的子类

public static GetRequest get(String url) {
    return new GetRequest(url);
}
    

对应的还有其他一系列的Request对象


OkGo源码分析_第1张图片
image_1b0he6eku19um1r5put91o1bv7d9.png-41.5kB

generateRequest是baseRequest的抽象方法,用来构造真正的okhttp的Request对象

public class GetRequest extends BaseRequest {

    public GetRequest(String url) {
        super(url);
    }

    @Override
    public RequestBody generateRequestBody() {
        return null;
    }

    @Override
    public Request generateRequest(RequestBody requestBody) {
        Request.Builder requestBuilder = HttpUtils.appendHeaders(headers);
        url = HttpUtils.createUrlFromParams(baseUrl, params.urlParamsMap);
        return requestBuilder.get().url(url).tag(tag).build();
    }
}
public BaseRequest(String url) {
        this.url = url;
        baseUrl = url;
        httpUrl = HttpUrl.parse(url);
        OkGo go = OkGo.getInstance();
        //默认添加 Accept-Language
        String acceptLanguage = HttpHeaders.getAcceptLanguage();
        if (!TextUtils.isEmpty(acceptLanguage)) headers(HttpHeaders.HEAD_KEY_ACCEPT_LANGUAGE, acceptLanguage);
        //默认添加 User-Agent
        String userAgent = HttpHeaders.getUserAgent();
        if (!TextUtils.isEmpty(userAgent)) headers(HttpHeaders.HEAD_KEY_USER_AGENT, userAgent);
        //添加公共请求参数
        if (go.getCommonParams() != null) params.put(go.getCommonParams());
        if (go.getCommonHeaders() != null) headers.put(go.getCommonHeaders());
        //添加缓存模式
        if (go.getCacheMode() != null) cacheMode = go.getCacheMode();
        cacheTime = go.getCacheTime();
        //超时重试次数
        retryCount = go.getRetryCount();
    }

2. execute执行请求

其实是调用的CacheCall.execute()

 /** 非阻塞方法,异步请求,但是回调在子线程中执行 */
    @SuppressWarnings("unchecked")
    public  void execute(AbsCallback callback) {
        mCallback = callback;
        mConverter = callback;
        new CacheCall(this).execute(callback);
    }

我们再来看看CacheCall

//防止重复execute
synchronized (this) {
            if (executed) throw new IllegalStateException("Already executed.");
            executed = true;
}
mCallback = callback;
if (mCallback == null) mCallback = new AbsCallbackWrapper<>();

//构建请求, 其实就是将baseRequest转换为okHttp的request, 调用的generateRequest, 这下明白了吧?
RequestBody requestBody = baseRequest.generateRequestBody();
final Request request = baseRequest.generateRequest(baseRequest.wrapRequestBody(requestBody));
//构建真正的okHttp的 callback
rawCall = baseRequest.generateCall(request);

接下来看下generateCall根据传入的request, 最终调用okhttp 生成Call调用对象

/** 根据当前的请求参数,生成对应的 Call 任务 */
public okhttp3.Call generateCall(Request request) {
    mRequest = request;
    if (readTimeOut <= 0 && writeTimeOut <= 0 && connectTimeout <= 0 && sslParams == null && userCookies.size() == 0) {
        return OkGo.getInstance().getOkHttpClient().newCall(request);
    } else {
        OkHttpClient.Builder newClientBuilder = OkGo.getInstance().getOkHttpClient().newBuilder();
        if (readTimeOut > 0) newClientBuilder.readTimeout(readTimeOut, TimeUnit.MILLISECONDS);
        if (writeTimeOut > 0) newClientBuilder.writeTimeout(writeTimeOut, TimeUnit.MILLISECONDS);
        if (connectTimeout > 0) newClientBuilder.connectTimeout(connectTimeout, TimeUnit.MILLISECONDS);
        if (hostnameVerifier != null) newClientBuilder.hostnameVerifier(hostnameVerifier);
        if (sslParams != null) newClientBuilder.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager);
        if (userCookies.size() > 0) OkGo.getInstance().getCookieJar().addCookies(userCookies);
        if (interceptors.size() > 0) {
            for (Interceptor interceptor : interceptors) {
                newClientBuilder.addInterceptor(interceptor);
            }
        }
        return newClientBuilder.build().newCall(request);
    }
}

最后设置请求回调

rawCall.enqueue(new okhttp3.Callback() {
    @Override
    public void onFailure(okhttp3.Call call, IOException e) {
       
    }

    @Override
    public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
        int responseCode = response.code();

        try {
            Response parseResponse = parseResponse(response);
            T data = parseResponse.body();
            //网络请求成功,保存缓存数据
            handleCache(response.headers(), data);
            //网络请求成功回调
            sendSuccessResultCallback(false, data, call, response);
        } catch (Exception e) {
            //一般为服务器响应成功,但是数据解析错误
            sendFailResultCallback(false, call, response, e);
        }
    }
});

请求成功则调用sendSuccessResultCallback
很简单, 就是最开始调用传入的new FileCallback()

/** 成功回调,发送到主线程 */
private void sendSuccessResultCallback(final boolean isFromCache, final T t, final okhttp3.Call call, final okhttp3.Response response) {
    final CacheMode cacheMode = baseRequest.getCacheMode();

    OkGo.getInstance().getDelivery().post(new Runnable() {
        @Override
        public void run() {
            if (isFromCache) {
                mCallback.onCacheSuccess(t, call);           //缓存成功回调 (UI线程)
                if (cacheMode == CacheMode.DEFAULT || cacheMode == CacheMode.REQUEST_FAILED_READ_CACHE || cacheMode == CacheMode.IF_NONE_CACHE_REQUEST) {
                    mCallback.onAfter(t, null);              //请求结束回调 (UI线程)
                }
            } else {
                mCallback.onSuccess(t, call, response);      //请求成功回调 (UI线程)
                mCallback.onAfter(t, null);                  //请求结束回调 (UI线程)
            }
        }
    });
}

再来看下FileCallback
他会创建FileConvert, 用来将返回的Response 转换为File

public FileCallback(String destFileDir, String destFileName) {
    convert = new FileConvert(destFileDir, destFileName);
    convert.setCallback(this);
}

@Override
public File convertSuccess(Response response) throws Exception {
    File file = convert.convertSuccess(response);
    response.close();
    return file;
}

其实, 这个文件下载请求, 最核心的封装就是在convertSuccess里面,也就是把这一大坨帮你封装好了。

@Override
public File convertSuccess(Response value) throws Exception {
    if (TextUtils.isEmpty(destFileDir)) destFileDir = Environment.getExternalStorageDirectory() + DM_TARGET_FOLDER;
    if (TextUtils.isEmpty(destFileName)) destFileName = HttpUtils.getNetFileName(value, value.request().url().toString());

    File dir = new File(destFileDir);
    if (!dir.exists()) dir.mkdirs();
    File file = new File(dir, destFileName);
    if (file.exists()) file.delete();

    long lastRefreshUiTime = 0;  //最后一次刷新的时间
    long lastWriteBytes = 0;     //最后一次写入字节数据

    InputStream is = null;
    byte[] buf = new byte[2048];
    FileOutputStream fos = null;
    try {
        is = value.body().byteStream();
        final long total = value.body().contentLength();
        long sum = 0;
        int len;
        fos = new FileOutputStream(file);
        while ((len = is.read(buf)) != -1) {
            sum += len;
            fos.write(buf, 0, len);

            //下载进度回调
            if (callback != null) {
                final long finalSum = sum;
                long curTime = System.currentTimeMillis();
                //每200毫秒刷新一次数据
                if (curTime - lastRefreshUiTime >= 200 || finalSum == total) {
                    //计算下载速度
                    long diffTime = (curTime - lastRefreshUiTime) / 1000;
                    if (diffTime == 0) diffTime += 1;
                    long diffBytes = finalSum - lastWriteBytes;
                    final long networkSpeed = diffBytes / diffTime;
                    OkGo.getInstance().getDelivery().post(new Runnable() {
                        @Override
                        public void run() {
                            callback.downloadProgress(finalSum, total, finalSum * 1.0f / total, networkSpeed);   //进度回调的方法
                        }
                    });

                    lastRefreshUiTime = System.currentTimeMillis();
                    lastWriteBytes = finalSum;
                }
            }
        }
        fos.flush();
        return file;
    } finally {
        try {
            if (is != null) is.close();
        } catch (IOException e) {
            OkLogger.e(e);
        }
        try {
            if (fos != null) fos.close();
        } catch (IOException e) {
            OkLogger.e(e);
        }
    }
}

okserver的分析

okserver主要是比okgo多了断点下载和下载状态的管理
获取DownloadService单例

 downloadManager = DownloadService.getDownloadManager();
 /** start 方式开启服务,保存全局的下载管理对象 */
public static DownloadManager getDownloadManager() {
    Context context = OkGo.getContext();
    if (!DownloadService.isServiceRunning(context)) context.startService(new Intent(context, DownloadService.class));
    if (DownloadService.DOWNLOAD_MANAGER == null) DownloadService.DOWNLOAD_MANAGER = DownloadManager.getInstance();
    return DOWNLOAD_MANAGER;
}

设置同时下载线程池数量

downloadManager.getThreadPool().setCorePoolSize(progress);

创建request

 GetRequest request = OkGo.get(apkModel.getUrl())//
                            .headers("headerKey1", "headerValue1")//
                            .headers("headerKey2", "headerValue2")//
                            .params("paramKey1", "paramValue1")//
                            .params("paramKey2", "paramValue2");
                    downloadManager.addTask(apkModel.getUrl(), request, null);

addTask其实就是添加一个ayncTask的子类到downloadManager的队列中

/**
 * 添加一个下载任务
 *
 * @param request   下载的网络请求
 * @param listener  下载监听
 * @param isRestart 是否重新开始下载
 */
private void addTask(String fileName, String taskTag, BaseRequest request, DownloadListener listener, boolean isRestart) {
    DownloadInfo downloadInfo = getDownloadInfo(taskTag);
    if (downloadInfo == null) {
        downloadInfo = new DownloadInfo();
        downloadInfo.setUrl(request.getBaseUrl());
        downloadInfo.setTaskKey(taskTag);
        downloadInfo.setFileName(fileName);
        downloadInfo.setRequest(request);
        downloadInfo.setState(DownloadManager.NONE);
        downloadInfo.setTargetFolder(mTargetFolder);
        DownloadDBManager.INSTANCE.replace(downloadInfo);
        mDownloadInfoList.add(downloadInfo);
    }
    //无状态,暂停,错误才允许开始下载
    if (downloadInfo.getState() == DownloadManager.NONE || downloadInfo.getState() == DownloadManager.PAUSE || downloadInfo.getState() == DownloadManager.ERROR) {
        //构造即开始执行
        DownloadTask downloadTask = new DownloadTask(downloadInfo, isRestart, listener);
        downloadInfo.setTask(downloadTask);
    }
}

下载方法是核心
断点下载主要是读取mDownloadInfo.getDownloadLength(),然后在http的header加入RANGE,以此来实现断点下载,任务的暂停,停止,等状态都在doInBackground里面处理,其实就是跟AsyncTask的状态处理一样。

/** 一旦该方法执行,意味着开始下载了 */
@Override
protected DownloadInfo doInBackground(Void... params) {
    if (isCancelled()) return mDownloadInfo;
    OkLogger.e("doInBackground:" + mDownloadInfo.getFileName());
    mPreviousTime = System.currentTimeMillis();
    mDownloadInfo.setNetworkSpeed(0);
    mDownloadInfo.setState(DownloadManager.DOWNLOADING);
    postMessage(null, null);

    long startPos = mDownloadInfo.getDownloadLength();
    Response response;
    try {
        //设置request的 header range
        response = mDownloadInfo.getRequest().headers("RANGE", "bytes=" + startPos + "-").execute();
    } catch (IOException e) {
        e.printStackTrace();
        mDownloadInfo.setNetworkSpeed(0);
        mDownloadInfo.setState(DownloadManager.ERROR);
        postMessage("网络异常", e);
        return mDownloadInfo;
    }
    //构建下载文件路径,如果有设置,就用设置的,否者就自己创建
    String url = mDownloadInfo.getUrl();
    String fileName = mDownloadInfo.getFileName();
    if (TextUtils.isEmpty(fileName)) {
        fileName = HttpUtils.getNetFileName(response, url);
        mDownloadInfo.setFileName(fileName);
    }
    if (TextUtils.isEmpty(mDownloadInfo.getTargetPath())) {
        File file = new File(mDownloadInfo.getTargetFolder(), fileName);
        mDownloadInfo.setTargetPath(file.getAbsolutePath());
    }
    //检查文件有效性,文件大小大于总文件大小
    if (startPos > mDownloadInfo.getTotalLength()) {
        mDownloadInfo.setNetworkSpeed(0);
        mDownloadInfo.setState(DownloadManager.ERROR);
        postMessage("断点文件异常,需要删除后重新下载", null);
        return mDownloadInfo;
    }
    if (startPos == mDownloadInfo.getTotalLength() && startPos > 0) {
        mDownloadInfo.setProgress(1.0f);
        mDownloadInfo.setNetworkSpeed(0);
        mDownloadInfo.setState(DownloadManager.FINISH);
        postMessage(null, null);
        return mDownloadInfo;
    }
    //设置断点写文件
    File file = new File(mDownloadInfo.getTargetPath());
    ProgressRandomAccessFile randomAccessFile;
    try {
        randomAccessFile = new ProgressRandomAccessFile(file, "rw", startPos);
        randomAccessFile.seek(startPos);
    } catch (Exception e) {
        e.printStackTrace();
        mDownloadInfo.setNetworkSpeed(0);
        mDownloadInfo.setState(DownloadManager.ERROR);
        postMessage("没有找到已存在的断点文件", e);
        return mDownloadInfo;
    }
    //获取流对象,准备进行读写文件
    long totalLength = response.body().contentLength();
    if (mDownloadInfo.getTotalLength() == 0) {
        mDownloadInfo.setTotalLength(totalLength);
    }
    InputStream is = response.body().byteStream();
    //读写文件流
    try {
        download(is, randomAccessFile);
    } catch (IOException e) {
        e.printStackTrace();
        mDownloadInfo.setNetworkSpeed(0);
        mDownloadInfo.setState(DownloadManager.ERROR);
        postMessage("文件读写异常", e);
        return mDownloadInfo;
    }

    //循环结束走到这里,a.下载完成     b.暂停      c.判断是否下载出错
    if (isCancelled()) {
        OkLogger.e("state: 暂停 " + mDownloadInfo.getState());
        mDownloadInfo.setNetworkSpeed(0);
        if (isPause) mDownloadInfo.setState(DownloadManager.PAUSE); //暂停
        else mDownloadInfo.setState(DownloadManager.NONE);          //停止
        postMessage(null, null);
    } else if (file.length() == mDownloadInfo.getTotalLength() && mDownloadInfo.getState() == DownloadManager.DOWNLOADING) {
        mDownloadInfo.setNetworkSpeed(0);
        mDownloadInfo.setState(DownloadManager.FINISH); //下载完成
        postMessage(null, null);
    } else if (file.length() != mDownloadInfo.getDownloadLength()) {
        mDownloadInfo.setNetworkSpeed(0);
        mDownloadInfo.setState(DownloadManager.ERROR); //由于不明原因,文件保存有误
        postMessage("未知原因", null);
    }
    return mDownloadInfo;
}

在看ProgressRandomAccessFile写文件
保存已下载的大小到DownloadLength属性

 //已下载大小
long downloadLength = lastDownloadLength + count;
curDownloadLength += count;
lastDownloadLength = downloadLength;
//这里会将以下载的大小保存到DownloadLength属性中
mDownloadInfo.setDownloadLength(downloadLength);

//计算下载速度
long totalTime = (System.currentTimeMillis() - mPreviousTime) / 1000;
if (totalTime == 0) {
    totalTime += 1;
}
long networkSpeed = curDownloadLength / totalTime;
mDownloadInfo.setNetworkSpeed(networkSpeed);

//下载进度
float progress = downloadLength * 1.0f / mDownloadInfo.getTotalLength();
mDownloadInfo.setProgress(progress);
long curTime = System.currentTimeMillis();

//每200毫秒刷新一次数据
if (curTime - lastRefreshUiTime >= 200 || progress == 1.0f) {
    postMessage(null, null);
    lastRefreshUiTime = System.currentTimeMillis();
}

addTask 最后一个参数为isRestart, 是否重新下载, 如果是true,则会删除临时文件

/**
     * 添加一个下载任务
     *
     * @param request   下载的网络请求
     * @param listener  下载监听
     * @param isRestart 是否重新开始下载
     */
    private void addTask(String fileName, String taskTag, BaseRequest request, DownloadListener listener, boolean isRestart) {

//如果是重新下载,需要删除临时文件
if (isRestartTask) {
    HttpUtils.deleteFile(mDownloadInfo.getTargetPath());
    mDownloadInfo.setProgress(0);
    mDownloadInfo.setDownloadLength(0);
    mDownloadInfo.setTotalLength(0);
    isRestartTask = false;
}

你可能感兴趣的:(OkGo源码分析)