美文网首页Android收藏集面试题高级UI
Android 版本更新(对话框和通知栏)下载和进度条下载(适合

Android 版本更新(对话框和通知栏)下载和进度条下载(适合

作者: 总会颠沛流离 | 来源:发表于2019-07-31 10:47 被阅读76次

    成年人的世界:

    1:在成年人的世界里,是没有“崩溃”这个选项的。

    2:他们需要小心翼翼地发泄,精打细算地缓解,并在最短的时间范围内恢复到正常。

    3:他们不会在真正的大事面前倒下,因为知道自己身后没有人,或者,身后有人。他们只不过是在平淡的琐事中感到悲伤与愤怒。

    效果图:

    图一:


    超级截屏_20190731_094936.png

    图二:


    超级截屏_20190731_095002.png

    图三:


    超级截屏_20190731_095036.png

    图四:

    image.png

    虽然代码很多, 我会为大家依次粘贴(按包粘贴)

    项目目录:


    image.png

    依赖:
    //retrofit2 + rxjava2 + okhttp3
    implementation 'com.squareup.retrofit2:retrofit:2.4.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.9.0'
    implementation 'io.reactivex.rxjava2:rxjava:2.1.12'
    implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
    implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
    implementation 'com.squareup.retrofit2:converter-scalars:2.4.0'

    第一步:exception包

    HttpTimeException类
    /**
     * 自定义错误信息,统一处理返回处理
     */
    public class HttpTimeException extends RuntimeException {
    
    public static final int NO_DATA = 0x2;
    
    public HttpTimeException(int resultCode) {
        this(getApiExceptionMessage(resultCode));
    }
    
    public HttpTimeException(String detailMessage) {
        super(detailMessage);
    }
    
    /**
     * 转换错误数据
     *
     * @param code 错误吗
     * @return 错误信息
     */
    private static String getApiExceptionMessage(int code) {
        String message;
        switch (code) {
            case NO_DATA:
                message = "无数据";
                break;
            default:
                message = "error";
                break;
        }
        return message;
    }
    }
    
    RetryWhenNetworkException 类
     /**
     * retry条件
     */
    public class RetryWhenNetworkException implements Function<Observable<? extends Throwable>, ObservableSource<?>> {
    
    //    retry次数
    private int count = 3;
    //    延迟
    private long delay = 3000;
    //    叠加延迟
    private long increaseDelay = 3000;
    
    public RetryWhenNetworkException() {
    }
    
    public RetryWhenNetworkException(int count, long delay) {
        this.count = count;
        this.delay = delay;
    }
    
    public RetryWhenNetworkException(int count, long delay, long increaseDelay) {
        this.count = count;
        this.delay = delay;
        this.increaseDelay = increaseDelay;
    }
    
    @Override
    public Observable<?> apply(Observable<? extends Throwable> input) {
        return input.zipWith(Observable.range(1, count + 1), new BiFunction<Throwable, Integer, Wrapper>() {
            @Override
            public Wrapper apply(Throwable throwable, Integer integer) {
                return new Wrapper(throwable, integer);
            }
        }).flatMap(new io.reactivex.functions.Function<Wrapper, ObservableSource<?>>() {
            @Override
            public ObservableSource<?> apply(Wrapper wrapper) {
                if ((wrapper.throwable instanceof ConnectException
                        || wrapper.throwable instanceof SocketTimeoutException
                        || wrapper.throwable instanceof TimeoutException)
                        && wrapper.index < count + 1) { //如果超出重试次数也抛出错误,否则默认是会进入onCompleted
                    Log.e("tag", "retry---->" + wrapper.index);
                    return Observable.timer(delay + (wrapper.index - 1) * increaseDelay, TimeUnit.MILLISECONDS);
                }
                return Observable.error(wrapper.throwable);
            }
        });
    }
    
    private class Wrapper {
        private int index;
        private Throwable throwable;
    
        Wrapper(Throwable throwable, int index) {
            this.index = index;
            this.throwable = throwable;
        }
    }
    
    }
    

    第二步:http 包

    HttpService类
     /**
     * service统一接口数据
     */
    public interface HttpService {
    
    //断点续传下载接口
    @Streaming//大文件需要加入这个判断,防止下载过程中写入到内存中
    @Headers("Content-type:application/octet-stream")
    @GET
    Observable<ResponseBody> download(@Header("RANGE") String start, @Url String url);
    }
    

    第三步 httpdowonload包

    DownloadInterceptor类
        /**
       * 成功回调处理
       * Created by pc12 on 2019/7/31.
       */
    
      public class DownloadInterceptor implements Interceptor {
    
    private DownloadProgressListener listener;
    
    public DownloadInterceptor(DownloadProgressListener listener) {
        this.listener = listener;
    }
    
    @Override
    public Response intercept(@NonNull Chain chain) throws IOException {
        Response originalResponse = chain.proceed(chain.request());
        return originalResponse.newBuilder()
                .body(new DownloadResponseBody(originalResponse.body(), listener))
                .build();
    }
    }
    
    DownloadProgressListener 类
      /**
     * 成功回调处理
     * Created by pc12 on 2019/7/31.
     */
    
    public interface DownloadProgressListener {
    /**
     * 下载进度
     * @param read 进度
     * @param count 总长度
     */
    void update(long read, long count, boolean done);
      }
    
    DownloadResponseBody 类
      /**
     * 自定义进度的body
     * Created by pc12 on 2019/7/31.
     */
    
    public class DownloadResponseBody extends ResponseBody {
    
    private ResponseBody responseBody;
    private DownloadProgressListener progressListener;
    private BufferedSource bufferedSource;
    
    DownloadResponseBody(ResponseBody responseBody, DownloadProgressListener progressListener) {
        this.responseBody = responseBody;
        this.progressListener = progressListener;
    }
    
    @Override
    public MediaType contentType() {
        return responseBody.contentType();
    }
    
    @Override
    public long contentLength() {
        return responseBody.contentLength();
    }
    
    @Override
    public BufferedSource source() {
        if (bufferedSource == null) {
            bufferedSource = Okio.buffer(source(responseBody.source()));
        }
        return bufferedSource;
    }
    
    private Source source(Source source) {
        return new ForwardingSource(source) {
            long totalBytesRead = 0L;
    
            @Override
            public long read(@NonNull Buffer sink, long byteCount) throws IOException {
                long bytesRead = super.read(sink, byteCount);
                // read() returns the number of bytes read, or -1 if this source is exhausted.
                totalBytesRead += bytesRead != -1 ? bytesRead : 0;
                if (null != progressListener) {
                    progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
                }
                return bytesRead;
            }
        };
    
    }
    }
    
    DownInfo类
      public class DownInfo {
    //存储位置
    private String savePath;
    //下载url
    private String url;
    //基础url
    private String baseUrl;
    //文件总长度
    private long countLength;
    //下载长度
    private long readLength;
    //下载唯一的HttpService
    private HttpService service;
    //回调监听
    //private HttpProgressOnNextListener listener;
    //超时设置
    private int DEFAULT_TIMEOUT = 6;
    //下载状态
    private DownState state;
    //public DownInfo(String url,HttpProgressOnNextListener listener) {
    //setUrl(url);
    //setBaseUrl(getBasUrl(url));
    // setListener(listener);
    //}
    
    public DownState getState() {
        return state;
    }
    
    public void setState(DownState state) {
        this.state = state;
    }
    
    public DownInfo(String url) {
        setUrl(url);
        setBaseUrl(getBasUrl(url));
    }
    
    public DownInfo() {
    
    }
    
    public int getConnectionTime() {
        return DEFAULT_TIMEOUT;
    }
    
    public void setConnectionTime(int DEFAULT_TIMEOUT) {
        this.DEFAULT_TIMEOUT = DEFAULT_TIMEOUT;
    }
    
    //public HttpProgressOnNextListener getListener() {
    //return listener;
    //}
    //
    //public void setListener(HttpProgressOnNextListener listener) {
    //this.listener = listener;
    //}
    
    public HttpService getService() {
        return service;
    }
    
    public void setService(HttpService service) {
        this.service = service;
    }
    
    public String getUrl() {
        return url;
    }
    
    public void setUrl(String url) {
        this.url = url;
        setBaseUrl(getBasUrl(url));
    }
    
    public String getSavePath() {
        return savePath;
    }
    
    public void setSavePath(String savePath) {
        this.savePath = savePath;
    }
    
    public String getBaseUrl() {
        return baseUrl;
    }
    
    public void setBaseUrl(String baseUrl) {
        this.baseUrl = baseUrl;
    }
    
    public long getCountLength() {
        return countLength;
    }
    
    public void setCountLength(long countLength) {
        this.countLength = countLength;
    }
    
    
    public long getReadLength() {
        return readLength;
    }
    
    public void setReadLength(long readLength) {
        this.readLength = readLength;
    }
    
    /**
     * 读取baseurl
     */
    private String getBasUrl(String url) {
        String head = "";
        int index = url.indexOf("://");
        if (index != -1) {
            head = url.substring(0, index + 3);
            url = url.substring(index + 3);
        }
        index = url.indexOf("/");
        if (index != -1) {
            url = url.substring(0, index + 1);
        }
        return head + url;
    }
    }
    

    DownState类

    /**
     * 下载状态
     */
    
    public enum  DownState {
    START,
    DOWN,
    PAUSE,
    STOP,
    ERROR,
    FINISH,
    
    }
    
    HttpDownManager类
    /**
     * http下载处理类
     * Created by pc12 on 2019/7/31.
     */
    
    public class HttpDownManager {
    //记录下载数据
    private Set<DownInfo> downInfos;
    //回调sub队列
    private HashMap<String, ProgressDownSubscriber> subMap;
    //进度监听队列
    private HashMap<String, HttpProgressOnNextListener<DownInfo>> mProgressListenerHashMap;
    
    //单利对象
    private volatile static HttpDownManager INSTANCE;
    
    private HttpDownManager() {
        downInfos = new HashSet<>();
        subMap = new HashMap<>();
        mProgressListenerHashMap = new HashMap<>();
    }
    
    /**
     * 获取单例
     */
    public static HttpDownManager getInstance() {
        if (INSTANCE == null) {
            synchronized (HttpDownManager.class) {
                if (INSTANCE == null) {
                    INSTANCE = new HttpDownManager();
                }
            }
        }
        return INSTANCE;
    }
    
    /**
     * 继续下载
     */
    public void continueDownload(DownInfo downInfo) {
        //根据下载的url获取到进度监听队列中的监听器
        HttpProgressOnNextListener<DownInfo> httpProgressOnNextListener = mProgressListenerHashMap.get(downInfo.getUrl());
        if (httpProgressOnNextListener != null) {
            startDown(downInfo, httpProgressOnNextListener);
        }
    }
    
    
    /**
     * 开始下载
     */
    public void startDown(final DownInfo info, HttpProgressOnNextListener<DownInfo> httpProgressOnNextListener) {
        //正在下载返回异常信息
        if (info.getState() == DownState.DOWN) {
            httpProgressOnNextListener.onError(new Exception("正在下载中"));
            return;
        }
        //添加回调处理类
        ProgressDownSubscriber<DownInfo> subscriber = new ProgressDownSubscriber<>(info, httpProgressOnNextListener);
        //将监听进度的器添加到队列中,以便于继续下载时获取
        mProgressListenerHashMap.put(info.getUrl(), httpProgressOnNextListener);
        //记录回调sub
        subMap.put(info.getUrl(), subscriber);
        //获取service,多次请求公用一个service
        HttpService httpService;
        //设置文件的状态为下载
        info.setState(DownState.START);
        if (downInfos.contains(info)) {
            httpService = info.getService();
        } else {
            DownloadInterceptor interceptor = new DownloadInterceptor(subscriber);
            OkHttpClient.Builder builder = new OkHttpClient.Builder();
            //手动创建一个OkHttpClient并设置超时时间
            builder.connectTimeout(info.getConnectionTime(), TimeUnit.SECONDS);
            builder.addInterceptor(interceptor);
    
            Retrofit retrofit = new Retrofit.Builder()
                    .client(builder.build())
                    .addConverterFactory(GsonConverterFactory.create())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .baseUrl(info.getBaseUrl())
                    .build();
            httpService = retrofit.create(HttpService.class);
            info.setService(httpService);
            downInfos.add(info);
        }
        //得到rx对象-上一次下载的位置开始下载
        httpService.download("bytes=" + info.getReadLength() + "-", info.getUrl())
                //指定线程
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                //失败后的retry配置
                .retryWhen(new RetryWhenNetworkException())
                .map(new Function<ResponseBody, DownInfo>() {
                    @Override
                    public DownInfo apply(ResponseBody responseBody) {
                        try {
                            writeCache(responseBody, new File(info.getSavePath()), info);
                        } catch (IOException e) {
                            //失败抛出异常
                            throw new HttpTimeException(e.getMessage());
                        }
                        return info;
                    }
                })//回调线程
                .observeOn(AndroidSchedulers.mainThread())
                //数据回调
                .subscribe(subscriber);
    }
    
    
    /**
     * 停止下载
     */
    public void stopDown(DownInfo info) {
        if (info == null) return;
        info.setState(DownState.STOP);
        if (subMap.containsKey(info.getUrl())) {
            ProgressDownSubscriber subscriber = subMap.get(info.getUrl());
            subscriber.unSubscribe();
            subMap.remove(info.getUrl());
        }
    }
    
    
    /**
     * 删除
     */
    @SuppressWarnings("unused")
    public void deleteDown(DownInfo info) {
        stopDown(info);
    }
    
    
    /**
     * 暂停下载
     */
    public void pause(DownInfo info) {
        if (info == null) return;
        info.setState(DownState.PAUSE);
        if (subMap.containsKey(info.getUrl())) {
            ProgressDownSubscriber subscriber = subMap.get(info.getUrl());
            subscriber.unSubscribe();
            subMap.remove(info.getUrl());
        }
    }
    
    /**
     * 停止全部下载
     */
    @SuppressWarnings("unused")
    public void stopAllDown() {
        for (DownInfo downInfo : downInfos) {
            stopDown(downInfo);
        }
        subMap.clear();
        downInfos.clear();
    }
    
    /**
     * 暂停全部下载
     */
    @SuppressWarnings("unused")
    public void pauseAll() {
        for (DownInfo downInfo : downInfos) {
            pause(downInfo);
        }
        subMap.clear();
        downInfos.clear();
    }
    
    
    /**
     * 返回全部正在下载的数据
     */
    @SuppressWarnings("unused")
    public Set<DownInfo> getDownInfos() {
        return downInfos;
    }
    
    
    /**
     * 写入文件
     */
    private void writeCache(ResponseBody responseBody, File file, DownInfo info) throws IOException {
        if (!file.getParentFile().exists()) {
            boolean bol = file.getParentFile().mkdirs();
            if (!bol) {
                Log.e("TAG", "文件创建失败");
            }
        }
        long allLength;
        if (info.getCountLength() == 0) {
            allLength = responseBody.contentLength();
        } else {
            allLength = info.getCountLength();
        }
        FileChannel channelOut;
        RandomAccessFile randomAccessFile;
        randomAccessFile = new RandomAccessFile(file, "rwd");
        channelOut = randomAccessFile.getChannel();
        MappedByteBuffer mappedBuffer = channelOut.map(FileChannel.MapMode.READ_WRITE,
                info.getReadLength(), allLength - info.getReadLength());
        byte[] buffer = new byte[1024 * 8];
        int len;
        while ((len = responseBody.byteStream().read(buffer)) != -1) {
            mappedBuffer.put(buffer, 0, len);
        }
        responseBody.byteStream().close();
        channelOut.close();
        randomAccessFile.close();
    }
    
    }
    

    ProgressDownSubscriberl类

        /**
       * 用于在Http请求开始时,自动显示一个ProgressDialog
       * 在Http请求结束是,关闭ProgressDialog
       * 调用者自己对请求数据进行处理
       */
      public class ProgressDownSubscriber<T> implements Observer<T>, DownloadProgressListener {
    //弱引用结果回调
    private WeakReference<HttpProgressOnNextListener<T>> mHttpProgressOnNextListener;
    
    /*下载数据*/
    private DownInfo downInfo;
    
    private Disposable mDisposable;
    
    public ProgressDownSubscriber(DownInfo downInfo, HttpProgressOnNextListener<T> httpProgressOnNextListener) {
        this.mHttpProgressOnNextListener = new WeakReference<>(httpProgressOnNextListener);
        this.downInfo = downInfo;
    }
    
    public void unSubscribe() {
        if (mDisposable != null) {
            mDisposable.dispose();
        }
    }
    
    
    @Override
    public void onSubscribe(Disposable disposable) {
        mDisposable = disposable;
    }
    
    @Override
    public void onNext(T t) {
        if (mHttpProgressOnNextListener.get() != null) {
            mHttpProgressOnNextListener.get().onNext(t);
        }
    }
    
    /**
     * 对错误进行统一处理
     * 隐藏ProgressDialog
     *
     * @param e 异常
     */
    @Override
    public void onError(Throwable e) {
        mDisposable.dispose();
        /*停止下载*/
        HttpDownManager.getInstance().stopDown(downInfo);
        if (mHttpProgressOnNextListener.get() != null) {
            mHttpProgressOnNextListener.get().onError(e);
        }
        downInfo.setState(DownState.ERROR);
    }
    
    /**
     * 完成,隐藏ProgressDialog
     */
    @Override
    public void onComplete() {
        mDisposable.dispose();
        if (mHttpProgressOnNextListener.get() != null) {
            mHttpProgressOnNextListener.get().onComplete();
        }
        downInfo.setState(DownState.FINISH);
    }
    
    @SuppressLint("CheckResult")
    @Override
    public void update(long read, long count, boolean done) {
        if (downInfo.getCountLength() > count) {
            read = downInfo.getCountLength() - count + read;
        } else {
            downInfo.setCountLength(count);
        }
        downInfo.setReadLength(read);
        if (mHttpProgressOnNextListener.get() != null) {
            /*接受进度消息,造成UI阻塞,如果不需要显示进度可去掉实现逻辑,减少压力*/
            Observable.just(read).observeOn(AndroidSchedulers.mainThread())
                    .subscribe(new Consumer<Long>() {
                        @Override
                        public void accept(Long aLong) {
                            /*如果暂停或者停止状态延迟,不需要继续发送回调,影响显示*/
                            if (downInfo.getState() == DownState.PAUSE || downInfo.getState() == DownState.STOP)
                                return;
                            downInfo.setState(DownState.DOWN);
                            mHttpProgressOnNextListener.get().updateProgress(aLong, downInfo.getCountLength());
                        }
                    });
        }
    }
    }
    

    第四步listener 包

    HttpProgressOnNextListener类

    /**
     * 下载过程中的回调处理
     */
    public abstract class HttpProgressOnNextListener<T> {
    
      /**
     * 成功后回调方法
     *
     * @param t 下载成功之后文件信息
     */
    public abstract void onNext(T t);
    
    
    /**
     * 完成下载
     * 主动调用,更加灵活
     */
    public void onComplete(){
    
    }
    
    
    /**
     * 下载进度
     *
     * @param readLength  当前下载进度
     * @param countLength 文件总长度
     */
    public abstract void updateProgress(long readLength, long countLength);
    
    /**
     * 失败或者错误方法
     * 主动调用,更加灵活
     *
     * @param e 下载失败异常
     */
    public void onError(Throwable e) {
    
    }
    
    
    
    
    
    
      ///**
      //* 开始下载
      //  */
      //public abstract void onStart();
    
      / /**
     //  * 暂停下载
    //  */
    // public void onPuase() {
    //
    // }
    //
    // /**
    //  * 停止下载销毁
    //  */
    // public void onStop() {
    //
    // }
      }
    

    第五步:entity包

    VersionInfo 类
    /**
     * 版本更新实体类
     */
    public class VersionInfo implements Serializable {
    
    private String downloadUrl;
    private Integer versionCode;
    private String versionName;
    private String updateMessage;
    
    public String getDownloadUrl() {
        return downloadUrl;
    }
    
    public void setDownloadUrl(String downloadUrl) {
        this.downloadUrl = downloadUrl;
    }
    
    public Integer getVersionCode() {
        return versionCode;
    }
    
    public void setVersionCode(Integer versionCode) {
        this.versionCode = versionCode;
    }
    
    public String getVersionName() {
        return versionName;
    }
    
    public void setVersionName(String versionName) {
        this.versionName = versionName;
    }
    
    public String getUpdateMessage() {
        return updateMessage;
    }
    
    public void setUpdateMessage(String updateMessage) {
        this.updateMessage = updateMessage;
    }
    }
    

    第六步:request 包

    ApiService 类

     public interface ApiService {
    
    /**
     * 版本检测
     */
    @GET(Constants.UPDATE_URL)
    Observable<Response<VersionInfo>> checkVersion();
    
      }
    
    OkHttpUtils 类
        public class OkHttpUtils {
    /**
     * okhttp
     */
    private static OkHttpClient okHttpClient;
    
    /**
     * Retrofit
     */
    private static Retrofit retrofit;
    
    /**
     * 获取Retrofit的实例
     *
     * @return retrofit
     */
    public static Retrofit getRetrofit() {
        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl("https://raw.githubusercontent.com/wj576038874/mvp-rxjava-retrofit-okhttp/master/")
                    .addConverterFactory(GsonConverterFactory.create())
                    .client(getOkHttpClient())
                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                    .build();
        }
        return retrofit;
    }
    
    private static OkHttpClient getOkHttpClient() {
        if (okHttpClient == null) {
            OkHttpClient.Builder builder = new OkHttpClient.Builder();
            builder.connectTimeout(15 * 1000, TimeUnit.SECONDS);
            builder.readTimeout(15 * 1000, TimeUnit.MILLISECONDS);//超时时间
            okHttpClient = builder.build();
        }
        return okHttpClient;
    }
    }
    
    RequestSubscriber类
    /**
     * 请求被订阅者
     * @param <T>
     */
    public abstract class RequestSubscriber<T> implements Observer<T> {
    
    /**
     * 定义一个请求成功的抽象方法 子类必须实现并在实现中进行处理服务器返回的数据
     *
     * @param t 服务器返回的数据
     */
    protected abstract void onSuccess(T t);
    
    /**
     * 定义一个请求失败的抽象方法 子类必须实现并在实现中进行服务器返回数据的处理
     *
     * @param msg 服务器返回的错误信息
     */
    protected abstract void onFailure(String msg);
    
    @Override
    public void onSubscribe(Disposable d) {
    
    }
    
    @Override
    public void onNext(T t) {
        /*
         * 请求成功将数据发出去
         */
        onSuccess(t);
    }
    
    @Override
    public void onError(Throwable e) {
        String msg;
        if (e instanceof SocketTimeoutException) {
            msg = "请求超时。请稍后重试!";
        } else if (e instanceof ConnectException) {
            msg = "请求超时。请稍后重试!";
        } else {
            msg = "请求未能成功,请稍后重试!";
        }
        if (!TextUtils.isEmpty(msg)) {
            onFailure(msg);
        }
    }
    
    @Override
    public void onComplete() {
    
    }
    }
    

    第七步: service 包

    DownloadService类
      /**
     * 下载服务
     */
    public class DownloadService extends Service {
    
    private DownInfo downInfo;
    private int oldProgress = 0;
    private NotificationHelper notificationHelper;
    
    @Override
    public void onCreate() {
        super.onCreate();
        downInfo = new DownInfo();
        notificationHelper = new NotificationHelper(this);
    }
    
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        assert intent != null;
        String urlStr = intent.getStringExtra(Constants.APK_DOWNLOAD_URL);
        downInfo.setUrl(urlStr);
        File dir = StorageUtils.getExternalCacheCustomDirectory(this);
      //File dir = StorageUtils.getExternalCacheDirectory(this);
      //File dir = StorageUtils.getCacheDirectory(this);
        String apkName = urlStr.substring(urlStr.lastIndexOf("/") + 1, urlStr.length());
        File apkFile = new File(dir, apkName);
        downInfo.setSavePath(apkFile.getAbsolutePath());
        downLoadFile();
        return super.onStartCommand(intent, flags, startId);
    }
    
    
    private void downLoadFile() {
        HttpDownManager.getInstance().startDown(downInfo, new HttpProgressOnNextListener<DownInfo>() {
            @Override
            public void onNext(DownInfo downInfo) {
                //收起通知栏
                NotificationBarUtil.setNotificationBarVisibility(DownloadService.this, false);
                //安装
                ApkUtils.installAPk(DownloadService.this, new File(downInfo.getSavePath()));
            }
    
            @Override
            public void onComplete() {
                notificationHelper.cancel();
                stopSelf();
            }
    
            @Override
            public void updateProgress(long readLength, long countLength) {
                int progress = (int) (readLength * 100 / countLength);
                // 如果进度与之前进度相等,则不更新,如果更新太频繁,否则会造成界面卡顿
                if (progress != oldProgress) {
                    notificationHelper.updateProgress(progress);
                }
                oldProgress = progress;
            }
    
            @Override
            public void onError(Throwable e) {
                Toast.makeText(DownloadService.this, e.getMessage(), Toast.LENGTH_SHORT).show();
            }
        });
    }
    
    
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    }
    

    第八步:utils 包

    ApkUtils 类

     public class ApkUtils {
    
    /**
     * 安装apk
     * @param context context
     * @param apkFile 安装文件
     */
    public static void installAPk(Context context, File apkFile) {
        Intent installAPKIntent = getApkInStallIntent(context, apkFile);
        context.startActivity(installAPKIntent);
    
    }
    
    /**
     *  获取安装文件意图
     * @param context context
     * @param apkFile 安装文件
     * @return 安装意图
     */
    private static Intent getApkInStallIntent(Context context, File apkFile) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    
        if (Build.VERSION.SDK_INT > Build.VERSION_CODES.M) {
            Uri uri = FileProvider.getUriForFile(context, "com.winfo.update.provider", apkFile);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(uri, "application/vnd.android.package-archive");
        } else {
            Uri uri = getApkUri(apkFile);
            intent.setDataAndType(uri, "application/vnd.android.package-archive");
        }
        return intent;
    }
    
    /**
     * 获取安装文件的Uri
     * @param apkFile 安装文件
     * @return Uri
     */
    private static Uri getApkUri(File apkFile) {
        //如果没有设置 SDCard 写权限,或者没有 SDCard,apk 文件保存在内存中,需要授予权限才能安装
        try {
            String[] command = {"chmod", "777", apkFile.toString()};
            ProcessBuilder builder = new ProcessBuilder(command);
            builder.start();
        } catch (IOException ignored) {
        }
        return Uri.fromFile(apkFile);
    }
    
    }
    
    AppUtils 类
        public class AppUtils {
    
    /**
     * 获取当前应用的版本号
     * @param mContext context
     * @return 版本号
     */
    public static int getVersionCode(Context mContext) {
        if (mContext != null) {
            try {
                return mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionCode;
            } catch (PackageManager.NameNotFoundException ignored) {
            }
        }
        return 0;
    }
    
    /**
     * 获取当前应用的版本名称
     * @param mContext context
     * @return 版本名称
     */
    public static String getVersionName(Context mContext) {
        if (mContext != null) {
            try {
                return mContext.getPackageManager().getPackageInfo(mContext.getPackageName(), 0).versionName;
            } catch (PackageManager.NameNotFoundException ignored) {
            }
        }
    
        return "";
    }
    }
    
    Constants类
    public class Constants {
    
    public static final String APK_DOWNLOAD_URL = "downloadUrl";
    public static final String UPDATE_URL = "version.json";
    
    }
    
    NotificationBarUtil类
    public class NotificationBarUtil {
    
    /**
     * 设置通知栏是否收起
     * @param context context
     * @param isVisible 是否收起
     */
    @RequiresPermission(EXPAND_STATUS_BAR)
    public static void setNotificationBarVisibility(Context context, boolean isVisible) {
        String methodName;
        if (isVisible) {
            methodName = (Build.VERSION.SDK_INT <= 16) ? "expand" : "expandNotificationsPanel";
        } else {
            methodName = (Build.VERSION.SDK_INT <= 16) ? "collapse" : "collapsePanels";
        }
        invokePanels(context, methodName);
    }
    
    private static void invokePanels(Context context, String methodName) {
        try {
            @SuppressLint("WrongConstant")
            Object service = context.getSystemService("statusbar");
            @SuppressLint("PrivateApi")
            Class<?> statusBarManager = Class.forName("android.app.StatusBarManager");
            Method expand = statusBarManager.getMethod(methodName);
            expand.invoke(service);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    }
    
    NotificationHelper类
     public class NotificationHelper {
    
    private NotificationManager manager;
    
    private Context mContext;
    
    private static String CHANNEL_ID = "dxy_app_update";
    
    private static final int NOTIFICATION_ID = 0;
    
    public NotificationHelper(Context context) {
        this.mContext = context;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, "应用更新", NotificationManager.IMPORTANCE_NONE);
            mChannel.setDescription("应用有新版本");
            mChannel.enableLights(true); //是否在桌面icon右上角展示小红点
            mChannel.setShowBadge(true);
            getManager().createNotificationChannel(mChannel);
        }
    }
    
    /**
     * 显示Notification
     */
    public void showNotification(String content, String apkUrl) {
    
        Intent myIntent = new Intent(mContext, DownloadService.class);
        myIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        myIntent.putExtra(Constants.APK_DOWNLOAD_URL, apkUrl);
        PendingIntent pendingIntent = PendingIntent.getService(mContext, 0, myIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    
        NotificationCompat.Builder builder = getNofity(content)
                .setContentIntent(pendingIntent);
    
        getManager().notify(NOTIFICATION_ID, builder.build());
    }
    
    /**
     * 不断调用次方法通知通知栏更新进度条
     */
    public void updateProgress(int progress) {
    
        String text = mContext.getString(R.string.android_auto_update_download_progress, progress);
    
        PendingIntent pendingintent = PendingIntent.getActivity(mContext, 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);
    
        NotificationCompat.Builder builder = getNofity(text)
                .setProgress(100, progress, false)
                .setContentIntent(pendingintent);
    
        getManager().notify(NOTIFICATION_ID, builder.build());
    }
    
    private NotificationCompat.Builder getNofity(String text) {
        return new NotificationCompat.Builder(mContext.getApplicationContext(), CHANNEL_ID)
                .setTicker(mContext.getString(R.string.android_auto_update_notify_ticker))
                .setContentTitle("版本更新")
                .setContentText(text)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setAutoCancel(true)
                .setOnlyAlertOnce(true)
                .setWhen(System.currentTimeMillis())
                .setPriority(NotificationCompat.PRIORITY_HIGH);
    
    }
    
    public void cancel() {
        getManager().cancel(NOTIFICATION_ID);
    }
    
    
    private NotificationManager getManager() {
        if (manager == null) {
            manager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
        }
        return manager;
    }
    }
    
    StorageUtils类
    public class StorageUtils {
    
    
    /*
     * context.getCacheDir()和context.getExternalCacheDir()
     * 目录的路径不同。
     * 前者的目录存在外部SD卡上的。在手机里可以直接看到
     * 后者的目录存在app的内部存储上,需要root以后,用Root Explorer 文件管理器才能看到
     */
    
    /**
     * 获取应用的缓存目录
     * 路径需要root以后,用Root Explorer 文件管理器才能看到
     */
    public static File getCacheDirectory(Context context) {
        File appCacheDir = context.getCacheDir();
        if (appCacheDir == null) {
            Log.w("StorageUtils", "Can't define system cache directory! The app should be re-installed.");
        }
        return appCacheDir;
    }
    
    /**
     * 获取应用的缓存目录 路径在手机里可以直接看到
     * apk下载路径为:SDCard/Android/data/com.winfo.update/cache/
     */
    public static File getExternalCacheDirectory(Context context) {
        File appCacheDir = context.getExternalCacheDir();
        if (appCacheDir == null) {
            Log.w("StorageUtils", "Can't define system cache directory! The app should be re-installed.");
        }
        return appCacheDir;
    }
    
    /**
     * 在cache下新增自定义缓存路径
     * apk下载路径为:SDCard/Android/data/com.winfo.update/cache/update_file/
     */
    public static File getExternalCacheCustomDirectory(Context context) {
        //在SDCard/Android/data/com.winfo.update/cache/update_file创建文件夹
        File appCacheDir = new File(context.getExternalCacheDir(), "update");
        //如果不存在就创建
        if (!appCacheDir.exists()) {
            if (appCacheDir.mkdirs()) {//创建成功就返回SDCard/Android/data/com.winfo.update/cache/update_file/
                return appCacheDir;
            } else {
                //创建失败就返回默认的SDCard/Android/data/com.winfo.update/cache/
                return context.getExternalCacheDir();
            }
        } else {
            //存在直接返回
            return appCacheDir;
        }
    }
     }
    
    UpdateChecker类
         /**
     * 对话框检测
     *
     * @param mContext context
     * @param dialog   加载框
     */
    public static void checkForDialog(final Context mContext, final Dialog dialog) {
        dialog.show();
        Observable<Response<VersionInfo>> observable = OkHttpUtils.getRetrofit().create(ApiService.class).checkVersion();
        Observer<Response<VersionInfo>> observer = new RequestSubscriber<Response<VersionInfo>>() {
            @Override
            protected void onSuccess(Response<VersionInfo> versionInfoResponse) {
                dialog.dismiss();
                if (versionInfoResponse.isSuccessful()) {
                    VersionInfo versionInfo = versionInfoResponse.body();
                    if (versionInfo != null) {
                        int apkCode = versionInfo.getVersionCode();
                        int versionCode = AppUtils.getVersionCode(mContext);
                        if (apkCode > versionCode) {
                            UpdateDialog.show(mContext, versionInfo.getUpdateMessage(), versionInfo.getDownloadUrl());
                        } else {
                            Toast.makeText(mContext, mContext.getString(R.string.android_auto_update_toast_no_new_update), Toast.LENGTH_SHORT).show();
                        }
                    } else {
                        Toast.makeText(mContext, "请求失败了", Toast.LENGTH_SHORT).show();
                    }
                } else {
                    Toast.makeText(mContext, "请求失败了", Toast.LENGTH_SHORT).show();
                }
            }
    
            @Override
            protected void onFailure(String msg) {
                dialog.dismiss();
                Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
            }
        };
        observable.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);
    }
    
    /**
     * 通知栏通知
     *
     * @param mContext context
     * @param dialog   加载框
     */
    public static void checkForNotification(final Context mContext, final Dialog dialog) {
        dialog.show();
        Observable<Response<VersionInfo>> observable = OkHttpUtils.getRetrofit().create(ApiService.class).checkVersion();
        Observer<Response<VersionInfo>> observer = new RequestSubscriber<Response<VersionInfo>>() {
            @Override
            protected void onSuccess(Response<VersionInfo> versionInfoResponse) {
                dialog.dismiss();
                if (versionInfoResponse.isSuccessful()) {
                    VersionInfo versionInfo = versionInfoResponse.body();
                    if (versionInfo != null) {
                        int apkCode = versionInfo.getVersionCode();
                        int versionCode = AppUtils.getVersionCode(mContext);
                        if (apkCode > versionCode) {
                            new NotificationHelper(mContext).showNotification(versionInfo.getUpdateMessage(), versionInfo.getDownloadUrl());
                        } else {
                            Toast.makeText(mContext, mContext.getString(R.string.android_auto_update_toast_no_new_update), Toast.LENGTH_SHORT).show();
                        }
                    } else {
                        Toast.makeText(mContext, "请求没有成功", Toast.LENGTH_SHORT).show();
                    }
                } else {
                    Toast.makeText(mContext, "请求没有成功", Toast.LENGTH_SHORT).show();
                }
            }
    
            @Override
            protected void onFailure(String msg) {
                dialog.dismiss();
                Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();
            }
        };
        observable.subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);
    }
    }
    
    UpdateDialog类
      /**
     * 弹出对话框提示更新信息,可自定义
     */
     public class UpdateDialog {
    
    /**
     * 显示对话框
     *
     * @param context     context
     * @param content     更新内容
     * @param downloadUrl apk下载地址
     */
    public static void show(final Context context, String content, final String downloadUrl) {
        if (isContextValid(context)) {
            new AlertDialog.Builder(context)
                    .setTitle(R.string.android_auto_update_dialog_title)
                    .setMessage(content)
                    .setPositiveButton(R.string.android_auto_update_dialog_btn_download, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            goToDownload(context, downloadUrl);
                        }
                    })
                    .setNegativeButton(R.string.android_auto_update_dialog_btn_cancel, new DialogInterface.OnClickListener() {
                        public void onClick(DialogInterface dialog, int id) {
                            dialog.dismiss();
                        }
                    })
                    .setCancelable(false)
                    .show();
        }
    }
    
    /**
     * 检测context是否是Activity
     *
     * @param context 上下文
     * @return 是否
     */
    private static boolean isContextValid(Context context) {
        return context instanceof Activity && !((Activity) context).isFinishing();
    }
    
    /**
     * 启动服务传递下载地址进行下载
     *
     * @param context     activity
     * @param downloadUrl 下载地址
     */
    private static void goToDownload(Context context, String downloadUrl) {
        Intent intent = new Intent(context.getApplicationContext(), DownloadService.class);
        intent.putExtra(Constants.APK_DOWNLOAD_URL, downloadUrl);
        context.startService(intent);
    }
    }
    

    第九步 xml

      <?xml version="1.0" encoding="utf-8"?>
      <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
    
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <Button
                android:id="@+id/btn_startDown_qq"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="下载QQ" />
    
            <Button
                android:id="@+id/btn_pauseDown_qq"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:tag="true"
                android:text="暂停下载" />
        </LinearLayout>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <ProgressBar
                android:id="@+id/progressBar_qq"
                style="?android:attr/progressBarStyleHorizontal"
                android:layout_width="0dp"
                android:layout_height="3dp"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_weight="1" />
    
            <TextView
                android:id="@+id/tv_text_qq"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:text="0%" />
    
        </LinearLayout>
    
        <TextView
            android:id="@+id/tv_msg_qq"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />
    </LinearLayout>
    
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <Button
                android:id="@+id/btn_startDown_alipay"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="下载支付宝" />
    
            <Button
                android:id="@+id/btn_pauseDown_alipay"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:tag="true"
                android:text="暂停下载" />
        </LinearLayout>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <ProgressBar
                android:id="@+id/progressBar_alipay"
                style="?android:attr/progressBarStyleHorizontal"
                android:layout_width="0dp"
                android:layout_height="3dp"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_weight="1" />
    
            <TextView
                android:id="@+id/tv_text_alipay"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:text="0%" />
    
        </LinearLayout>
    
        <TextView
            android:id="@+id/tv_msg_alipay"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />
    </LinearLayout>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <Button
                android:id="@+id/btn_startDown_weixin"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:text="下载微信" />
    
            <Button
                android:id="@+id/btn_pauseDown_weixin"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:tag="true"
                android:text="暂停下载" />
        </LinearLayout>
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">
    
            <ProgressBar
                android:id="@+id/progressBar_weixin"
                style="?android:attr/progressBarStyleHorizontal"
                android:layout_width="0dp"
                android:layout_height="3dp"
                android:layout_gravity="center_vertical"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_weight="1" />
    
            <TextView
                android:id="@+id/tv_text_weixin"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_vertical"
                android:text="0%" />
    
        </LinearLayout>
    
        <TextView
            android:id="@+id/tv_msg_weixin"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center" />
    </LinearLayout>
    
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="50dp"
        android:onClick="updateDialog"
        android:text="对话框版本更新" />
    
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="updateNotification"
        android:text="通知栏版本更新" />
    
    
    </LinearLayout>
    

    最后一步 MainActivity

     public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
    private ProgressBar qqProgressBar, alipayProgressBar, weixinProgressbar;
    private TextView tvQQMsg, tvAlipayMsg, tvWeixinMsg;
    private TextView tvQQProgress, tvAlipayProgress, tvWeixinProgress;
    private Button btnQQStart, btnQQPause, btnAlipayStart, btnAlipayPause, btnWeixinStart, btnWeixinPause;
    
    private DownInfo qqDownInfo, alipayDownInfo, weixinDownInfo;
    
    private ProgressDialog dialog;
    
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        dialog = new ProgressDialog(this);
        dialog.setMessage(getString(R.string.android_auto_update_dialog_checking));
    
        qqProgressBar = findViewById(R.id.progressBar_qq);
        alipayProgressBar = findViewById(R.id.progressBar_alipay);
        weixinProgressbar = findViewById(R.id.progressBar_weixin);
    
        tvQQProgress = findViewById(R.id.tv_text_qq);
        tvAlipayProgress = findViewById(R.id.tv_text_alipay);
        tvWeixinProgress = findViewById(R.id.tv_text_weixin);
    
        tvQQMsg = findViewById(R.id.tv_msg_qq);
        tvAlipayMsg = findViewById(R.id.tv_msg_alipay);
        tvWeixinMsg = findViewById(R.id.tv_msg_weixin);
    
        btnQQStart = findViewById(R.id.btn_startDown_qq);
        btnQQPause = findViewById(R.id.btn_pauseDown_qq);
    
        btnAlipayStart = findViewById(R.id.btn_startDown_alipay);
        btnAlipayPause = findViewById(R.id.btn_pauseDown_alipay);
    
        btnWeixinStart = findViewById(R.id.btn_startDown_weixin);
        btnWeixinPause = findViewById(R.id.btn_pauseDown_weixin);
    
        btnQQStart.setOnClickListener(this);
        btnQQPause.setOnClickListener(this);
        btnAlipayStart.setOnClickListener(this);
        btnAlipayPause.setOnClickListener(this);
        btnWeixinStart.setOnClickListener(this);
        btnWeixinPause.setOnClickListener(this);
    
        String weixinDwonloadUrl = "http://dldir1.qq.com/weixin/android/weixin667android1320.apk";
        weixinDownInfo = new DownInfo(weixinDwonloadUrl);
        String weixinApkName = weixinDwonloadUrl.substring(weixinDwonloadUrl.lastIndexOf("/") + 1, weixinDwonloadUrl.length());
        File weixinApkFile = new File(getExternalCacheDir(), weixinApkName);
        weixinDownInfo.setSavePath(weixinApkFile.getAbsolutePath());
        weixinDownInfo.setState(DownState.START);
    
    
        String qqDwonloadUrl = "https://qd.myapp.com/myapp/qqteam/AndroidQQ/mobileqq_android.apk";
        qqDownInfo = new DownInfo(qqDwonloadUrl);
        String qqApkName = qqDwonloadUrl.substring(qqDwonloadUrl.lastIndexOf("/") + 1, qqDwonloadUrl.length());
        File qqApkFile = new File(getExternalCacheDir(), qqApkName);
        qqDownInfo.setSavePath(qqApkFile.getAbsolutePath());
        qqDownInfo.setState(DownState.START);
    
        String alipayDwonloadUrl = "http://gdown.baidu.com/data/wisegame/87b1af6e50012cb5/zhifubao_128.apk";
        alipayDownInfo = new DownInfo(alipayDwonloadUrl);
        String alipayApkName = alipayDwonloadUrl.substring(alipayDwonloadUrl.lastIndexOf("/") + 1, alipayDwonloadUrl.length());
        File alipayApkFile = new File(getExternalCacheDir(), alipayApkName);
        alipayDownInfo.setSavePath(alipayApkFile.getAbsolutePath());
        alipayDownInfo.setState(DownState.START);
    
    }
    
    @SuppressLint("SetTextI18n")
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_startDown_qq:
                HttpDownManager.getInstance().startDown(qqDownInfo, new HttpProgressOnNextListener<DownInfo>() {
    
                    @Override
                    public void onNext(DownInfo downInfo) {
                        tvQQMsg.setText("QQ下载完成" + downInfo.getSavePath());
                    }
    
                    @Override
                    public void updateProgress(long readLength, long countLength) {
                        qqProgressBar.setMax((int) countLength);
                        qqProgressBar.setProgress((int) readLength);
                        tvQQProgress.setText(readLength * 100 / countLength + "%");
                    }
    
                    @Override
                    public void onError(Throwable e) {
                        tvQQMsg.setText(e.getMessage());
                    }
                });
                break;
            case R.id.btn_pauseDown_qq:
                String tag = (String) btnQQPause.getTag();
                if (tag.equals("true")) {
                    btnQQPause.setText("继续下载");
                    btnQQPause.setTag("false");
                    HttpDownManager.getInstance().pause(qqDownInfo);
                } else {
                    btnQQPause.setText("暂停下载");
                    btnQQPause.setTag("true");
                    HttpDownManager.getInstance().continueDownload(qqDownInfo);
                }
    
                break;
            case R.id.btn_startDown_alipay:
                HttpDownManager.getInstance().startDown(alipayDownInfo, new HttpProgressOnNextListener<DownInfo>() {
    
                    @Override
                    public void onNext(DownInfo downInfo) {
                        tvAlipayMsg.setText("下载完成" + downInfo.getSavePath());
                    }
    
                    @Override
                    public void updateProgress(long readLength, long countLength) {
                        alipayProgressBar.setMax((int) countLength);
                        alipayProgressBar.setProgress((int) readLength);
                        tvAlipayProgress.setText(readLength * 100 / countLength + "%");
                    }
    
                    @Override
                    public void onError(Throwable e) {
                        tvAlipayMsg.setText(e.getMessage());
                    }
                });
                break;
            case R.id.btn_pauseDown_alipay:
                String tag1 = (String) btnAlipayPause.getTag();
                if (tag1.equals("true")) {
                    btnAlipayPause.setText("继续下载");
                    btnAlipayPause.setTag("false");
                    HttpDownManager.getInstance().pause(alipayDownInfo);
                } else {
                    btnAlipayPause.setText("暂停下载");
                    btnAlipayPause.setTag("true");
                    HttpDownManager.getInstance().continueDownload(alipayDownInfo);
                }
                break;
            case R.id.btn_startDown_weixin:
                HttpDownManager.getInstance().startDown(weixinDownInfo, new HttpProgressOnNextListener<DownInfo>() {
    
                    @Override
                    public void onNext(DownInfo downInfo) {
                        tvWeixinMsg.setText("下载完成" + downInfo.getSavePath());
                    }
    
                    @Override
                    public void updateProgress(long readLength, long countLength) {
                        weixinProgressbar.setMax((int) countLength);
                        weixinProgressbar.setProgress((int) readLength);
                        tvWeixinProgress.setText(readLength * 100 / countLength + "%");
                    }
    
                    @Override
                    public void onError(Throwable e) {
                        tvWeixinMsg.setText(e.getMessage());
                    }
                });
                break;
            case R.id.btn_pauseDown_weixin:
                String tag2 = (String) btnWeixinPause.getTag();
                if (tag2.equals("true")) {
                    btnWeixinPause.setText("继续下载");
                    btnWeixinPause.setTag("false");
                    HttpDownManager.getInstance().pause(weixinDownInfo);
                } else {
                    btnWeixinPause.setText("暂停下载");
                    btnWeixinPause.setTag("true");
                    HttpDownManager.getInstance().continueDownload(weixinDownInfo);
                }
                break;
        }
    }
    
    
    public void updateDialog(View view) {
        UpdateChecker.checkForDialog(this, dialog);
    }
    
    public void updateNotification(View view) {
        UpdateChecker.checkForNotification(this, dialog);
    }
    }
    

    配置问件

    strings 中
      <resources>
    <string name="app_name">DownloadUpdateDemo</string>
    
    <string name="android_auto_update_dialog_title">发现新版本</string>
    
    <string name="android_auto_update_notify_ticker">发现新版本,点击进行升级</string>
    <string name="android_auto_update_notify_content">发现新版本,点击进行升级</string>
    
    <string name="android_auto_update_dialog_btn_download">立即下载</string>
    <string name="android_auto_update_dialog_btn_cancel">以后再说</string>
    
    <string name="android_auto_update_toast_no_new_update">已经是最新版本</string>
    
    <string name="android_auto_update_download_progress">正在下载:%1$d%%</string>
    
    <string name="android_auto_update_dialog_checking">正在检查版本</string>
    </resources>
    
    style中
        <resources>
    
    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
    
    </resources>
    
    res下 xml 包中的update_apk_paths.xml
    <?xml version="1.0" encoding="utf-8"?>
    <paths>
    
    <cache-path name="cache-path" path="." /><!--name自定义   .为根路径-->
    
    <external-cache-path name="external-cache-path" path="." /><!--name自定义  .为SDCard/Android/data/应用包名/cache/-->
    
    <!--name自定义  update_file为SDCard/Android/data/应用包名/cache/update/和gettExternalCacheDirectory对应创建的文件夹保持一致-->
    <external-cache-path name="external-cache--custom-path" path="update" />
    </paths>
    

    最最后一步:清单问件中

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.winfo.update">
    
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
    
    <application
        android:allowBackup="false"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    
        <service
            android:name=".version_update.service.DownloadService"
            android:exported="false" />
    
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.winfo.update.provider"
            android:exported="false"
            android:grantUriPermissions="true">
    
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/update_apk_paths" />
    
        </provider>
    
    </application>
    
    </manifest>
    
    image

    githip地址 https://github.com/xuezhihuixzh/Downloadupdate.git

    相关文章

      网友评论

        本文标题:Android 版本更新(对话框和通知栏)下载和进度条下载(适合

        本文链接:https://www.haomeiwen.com/subject/ixzarctx.html