美文网首页Android开发经验谈Android开发性能优化
把Android的下载更新做成后台服务

把Android的下载更新做成后台服务

作者: 淮左明都 | 来源:发表于2019-03-09 11:11 被阅读59次

    做Android的版本更新,首先要有一个接口从网络获取最新版本,再根据最新版本的url,也就是下载链接进行下载安装。总的流程就是这样,这篇博客就不讲如何获取最新版本了,这个需要就跟写接口的哥们好好交流一下了~

    那么我们开始吧!

    一开始我做的这个功能是这样的:(上图!)


    后来发现,这样用户体验不是很好,需要等到进度条读完才能安装更新,这过程中什么都不能干,的确有点体验差。如果将Apk文件的下载放置在后台,并在通知栏以静默下载的消失去展示,会不会更好呢?先来看一下成果:


    显然,效果好多了,产品经理再也不用担心啦!接下来展开讲诉一下实现过程。

    实现

    1. 创建一个服务Service
      相信这一步大家都会,创建一个DownAPKService继承自Service,写完不要忘记在清单文件AndroidManifest.xml中注册该服务。

    DownAPKService.java

    public class DownAPKService extends Service {
        
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
    

    AndroidManifest.xml

     <service
                android:name=".service.DownAPKService" />
    
    1. 在Service生命周期方法中的具体实现
      这一步贴一下完整代码,相信大家都能看得懂~
    package com.xxx.xxx.service;
    
    import android.app.NotificationChannel;
    import android.app.NotificationManager;
    import android.app.PendingIntent;
    import android.app.Service;
    import android.content.Context;
    import android.content.Intent;
    import android.content.pm.ApplicationInfo;
    import android.content.pm.PackageManager;
    import android.net.Uri;
    import android.os.Build;
    import android.os.Environment;
    import android.os.IBinder;
    import android.os.Vibrator;
    import android.support.v4.app.NotificationCompat;
    import android.util.Log;
    import android.widget.Toast;
    
    import xxx.xxx.logger.Logger;
    import xxx.xxx.goldfields.R;
    
    import org.xutils.common.Callback;
    import org.xutils.http.RequestParams;
    import org.xutils.x;
    
    import java.io.File;
    import java.text.DecimalFormat;
    
    /**
     * @author lam
     * @date 2019/02/27
     */
    public class DownAPKService extends Service {
        private final int NotificationID = 0x10000;
        private NotificationManager mNotificationManager = null;
        private NotificationCompat.Builder builder;
    
        // 文件保存路径(如果有SD卡就保存SD卡,如果没有SD卡就保存到手机包名下的路径)
        private String APK_dir = "";
    
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            initAPKDir();// 创建保存路径
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            System.out.println("onStartCommand");
            // 接收Intent传来的参数:
            // 文件下载路径
            String APK_URL = intent.getStringExtra("apk_url");
            Logger.i("DownAPKService:url=" + APK_URL);
    
            DownFile(APK_URL, APK_dir + "Club.apk");
    
            return super.onStartCommand(intent, flags, startId);
        }
    
        private void initAPKDir() {
            /**
             * 创建路径的时候一定要用[/],不能使用[\],但是创建文件夹加文件的时候可以使用[\].
             * [/]符号是Linux系统路径分隔符,而[\]是windows系统路径分隔符 Android内核是Linux.
             */
            if (isHasSdcard())// 判断是否插入SD卡
            {
                APK_dir = Environment.getExternalStorageDirectory().getAbsolutePath() + "/VersionChecker/"; // 保存到SD卡路径下
            } else {
                APK_dir = getApplicationContext().getFilesDir().getAbsolutePath() + "/VersionChecker/"; // 保存到app的包名路径下
            }
            File destDir = new File(APK_dir);
            if (!destDir.exists()) {// 判断文件夹是否存在
                destDir.mkdirs();
            }
        }
    
        /**
         * @Description 判断是否插入SD卡
         */
        private boolean isHasSdcard() {
            String status = Environment.getExternalStorageState();
            if (status.equals(Environment.MEDIA_MOUNTED)) {
                return true;
            } else {
                return false;
            }
        }
    
        /**
         * @param file_url    下载链接
         * @param target_name 保存路径
         */
        private void DownFile(String file_url, String target_name) {
            RequestParams params = new RequestParams(file_url);
            params.setSaveFilePath(target_name);  //设置下载后的文件保存的位置
            params.setAutoResume(true);  //设置是否在下载是自动断点续传
            params.setAutoRename(true);  //设置是否根据头信息自动命名文件
            x.http().get(params, new Callback.ProgressCallback<File>() {
                @Override
                public void onSuccess(File result) {
                    System.out.println("文件下载完成");
                    Intent installIntent = new Intent(Intent.ACTION_VIEW);
                    System.out.println(result.getPath());
                    Uri uri = Uri.fromFile(new File(result.getPath()));
                    installIntent.setDataAndType(uri, "application/vnd.android.package-archive");
                    installIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    PendingIntent mPendingIntent = PendingIntent.getActivity(DownAPKService.this, 0, installIntent, 0);
                    builder.setContentText("下载完成,请点击安装");
                    builder.setContentIntent(mPendingIntent);
                    mNotificationManager.notify(NotificationID, builder.build());
                    // 震动提示
                    Vibrator vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
                    assert vibrator != null;
                    vibrator.vibrate(250L);// 参数是震动时间(long类型)
                    stopSelf();
                    startActivity(installIntent);// 下载完成之后自动弹出安装界面
                    mNotificationManager.cancel(NotificationID);
                }
    
                @Override
                public void onError(Throwable ex, boolean isOnCallback) {
                    System.out.println("文件下载失败");
                    mNotificationManager.cancel(NotificationID);
                    Toast.makeText(getApplicationContext(), "下载失败,请检查网络!", Toast.LENGTH_SHORT).show();
                }
    
                @Override
                public void onCancelled(CancelledException cex) {
                    System.out.println("文件下载结束,停止下载器");
                }
    
                @Override
                public void onFinished() {
                    System.out.println("文件下载完成");
                }
    
                @Override
                public void onWaiting() {
                    System.out.println("文件下载处于等待状态");
                }
    
                @Override
                public void onStarted() {
                    Toast.makeText(getApplicationContext(), "开始后台下载更新文件...", Toast.LENGTH_SHORT).show();
                    System.out.println("开始下载文件");
                    String id = "my_channel_01";
                    String name = "我是渠道名字";
                    mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
                    // 针对Android 8.0版本对于消息栏的限制,需要加入channel渠道这一概念
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {  //Android 8.0以上
                        NotificationChannel mChannel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_LOW);
                        Log.i("DownAPKService", mChannel.toString());
                        mNotificationManager.createNotificationChannel(mChannel);
                        builder = new NotificationCompat.Builder(getApplicationContext());
                        builder.setSmallIcon(R.drawable.icon);
                        builder.setTicker("正在下载新版本");
                        builder.setContentTitle(getApplicationName());
                        builder.setContentText("正在下载,请稍后...");
                        builder.setNumber(0);
                        builder.setChannelId(id);
                        builder.setAutoCancel(true);
                    } else {    //Android 8.0以下
                        builder = new NotificationCompat.Builder(getApplicationContext());
                        builder.setSmallIcon(R.drawable.icon);
                        builder.setTicker("正在下载新版本");
                        builder.setContentTitle(getApplicationName());
                        builder.setContentText("正在下载,请稍后...");
                        builder.setNumber(0);
                        builder.setAutoCancel(true);
                    }
    
                    mNotificationManager.notify(NotificationID, builder.build());
                }
    
                @Override
                public void onLoading(long total, long current, boolean isDownloading) {
                    System.out.println("文件下载中");
                    int x = Long.valueOf(current).intValue();
                    int totalS = Long.valueOf(total).intValue();
                    builder.setProgress(totalS, x, false);
                    builder.setContentInfo(getPercent(x, totalS));
                    mNotificationManager.notify(NotificationID, builder.build());
                    //当前进度和文件总大小
                    Log.i("DownAPKService", "current:" + current + ",total:" + total);
                }
            });
    
        }
    
        /**
         * @param x     当前值
         * @param total 总值
         *              [url=home.php?mod=space&uid=7300]@return[/url] 当前百分比
         * @Description 返回百分之值
         */
        private String getPercent(int x, int total) {
            String result = "";// 接受百分比的值
            double x_double = x * 1.0;
            double tempresult = x_double / total;
            // 百分比格式,后面不足2位的用0补齐 ##.00%
            DecimalFormat df1 = new DecimalFormat("0.00%");
            result = df1.format(tempresult);
            return result;
        }
    
        /**
         * @return
         * @Description 获取当前应用的名称
         */
        private String getApplicationName() {
            PackageManager packageManager = null;
            ApplicationInfo applicationInfo = null;
            try {
                packageManager = getApplicationContext().getPackageManager();
                applicationInfo = packageManager.getApplicationInfo(getPackageName(), 0);
            } catch (PackageManager.NameNotFoundException e) {
                applicationInfo = null;
            }
            String applicationName = (String) packageManager.getApplicationLabel(applicationInfo);
            return applicationName;
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            stopSelf();
        }
    }
    
    
    1. 注意事项

    (1)在onStartCommand中需要用到的url是通过Intent获取的,在开启服务的Activity或者Fragment中需要传递这个值:

    Intent intent = new Intent(context, DownAPKService.class);
    intent.putExtra("apk_url", downUrl);
    context.startService(intent);
    

    (2)实现下载的方法中用到的是XUtils这个网络框架,大家各持所需,用自己顺手的网络框架即可。之所以使用XUtils是因为它很好的帮我们实现了断点续传的功能。
    (3)上述代码中关于Android通知栏Notification的用法,在Android O(8.0)版本之后新增了渠道的概念,这一部分知识点我会开另外一篇博客着重讲解。我们只需要知道这个部分需要做版本兼容的适配。
    (4)最后,大家别忘记在onDestroy中关闭服务:

    stopSelf();
    

    至此,这个后台服务去更新App的功能就已经实现了,你可以在你的真机或者模拟器上看看效果。

    相关文章

      网友评论

        本文标题:把Android的下载更新做成后台服务

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