Android开发App自动更新的实现(基于AsyncTask异步下载)

Android开发App自动更新的实现(基于AsyncTask异步下载)

平时再做app开发的过程中,app自动更新的功能必不可少,但是自身又不是专业安卓开发,因此在这个过程踩过很多坑,特此写一篇博客记录下来,以便后面在其他地方也用到这个功能的时候还能顺利的写出来

首先需要在app进入第一个页面加载的过程中检查app版本,决定是否要更新


 一 . 我在首页的初始化方法里面new一个检查版本的类,继承AsyncTask,并调用                   execute()方法去执行它;

new CheckVersionInfoTask(ChuShiYe_ZhuJieMian_Main.this,true).execute();

 二.在CheckVersionInfoTask类里面进行检查更新等操作,先贴出整个类的源码,再作解释

package com.upload;

/**
 * Created by cl on 2019/1/18.
 */

import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.text.Html;
import android.util.Log;

import com.Common.MyApplication;
import com.szmj.wjlappandroid.R;
import com.tools.PublicMethod;
import com.utils.UtilityVar;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 *
 */
public class CheckVersionInfoTask extends AsyncTask<Void, Integer, JSONObject> implements ProgressUtil.OnProgressChangeListener{

    private static final String TAG = "CheckVersionInfoTask";
    private ProgressDialog dialog;
    private Context mContext;
    private boolean mShowProgressDialog;
    private static String VERSION_INFO_URL = "";
    private static CheckVersionInfoTask checkVersionInfoTask = null;

    private JSONObject result = null,data = null;
    private ProgressDialog progressDialog = null;

    public static CheckVersionInfoTask getCheckVersionInfoTask(){
        return checkVersionInfoTask;
    }

    public CheckVersionInfoTask(Context context, boolean showProgressDialog) {
        this.mContext = context;
        this.mShowProgressDialog = showProgressDialog;
        this.checkVersionInfoTask = this;
    }

    //初始化显示Dialog
    protected void onPreExecute() {
        if (mShowProgressDialog) {
            dialog = new ProgressDialog(mContext);
            dialog.setMessage(mContext.getString(R.string.check_new_version));
            dialog.show();
        }
    }

    //在后台任务(子线程)中检查服务器的版本信息
    @Override
    protected JSONObject doInBackground(Void... params) {
        String banen = AppUtils.getVersionName(MyApplication.getContext());
        String b = banen.substring(banen.indexOf(".")-1,banen.length());
        VERSION_INFO_URL = UtilityVar.Url_MES + "Login/mesupdate?banben=" + b;
        return getVersionInfo(VERSION_INFO_URL);
    }

    //重写的进度回调方法,current为当前下载进度
    @Override
    public void onProgressChange(int current) {
        //将下载进度发送给进度跟新的方法onProgressUpdate,更新ui
        publishProgress(current);
    }

    //更行当前进度ui
    @Override
    protected void onProgressUpdate(Integer... value){
        Log.e("value",value[0].intValue()+"");
        //显示进度条
        showProgressDialog(value);
    }

    //后台任务执行完毕后,解除Dialog并且解析return返回的结果
    @Override
    protected void onPostExecute(JSONObject result) {
        if (dialog != null && dialog.isShowing()) {
            dialog.dismiss();
        }
        if (result != null) {
            parseJson(result);
        }
    }


