rxjava+mvp模式实现app版本更新

相信大多数人都知道,要实现版本更新,首先要获取本地版本号,以及服务器apk的版本号,然后比较两者大小,如果服务器apk版本号大于本地版本,此时我们就需要app版本更新了,版本更新有很多种方法,有的直接开启一个dialog下载apk,有的开启service下载并跟踪进度,我个人比较喜欢开启service来下载apk,那么开启service跟踪下载进度,并安装apk我们要做些什么呐?

      (1)跟踪下载进度:首先我们想到的是我们的网络请求是用的rxjava+retrofit,然而这个请求并没有获取下载进度的api,那么我们要怎么实现这个功能了,这里就涉及到重写了.

    (2)ResponseBody,Interceptor的重写,获取下载进度


   class ProgressInterceptor(var downApkListener:DownApkModel.DownApkListener) : Interceptor {

    override fun intercept(chain: Interceptor.Chain?): Response {
        var response = chain!!.proceed(chain.request())
        return response.newBuilder().body(ProgressResponseBody(response.body()!!, downApkListener)).build()
    }

}

 

         

class ProgressResponseBody(var responseBody: ResponseBody, downApkListener: DownApkModel.DownApkListener) : ResponseBody() {
    var downApkListener = downApkListener
    private var bufferedSource: BufferedSource? = null
    override fun contentLength(): Long {
        return responseBody.contentLength()
    }

    override fun contentType(): MediaType? {
        return responseBody.contentType()
    }

    override fun source(): BufferedSource {
        if (bufferedSource == null) {

            bufferedSource = Okio.buffer(source(responseBody.source()))
        }

        return bufferedSource!!

    }

    private fun source(source: Source): Source {
        return object : ForwardingSource(source) {
            var totalBytesRead = 0L
            override fun read(sink: Buffer?, byteCount: Long): Long {
                //当前读取字节数
                var bytesRead = super.read(sink, byteCount)
                //增加当前读取的字节数,如果读取完成了bytesRead会返回-1
                totalBytesRead += if (bytesRead != -1L) bytesRead else 0L
                downApkListener.onProgress((totalBytesRead * 100 / responseBody.contentLength()).toInt())
                return bytesRead

            }
        }
    }

}

 我们可以看到downApkListener.onProgress()调用了这么一个方法,他就是我们mvp中获取下载进度的方法了,后面代码中我会详解。好了现在我们获取到下载进度了,那么怎么下载,怎么开启service,并安装apk呐,这里我用mvp模式,请看我的项目结构

rxjava+mvp模式实现app版本更新_第1张图片

这个service一共有8个类和接口组成的,很多朋友就会说就一个版本更新需要这么多类和接口吗?从不利用第三方库实现更新版本,而从项目维护,代码可读性上面讲这是我目前想到的最好的办法,mvp模式他最大的特点就是逻辑,代码层次清晰,不再像以前所有的代码都放到v里面,p层负责逻辑,使m与v层完全解耦。好了回归正题,首先我讲解下mvp模式的设计思路,由于就一个service所以我就没有把m,v,p层单独分包了,

      v层:就一个service和接口我们看下这个接口怎么定义的

 

interface DownApkView {
    fun onStartDownload()
    fun onProgress(progress: Int)
    fun onFinishDownload()
    fun onFail(errorInfo: String)
}

downapkView定义了四个方法onStartDownload,onProgress,onFinishDownload,onFail,然后我们的service类要实现这个接口

class DownApkService : Service(), DownApkView {
    lateinit var notificationManager: NotificationManager
    lateinit var notificationCompat: NotificationCompat.Builder
    private lateinit var downApkPresenterImp: DownApkPresenterImp
    private var downApkModelImp = DownApkModelImp()
    override fun onBind(p0: Intent?): IBinder {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    //--service启动的时候调用
    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
      
        downApkPresenterImp = DownApkPresenterImp(this, downApkModelImp)
        downApkPresenterImp.DownApk()
        return super.onStartCommand(intent, flags, startId)
    }

    //-开始下载
    override fun onStartDownload() {
        notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        notificationCompat = NotificationCompat.Builder(this@DownApkService)
                .setContentTitle("xxx")
                .setContentText("正在更新...")
                .setProgress(100, 0, false)
                .setSmallIcon(R.mipmap.applogo)
    }

    //-下载进度
    override fun onProgress(progress: Int) {
        notificationCompat.setProgress(100, progress, false).setContentText("已下载$progress%")
        //给通知栏添加一个id
        notificationManager.notify(0, notificationCompat.build())
    }

