Android更新使用Service自动下载apk自动安装 (可暂停和继续)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_33718648/article/details/79726402
一、首先需要判断版本号,根据版本号显示提示更新的弹窗,并启动下载服务
     
            // DialogUtil是一个弹出弹窗的工具类,可以根据自己的需求定制。
            DialogUtil.showupdataDialog(mWXSDKInstance.getUIContext(), title, content, new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //下面几行代码用来判断是否在使用wifi下载
                ConnectivityManager connectivityManager=(ConnectivityManager)getSystemService(mWXSDKInstance.getContext(),Context.CONNECTIVITY_SERVICE);
                TelephonyManager mTelephony = (TelephonyManager)getSystemService(mWXSDKInstance.getContext(),Context.TELEPHONY_SERVICE);
                NetworkInfo info = connectivityManager.getActiveNetworkInfo();
                int netType = info.getType();
                 
                if (netType == ConnectivityManager.TYPE_WIFI) {  //是在使用WIFI
                    //这里的 ApkDownloadUtils是下载的工具类,用于启动下载服务
                    ApkDownloadUtils.startDownload(mWXSDKInstance.getContext(), url, FileUtils.getApkFile(Constant.FILE_NAME_APK) + "yunyigc.apk", fileSize);
                } else {                                          //在使用流量
                    //弹出confirm弹窗,确定是否用流量更新
                    DialogUtil.showDialogConfirm(mWXSDKInstance.getContext(), "提醒", "当前非WiFi环境,继续使用会产生流量费用,是否继续下载??",
                        "取消",new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                        }
                    }, "确定", new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            ApkDownloadUtils.startDownload(mWXSDKInstance.getContext(), url, FileUtils.getApkFile(Constant.FILE_NAME_APK) + "yunyigc.apk", fileSize);

                        }
                    },View.VISIBLE).show();
                }
            }
        }, isCancel).show();
// DialogUtil是一个弹出弹窗的工具类,可以根据自己的需求定制。
public class ApkDownloadUtils {