    /**
     * 从服务器取得版本信息
     * @return
     */
    public JSONObject getVersionInfo(String urlStr){
        try {
            // 新建一个URL对象
            URL url =  new URL(urlStr);

            // 打开一个HttpURLConnection连接
            HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
            // 设置连接主机超时时间
            urlConn.setConnectTimeout(5 * 1000);
            //设置从主机读取数据超时
            urlConn.setReadTimeout(5 * 1000);
            // 设置是否使用缓存  默认是true
            urlConn.setUseCaches(true);
            // 设置为Post请求
            urlConn.setRequestMethod("GET");
            //urlConn设置请求头信息
            //设置请求中的媒体类型信息。
            urlConn.setRequestProperty("Content-Type", "application/json");
            //设置客户端与服务连接类型
            urlConn.addRequestProperty("Connection", "Keep-Alive");
            // 开始连接
            urlConn.connect();
            // 判断请求是否成功
            if (urlConn.getResponseCode() == 200) {
                // 获取返回的数据
                String res = streamToString(urlConn.getInputStream());
                result = new JSONObject(res);
                if( (boolean)result.get("status") ){
                    data = result.getJSONObject("datas");
                }
                Log.e(TAG, "Get方式请求成功,result--->" + res);
            } else {
                Log.e(TAG, "Get方式请求失败");
                PublicMethod.showToast(mContext,"网络错误!请稍后再试");
            }
            // 关闭连接
            urlConn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return data;
    }

    /**
     *
     * @param result
     */
    private void parseJson(JSONObject result) {
        try {
            String apkUrl = result.getString("apkUrl");//APK下载路径
            JSONArray arr = result.getJSONArray("texing");//新特性

            //对更新的特新信息进行处理
            String updateMessage = "";
            for(int i = 0; i < arr.length() ; i++){
                String s;
                if( i == arr.length() -1 ){
                    s = "["+ (i+1) +"]" +arr.getString(i);
                } else {
                    s = "["+ (i+1) +"]" +arr.getString(i) + "<br/>";
                }
                updateMessage  += s;
            }
            showDialog(updateMessage, apkUrl);
        } catch (JSONException e) {
            Log.e(TAG, "parse json error");
        }
    }

    /**
     * 显示对话框提示用户有新版本,并且让用户选择是否更新版本
     * @param content
     * @param downloadUrl
     */
    public void showDialog(String content, final String downloadUrl) {
        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
        builder.setTitle(R.string.dialog_choose_update_title);
        builder.setMessage(Html.fromHtml(content))
                .setPositiveButton(R.string.dialog_btn_confirm_download, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        //下载apk文件
                        goToDownloadApk(downloadUrl);
                    }
                })
                .setNegativeButton(R.string.dialog_btn_cancel_download, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                    }
                });

        AlertDialog dialog = builder.create();
        //点击对话框外面,对话框不消失
        dialog.setCanceledOnTouchOutside(false);
        dialog.show();
    }

    /**
     * 显示下载进度条
     * @param
     *
     */
    private void showProgressDialog(Integer... value) {
        if( progressDialog == null){
            progressDialog = new ProgressDialog(mContext);
        }
        progressDialog.setTitle("温馨提示");
        progressDialog.setMessage("正在下载...");
        progressDialog.setProgressStyle(progressDialog.STYLE_HORIZONTAL);
        progressDialog.setProgress(0);
        progressDialog.incrementProgressBy(value[0].intValue());
        if(value != null || value.length != 0){
            progressDialog.show();
        }
        if(value[0].intValue() >= 100){
            progressDialog.dismiss();
        }
    }

    /**
     * 用intent启用DownloadService服务去下载AKP文件
     * @param downloadUrl
     */
    private void goToDownloadApk(String downloadUrl) {
        Intent intent = new Intent(mContext, DownloadApkService.class);
        intent.putExtra("apkUrl", downloadUrl);
        mContext.startService(intent);
    }

    /**
     * 将输入流转换成字符串
     *
     * @param is 从网络获取的输入流
     * @return
     */
    public String streamToString(InputStream is) {
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = 0;
            while ((len = is.read(buffer)) != -1) {
                baos.write(buffer, 0, len);
            }
            baos.close();
            is.close();
            byte[] byteArray = baos.toByteArray();
            return new String(byteArray);
        } catch (Exception e) {
            Log.e(TAG, e.toString());
            return null;
        }
    }
}

          1.在构造方法中,将当前的context对象传进来,后面的布尔值用于检查更新时候的转圈动画显示,若为true则显示new一个Progressdialog用于显示检查更新的转圈动画,同时将自身对象保存在对象的的一个变量中,这个对象在后面的进度条需要用到

public CheckVersionInfoTask(Context context, boolean showProgressDialog) {
        this.mContext = context;
        this.mShowProgressDialog = showProgressDialog;
        this.checkVersionInfoTask = this;
    }
