实战|仿应用宝下载并安装App(附源码)

2019-12-12 11:14:28 浏览数 (1)

学更好的别人,

做更好的自己。

——《微卡智享》

本文长度为2499,预计阅读7分钟

仿应用宝下载安装App

前面几章我们学习了检测App是否安装,能过AsyncTask下载App并调用安装,还有实现进度条按钮的功能,这一篇我们把这几章连着RecyclerView进行一个综合实战练习,在实战过程中又顺便学到了几个新的知识点。

实现效果

根据上面的视频我们可以看到,做了一个检测App是否已经安装,如果未安装直接下载并安装的小Demo效果,接下来我们说说实现方式。

实现思路

定义一个AppInfo的类,里面有App的名称,包名、下载地址,存放的路径及当前的状态标志(未安装,已下载,已安装),再加一个判断当前标志的函数,

利用我们《学习|Android检测并自动下载安装包(Kotlin)》文章中的那个已经封装了的类进行程序下载,这个类在这里又有改动,主要是因为原来的类中AsyncTask都是串行方式,只能一个一个等的下载,这里我重新加了一下并行运行的方式的调用方法

利用RecyclerView的布局,显示程序中文名及包名,右边为进度条的按钮,这样可以实时看到下载进度。

代码实现

微卡智享

我们新建一个AcTest的项目,整体完成的目录如下

activity_main的布局中直接添加一个RecyclerView的控件

CAppInfo

新建一个CAppInfo的类,用于记录我们的App的相关信息

代码语言:javascript复制
ackage dem.vac.actest

import android.content.Context
import java.io.File

class CAppInfo{
    //程序名称
    lateinit var AppName: String

    //程序包名
    lateinit var PackageName: String

    //下载文件名称
    lateinit var FileName: String

    //下载地址
    lateinit var DownloadUrl: String

    //状态 0-下载 1-已下载 2-已安装
    var Status: Int = 0

    fun CheckfileStaus(context: Context) {
        Status = 0
        if (DownloadUrl == "" || FileName == "") {
            return
        }
        if (CheckAppInstall.isAppInstalled(context, PackageName)) {
            Status = 2
        } else {
            var file = File(FileName)
            //存在的话说明已下载
            if (file.exists()) {
                Status = 1
            } else {
                Status = 0
            }
        }
    }
}

CheckAppInstall

这个就是检测App是否已经安装的类,前面《学习|Android检测并自动下载安装包(Kotlin)》介绍过

代码语言:javascript复制
package dem.vac.actest

import android.content.Context
import android.content.pm.PackageManager
import android.text.TextUtils
import android.util.Log
import java.lang.Exception

class CheckAppInstall {
    companion object StaticFun {
        fun isAppInstalled(context: Context, uri: String): Boolean {
            var pm: PackageManager = context.packageManager
            var installed = false
            if(TextUtils.isEmpty(uri)) return installed
            try {
                pm.getPackageInfo(uri, PackageManager.GET_ACTIVITIES)
                installed = true
            } catch (ex: Exception) {
                Log.i("install", ex.message)
                installed = false
            }
            return installed
        }
    }
}

DownloadHelper(划重点、新的知识点)

DownloadHelper前面文章也列出来过,这里一开始直接套用了,在测试过程中发现几个APP同时下载时点击是无反应的,后台网上找了找资料发现用AsyncTask默认的串行的,想要并行实现需要通过ThreadPoolExecutor配合着executeOnExecutor来实现,所以这个类里我又加了一下方法,保留原来的串行方法,然后加了一下并行的方法。

代码语言:javascript复制
package dem.vac.actest

import android.os.AsyncTask
import java.io.File
import java.io.FileOutputStream
import java.lang.Exception
import java.net.URL
import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.Executor
import java.util.concurrent.ThreadPoolExecutor
import java.util.concurrent.TimeUnit


class DownloadHelper {

    companion object StaticFun {

        //定义多线程异步执行
        private var Thread_Pool_executor: Executor = ThreadPoolExecutor(
            3, 3, 10, TimeUnit.SECONDS,
            ArrayBlockingQueue<Runnable>(2)
        )

        fun download(url: String, localPath: String, listener: OnDownloadListener) {
            var task = DownloadAsyncTask(url, localPath, listener)
            task.execute()
        }

        fun downloadasync(url: String, localPath: String, listener: OnDownloadListener) {
            var task = DownloadAsyncTask(url, localPath, listener)
            task.executeOnExecutor(Thread_Pool_executor)
        }

        class DownloadAsyncTask(mUrl: String, mFilepath: String, Listener: OnDownloadListener) : AsyncTask<String, Int, Boolean>() {
            lateinit var mFailInfo: String
            private var mUrl: String = mUrl
            private var mFilePath: String = mFilepath
            private var mListener: OnDownloadListener = Listener

            override fun onPreExecute() {
                super.onPreExecute()
                this.mListener.onStart()
            }

            override fun onProgressUpdate(vararg values: Int?) {
                super.onProgressUpdate(*values)
                if (values.isNotEmpty()) {
                    values[0]?.let { mListener.onProgress(it) }
                }
            }

            override fun doInBackground(vararg p0: String?): Boolean {
                var pdfurl: String = mUrl
                try {
                    var url = URL(pdfurl)
                    var urlConnection = url.openConnection()
                    var inputStream = urlConnection.getInputStream()
                    var contentlen = urlConnection.contentLength
                    var pdffile = File(mFilePath)
                    //如果存在直接提示安装
                    if (pdffile.exists()) {
                        var result = pdffile.delete()
                        if (!result) {
                            mFailInfo = "存储路径下的同名文件删除失败!"
                            return false
                        }
                    }
                    var downloadSize = 0
                    var bytes = ByteArray(1024)
                    var length : Int
                    var outputStream = FileOutputStream(mFilePath)
                    do {
                        length = inputStream.read(bytes)
                        if (length == -1) break
                        outputStream.write(bytes, 0, length)
                        downloadSize  = length
                        publishProgress(downloadSize * 100 / contentlen)
                    } while (true)

                    inputStream.close()
                    outputStream.close()

                } catch (ex: Exception) {
                    ex.printStackTrace()
                    mFailInfo = ex.message.toString()
                    return false
                }
                return true
            }

            override fun onPostExecute(result: Boolean?) {
                super.onPostExecute(result)
                if (result!!) {
                    mListener.onSuccess(File(mFilePath))
                } else {
                    mListener.onFail(File(mFilePath), mFailInfo)
                }
            }

        }

        interface OnDownloadListener {
            fun onStart()
            fun onSuccess(file: File)
            fun onFail(file: File, failInfo: String)
            fun onProgress(progress: Int)
        }
    }
}

