美文网首页
android:版本更新概念,整个流程,及解决相关坑

android:版本更新概念,整个流程,及解决相关坑

作者: 你在心上_b28f | 来源:发表于2018-09-30 10:23 被阅读225次

如需转载请标明出处 https://www.jianshu.com/p/c0f7b3418c24

首先,看了很多安卓版本更新的文章,然后有小伙伴们反映文章不够全面。然后决定写一篇版本更新的文章。
版本更新设计到两个概念:非强制更新,强制更新

(一)非强制更新:就是客户端请求服务端,通过请求的版本号 version code(整数),和服务器的最新版本号进行对比,如果当前版本号,小于服务器版本号,则客户端需要更新,否则不需要更新,客户端可以选择是否更新。
强制更新:就是客户端请求服务端,服务端返回需要强制更新的命令,客户端就需要强制用户升级,不升级,就不允许继续使用客户端。

这里引出了,为什么要强制更新。
1、强制解决掉旧版APP中带有的某些bug,提升用户体验以及安全性,保障用户;
2、减轻工作量。因为,一个服务器API接口每多支持一个版本,就会给服务器接口开发者带来很大的工作量;
3、在新版本的APP里,会有新的特性和新的功能。强制用户更新,能够让用户在大概率上跟上产品经理给用户设定的套路,希望用户去使用新的功能,新的特性,已达到自己想要的目的。

版本更新的时机
1.可以是整个app的接口进行携带版本号(所有请求服务端页面),然后服务端和最新的版本号进行判断,返回是否更新,或者强制更新
2.可以在app设置页面,增加检查更新的功能。

(二)整体流程

请求服务端--->不需要更新
请求服务端 --->非强制更新--->下载服务器的apk--->安装apk
请求服务端 ---->强制更新--->下载服务器的apk--->安装apk

(三)相关的坑(含代码)
下载apk保存在sd卡,需要android动态检查权限, 6.0动态检查权限

7.0以上安装apk,Uri.fromFile(file)异常奔溃, 7.0以上通过隐式意图调用系统安装程序安装APK
可参考eg:https://www.jianshu.com/p/577816c3ce93

8.0以上通知栏不显示通知 8.0通知不显示问题
可参考eg:https://blog.csdn.net/u010231682/article/details/80732879

8.0以上安装失败 8.0未知应用安装权限
可参考eg:https://blog.csdn.net/changmu175/article/details/78906829

这里下载用的后台service进行下载
1.下载

  private void downLoad(String fileUrl) {

        MyObserverNoDialog<ResponseBody> observerNoDialog = new MyObserverNoDialog<ResponseBody>() {
            @Override
            public void onNext(final ResponseBody body) {
                writeResponseBodyToDisk(body);
            }

            @Override
            protected void onError(ResultException e) {
                builder1.setContentText("下载失败,请稍后重试"); //消息内容
                notification = builder1.build();
                notificationManager.notify(PUSH_NOTIFICATION_ID, notification); // 通过通知管理器发送通知
            }
        };
        Retrofit client = RetrofitUtil.getRetrofit();
        HomePageService service = client.create(HomePageService.class);
        service.versionUpdate(fileUrl)
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .subscribe(observerNoDialog);

    }