//初始化显示Dialog,继承AsyncTask重写的方法,是在异步任务执行之前执行
//可以理解为,new了这个类就执行,根据状态,决定是否显示这个转圈检查更新的ProgressDialog
    @Override
    protected void onPreExecute() {
        if (mShowProgressDialog) {
            dialog = new ProgressDialog(mContext);
            dialog.setMessage(mContext.getString(R.string.check_new_version));
            dialog.show();
        }
    }

         2.然后是异步的请求服务器,获取更新信息 
         此处还有一个小问题,app版本号只能为整数,若只用这个版本号的话,后期版本更新太多,数据维护将非常麻烦,很容易出错,这里用VersionName比较合适,我的格式为1.0.0,VersionCode只做app内部用于更新的数据,因此我这个取出VersionName并取出其中的版本号,带着这个数据请求后台

//在后台任务(子线程)中检查服务器的版本信息
//该方法是继承AsyncTask重写的方法,相当于是开启一个线程在后台执行任务
//我此处的做法是将app更新的版本信息和更新的地址放在数据库去维护
//每次将当前app更新的版本号带过去请求后台,若版本号大于数据库维护的版本号
//则将版本特性和app更新地址返回
    @Override
    protected JSONObject doInBackground(Void... params) {
        String banen = AppUtils.getVersionName(MyApplication.getContext());
        String b = banen.substring(banen.indexOf(".")-1,banen.length());
        VERSION_INFO_URL = UtilityVar.Url_MES + "Login/mesupdate?banben=" + b;
        return getVersionInfo(VERSION_INFO_URL);
    }


/**
     * 从服务器取得版本信息
     * @return
     */
    public JSONObject getVersionInfo(String urlStr){
        try {
            // 新建一个URL对象
            URL url =  new URL(urlStr);

            // 打开一个HttpURLConnection连接
            HttpURLConnection urlConn = (HttpURLConnection) url.openConnection();
            // 设置连接主机超时时间
            urlConn.setConnectTimeout(5 * 1000);
            //设置从主机读取数据超时
            urlConn.setReadTimeout(5 * 1000);
            // 设置是否使用缓存  默认是true
            urlConn.setUseCaches(true);
            // 设置为Post请求
            urlConn.setRequestMethod("GET");
            //urlConn设置请求头信息
            //设置请求中的媒体类型信息。
            urlConn.setRequestProperty("Content-Type", "application/json");
            //设置客户端与服务连接类型
            urlConn.addRequestProperty("Connection", "Keep-Alive");
            // 开始连接
            urlConn.connect();
            // 判断请求是否成功
            if (urlConn.getResponseCode() == 200) {
                // 获取返回的数据
                String res = streamToString(urlConn.getInputStream());
                result = new JSONObject(res);
                if( (boolean)result.get("status") ){
                    data = result.getJSONObject("datas");
                }
                Log.e(TAG, "Get方式请求成功,result--->" + res);
            } else {
                Log.e(TAG, "Get方式请求失败");
                PublicMethod.showToast(mContext,"网络错误!请稍后再试");
            }
            // 关闭连接
            urlConn.disconnect();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return data;
    }
    3.doInBackground方法执行完毕会按照定义的返回类型返回从后台获取的数据类型,我这里是json对象,那么此方法执行完毕,会执行onPostExecute方法,并将之前返回的数据将做参数传入
//后台任务执行完毕后,解除Dialog并且解析return返回的结果
//此方法是继承AsyncTask重写的方法,在doInBackground执行完毕执行,并将其返回结果当作参数
//此处将之前的检查更新动画隐藏
    @Override
    protected void onPostExecute(JSONObject result) {
        if (dialog != null && dialog.isShowing()) {
            dialog.dismiss();
        }
        if (result != null) {
            parseJson(result);
        }
    }


    /**
     *    
     * @param result
     */
    //这个将json数据作一定的处理,具体处理方式,根据自己的json数据来看
    private void parseJson(JSONObject result) {
        try {
            String apkUrl = result.getString("apkUrl");//APK下载路径
            JSONArray arr = result.getJSONArray("texing");//新特性

            //对更新的特新信息进行处理
            String updateMessage = "";
            for(int i = 0; i < arr.length() ; i++){
                String s;
                if( i == arr.length() -1 ){
                    s = "["+ (i+1) +"]" +arr.getString(i);
                } else {
                    s = "["+ (i+1) +"]" +arr.getString(i) + "<br/>";
                }
                updateMessage  += s;
            }
            showDialog(updateMessage, apkUrl);
        } catch (JSONException e) {
            Log.e(TAG, "parse json error");
        }
    }

    /**
     * 显示对话框提示用户有新版本,并且让用户选择是否更新版本
     * @param content
     * @param downloadUrl
     */
    public void showDialog(String content, final String downloadUrl) {
        AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
        builder.setTitle(R.string.dialog_choose_update_title);
        builder.setMessage(Html.fromHtml(content))
                .setPositiveButton(R.string.dialog_btn_confirm_download, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        //下载apk文件
                        goToDownloadApk(downloadUrl);
                    }
                })
                .setNegativeButton(R.string.dialog_btn_cancel_download, new DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                    }
                });

        AlertDialog dialog = builder.create();
        //点击对话框外面,对话框不消失
        dialog.setCanceledOnTouchOutside(false);
        dialog.show();
    }

