手机安全卫士04_02:从服务器下载并安装新版本安装包

最后更新于:2022-04-01 16:16:09

### 文件下载 ### 1. 下载文件业务类 下载文件的操作也属于业务方法,所以在com.liuhao.mobilesafe.engine中创建一个DownloadFileTask下载文件的类。 其中的getFile方法,用于从服务器文件路径上下载文件至本地文件目录。 ~~~ package com.liuhao.mobilesafe.engine; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public class DownloadFileTask { /** * @param path * 服务器文件路径 * @param filepath * 本地文件路径 * @return 本节文件对象 * @throws Exception */ public static File getFile(String path, String filepath) throws Exception { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setReadTimeout(5000); // 读取数据没有异常 if (conn.getResponseCode() == 200) { InputStream is = conn.getInputStream();// 获取文件输入流 File file = new File(filepath);// 本地文件对象 FileOutputStream fos = new FileOutputStream(file);//本地文件输出流 byte[] buffer = new byte[1024]; int length = 0; while ((length = is.read(buffer)) != -1) { fos.write(buffer, 0, length); } fos.flush(); fos.close(); is.close(); return file; } return null; } } ~~~ ### 2.使用下载文件类 在用户点击“确定”后,会进行下载。 其中定义了一个进度条,用来显示下载过程: ~~~ private ProgressDialog pd;// 进度条 pd = new ProgressDialog(this); pd.setMessage("正在下载,请耐心等待。o(∩_∩)o");// 设置进度条显示的内容 builder.setPositiveButton("确定", new OnClickListener() { // 设置用户选择确定时的按键操作 @Override public void onClick(DialogInterface dialog, int which) { Log.i(TAG, "下载pak文件:" + info.getApkurl()); // 判断sd卡是否可用 if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { // 调用子线程进行下载 DownloadFileThreadTask task = new DownloadFileThreadTask( info.getApkurl(), Environment.getExternalStorageDirectory().getPath() + "/aanew.apk"); pd.show();// 显示下载进度条 new Thread(task).start();// 启动子线程 } else { Toast.makeText(getApplicationContext(), "sd卡不可用", Toast.LENGTH_LONG).show(); loadMainUI(); } } }); // 子线程,用于下载文件,因为下载文件比较耗时 private class DownloadFileThreadTask implements Runnable { private String path;// 服务器路径 private String filepath;// 本地文件路径 public DownloadFileThreadTask(String path, String filepath) { this.path = path; this.filepath = filepath; } @Override public void run() { try { File file = DownloadFileTask.getFile(path, filepath); Log.i(TAG, "下载更新apk成功"); pd.dismiss(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(getApplicationContext(), "下载文件失败", Toast.LENGTH_LONG).show(); pd.dismiss(); loadMainUI(); } } } ~~~ ### 3.添加权限 由于下载文件需要对sd卡进行读写,因袭需要sd卡的权限:<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> 写外部存储设备的权限:<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> ### 4.配置服务器端的apk文件(高版本的) 将当前版本号改为2.0,生成一个apk安装包,放到之前指定的目录(%TOMCAT_HOME%\webapps\ROOT),然后再改回来。 [![image](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5a9537bce3.jpg "image")](http://img.blog.csdn.net/20141030113722327) ### 异常处理: ERROR/AndroidRuntime(1540): Caused by: android.view.WindowManager$BadTokenException: Unable to add window -- token null is not for an application 我们在ActivityGroup或者TabActivity中的子Activity创建Dialog若使用以下的代码 ~~~ progressDialog = new ProgressDialog(XXX.this) ~~~ 创建就会出现如下Exception: ERROR/AndroidRuntime(6362): android.view.WindowManager$BadTokenException: Unable to add window -- token android.app.LocalActivityManager$LocalActivityRecord@43e5b158 is not valid; is your activity running?  而该使用: ~~~ progressDialog = new ProgressDialog(getParent()) ~~~ 原因分析: 因为new对话框的时候,参数content 指定成了this,即指向当前子Activity的content。但子Activity是动态创建的,不能保证一直存在。其父Activity的content是稳定存在的,所以有下面的解决办法。 若ActivityGroup中嵌套ActivityGroup,嵌套多少就该使用多少个getParent()。 为什么要使用getParent我们可以从柯元旦的《Android内核剖析》中第十章”Ams内部原理“中的ActivityGroup的内部机制来理解: TabActivity的父类是ActivityGroup,而ActivityGroup的父类是Activity。因此从Ams的角度来看,ActivityGroup与普通的Activity没有什么区别,其生命周期包括标准的start,stop,resume,destroy等,而且系统中只允许同时允许一个ActivityGroup.但ActivityGroup内部有一个重要成员变量,其类型为LocalActivityManager,该类的最大特点在于它可以访问应用进程的主类,即ActivityThread类。Ams要启动某个Activity或者赞同某个Activity都是通过ActivityThread类执行的,而LocalActivityManager类就意味着可以通过它来装载不同的Activity,并控制Activity的不同的状态。注意,这里是装载,而不是启动,这点很重要。所谓的启动,一般是指会创建一个进程(如果所在的应用经常还不存在)运行该Activity,而装载仅仅是指把该Activity作为一个普通类进行加载,并创建一个该类的对象而已,而该类的任何函数都没有被运行。装载Activity对象的过程对AmS来讲是完全不可见的,那些嵌入的Activity仅仅贡献了自己所包含的Window窗口而已。而子Activity的不同状态是通过moveToState来处理的。 所以子Activity不是像普通的Activity一样,它只是提供Window而已,所以在创建Dialog时就应该使用getParent获取ActivityGroup真正的Activity,才可以加Dialog加入Activity中。 参考: [http://aijiawang-126-com.iteye.com/blog/1717368](http://aijiawang-126-com.iteye.com/blog/1717368 "http://aijiawang-126-com.iteye.com/blog/1717368") 下载部署完毕后,运行效果 [![Screenshot_2014-10-29-17-13-43](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5a95398644.jpg "Screenshot_2014-10-29-17-13-43")](http://img.blog.csdn.net/20141030113723747) [![Screenshot_2014-10-29-17-14-03](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5a953bedc1.jpg "Screenshot_2014-10-29-17-14-03")](http://img.blog.csdn.net/20141030113724995) ### 文件安装(下载完成后自动安装)(知识点:[Intent](http://blog.csdn.net/bruce_6/article/details/40619213)) ~~~ /** * 安装apk文件 * @param file */ private void install(File file){ Intent intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); finish();// 终结当前Activity startActivity(intent);// 激活新的Activity } ~~~ [![Screenshot_2014-10-30-15-50-15](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5a953cea90.jpg "Screenshot_2014-10-30-15-50-15")](http://img.blog.csdn.net/20141030162312654) ### 让当前Activity延时2秒再判断是否需要更新(知识点:Handler) ~~~ private String versiontext; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); if (isNeedUpdate(versiontext)) { Log.i(TAG, "弹出升级对话框"); showUpdateDialog(); } } }; ~~~ onCreate方法中: ~~~ // 让当前Activity延时两秒钟,再去检查更新 new Thread(){ public void run() { try { sleep(2000); handler.sendEmptyMessage(0);// 向主线程发送一条空消息 } catch (InterruptedException e) { e.printStackTrace(); } }; }.start(); ~~~ ### 设置下载进度条显示下载进度(知识点:[ProgressDialog](http://blog.csdn.net/bruce_6/article/details/40614999)) 在下载任务类DownloadFileTask的getFile()方法中添加一个ProgressDialog作为参数,在下载过程中对其进行设置。 ~~~ /** * @param path * 服务器文件路径 * @param filepath * 本地文件路径 * @param pd * 进度条,用以显示下载进度 * @return 本地文件对象 * @throws Exception */ public static File getFile(String path, String filepath, ProgressDialog pd) throws Exception { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("GET"); conn.setReadTimeout(5000); // 读取数据没有异常 if (conn.getResponseCode() == 200) { int total = conn.getContentLength();// 获取内容的总长度 pd.setMax(total); InputStream is = conn.getInputStream();// 获取文件输入流 File file = new File(filepath);// 本地文件对象 FileOutputStream fos = new FileOutputStream(file);//本地文件输出流 byte[] buffer = new byte[1024]; int length = 0; int process = 0;// 当前进度 while ((length = is.read(buffer)) != -1) { fos.write(buffer, 0, length); process += length; pd.setProgress(process);// 设置当前进度 Thread.sleep(50); } fos.flush(); fos.close(); is.close(); return file; } return null; } ~~~ 由于默认的ProgressDialog是不显示下载进度的,因此需要进行设置。 pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);// 默认情况下不显示进度,这个设置用于显示进度 效果: [![Screenshot_2014-10-30-15-49-56](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-02-18_56c5a953dca5b.jpg "Screenshot_2014-10-30-15-49-56")](http://img.blog.csdn.net/20141030162313762)
';