前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >实战|仿应用宝下载并安装App(附源码)

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

作者头像
Vaccae
发布2019-12-12 11:14:28
1.3K0
发布2019-12-12 11:14:28
举报
文章被收录于专栏:微卡智享微卡智享

学更好的别人,

做更好的自己。

——《微卡智享》

本文长度为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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-12-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微卡智享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档