AppViewAdapter(RecyclerView的操作)

AppViewAdapter中写了相关RecyclerView的UI逻辑操作,算是把我们的RecyclerView重新复习了一下,并且全都改为Kotlin写的

代码语言:javascript复制
package dem.vac.actest

import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.net.Uri
import android.os.Build
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.core.content.ContextCompat.startActivity
import androidx.core.content.FileProvider
import androidx.recyclerview.widget.RecyclerView
import dem.vac.actest.DownloadHelper.StaticFun.OnDownloadListener
import java.io.File


class AppViewAdapter(context: Context, appinfos: List<CAppInfo>, statusChange: OnItemStatusChange) :
    RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    private var mAppinfos: List<CAppInfo> = appinfos
    private var mContext: Context = context
    private var mStatusLister: OnItemStatusChange = statusChange


    class AppViewHolder(context: Context, view: View) : RecyclerView.ViewHolder(view) {
        var tvAppName: TextView = view.findViewById(R.id.tvAppName)
        var tvPackageName: TextView = view.findViewById(R.id.tvPackageName)
        var progressButton: ProgressButton = view.findViewById(R.id.progressbtn)
    }


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        var v: View = LayoutInflater.from(mContext).inflate(R.layout.rcl_item, parent, false)
        return AppViewHolder(mContext, v)
    }

    override fun getItemCount(): Int {
        return mAppinfos.size
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        var viewHolder = holder as AppViewHolder
        var item = mAppinfos[position]
        //检测当前程序状态
        item.CheckfileStaus(mContext)

        viewHolder.tvAppName.text = item.AppName
        viewHolder.tvPackageName.text = item.PackageName
        when (item.Status) {
            0 -> {
                viewHolder.progressButton.setText("下载")
                viewHolder.progressButton.setTextColor(Color.WHITE)
            }
            1 -> {
                viewHolder.progressButton.setText("安装")
                viewHolder.progressButton.setTextColor(Color.WHITE)
            }
            2 -> {
                viewHolder.progressButton.setText("已安装")
                viewHolder.progressButton.setTextColor(Color.WHITE)
            }
            else -> {
                viewHolder.progressButton.setText("重试")
                viewHolder.progressButton.setTextColor(Color.WHITE)
            }

        }

        viewHolder.progressButton.setOnProgressButtonClickListener(object :
            ProgressButton.OnProgressButtonClickListener {
            override fun onClickListener() {
                when (item.Status) {
                    0 -> {
                        DownloadHelper.downloadasync(
                            item.DownloadUrl,
                            item.FileName,
                            object :
                                OnDownloadListener {
                                override fun onStart() {
                                    viewHolder.progressButton.setProgress(0)
                                    mStatusLister.onRefreshAll()
                                }

                                override fun onSuccess(file: File) {
                                    viewHolder.progressButton.setText("安装")
                                    item.Status = 1
                                    InstallApp(item)
                                    mStatusLister.onRefreshAll()
                                }

                                override fun onFail(file: File, failInfo: String) {
                                    viewHolder.progressButton.setText("重试")
                                    item.Status = 0
                                    mStatusLister.onRefreshAll()
                                }

                                override fun onProgress(progress: Int) {
                                    viewHolder.progressButton.setProgress(progress)
                                    viewHolder.progressButton.setText("下载中 $progress%")
                                }
                            })
                    }
                    1 -> {
                        InstallApp(item)
                    }
                }

            }

        })

    }

    //安装
    private fun InstallApp(item: CAppInfo) {
        val intent = Intent(Intent.ACTION_VIEW)
        val uri: Uri
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
            uri = FileProvider.getUriForFile(
                mContext,
                mContext.packageName   ".provider",
                File(item.FileName)
            )
        } else {
            uri = Uri.fromFile(File(item.FileName))
        }

        intent.setDataAndType(
            uri,
            "application/vnd.android.package-archive"
        )
        startActivity(mContext, intent, null)
    }
}

interface OnItemStatusChange {
    fun onRefreshAll()
}

上在最后我加了一个了OnItemStatusChange的接口,在下载完或安装是周用重新刷新一下界面显示的,不过效果只能说挺一般,有小伙伴如果有更好的思路也可以留言告诉我。

我们看一下动图的展示效果

源码地址

https://github.com/Vaccae/AcTest.git

0 人点赞