美文网首页
Android应用内更新(适配Android Q)

Android应用内更新(适配Android Q)

作者: 雨落川川 | 来源:发表于2020-04-06 20:47 被阅读0次

下班,走路,春寒料峭
想,你是人间的四月天
霎时风起,一川烟草,满城风絮,梅子黄时雨
可惜了,此时无雨
喜欢下雨,要等梅子黄时。

青玉案·凌波不过横塘路

作者:[贺铸]

凌波不过横塘路。但目送、芳尘去。锦瑟华年谁与度。月桥花院,琐窗朱户。只有春知处。
飞云冉冉蘅皋暮。彩笔新题断肠句。若问闲情都几许。一川烟草,满城风絮。梅子黄时雨。

书归正传,之前写的Android应用内更新组件,从6.0开始,为了适配7.0改动了一次,为了适配8.0又改了,前段时间又为了适配Android Q又改了,累积下来的冗余代码相当多,程序员都是有强迫症的,恰好最近任务不紧不慢,抽空整理起来。

第一步:检查是否有新的版本

应用内的app.gradle文件中已经配置了versionCode和VersionName,我们这里需要从服务端请求一个配置文件,用配置文件的版本信息和本地的进行对比,从而决定是否提示用户更新。

a.使用PackageManager 获取本地versionCode
PackageManager packageManager = mContext.getPackageManager();
        try {
            PackageInfo info = packageManager.getPackageInfo(mContext.getPackageName(), 0);
            if (info != null) {
               int vLocalCode = info.versionCode;
            }
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
b.Retrofit2请求服务端配置文件

我这里的配置文件是这样定义的

{
    "data": {
        "downUrl": "http://www.xx.com/update/app-release.apk",
        "isForceUpdate": "1",
        "preBaselineCode": "1",
        "updateLog": "版本更新内容:\r\n1.更新了...。\r\n2.适配了...。",
        "versionCode": "3",
        "versionName": "1.1.3"
    },
    "message": "操作成功!",
    "status": 1
}

当然这个文件的定义随意,但必须包含了版本信息,更新提示,apk的下载地址等要素。

第二步:提示用户有新版本,让用户选择是否更新

这一步通过一个自定的Dialog实现,内容大概长成这样:



这个并没有什么技术含量,代码就不贴了,这里吐槽一下,有的应用只放更新的Button,不更新就无法使用,这种做法很不友好,所以这里放了取消的Button,只是关闭当前弹窗,不更新可以继续使用。

第三步:下载新的安装包
 private void downLoadApk() {
        Call<ResponseBody> download = apiService.download(downloadUrl);
        download.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {


                String path = mContext.getExternalFilesDir(null).getPath() + "/download/myApk";

                Thread mThread = new Thread() {
                    @Override
                    public void run() {
                        super.run();
                        writeResponseToDisk(path, response, downloadListener);
                    }
                };
                mThread.start();
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                t.printStackTrace();
            }
        });
    }

Retrofit2请求,但是这里我们需要获取到下载文件的进度,所以在onResponse回调中,开启子线程,做一些处理

  private static void writeResponseToDisk(String path, Response<ResponseBody> response, DownloadListener downloadListener) {
        writeFileFromIS(new File(path), response.body().byteStream(), response.body().contentLength(), downloadListener);
    }

同是,我们定义了一个DownLoadListener接口,开放方法,对不同的情况做不同的处理

public interface DownloadListener {
    void onStart();

    void onProgress(int progress);

    void onFinish(File file);

    void onFail(String errorInfo);
}