/**
     * 用intent启用DownloadService服务去下载AKP文件
     * @param downloadUrl
     */
    private void goToDownloadApk(String downloadUrl) {
        Intent intent = new Intent(mContext, DownloadApkService.class);
        intent.putExtra("apkUrl", downloadUrl);
        mContext.startService(intent);
    }

 三.至此,更新只完成了前面一部分,后面是调用服务下载apk,用intent启动服务下载,下面是完整的服务代码

package com.upload;

import android.app.IntentService;
import android.app.NotificationManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.FileProvider;
import android.util.Log;

import com.szmj.wjlappandroid.BuildConfig;
import com.Common.MyApplication;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

/**
 * Created by cl on 2019/1/18.
 */

public class DownloadApkService extends IntentService {

    private static final int BUFFER_SIZE = 1024 * 1024; //缓存大小
    private static final String TAG = "DownloadApkService";

    private static final int NOTIFICATION_ID = 0;
    private NotificationManager mNotifyManager;
    private NotificationCompat.Builder mBuilder;
    private ProgressUtil.OnProgressChangeListener progressListener = null;

    public DownloadApkService() {
        super("DownloadService");
    }

    /**
     * 在onHandleIntent中下载apk文件
     * @param intent
     */
    @Override
    protected void onHandleIntent(Intent intent) {
        progressListener = CheckVersionInfoTask.getCheckVersionInfoTask();
        //初始化通知,用于显示下载进度
        mNotifyManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        mBuilder = new NotificationCompat.Builder(this);

        String urlStr = intent.getStringExtra("apkUrl"); //从intent中取得apk下载路径

        InputStream in = null;
        FileOutputStream out = null;
        try {
            //建立下载连接
            URL url = new URL(urlStr);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            urlConnection.setRequestMethod("GET");
            urlConnection.setDoOutput(false);
            urlConnection.setConnectTimeout(10 * 1000);
            urlConnection.setReadTimeout(10 * 1000);
            urlConnection.setRequestProperty("Connection", "Keep-Alive");
            urlConnection.setRequestProperty("Charset", "UTF-8");
            urlConnection.setRequestProperty("Accept-Encoding", "gzip, deflate");
            urlConnection.connect();

            //以文件流读取数据
            long bytetotal = urlConnection.getContentLength(); //取得文件长度
            long bytesum = 0;
            int byteread = 0;
            in = urlConnection.getInputStream();
            File dir = StorageUtils.getCacheDirectory(this); //取得应用缓存目录
            //Log.d("缓存地址",dir.getPath());
            String apkName = urlStr.substring(urlStr.lastIndexOf("/") + 1, urlStr.length());//取得apK文件名
            File apkFile = new File(dir, apkName);
            out = new FileOutputStream(apkFile);
            byte[] buffer = new byte[BUFFER_SIZE];

            int limit = 0;
            int oldProgress = 0;
            while ((byteread = in.read(buffer)) != -1) {
                bytesum += byteread;
                out.write(buffer, 0, byteread);
                int progress = (int) (bytesum * 100L / bytetotal);
                // 如果进度与之前进度相等,则不更新,如果更新太频繁,则会造成界面卡顿
                if (progress != oldProgress) {
                    //进度回调
                    progressListener.onProgressChange(progress);
                }
                oldProgress = progress;
            }

            //卸载原来的apk
            /*Uri uri = Uri.fromParts("package", BuildConfig.APPLICATION_ID, null);
            Intent intent1 = new Intent(Intent.ACTION_DELETE, uri);
            startActivity(intent1);*/

            // 下载完成,调用installAPK开始安装文件
            installAPk(apkFile);
            //Log.d("调试","download apk finish");
            mNotifyManager.cancel(NOTIFICATION_ID);

        } catch (Exception e) {
            Log.e(TAG, "download apk file error");
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException ignored) {

                }
            }
            if (in != null) {
                try {
                    in.close();
                } catch (IOException ignored) {

                }
            }
        }
    }

    /**
     * 调用系统安装程序安装下载好的apk
     * @param apkFile
     */
    private void installAPk(File apkFile) {
        //Intent intent = new Intent(Intent.ACTION_VIEW);
        //如果没有设置SDCard写权限,或者没有sdcard,apk文件保存在内存中,需要授予权限才能安装
        try {
           // Log.i(TAG, "开始执行安装: ");
            Intent intent = new Intent(Intent.ACTION_VIEW);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                //Log.w(TAG, "版本大于 N ,开始使用 fileProvider 进行安装");
                intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                Uri contentUri = FileProvider.getUriForFile(
                        MyApplication.getContext()
                        , BuildConfig.APPLICATION_ID + ".provider"
                        , apkFile);
                intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
            } else {
                //Log.w(TAG, "正常进行安装");
                intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }
            startActivity(intent);
        } catch (Exception e) {
            Log.e(TAG, e.getMessage());

        }

    }
}

         1). intent启动服务后,在重写的onHandleIntent方法中用io流下载apk,下载完成之后调用安装,该服务的方法没有太多需要解释的地方,需要注意的有两点,一个是下载过程下载进度的显示问题,第二是安装apk的安装目录的权限问题,本人也在此处坑了大概4个小时,最后一步步debug,终于解决了,下面我来一一说明。。

