Android下载更新(兼容7.0文件,支持5.0通知栏显示Icon)

参考文献:

1.okhttp获取下载进度:https://github.com/lizhangqu/CoreProgress

2.HttpURLConnection下载文件:https://github.com/feicien/android-auto-update

3.5.0通知栏显示Icon:https://github.com/WVector/AppUpdate

用两种方式实现了更新的效果,项目结构如下:

5.0通知栏不显示icon的解决方式:


    /**
     * 初始化通知栏
     */
    private void initNotify() {
        mNotifyManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        mBuilder = new NotificationCompat.Builder(this);
        mBuilder.setContentTitle(String.format("%s V%s", getString(getApplicationInfo().labelRes), mVersionName))
                //通知首次出现在通知栏,带上升动画效果的
                .setTicker("捷账宝新版本诚邀体验")
                //通常是用来表示一个后台任务
                .setOngoing(true)
                //通知产生的时间,会在通知信息里显示,一般是系统获取到的时间
                .setWhen(System.currentTimeMillis());

        //解决5.0系统通知栏白色Icon的问题
        Drawable appIcon = getAppIcon(this);
        Bitmap drawableToBitmap = null;
        if (appIcon != null) {
            drawableToBitmap = drawableToBitmap(appIcon);
        }
        if (drawableToBitmap != null) {
            mBuilder.setSmallIcon(R.mipmap.app_update_icon);
            mBuilder.setLargeIcon(drawableToBitmap);
        } else {
            mBuilder.setSmallIcon(getApplicationInfo().icon);
        }
    }

    /**
     * 合成更新的Icon
     *
     * @param drawable
     * @return
     */
    public Bitmap drawableToBitmap(Drawable drawable) {
        Bitmap bitmap = Bitmap.createBitmap(
                drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight(),
                drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }

    /**
     * 获取App的Icon
     *
     * @param context
     * @return
     */
    public Drawable getAppIcon(Context context) {
        try {
            return context.getPackageManager().getApplicationIcon(context.getPackageName());
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

7.0文件读取的解决方式:

1.配置文件增加provider

    <!--authorities:app的包名.fileProvider (fileProvider可以随便写),上面采用的是gradle的替换直接写成包名也可以,但是推荐这种方式,多渠道分包的时候不用关心了-->
    <!--grantUriPermissions:必须是true,表示授予 URI 临时访问权限-->
    <!--exported:必须是false-->
    <provider
         android:name="android.support.v4.content.FileProvider"
         android:authorities="${applicationId}.fileProvider"
         android:exported="false"
         android:grantUriPermissions="true">
         <meta-data
             android:name="android.support.FILE_PROVIDER_PATHS"
             android:resource="@xml/provider_paths"/>
    </provider>

2.在res新建xml文件件,然后新建provider_paths.xml,copy如下代码:

    <?xml version="1.0" encoding="utf-8"?>
    <paths xmlns:android="http://schemas.android.com/apk/res/android">
        <!--external-path中,-->
        <!--name:可以随意写,只是一个名字-->
        <!--path:表示文件路径,.表示所有-->
        <external-path name="external_files" path="."/>
    </paths>

3.启动安装界面的时候

    /**
     * 启动安装界面
     *
     * @return
     */
    private Intent getInstallIntent() {
        File apkInstallDir = FileManager.getApkInstallDir(getApplicationContext());
        Log.i("TEST", "路径---" + apkInstallDir.getAbsolutePath());
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            intent.setDataAndType(Uri.fromFile(apkInstallDir), "application/vnd.android.package-archive");
        } else {
            // 声明需要的零时权限
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            // 第二个参数,即第一步中配置的authorities
            Uri contentUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileProvider",
                    apkInstallDir);
            intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
        }
        return intent;
    }

methone:第一种方式是用IntentService实现的,这种方式的好处是,下载完后,通知栏自动移除,服务自动销毁

/**
 * @author zhouhui
 */
public class DownloadIntentService extends IntentService {
    /**
     * 默认超时时间
     */
    private static final int DEFAULT_TIME_OUT = 10 * 1000;
    /**
     * 缓存大小
     */
    private static final int BUFFER_SIZE = 10 * 1024;

    public static final String INTENT_VERSION_NAME = "service.intent.version_name";
    public static final String INTENT_DOWNLOAD_URL = "service.intent.download_url";
    public static final String SAVE_FILE_NAME = "CMPP.apk";

    private static final int NOTIFICATION_ID = UUID.randomUUID().hashCode();

    private String mDownloadUrl;
    private String mVersionName;

    private NotificationManager mNotifyManager;
    private NotificationCompat.Builder mBuilder;


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

    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public DownloadIntentService(String name) {
        super(name);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // 移除通知
        mNotifyManager.cancel(NOTIFICATION_ID);
    }


    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            mVersionName = intent.getStringExtra(INTENT_VERSION_NAME);
            mDownloadUrl = intent.getStringExtra(INTENT_DOWNLOAD_URL);
            if (!TextUtils.isEmpty(mVersionName) && !TextUtils.isEmpty(mDownloadUrl)) {
                Log.i("TEST", "INTENT_VERSION_NAME---" + mVersionName);
                Log.i("TEST", "INTENT_DOWNLOAD_URL---" + mDownloadUrl);
                initNotify();
                startDownload();
            }
        }
    }

    /**
     * 初始化通知栏
     */
    private void initNotify() {
        mNotifyManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        mBuilder = new NotificationCompat.Builder(this);
        mBuilder.setContentTitle(String.format("%s V%s", getString(getApplicationInfo().labelRes), mVersionName))
                //通知首次出现在通知栏,带上升动画效果的
                .setTicker("捷账宝新版本诚邀体验")
                //通常是用来表示一个后台任务
                .setOngoing(true)
                //通知产生的时间,会在通知信息里显示,一般是系统获取到的时间
                .setWhen(System.currentTimeMillis());

        //解决5.0系统通知栏白色Icon的问题
        Drawable appIcon = getAppIcon(this);
        Bitmap drawableToBitmap = null;
        if (appIcon != null) {
            drawableToBitmap = drawableToBitmap(appIcon);
        }
        if (drawableToBitmap != null) {
            mBuilder.setSmallIcon(R.mipmap.app_update_icon);
            mBuilder.setLargeIcon(drawableToBitmap);
        } else {
            mBuilder.setSmallIcon(getApplicationInfo().icon);
        }
    }

    /**
     * 开始下载
     */
    private void startDownload() {
        InputStream in = null;
        FileOutputStream out = null;
        try {
            URL url = new URL(mDownloadUrl);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

            urlConnection.setRequestMethod("GET");
            urlConnection.setDoOutput(false);
            urlConnection.setConnectTimeout(DEFAULT_TIME_OUT);
            urlConnection.setReadTimeout(DEFAULT_TIME_OUT);
            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();
            String apkDownLoadDir = FileManager.getApkDownLoadDir(getApplicationContext());
            File apkFile = new File(apkDownLoadDir, SAVE_FILE_NAME);
            out = new FileOutputStream(apkFile);
            byte[] buffer = new byte[BUFFER_SIZE];

            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) {
                    updateProgress(progress);
                }
                oldProgress = progress;
            }
            // 下载完成
            installAPk();
        } catch (Exception e) {
            if (e != null) {
                Log.e("TEST", "download apk file error:" + e.getMessage());
            } else {
                Log.e("TEST", "download apk file error:下载失败");
            }
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException ignored) {

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

                }
            }
        }

    }

    /**
     * 更新通知栏的进度(下载中)
     *
     * @param progress
     */
    private void updateProgress(int progress) {
        mBuilder.setContentText(String.format("正在下载:%1$d%%", progress)).setProgress(100, progress, false);
        PendingIntent pendingintent = PendingIntent.getActivity(this, 0, new Intent(), PendingIntent
                .FLAG_UPDATE_CURRENT);
        mBuilder.setContentIntent(pendingintent);
        mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
    }


    /**
     * 开始安装
     */
    private void installAPk() {
        startActivity(getInstallIntent());
    }

    /**
     * 启动安装界面
     *
     * @return
     */
    private Intent getInstallIntent() {
        File apkInstallDir = FileManager.getApkInstallDir(getApplicationContext());
        Log.i("TEST", "路径---" + apkInstallDir.getAbsolutePath());
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            intent.setDataAndType(Uri.fromFile(apkInstallDir), "application/vnd.android.package-archive");
        } else {
            // 声明需要的零时权限
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            // 第二个参数,即第一步中配置的authorities
            Uri contentUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileProvider",
                    apkInstallDir);
            intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
        }
        return intent;
    }

    /**
     * 合成更新的Icon
     *
     * @param drawable
     * @return
     */
    public Bitmap drawableToBitmap(Drawable drawable) {
        Bitmap bitmap = Bitmap.createBitmap(
                drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight(),
                drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }

    /**
     * 获取App的Icon
     *
     * @param context
     * @return
     */
    public Drawable getAppIcon(Context context) {
        try {
            return context.getPackageManager().getApplicationIcon(context.getPackageName());
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

methtwo:第二种方式是用Service实现的,这种方式的好处是,下载完成后可以监听安装完成后删除安装包

/**
 * @author zhouhui
 */
public class DownloadService extends Service {
    public static final String INTENT_VERSION_NAME = "service.intent.version_name";
    public static final String INTENT_DOWNLOAD_URL = "service.intent.download_url";
    public static final String SAVE_FILE_NAME = "CMPP.apk";

    private static final int NOTIFICATION_ID = UUID.randomUUID().hashCode();

    private String mDownloadUrl;
    private String mVersionName;

    private NotificationManager mNotifyManager;
    private NotificationCompat.Builder mBuilder;

    /**
     * 安装成功的广播
     */
    private InstalledReceiver mInstalledReceiver;

    private Context mContext;


    @Override
    public void onDestroy() {
        super.onDestroy();
        // 移除通知
        mNotifyManager.cancel(NOTIFICATION_ID);
        unregisterReceiver(mInstalledReceiver);
    }

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


    @Override
    public void onCreate() {
        super.onCreate();
        mContext = this;
        initNotify();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null) {
            mVersionName = intent.getStringExtra(INTENT_VERSION_NAME);
            mDownloadUrl = intent.getStringExtra(INTENT_DOWNLOAD_URL);
            if (!TextUtils.isEmpty(mVersionName) && !TextUtils.isEmpty(mDownloadUrl)) {
                Log.i("TEST", "INTENT_VERSION_NAME---" + mVersionName);
                Log.i("TEST", "INTENT_DOWNLOAD_URL---" + mDownloadUrl);
                initNotify();
                startDownload();
            }
        }
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 初始化通知栏
     */
    private void initNotify() {
        mNotifyManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        mBuilder = new NotificationCompat.Builder(this);
        mBuilder.setContentTitle(String.format("%s V%s", getString(getApplicationInfo().labelRes), mVersionName))
                //通知首次出现在通知栏,带上升动画效果的
                .setTicker("捷账宝新版本诚邀体验")
                //通常是用来表示一个后台任务
                .setOngoing(true)
                //通知产生的时间,会在通知信息里显示,一般是系统获取到的时间
                .setWhen(System.currentTimeMillis());

        //解决5.0系统通知栏白色Icon的问题
        Drawable appIcon = getAppIcon(this);
        Bitmap drawableToBitmap = null;
        if (appIcon != null) {
            drawableToBitmap = drawableToBitmap(appIcon);
        }
        if (drawableToBitmap != null) {
            mBuilder.setSmallIcon(R.mipmap.app_update_icon);
            mBuilder.setLargeIcon(drawableToBitmap);
        } else {
            mBuilder.setSmallIcon(getApplicationInfo().icon);
        }

        //设置通知栏常住(服务前台运行)
//        startForeground(NOTIFICATION_ID, mBuilder.build());

        mInstalledReceiver = new InstalledReceiver(handler);
        IntentFilter filter = new IntentFilter();
        filter.addAction("android.intent.action.PACKAGE_ADDED");
        filter.addAction("android.intent.action.PACKAGE_REMOVED");
        filter.addDataScheme("package");
        registerReceiver(mInstalledReceiver, filter);
    }

    /**
     * 开始下载
     */
    private void startDownload() {
        OkHttpClient client = new OkHttpClient();
        Request.Builder builder = new Request.Builder();
        builder.url(mDownloadUrl);
        builder.get();
        Call call = client.newCall(builder.build());

        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                e.printStackTrace();
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                ResponseBody responseBody = ProgressHelper.withProgress(response.body(), new ProgressUIListener() {
                    @Override
                    public void onUIProgressStart(long totalBytes) {
                        super.onUIProgressStart(totalBytes);
                        //开始下载
                    }

                    int oldProgress = 0;

                    @Override
                    public void onUIProgressChanged(long numBytes, long totalBytes, float percent, float speed) {
                        //下载中
                        Log.e("TAG", "=============start===============");
                        Log.e("TAG", "numBytes:" + numBytes);
                        Log.e("TAG", "totalBytes:" + totalBytes);
                        Log.e("TAG", "percent:" + percent);
                        Log.e("TAG", "speed:" + speed);
                        Log.e("TAG", "============= end ===============");

                        int progress = (int) (100 * percent);
                        // 如果进度与之前进度相等,则不更新,如果更新太频繁,否则会造成界面卡顿
                        if (progress != oldProgress) {
                            updateProgress(progress);
                        }
                        oldProgress = progress;
                    }

                    @Override
                    public void onUIProgressFinish() {
                        super.onUIProgressFinish();
                        // 下载完成
                        installAPk();
                        updateProgressCompleted();
                    }
                });

                BufferedSource source = responseBody.source();

                String apkDownLoadDir = FileManager.getApkDownLoadDir(mContext.getApplicationContext());
                File apkFile = new File(apkDownLoadDir, SAVE_FILE_NAME);

                apkFile.delete();
                apkFile.getParentFile().mkdirs();
                apkFile.createNewFile();

                BufferedSink sink = Okio.buffer(Okio.sink(apkFile));
                source.readAll(sink);
                sink.flush();
                source.close();
            }
        });
    }

    /**
     * 更新通知栏的进度(下载中)
     *
     * @param progress
     */
    private void updateProgress(int progress) {
        mBuilder.setContentText(String.format("正在下载:%1$d%%", progress)).setProgress
                (100, progress, false);
        mBuilder.setContentIntent(getDefalutIntent());
        mNotifyManager.notify(NOTIFICATION_ID, mBuilder.build());
    }

    /**
     * 更新通知栏的进度(下载完成)
     */
    private void updateProgressCompleted() {
        mBuilder.setContentText("下载完成,点击安装").setProgress(100, 100, false);
        mBuilder.setContentIntent(getInstallPendingIntent());
        Notification build = mBuilder.build();
        //用户单击通知后自动消失
        build.flags = Notification.FLAG_AUTO_CANCEL;
        //只有全部清除时,Notification才会清除 ,不清楚该通知(QQ的通知无法清除,就是用的这个)
        //build.flags = Notification.FLAG_NO_CLEAR;
        mNotifyManager.notify(NOTIFICATION_ID, build);
    }

    /**
     * 获取默认的通知栏事件
     *
     * @return
     */
    public PendingIntent getDefalutIntent() {
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, new Intent(), PendingIntent
                .FLAG_UPDATE_CURRENT);
        return pendingIntent;
    }

    /**
     * 获取安装的通知栏事件
     *
     * @return
     */
    public PendingIntent getInstallPendingIntent() {
        // 表示相应的PendingIntent已经存在,则取消前者,然后创建新的PendingIntent,这个有利于数据保持为最新的,可以用于即时通信的通信场景
        //PendingIntent.FLAG_CANCEL_CURRENT
        // 表示更新的PendingIntent
        //PendingIntent.FLAG_UPDATE_CURRENT
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 1, getInstallIntent(), PendingIntent
                .FLAG_UPDATE_CURRENT);
        return pendingIntent;
    }

    /**
     * 开始安装
     */
    private void installAPk() {
        startActivity(getInstallIntent());
        //设置通知栏常住(服务退出前台运行转后台)
        //stopForeground(true);
    }

    /**
     * 启动安装界面
     *
     * @return
     */
    private Intent getInstallIntent() {
        File apkInstallDir = FileManager.getApkInstallDir(getApplicationContext());
        Log.i("TEST", "路径---" + apkInstallDir.getAbsolutePath());
        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            intent.setDataAndType(Uri.fromFile(apkInstallDir), "application/vnd.android.package-archive");
        } else {
            // 声明需要的零时权限
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            // 第二个参数,即第一步中配置的authorities
            Uri contentUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileProvider",
                    apkInstallDir);
            intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
        }
        return intent;
    }


    /**
     * 安装成功的广播
     */
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                // 安装成功
                case InstalledReceiver.INSTALL_SUCCESS:
                    // 移除通知
                    mNotifyManager.cancel(NOTIFICATION_ID);
                    // 删除安装包
                    deleteApk();
                    /*** stop service *****/
                    stopSelf();
                    break;
                default:
                    break;
            }
        }
    };

    /**
     * 安装完成后删除安装包
     */
    private void deleteApk() {
        File apkInstallDir = FileManager.getApkInstallDir(getApplicationContext());
        if (apkInstallDir != null && apkInstallDir.exists()) {
            if (apkInstallDir.delete()) {
                Toast.makeText(this, "捷账宝安装包已删除", Toast.LENGTH_LONG).show();
            } else {
                Log.i("TEST", "捷账宝安装包删除失败");
            }
        } else {
            Log.i("TEST", "安装包不存在");
        }
    }


    /**
     * 合成更新的Icon
     *
     * @param drawable
     * @return
     */
    public Bitmap drawableToBitmap(Drawable drawable) {
        Bitmap bitmap = Bitmap.createBitmap(
                drawable.getIntrinsicWidth(),
                drawable.getIntrinsicHeight(),
                drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
        drawable.draw(canvas);
        return bitmap;
    }

    /**
     * 获取App的Icon
     *
     * @param context
     * @return
     */
    public Drawable getAppIcon(Context context) {
        try {
            return context.getPackageManager().getApplicationIcon(context.getPackageName());
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Demo:http://download.csdn.net/download/zhouhui520w/10169671

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值