2.写入到sd卡

 private void writeResponseBodyToDisk(ResponseBody body) {
        try {
            long time = System.currentTimeMillis();//当前时间的毫秒数
            //创建下载APK的路径
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                File futureStudioIconFile = new File(Environment.getExternalStorageDirectory(), time + "updata.apk");
                InputStream inputStream = null;
                OutputStream outputStream = null;

                try {
                    byte[] fileReader = new byte[4096];

                    long fileSize = body.contentLength();
                    long fileSizeDownloaded = 0;

                    inputStream = body.byteStream();
                    outputStream = new FileOutputStream(futureStudioIconFile);

                               while (true) {
                        int read = inputStream.read(fileReader);
                        if (read == -1) {
                            break;
                        }
                        outputStream.write(fileReader, 0, read);
                        fileSizeDownloaded += read;
                        long currentTime = System.currentTimeMillis();
                        if (currentTime- lasstTime > 500) {
                            builder1.setProgress(100, (int) (fileSizeDownloaded * 100 / fileSize), false);
                            builder1.setContentText("正在下载:" + fileSizeDownloaded * 100 / fileSize + "/100"); //消息内容
                            notification = builder1.build();
                            notificationManager.notify(PUSH_NOTIFICATION_ID, notification); // 通过通知管理器发送通知
                            lasstTime = currentTime;
                        }
                    }

                    if (fileSizeDownloaded == fileSize) {
                        //builder1.setDefaults(Notification.DEFAULT_ALL); //设置默认的提示音,振动方式,灯光
                        builder1.setProgress(100, 100, false);
                        builder1.setContentText("下载完成"); //消息内容
                        notification = builder1.build();
                        notificationManager.notify(PUSH_NOTIFICATION_ID, notification); // 通过通知管理器发送通知
                    }

                    install(getApplicationContext(), futureStudioIconFile);
                    outputStream.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (inputStream != null) {
                        inputStream.close();
                    }

                    if (outputStream != null) {
                        outputStream.close();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

3.安装

 /**
     * 通过隐式意图调用系统安装程序安装APK
     */
    public static void install(Context context, File file) {

        Intent intent = new Intent(Intent.ACTION_VIEW);
        // 由于没有在Activity环境下启动Activity,设置下面的标签
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= 24) { //判读版本是否在7.0以上
            //参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致   参数3  共享的文件
            Uri apkUri =
                    FileProvider.getUriForFile(context, "com.zz.zz.fileprovider", file);
            //添加这一句表示对目标应用临时授权该Uri所代表的文件
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(Uri.fromFile(file),
                    "application/vnd.android.package-archive");
        }
        context.startActivity(intent);
    }

4.未知权限检测

  boolean haveInstallPermission1;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    //先获取是否有安装未知来源应用的权限
                    haveInstallPermission1 = mContext.getPackageManager().canRequestPackageInstalls();
                    if (!haveInstallPermission1) {//没有权限

                        Utils.showAlertDialog(mContext, "提示", "安装应用需要打开未知来源权限,请去设置中开启权限", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                                    startInstallPermissionSettingActivity();
                                }
                            }
                        }, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {

                            }
                        });
                        return;
                    } else {
                        PermissionApplyUtil.getInstance().checkStoragePermission(mContext, new PermissionApplyUtil.PermissionCheckCallBack() {
                            @Override
                            public void onHasPermission() {
                                Intent intent = new Intent(mContext, VersionUpdateService.class);
                                intent.putExtra("Key_Down_Url", apkUrl);
                                mContext.startService(intent);
                                 ((TextView) view).setText("正在下载");
                                view.setEnabled(false);
                            }
                        });
                    }
                } else {
                    PermissionApplyUtil.getInstance().checkStoragePermission(mContext, new PermissionApplyUtil.PermissionCheckCallBack() {
                        @Override
                        public void onHasPermission() {
                            Intent intent = new Intent(mContext, VersionUpdateService.class);
                            intent.putExtra("Key_Down_Url", apkUrl);
                            mContext.startService(intent);
                            ((TextView) view).setText("正在下载");
                            view.setEnabled(false);
                        }
                    });
                }

