美文网首页程序员
自定义Androidk全量更新组件

自定义Androidk全量更新组件

作者: 饮水思源为名 | 来源:发表于2018-08-09 10:14 被阅读27次

      自动更新功能对于一个APP来说是必备的功能,特别是对于未投放市场下载的APP,每次都让用户删掉原来的,再下载新的版本,肯定是不合适的。

    实现思路:

    1. 后台提供接口,返回服务端版本号serviceVersion以及APK下载地址
    2. 前端对接接口,用拿到的serviceVersion和APK配置的localVersion比较,如果serviceVersion>localVersion则提示可以更新,通过获取的APK下载地址下载,然后通过api打开安装完成更新。

    注意:

    1. localVersion笔者使用的是versionCode,可以再AndroidManifest中配置,通过java代码获取。笔者与后台约定了Code的规则,采用更新时间编辑,例如2018年8月2号,则versionCode=“180802”
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        package="com.mintu.dcdb"
        android:versionCode="180802"
        android:versionName="3.6">
    
      /**
         * 获取当前本地apk的版本号
         * @param mContext
         * @return
         */
        public static int getVersionCode(Context mContext) {
            int versionCode = 0;
            try {
                //获取软件版本号,对应AndroidManifest.xml下android:versionCode
                versionCode = mContext.getPackageManager().
                        getPackageInfo(mContext.getPackageName(), 0).versionCode;
            } catch (PackageManager.NameNotFoundException e) {
                e.printStackTrace();
            }
            return versionCode;
        }
    
    1. 对于Android7.0以上的手机,打开附件做了改变,无法使用以往的uri发布意图,详情可见笔者之前的一篇文章。Android7.0以上版本打开附件失败问题
    2. 本文的文件下载、附件打开方法使用的是笔者封装的OkHttp3工具类,使用者可以自己随意替换。只要将APK从url上下载下来,用API打开即可。

    核心代码:

         /***
         * 检查是否更新版本
         */
        private void checkVersion() {
            if (Integer.parseInt((String) sharedPreferencesUtil.getData(Constant.VERSION_CODE_LOCAL,"")) < Integer
                    .parseInt(CommonUtil.getInstance().isNull(sharedPreferencesUtil.getData(Constant.VERSION_CODE,"")) ? "0"
                            : (String) sharedPreferencesUtil.getData(Constant.VERSION_CODE,""))) {
                showDialog(new DownLoadBroadCastReceiver());
            }else{
                if(!isAutoUpdate) Toast.makeText(activity, "未检查到新版本", Toast.LENGTH_SHORT).show();
            }
        }  
    
        /***
         * 开线程下载
         */
        public void createThread(final String downUrl) {
            final Message message = new Message();
            if (SystemUtils.isNetworkAvailable(getApplicationContext())) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            OkHttpUtil.getInstance().download(downUrl, newApkUrl, file_name, new OkHttpUtil.OnDownloadListener() {
                                @Override
                                public void onDownloadSuccess(File downfile, File file) {
                                    hand();
                                    stopSelf();
                                }
                                @Override
                                public void onDownloading(int progress, File file) {
                                    LogUtil.i("update progress---"+progress);
                                    broadCast.putExtra("download", progress);
                                    sendBroadcast(broadCast);
                                }
                                @Override
                                public void onDownloadFailed(String error) {
                                }
                            });
                        } catch (Exception e) {
                        }
                    }
                }).start();
            } else {
                Toast.makeText(getApplicationContext(), "网络无连接,请稍后下载!",
                        Toast.LENGTH_SHORT).show();
            }
        }
    
     /**
         * 打开附件的方法
         * @param f
         * @param context
         */
        public void openFile(File f, Context context) {
            Log.i(LOGTAG, "正在打开附件打--------" + f.getName()+"。附件大小为"+f.length());
            try {
                String end = f.getName().substring(f.getName().lastIndexOf(".")
                        + 1, f.getName().length()).toLowerCase();
                if(end.equals("amr")){
                    AudioRecoderUtils.getInstance().playerStart(f.getAbsolutePath());
                }else {
                    Intent intent = new Intent();
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    intent.setAction(android.content.Intent.ACTION_VIEW);
                    intent.addCategory("android.intent.category.DEFAULT");
          /* 调用getMIMEType()来取得MimeType */
                    String type = getMIMEType(f);
          /* 设置intent的file与MimeType */
                    if(Build.VERSION.SDK_INT>=24){
                        Uri contenturi=FileProvider.getUriForFile(context, "com.mintu.dcdb.fileprovider",f);
                        intent.setDataAndType(contenturi,type);
                        intent.putExtra(MediaStore.EXTRA_OUTPUT, contenturi);
                        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    }else{
                        intent.setDataAndType(Uri.fromFile(f), type);
                    }
                    context.startActivity(intent);
                }
            } catch (Exception e) {
    //            Toast.makeText(context,"打开附件---"+f.getName()+",发生了错误",Toast.LENGTH_SHORT).show();
                Log.e(LOGTAG, "打开附件" + f.getName() + "报错了,错误是-----" + e.getMessage());
            }
        }
    

    项目中全量更新源码:

    package com.mintu.dcdb.util.updateAppUtil;
    
    import android.app.Dialog;
    import android.app.ProgressDialog;
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.text.Html;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    import android.widget.Toast;
    
    import com.google.gson.Gson;
    import com.mintu.dcdb.R;
    import com.mintu.dcdb.config.Constant;
    import com.mintu.dcdb.config.RequestUrl;
    import com.mintu.dcdb.main.bean.UpdateBean;
    import com.mintu.dcdb.main.view.UpdateDialog;
    import com.mintu.dcdb.util.LogUtil;
    import com.wusy.wusylibrary.base.BaseActivity;
    import com.wusy.wusylibrary.util.CommonUtil;
    import com.wusy.wusylibrary.util.OkHttpUtil;
    import com.wusy.wusylibrary.util.SharedPreferencesUtil;
    
    import java.io.IOException;
    import java.util.ArrayList;
    
    import okhttp3.Call;
    
    /**
     * Created by XIAO RONG on 2018/7/19.
     */
    
    public class UpdateAppUtil {
        private SharedPreferencesUtil sharedPreferencesUtil;
        private BaseActivity activity;
        private  ProgressDialog m_pDialog;
        private boolean isAutoUpdate=false;
    
        public UpdateAppUtil(BaseActivity activity,boolean isAutoUpdate){
            sharedPreferencesUtil=SharedPreferencesUtil.getInstance(activity);
            this.activity=activity;
            this.isAutoUpdate=isAutoUpdate;
        }
    
        public void updateApp(){
            String url = RequestUrl.getInstance().getUpdateUrl((String) SharedPreferencesUtil.getInstance(activity).getData(Constant.VERSION_CODE, ""));
            OkHttpUtil.getInstance().asynGet(url, new OkHttpUtil.ResultCallBack() {
                @Override
                public void successListener(Call call, String responseStr) {
                    LogUtil.i("update result str--"+responseStr);
                    Gson gson = new Gson();
                    UpdateBean bean = gson.fromJson(responseStr, UpdateBean.class);
                    sharedPreferencesUtil.saveData(Constant.VERSION_CODE,bean.getVersion());
                    sharedPreferencesUtil.saveData(Constant.VERSION_URL,bean.getUrl());
                    sharedPreferencesUtil.saveData(Constant.VERSION_CONTENT,bean.getContent());
                    sharedPreferencesUtil.saveData(Constant.VERSION_PATCH_FILE_PATH,bean.getPatchFilePath());
                    sharedPreferencesUtil.saveData(Constant.VERSION_IS_PATCH_FILE_PATH,bean.getIsPatchFilePath());
                    activity.runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            checkVersion();
                        }
                    });
                }
    
                @Override
                public void failListener(Call call, IOException e) {
    
                }
            });
        }
        /***
         * 检查是否更新版本
         */
    
        private void checkVersion() {
    
            if (Integer.parseInt((String) sharedPreferencesUtil.getData(Constant.VERSION_CODE_LOCAL,"")) < Integer
                    .parseInt(CommonUtil.getInstance().isNull(sharedPreferencesUtil.getData(Constant.VERSION_CODE,"")) ? "0"
                            : (String) sharedPreferencesUtil.getData(Constant.VERSION_CODE,""))) {
                showDialog(new DownLoadBroadCastReceiver());
            }else{
                if(!isAutoUpdate) Toast.makeText(activity, "未检查到新版本", Toast.LENGTH_SHORT).show();
            }
        }
        private void showDialog(final BroadcastReceiver receiver) {
            final Dialog alert = new UpdateDialog(activity, R.style.MyDialogStyle);
            alert.setContentView(R.layout.upgrade_dialog);
            TextView tvView = (TextView) alert.findViewById(R.id.upgradeText);
            if (!CommonUtil.getInstance().isNull(sharedPreferencesUtil.getData(Constant.VERSION_CONTENT, ""))) {
                tvView.setText(Html.fromHtml((String) sharedPreferencesUtil.getData(Constant.VERSION_CONTENT, "")));
            }
    
            tvView.setWidth(activity.getWindowManager().getDefaultDisplay().getWidth() * 3 / 4);
            Button sureBtn = (Button) alert.findViewById(R.id.btn_sure);
            Button cancleBtn = (Button) alert.findViewById(R.id.btn_cancle);
    
            sureBtn.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
    
    
                    ArrayList<String> actionList = new ArrayList<>();
                    actionList.add(Constant.DOWNLOAD_ACTION);
                    activity.addBroadcastAction(actionList, receiver);
                    Intent updateIntent = new Intent(activity,
                            UpdateService.class);
                    updateIntent.putExtra("app_name", activity.getResources()
                            .getString(R.string.app));
    
                    activity.startService(updateIntent);
                    alert.dismiss();
                }
            });
            cancleBtn.setOnClickListener(new View.OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    alert.dismiss();
                }
            });
            alert.show();
        }
        class DownLoadBroadCastReceiver extends BroadcastReceiver{
    
            @Override
            public void onReceive(Context context, Intent intent) {
                switch (intent.getAction()) {
                    case Constant.DOWNLOAD_ACTION:
                        int progress = intent.getIntExtra("download", 0);
                        showProgress(progress);
                        break;
    
                    default:
                        break;
                }
    
            }
        }
        private void showProgress(Integer progress) {
            if (m_pDialog == null) {
                createProgressDialog("正在更新请稍后...");
            }
            m_pDialog.setProgress(progress);
            if (progress == 100) {
                m_pDialog.dismiss();
            }
        }
        private void createProgressDialog(String title) {
    
            // 创建ProgressDialog对象
            m_pDialog = new ProgressDialog(activity);
    
            // 设置进度条风格,风格为长形
            m_pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
    
            // 设置ProgressDialog 标题
            m_pDialog.setTitle(title);
            // 设置ProgressDialog 进度条进度
            m_pDialog.setProgress(100);
    
            // 设置ProgressDialog 的进度条是否不明确
            m_pDialog.setIndeterminate(false);
    
            // 设置ProgressDialog 是否可以按退回按键取消
            m_pDialog.setCancelable(true);
            // 设置点击进度对话框外的区域对话框不消失
            m_pDialog.setCanceledOnTouchOutside(false);
            // 让ProgressDialog显示
            m_pDialog.show();
        }
    }
    
    package com.mintu.dcdb.util.updateAppUtil;
    
    import android.app.Notification;
    import android.app.NotificationManager;
    import android.app.PendingIntent;
    import android.app.ProgressDialog;
    import android.app.Service;
    import android.content.Intent;
    import android.net.Uri;
    import android.os.Environment;
    import android.os.IBinder;
    import android.os.Message;
    import android.util.Log;
    import android.widget.Toast;
    
    import com.mintu.dcdb.config.Constant;
    import com.mintu.dcdb.util.LogUtil;
    import com.mintu.dcdb.util.SystemUtils;
    import com.wusy.wusylibrary.util.CommonUtil;
    import com.wusy.wusylibrary.util.OkHttpUtil;
    import com.wusy.wusylibrary.util.SharedPreferencesUtil;
    
    import java.io.File;
    
    /**
     *
     * Filename: UpdateService.java Description: 今天修改了当增量包合成失败的时候,重新下载整个最新的apk
     * Company: minto
     *
     * @author: chjr
     * @version: 2.8.0 Create at: 2016年5月20日 下午3:25:52
     *
     */
    public class UpdateService extends Service {
        private final int TIMEOUT = 10 * 1000;// 超时
        private final int DOWN_OK = 1;
        private final int DOWN_ERROR = 0;
        private String app_name;
        private String file_name;
    
        private NotificationManager notificationManager;
        private Notification notification;
    
        private Intent updateIntent;
        private PendingIntent pendingIntent;
    
        private int notification_id = 0;
        ProgressDialog m_pDialog;
        private File updateDir = new File(Constant.FILEDIR);
        File updateFile;
        SharedPreferencesUtil vsPreference;
        String packageName = "com.minto.workhi";
        String patchUrl = Constant.FILEDIR;
        String newApkUrl = Constant.FILEDIR;
        private String downUrl = "";
        private int flag = 2;
        private Intent broadCast;
        @Override
        public IBinder onBind(Intent arg0) {
            stopSelf();
            return null;
        }
        @Override
        public void onCreate() {
            super.onCreate();
            vsPreference = SharedPreferencesUtil.getInstance(getApplicationContext());
        }
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
    
            if (CommonUtil.getInstance().isNull(intent)) {
                stopSelf();
                Log.d("UpService", intent + "--------------------");
            } else {
    
                app_name = intent.getStringExtra("app_name");
                file_name = app_name + ".apk";
    
    
                patchUrl = patchUrl + app_name + ".patch";
                // 创建文件
                CommonUtil.getInstance().createFile(app_name + ".patch");
                CommonUtil.getInstance().createFile(file_name);
                updateFile = new File(updateDir + "/" + app_name + ".patch");
                if(flag==1)flag=2;
                broadCast = new Intent();
                broadCast.setAction(Constant.DOWNLOAD_ACTION);
                broadCast.putExtra("download", 0);
                sendBroadcast(broadCast);
                downFile();
            }
            return super.onStartCommand(intent, flags, startId);
        }
    
        /***
         * 开线程下载
         */
        public void createThread(final String downUrl) {
            final Message message = new Message();
            if (SystemUtils.isNetworkAvailable(getApplicationContext())) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            OkHttpUtil.getInstance().download(downUrl, newApkUrl, file_name, new OkHttpUtil.OnDownloadListener() {
                                @Override
                                public void onDownloadSuccess(File downfile, File file) {
                                    hand();
                                    stopSelf();
                                }
                                @Override
                                public void onDownloading(int progress, File file) {
                                    LogUtil.i("update progress---"+progress);
                                    broadCast.putExtra("download", progress);
                                    sendBroadcast(broadCast);
                                }
                                @Override
                                public void onDownloadFailed(String error) {
                                }
                            });
                        } catch (Exception e) {
                        }
                    }
                }).start();
            } else {
                Toast.makeText(getApplicationContext(), "网络无连接,请稍后下载!",
                        Toast.LENGTH_SHORT).show();
            }
        }
    
        /**
         * 根据类型下载不同的文件
         *
         * @exception
         * @since 1.0.0
         */
        protected void downFile() {
            // 下载增量包路径
            if (1 == flag) {
                downUrl = (String) vsPreference.getData(Constant.VERSION_PATCH_FILE_PATH,"");
            } else if (2 == flag) {
                downUrl = (String) vsPreference.getData(Constant.VERSION_URL,"");
            }
            createThread(downUrl);
        }
    
        /**
         * 根据不同的包安装
         *
         * @exception
         * @since 1.0.0
         */
        protected void hand() {
            // 增量合成方法
            if (1 == flag) {
    //          ApkUpdate update = new ApkUpdate(getApplicationContext(),
    //                  packageName, patchUrl, newApkUrl);
    //          // 检查低版本的apk 是否存在
    //          if (update.isOldApkExist()) {
    //              // 判断是否成功合成新的apk
    //              if (update.newApkGet()) {
    //                  update.installApk();
    //                  delData();
    //              } else {
    //                  // Toast.makeText(getApplicationContext(),
    //                  // "下载失败!:" + newApkUrl + "增量包地址:" + patchUrl,
    //                  // Toast.LENGTH_SHORT).show();
    //                  flag = 2;
    //                  downFile();
    //              }
    //          }
            }
            // 整包安装方法
            else if (2 == flag) {
                File apkFile = new File(newApkUrl, file_name);
                OkHttpUtil.getInstance().openFile(apkFile,this);
            }
        }
    }
    

    相关文章

      网友评论

        本文标题:自定义Androidk全量更新组件

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