先说第一个下载进度的问题,我这里是在用一个简单的进度提示框来实现,那么问题来了,如何将服务中的进度回调给CheckVersionTask,让其进行ui更新,将进度显示呢。。。

我这个定义了一个工具类,在其中定义一个接口

package com.upload;

/**
 * Created by cl on 2019/1/23.
 */

public class ProgressUtil {

    public interface OnProgressChangeListener{
        void onProgressChange(int val);
    }
}

然后再服务类定义一个属性

private ProgressUtil.OnProgressChangeListener progressListener = null;

在onHandleIntent方法中获取

progressListener = CheckVersionInfoTask.getCheckVersionInfoTask();

此处有没有一点熟悉,这个方法就是之前的类CheckVersionInfoTask中的一个方法,我将部分代码贴出来加以说明,将该类实现该接口,并定义一个静态方法获取它本身,那么在服务中,用该接口获取进度,在CheckVersionInfoTask实现该方法就可以获取下载进度了

public class CheckVersionInfoTask extends AsyncTask<Void, Integer, JSONObject> implements ProgressUtil.OnProgressChangeListener{

    public static CheckVersionInfoTask getCheckVersionInfoTask(){
        return checkVersionInfoTask;
    }

    public CheckVersionInfoTask(Context context, boolean showProgressDialog) {
        this.mContext = context;
        this.mShowProgressDialog = showProgressDialog;
        this.checkVersionInfoTask = this;
    }

}

 在DownloadApkService中用该接口接收进度

while ((byteread = in.read(buffer)) != -1) {
                bytesum += byteread;
                out.write(buffer, 0, byteread);
                int progress = (int) (bytesum * 100L / bytetotal);
                // 如果进度与之前进度相等,则不更新,如果更新太频繁,则会造成界面卡顿
                if (progress != oldProgress) {
                    //进度回调
                    progressListener.onProgressChange(progress);
                }
                oldProgress = progress;
            }

那么在CheckVersionInfoTask实现该接口的重写方法中,获取下载进度,其中publishProgress方法是doInBackground将进度发送给onProgressUpdate的方法

//重写的进度回调方法,current为当前下载进度
//实现ProgressUtil.OnProgressChangeListener接口重写的方法,用于获取当前下载进度
    @Override
    public void onProgressChange(int current) {
        //将下载进度发送给进度跟新的方法onProgressUpdate,更新ui
        publishProgress(current);
    }

    //更行当前进度ui