    /**
     * @param context
     * @param url
     * @param filePath
     * @param fileSize
     */
    public static void startDownload(Context context, String url, String filePath, long fileSize) {
        Intent i = new Intent(context, DownloadService.class);
        i.putExtra(DownloadService.DOWNLOAD_URL, url);
        i.putExtra(DownloadService.DOWNLOAD_PATH, filePath);
        i.putExtra(DownloadService.DOWNLOAD_SIZE, fileSize);
        context.startService(i);
    }
}
//得到要下载到的文件路径
public class FileUtils {
public static String getApkFile(String path) {
String savePath = null;
String storageState = Environment.getExternalStorageState();
if (storageState.equals(Environment.MEDIA_MOUNTED)) {
savePath = Environment.getExternalStorageDirectory()
.getAbsolutePath() + path;
File file = new File(savePath);
if (!file.exists()) {
file.mkdirs();
}
}
if (TextUtils.isEmpty(savePath)) {
return null;
}
return savePath;
}

public static final boolean saveBytesToFile(byte[] bytes, File file) {
if (bytes == null) {
return false;
}

ByteArrayInputStream bais = null;
BufferedOutputStream bos = null;
try {
file.getParentFile().mkdirs();
file.createNewFile();

bais = new ByteArrayInputStream(bytes);
bos = new BufferedOutputStream(new FileOutputStream(file));

int size;
byte[] temp = new byte[1024];
while ((size = bais.read(temp, 0, temp.length)) != -1) {
bos.write(temp, 0, size);
}

bos.flush();

return true;

} catch (IOException e) {
e.printStackTrace();
} finally {
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
bos = null;
}
if (bais != null) {
try {
bais.close();
} catch (IOException e) {
e.printStackTrace();
}
bais = null;
}
}
return false;
}
}
二、在服务中下载文件,网络请求用的是okhttp3框架
下载服务,在下载服务中,首先在开启服务的时候需要传递必要的参数给通知控制工具类 UploadUtil,该类主要是用于对通知栏下载进度和点击事件的控制。
public class DownloadService extends Service {
    private List<String> urls = new ArrayList<String>();
    private final String FILE_DIR = Environment.getExternalStorageDirectory().toString() + "";
    private final int DOWNLOAD_ERROR = 0;
    private final int DOWNLOAD_SUCCESS = 1;
    private final int DOWNLOAD_PROGRESS = 2;
    private final int DOWNLOAD_PASUE=3;
    public static final String DOWNLOAD_URL = "url";
    public static final String DOWNLOAD_PATH = "path";
    public static final String DOWNLOAD_SIZE = "size";
    public static final String IS_PASUE="isPasue";
    public static final String NOTIFY_LARGE_ICON = "large_icon";
    public static final String NOTIFY_SMALL_ICON = "small_icon";
    private String url;
    private String path;
    private long size;
    //静态变量用于控制下载的暂停和开始
    public static boolean isPasue=false;
    Handler handler;
    UploadUtil downloadUtil;
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        initHandler();
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null) {
            url = intent.getStringExtra(DOWNLOAD_URL);
            path = intent.getStringExtra(DOWNLOAD_PATH);
            size = intent.getLongExtra(DOWNLOAD_SIZE, 0);
            //初始化通知栏下载通知栏工具类,传入必要的参数下载地址,下载到的文件路径,文件大小
            downloadUtil=new UploadUtil(DownloadService.this,url,path,size);
            //开始下载
            startDownload(url, path, size);
        }

        return super.onStartCommand(intent, flags, startId);
    }
    //更新UI的处理器
    private void initHandler() {
        handler = new Handler(getMainLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                if (msg.what == DOWNLOAD_PROGRESS || msg.what == DOWNLOAD_SUCCESS) {
                    Bundle b = msg.getData();
                    String file = b.getString("file");
                    long max = b.getLong("max");
                    long current = b.getLong("current");
                    String url = b.getString("url");
                    int percent = (int) (current * 100 / max);
                    downloadUtil.notifyChange(DownloadService.this, getString(R.string.app_name), percent);
                    if (msg.what == DOWNLOAD_SUCCESS && urls.contains(url)) {
                        File filetemp = new File(file);
                        if (filetemp.exists()) {
                            Log.i("lllttt", "DownloadService : " + filetemp.getAbsolutePath());
                            update(filetemp, DownloadService.this.getApplicationContext(),size);
                            Toast.makeText(DownloadService.this, R.string.completed_download, Toast.LENGTH_SHORT).show();
                        }
                        urls.remove(url);
                    }
                } else if (msg.what == DOWNLOAD_ERROR) {
                    Bundle b = msg.getData();
                    String name = b.getString("name");
                    String url = b.getString("url");
                    String file = b.getString("file");
                    String message = b.getString("error_message");
                    Toast.makeText(DownloadService.this, message, Toast.LENGTH_LONG).show();
                    if (urls.contains(url)) {
                        urls.remove(url);
                    }
                    File fileTemp = new File(file);
                    if (fileTemp != null && fileTemp.exists()) {
                        fileTemp.delete();
                    }
                    Toast.makeText(DownloadService.this, String.format(getString(R.string.download_fail), name), Toast.LENGTH_LONG).show();
                }else{
                    if (urls.contains(url)) {
                        urls.remove(url);
                    }
                    Bundle b = msg.getData();
                    long current = b.getLong("current");
                    long max = b.getLong("max");
                    int percent = (int) (current * 100 / max);
                    downloadUtil.notifyPasue(DownloadService.this,"已暂停下载",percent);
                    Toast.makeText(DownloadService.this,"已暂停下载", Toast.LENGTH_LONG).show();
                }
            }
        };
    }

    File dir;
    long downloadLength=0; //记录已经下载的文件长度

    /**
     * @param url
     * @param filePath
     * @param size
     */
    private void startDownload(final String url, String filePath, final long size) {
          dir= new File(filePath);
        try {
            //如果文件已经存在,并且文件大小大于等于要下载的文件大小,就将文件删除
            if (dir.exists()&&dir.length()>=size) {
                dir.delete();
            }
            else if(dir.exists()){//如果文件只是已存在,那么获取文件大小
              downloadLength=dir.length();
            }
            dir.createNewFile();//如果文件不存在,创建新文件,文件存在,不作处理
        } catch (IOException e) {
            e.printStackTrace();
        }
        if (!TextUtils.isEmpty(url) && !urls.contains(url)) {
            urls.add(url);
        } else {
            return;
        }
        OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(120, TimeUnit.SECONDS)
                .readTimeout(120, TimeUnit.SECONDS)
                .build();
        /**
         * HTTP请求是有一个Header的,里面有个Range属性是定义下载区域的,它接收的值是一个区间范围,
         * 比如:Range:bytes=0-10000。这样我们就可以按照一定的规则,将一个大文件拆分为若干很小的部分,
         * 然后分批次的下载,每个小块下载完成之后,再合并到文件中;这样即使下载中断了,重新下载时,
         * 也可以通过文件的字节长度来判断下载的起始点,然后重启断点续传的过程,直到最后完成下载过程。
         * 这里是需要后台来做配合的
         */
        Request request;
        String downPoint=String.valueOf(downloadLength);
        request = new Request.Builder().url(url).header("Range",downPoint).build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call,final IOException e) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(DownloadService.this, "请求失败", Toast.LENGTH_SHORT).show();
                    }
                });
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.e("message-----","正在下载2"+"-code:"+response.code());
                if (response != null && response.code() == 200) {
                    InputStream is = null;
                    Log.e("message-----","正在下载3");
                    FileOutputStream out = null;
                    long total = 0;
                    try {
                        ResponseBody body = response.body();
                        is = body.byteStream();
                                         //可以看一下 RandomAccessFile的用法
                        RandomAccessFile savedFile=null;
                        savedFile=new RandomAccessFile(dir,"rw");
                         //跳过已经下载的字节
                        savedFile.seek(downloadLength);
                        total = size;
                        byte[] buf = new byte[2048];
                        int len = 0;
                        long lastTime = System.currentTimeMillis();
                        while ((len = is.read(buf)) != -1&&!isPasue) {//当文件还没有读完,并且不是暂停状态,也就是暂停状态下停止
                            savedFile.write(buf, 0, len);
                            downloadLength += len;
                            long currentTime = System.currentTimeMillis();
                            if (currentTime - lastTime > 1000) {
                                Message msg = handler.obtainMessage();
                                msg.what = DOWNLOAD_PROGRESS;
                                Bundle b = msg.getData();
                                b.putString("file", dir.getAbsolutePath());
                                b.putLong("max", total);
                                b.putLong("current", downloadLength);
                                b.putString("name", "xinao.apk");
                                handler.sendMessage(msg);
                                lastTime = currentTime;
                            }
                        }
                        Message msg = handler.obtainMessage();
                        if(isPasue){//更新下载通知栏的视图效果
                            msg.what=DOWNLOAD_PASUE;
                            is.close();
                        }else{
                            msg.what = DOWNLOAD_SUCCESS;
                        }
                        Bundle b = msg.getData();
                        b.putString("file", dir.getAbsolutePath());
                        b.putLong("max", total);
                        b.putLong("current", downloadLength);
                        b.putString("url", url);
                           //发送更新进度条的消息
                        handler.sendMessageDelayed(msg, 1000);
                        savedFile.close();
                    } catch (Exception e) {
                        if (downloadLength == 0 || downloadLength != total) {
                            Message msg = handler.obtainMessage();
                            Bundle b = msg.getData();
                            b.putString("url", url);
                            b.putString("name", "xinao.apk");
                            b.putString("file", dir.getAbsolutePath());
                            b.putString("error_message", "message: " + e.getMessage());
                            Log.e("Error-------",e.getMessage());
                            msg.what = DOWNLOAD_ERROR;
                               //发送包含错误信息的消息
                            handler.sendMessage(msg);
                        }
                    } finally {
                        if (is != null) {
                            is.close();
                        }
                        if (out != null) {
                            out.close();
                        }
                    }

                }
            }
        });
    }


    /**
     * 安装应用
     */
    public static void update(File apkFile, Context context,long size) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        if (apkFile != null && apkFile.exists()&&apkFile.length()==size) {
            chmod(apkFile.getAbsolutePath());//授权
            intent.setDataAndType(Uri.fromFile(apkFile),
                    "application/vnd.android.package-archive");
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        } else {
            Toast.makeText(context, "安装失败,安装文件未找到", Toast.LENGTH_SHORT).show();
        }
    }

    public static void chmod(String pathc) {
        String chmodCmd = "chmod 666 " + pathc;
        try {
            Runtime.getRuntime().exec(chmodCmd);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
三、用广播接收顶部通知栏的点击事件,并通过改变服务中的静态变量启动和暂停下载服务
public class ApkDownReceiver extends BroadcastReceiver{
    /**
     * @param context
     * @param intent
     */
    @Override
    public void onReceive(Context context, Intent intent) {
            isPasue=!isPasue;
            if(isPasue){//改变完状态如果是暂停状态,那么不在通知中做其他操作,下载服务类中可以接收到全局变量pasue的变化
                       //然后在服务中停止下载,并更新下载的通知栏状态
                Log.e("Error-------","是否暂停"+isPasue);
            }else {//改变后是下载状态,那么就启动服务继续下载
                String url=intent.getStringExtra(DownloadService.DOWNLOAD_URL);
                String filePath=intent.getStringExtra(DownloadService.DOWNLOAD_PATH);
                long fileSize=intent.getLongExtra(DownloadService.DOWNLOAD_SIZE,0);
                Intent startService=new Intent(context,DownloadService.class);
                startService.putExtra(DownloadService.DOWNLOAD_URL,url);
                startService.putExtra(DownloadService.DOWNLOAD_PATH,filePath);
                startService.putExtra(DownloadService.DOWNLOAD_SIZE,fileSize);
                context.startService(startService);
            }
    }
}

猜你喜欢

转载自blog.csdn.net/qq_33718648/article/details/79726402
今日推荐