这个service源码。

  public class VersionUpdateService extends Service {

    private String appName;
    private String url;
    // 通知栏
    private Notification notification = null;
    private NotificationManager notificationManager = null;
    // 通知栏跳转Intent
    private Intent updateIntent = null;
    private PendingIntent pendingIntent = null;
    Notification.Builder builder1;
    private long lasstTime = 0;
    /***
     * 创建通知栏
     */
    RemoteViews contentView;
    /**
     * 8.0通知不显示问题
     *
     * @param
     * @return
     */
    private static final int PUSH_NOTIFICATION_ID = (0x001);
    private static final String PUSH_CHANNEL_ID = "PUSH_NOTIFY_ID";
    private static final String PUSH_CHANNEL_NAME = "PUSH_NOTIFY_NAME";

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null)
            url = intent.getStringExtra("Key_Down_Url");
        createNofity();
        downLoad(url);
        return super.onStartCommand(intent, flags, startId);
    }

    public void createNofity() {
        notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(NOTIFICATION_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(PUSH_CHANNEL_ID, PUSH_CHANNEL_NAME, NotificationManager.IMPORTANCE_HIGH);
            if (notificationManager != null) {
                notificationManager.createNotificationChannel(channel);
            }
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            builder1 = new Notification.Builder(this, PUSH_CHANNEL_ID);
        } else {
            builder1 = new Notification.Builder(this);
        }
        builder1.setSmallIcon(R.mipmap.ic_placeholder); //设置图标
        builder1.setTicker("开始下载");
        builder1.setContentTitle(appName); //设置标题
        builder1.setContentText("正在下载:"); //消息内容
        builder1.setWhen(System.currentTimeMillis()); //发送时间
//      builder1.setDefaults(Notification.DEFAULT_ALL); //设置默认的提示音,振动方式,灯光
        builder1.setAutoCancel(true);//打开程序后图标消失
//      // 设置下载过程中,点击通知栏,回到主界面
//      updateIntent = new Intent(this, MainActivity.class);
//      pendingIntent =PendingIntent.getActivity(this, 0, updateIntent, 0);
//      builder1.setContentIntent(pendingIntent);
        //设置进度条
        builder1.setProgress(100, 0, false);
        notification = builder1.build();
        notificationManager.notify(PUSH_NOTIFICATION_ID, notification); // 通过通知管理器发送通知
    }

    private void downLoad(String fileUrl) {

        MyObserverNoDialog<ResponseBody> observerNoDialog = new MyObserverNoDialog<ResponseBody>() {
            @Override
            public void onNext(final ResponseBody body) {
                writeResponseBodyToDisk(body);
            }

            @Override
            protected void onError(ResultException e) {
                builder1.setContentText("下载失败,请稍后重试"); //消息内容
                notification = builder1.build();
                notificationManager.notify(PUSH_NOTIFICATION_ID, notification); // 通过通知管理器发送通知
            }
        };
        Retrofit client = RetrofitUtil.getRetrofit();
        HomePageService service = client.create(HomePageService.class);
        service.versionUpdate(fileUrl)
                .subscribeOn(Schedulers.io())
                .unsubscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .subscribe(observerNoDialog);

    }

    private void writeResponseBodyToDisk(ResponseBody body) {
        try {
            long time = System.currentTimeMillis();//当前时间的毫秒数
            //创建下载APK的路径
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                File futureStudioIconFile = new File(Environment.getExternalStorageDirectory(), time + "updata.apk");
                InputStream inputStream = null;
                OutputStream outputStream = null;

                try {
                    byte[] fileReader = new byte[4096];

                    long fileSize = body.contentLength();
                    long fileSizeDownloaded = 0;

                    inputStream = body.byteStream();
                    outputStream = new FileOutputStream(futureStudioIconFile);

                      while (true) {
                        int read = inputStream.read(fileReader);
                        if (read == -1) {
                            break;
                        }
                        outputStream.write(fileReader, 0, read);
                        fileSizeDownloaded += read;
                        long currentTime = System.currentTimeMillis();
                        if (currentTime- lasstTime > 500) {
                            builder1.setProgress(100, (int) (fileSizeDownloaded * 100 / fileSize), false);
                            builder1.setContentText("正在下载:" + fileSizeDownloaded * 100 / fileSize + "/100"); //消息内容
                            notification = builder1.build();
                            notificationManager.notify(PUSH_NOTIFICATION_ID, notification); // 通过通知管理器发送通知
                            lasstTime = currentTime;
                        }
                    }

                    if (fileSizeDownloaded == fileSize) {
                        //builder1.setDefaults(Notification.DEFAULT_ALL); //设置默认的提示音,振动方式,灯光
                        builder1.setProgress(100, 100, false);
                        builder1.setContentText("下载完成"); //消息内容
                        notification = builder1.build();
                        notificationManager.notify(PUSH_NOTIFICATION_ID, notification); // 通过通知管理器发送通知
                    }

                    install(getApplicationContext(), futureStudioIconFile);
                    outputStream.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    if (inputStream != null) {
                        inputStream.close();
                    }

                    if (outputStream != null) {
                        outputStream.close();
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    /**
     * 通过隐式意图调用系统安装程序安装APK
     */
    public static void install(Context context, File file) {

        Intent intent = new Intent(Intent.ACTION_VIEW);
        // 由于没有在Activity环境下启动Activity,设置下面的标签
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= 24) { //判读版本是否在7.0以上
            //参数1 上下文, 参数2 Provider主机地址 和配置文件中保持一致   参数3  共享的文件
            Uri apkUri =
                    FileProvider.getUriForFile(context, "com.zz.zz.fileprovider", file);
            //添加这一句表示对目标应用临时授权该Uri所代表的文件
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(Uri.fromFile(file),
                    "application/vnd.android.package-archive");
        }
        context.startActivity(intent);
    }
}

未知应用权限源码

  boolean haveInstallPermission1;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    //先获取是否有安装未知来源应用的权限
                    haveInstallPermission1 = mContext.getPackageManager().canRequestPackageInstalls();
                    if (!haveInstallPermission1) {//没有权限

                        Utils.showAlertDialog(mContext, "提示", "安装应用需要打开未知来源权限,请去设置中开启权限", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                                    startInstallPermissionSettingActivity();
                                }
                            }
                        }, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {

                            }
                        });
                        return;
                    } else {
                        PermissionApplyUtil.getInstance().checkStoragePermission(mContext, new PermissionApplyUtil.PermissionCheckCallBack() {
                            @Override
                            public void onHasPermission() {
                                Intent intent = new Intent(mContext, VersionUpdateService.class);
                                intent.putExtra("Key_Down_Url", apkUrl);
                                mContext.startService(intent);
                                ((TextView) view).setText("正在下载");
                                view.setEnabled(false);
                            }
                        });
                    }
                } else {
                    PermissionApplyUtil.getInstance().checkStoragePermission(mContext, new PermissionApplyUtil.PermissionCheckCallBack() {
                        @Override
                        public void onHasPermission() {
                            Intent intent = new Intent(mContext, VersionUpdateService.class);
                            intent.putExtra("Key_Down_Url", apkUrl);
                            mContext.startService(intent);
                            ((TextView) view).setText("正在下载");
                            view.setEnabled(false);
                        }
                    });
                }

总结:其实呢,这些坑都是在实际开发中遇到的,然后查阅相关的资料,一步一步的解决。项目完成后,就得总结下,吸取下教训。这里主要是版本兼容性问题,所以作为开发者还是特时刻关注andorid 版本的更新的内容,新特性,才能在开发中少走弯路,游刃有余。

希望能帮助到各位看官,如有帮助,点个赞哦!~
若发现错误,请留言,一起讨论,能力有限,敬请原谅。

相关文章

网友评论

      本文标题:android:版本更新概念,整个流程,及解决相关坑

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