//继承AsyncTask重写的方法,用于进度更新
    @Override
    protected void onProgressUpdate(Integer... value){
        Log.e("value",value[0].intValue()+"");
        //显示进度条
        showProgressDialog(value);
    }

 /**
     * 显示下载进度条
     * @param
     *
     */
    private void showProgressDialog(Integer... value) {
        if( progressDialog == null){
            progressDialog = new ProgressDialog(mContext);
        }
        progressDialog.setTitle("温馨提示");
        progressDialog.setMessage("正在下载...");
        progressDialog.setProgressStyle(progressDialog.STYLE_HORIZONTAL);
        progressDialog.setProgress(0);
        progressDialog.incrementProgressBy(value[0].intValue());
        if(value != null || value.length != 0){
            progressDialog.show();
        }
        if(value[0].intValue() >= 100){
            progressDialog.dismiss();
        }
    }

第二个问题就是apk的安装啦,这里主要有几个地方需要正确配置

1.主配置文件的provider以及若干权限,authortites为自定义的安装目录,一般设置为包名,在安装的方法里需要和这个保持一致,一般可以这样写,如图:

<provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.provider"
            android:grantUriPermissions="true"
            android:exported="false">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"/>
        </provider>



<uses-permission android:name="android.permission.DOWNLOAD_WITHOUT_NOTIFICATION" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
Uri contentUri = FileProvider.getUriForFile(
                        MyApplication.getContext()
                        , BuildConfig.APPLICATION_ID + ".provider"
                        , apkFile);

2.主配置文件的xml文件配置,新建xml文件夹,如上显示,新建xml文件

        第一个路径为内置sd卡的路径配置

        第二个路径为外置sd卡的路径配置

        第三个路径为全路径,这个一定要配,不然程序会找不到你下载的apk的缓存文件

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path path="Android/data/local/tmp/你的包名" name="files_root"/>
    <external-path path="." name="external_storage_root"/>
    <root-path path="" name ="name"/>
</paths>

再贴一个工具类

package com.upload;

import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

/**
 * Created by cl on 2019/1/22.
 * 跟App相关的辅助类
 */
public class AppUtils {
        /**
         * 获取应用程序名称
         */
        public static synchronized String getAppName(Context context) {
            try {
                PackageManager packageManager = context.getPackageManager();
                PackageInfo packageInfo = packageManager.getPackageInfo(
                        context.getPackageName(), 0);
                int labelRes = packageInfo.applicationInfo.labelRes;
                return context.getResources().getString(labelRes);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        /**
         * [获取应用程序版本名称信息]
         * @param context
         * @return 当前应用的版本名称
         */
        public static synchronized String getVersionName(Context context) {
            try {
                PackageManager packageManager = context.getPackageManager();
                PackageInfo packageInfo = packageManager.getPackageInfo(
                        context.getPackageName(), 0);
                return packageInfo.versionName;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }


        /**
         * [获取应用程序版本名称信息]
         * @param context
         * @return 当前应用的版本号
         */
        public static synchronized int getVersionCode(Context context) {
            try {
                PackageManager packageManager = context.getPackageManager();
                PackageInfo packageInfo = packageManager.getPackageInfo(
                        context.getPackageName(), 0);
                return packageInfo.versionCode;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return 0;
        }


        /**
         * [获取应用程序版本名称信息]
         * @param context
         * @return 当前应用的包名
         */
        public static synchronized String getPackageName(Context context) {
            try {
                PackageManager packageManager = context.getPackageManager();
                PackageInfo packageInfo = packageManager.getPackageInfo(
                        context.getPackageName(), 0);
                return packageInfo.packageName;
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }


        /**
         * 获取图标 bitmap
         * @param context
         */
        public static synchronized Bitmap getBitmap(Context context) {
            PackageManager packageManager = null;
            ApplicationInfo applicationInfo = null;
            try {
                packageManager = context.getApplicationContext()
                        .getPackageManager();
                applicationInfo = packageManager.getApplicationInfo(
                        context.getPackageName(), 0);
            } catch (PackageManager.NameNotFoundException e) {
                applicationInfo = null;
            }
            Drawable d = packageManager.getApplicationIcon(applicationInfo); //xxx根据自己的情况获取drawable
            BitmapDrawable bd = (BitmapDrawable) d;
            Bitmap bm = bd.getBitmap();
            return bm;
        }

}

至此,app的下载功能已经全部完成了。。。整个代码基本都是从网上半copy半改完成的,也算是自己的一个进步

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值