继续处理我们的下载内容,其实也就是文件流的写入过程,很基础的东西,唯一不同是我们在不同的过程中调用了不同的监听方法,用来在通知中提示,还有一点需要注意一下,android Q 对于本地的文件读取写入机制做了改动,具体的很多文档都有说,这里不再赘述,这里只把代码贴出来,亲测可用。

 private static void writeFileFromIS(File file, InputStream inputStream, long totalLength, DownloadListener downloadListener) {
        downloadListener.onStart();
        if (!file.exists()) {
            if (!Objects.requireNonNull(file.getParentFile()).exists()) {
                file.getParentFile().mkdir();
            } else {
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                    downloadListener.onFail("createNewFile IoException");
                }
            }
        }

        OutputStream os = null;
        long currentLength = 0;

        try {
            os = new BufferedOutputStream(new FileOutputStream(file));
            byte data[] = new byte[sBufferSize];
            int len;

            while ((len = inputStream.read(data, 0, sBufferSize)) != -1) {
                os.write(data, 0, len);
                currentLength += len;
                //计算当前下载进度
                downloadListener.onProgress((int) (100 * currentLength / totalLength));
            }
            downloadListener.onFinish(file);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            if (os != null) {
                try {
                    os.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
第四步:给用户通知下载进度

其实这按照功能,也是第三步的内容,但是按照写代码的顺序,这是第四部分。
通知进度不外乎几种,一种是Notification,通过系统的通知栏给下载进度,一种是在自己应用内,给个进度条或者什么的,可能还有,才疏学浅,慢慢见识,我这里是第一种方式,使用Notification进行通知。

   NotificationManager manager;
   NotificationCompat.Builder mBuilder;
   Notification notification;

  private void initNotification() {
        manager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(String.valueOf(channelId), "通知", NotificationManager.IMPORTANCE_DEFAULT);
            channel.enableLights(true);
            channel.setLightColor(ContextCompat.getColor(mContext, R.color.base_color));
            channel.setShowBadge(true);
            channel.setDescription("我的项目");
            manager.createNotificationChannel(channel);
        }

        mBuilder = new NotificationCompat.Builder(mContext, channelId);
        mBuilder.setContentTitle("正在下载...")
                .setSmallIcon(R.mipmap.logo_fupin)
                .setLargeIcon(BitmapFactory.decodeResource(mContext.getResources(), R.mipmap.logo_fupin))
                .setDefaults(Notification.DEFAULT_LIGHTS)
                .setPriority(NotificationCompat.PRIORITY_MAX)
                .setAutoCancel(false)
                .setContentText("下载进度:" + "0%")
                .setProgress(100, 0, false);
        notification = mBuilder.build();
    }

这里也需适配android Q,新版本加入了NotificationChannel ,必须设置channelId,否则这个通知栏不会显示的。
这里初始化好后,就可以在监听方法中为所欲为的使用了,例如:

     downloadListener = new DownloadListener() {
       
            @Override
            public void onFinish(File file) {
                mBuilder.setProgress(100, 100, false);
                mBuilder.setContentText("下载完成" + 100 + "%");
                notification = mBuilder.build();
                mBuilder.setAutoCancel(true);
                manager.notify(1, notification);
                manager.cancel(1);
                installApk2(file);
            }
        };

篇幅有限,这里只贴出onFinish()方法,用于上线连贯

第四步:安装apk
  /**
     * 安装apk  适配androidQ
     */
    private static void installApk2(File file) {
        try {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                Uri apkUri = FileProvider.getUriForFile(mContext, "我们的包名.provider", file);
                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");
            }
            mContext.startActivity(intent);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

相信很多同学都遇到过下载完成,安装调用不起来的问题,放心,这都是源于Android一次一次的更新,心累,这里只贴代码,核心就是引入了一个FileProvider的东西,这个东西是需要在Manifest.xml文件里配置的

   <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="我们的包名.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>

@xml/file_paths是存放在xml文件夹下的path配置文件,需要自己去配置一个,位置在这:


内容其实更简单:
<?xml version="1.0" encoding="utf-8"?>
<resources >
    <paths>
        <external-path path="" name="download"/>
    </paths>
</resources>

写到这里,大概一个应用内更新的组件就算完成了,交付测试组。

相关文章

网友评论

      本文标题:Android应用内更新(适配Android Q)

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