    //--下载完成后进行安装
    override fun onFinishDownload() {
        //--通过id取消通知并关闭service
        notificationManager.cancel(0)
        stopSelf()
        //---安装apk
        val intent = Intent()
        intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
        //执行动作
        intent.action = Intent.ACTION_VIEW
        //判读版本是否在7.0以上
        if (Build.VERSION.SDK_INT >= 24) {
            //provider authorities
            val apkUri = FileProvider.getUriForFile(applicationContext, "xxx", downApkModelImp.file)
            //Granting Temporary Permissions to a URI
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive")
        } else {
            intent.setDataAndType(Uri.fromFile(downApkModelImp.file), "application/vnd.android.package-archive")
        }
        startActivity(intent)
    }

    //--下载失败
    override fun onFail(errorInfo: String) {
        notificationManager.cancel(0)
        stopSelf()
        Utils.showToast(this, "更新失败")
    }

}

    然后就是我们的M层:也是一个model接口以及对应接口的实现类

interface DownApkModel {
    interface DownApkListener {
        fun onStartDownload()
        fun onProgress(progress: Int)
        fun onFinishDownload()
        fun onFail(errorInfo: String)
    }

    fun DownApk(downApkListener: DownApkListener)
}

DownApkModel接口定义了一个下载方法,以及内部接口中又定义了v接口中几个方法,而

DownApkModelImp就是实现这个接口的类
class DownApkModelImp : DownApkModel {
    lateinit var file: File
    override fun DownApk(downApkListener: DownApkModel.DownApkListener) {
        var progressInterceptor = ProgressInterceptor(downApkListener)
        var httpClient = OkHttpClient.Builder()
                .addInterceptor(progressInterceptor)
                .connectTimeout(15, TimeUnit.SECONDS)
                .readTimeout(15, TimeUnit.SECONDS)
                .writeTimeout(15, TimeUnit.SECONDS)
                .build()

        var retrofit = Retrofit.Builder()
                .baseUrl("xxx")
                .client(httpClient)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build()
        //--开始下载
        downApkListener.onStartDownload()
        var service = retrofit.create(HttpApi.DownApkService::class.java)
        service.DownApk()
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .doOnNext {
                    //--切换到io线程进行文件流操作
                    savefiles(it)
                }
                //下载完成切换到主线程进行操作
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(object : Observer {
                    override fun onComplete() {
                    }

                    override fun onSubscribe(d: Disposable) {

                    }

                    override fun onNext(t: ResponseBody) {
                        //-下载成功后安装apk
                        downApkListener.onFinishDownload()
                    }

                    override fun onError(e: Throwable) {
                        //--下载失败
                        downApkListener.onFail(e.message!!)
                    }


                })

    }

    private fun savefiles(responseBody: ResponseBody) {
        var inputStream: InputStream = responseBody.byteStream()
        file = File(Environment.getExternalStorageDirectory(), System.currentTimeMillis().toString() + "health.apk")
        //--不存在就创建file
        if (!file.exists()) {
            file.createNewFile()
        }
        var len = 0
        var bytes = ByteArray(2048)
        var fileOutputStream = FileOutputStream(file)
        fileOutputStream.use {
            while ({ len = inputStream.read(bytes);len }() != -1) {
                fileOutputStream.write(bytes,0,len)
            }
        }
        inputStream.close()
        fileOutputStream.close()

    }
}

最后就是我们的p层了:与上面一样一个类一个接口

 

interface DownApkPresenter {
    fun DownApk()
}
class DownApkPresenterImp(var downApkView: DownApkView, var downApkModel: DownApkModel) : DownApkModel.DownApkListener, DownApkPresenter {
    override fun onStartDownload() {
        downApkView.onStartDownload()
    }

    override fun onProgress(progress: Int) {
        downApkView.onProgress(progress)
    }

    override fun onFinishDownload() {
        downApkView.onFinishDownload()
    }

    override fun onFail(errorInfo: String) {
        downApkView.onFail(errorInfo)
    }


    override fun DownApk() {
        downApkModel.DownApk(this)
    }
}

可以看到p层并没有什么代码就是一些接口实现,方法调用,接口回调等,他控制v与m层。

     好了到这里我们所有的代码都已经写好了,我们回顾下整体思路,开启service时候我们调用接口DownApk开始下载,然后下载过程中我们调用了onStartDownload,onProgress,onFinishDownload这三个方法来监听我们的下载过程,当我们开始下载的时候我们在service开始一个通知栏通过onProgress来更新通知栏下载进度,当service回调了onFinishDownload就关闭通知栏信息以及service当然如果我们下载失败同样关闭,最后我们通过判断sdk版本来安装apk.

     mvp:p层中调用m层接口定义的方法,在m层实现类网络请求中根据实际情况来调用m层定义的接口方法,又因为p层控制着m,v层,所以我们p层又实现了这些方法,最后我们通过这些方法去调用v层接口方法,又因为service实现了v层接口,所以从开始下载-下载结束这个过程我们都可以在service里面实现

欢迎各位android爱好者加群交流:290611780

你可能感兴趣的:(android)