### 文件下載
### 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),然后再改回來。
[](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")
下載部署完畢后,運行效果
[](http://img.blog.csdn.net/20141030113723747)?[](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
}
~~~
[](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);// 默認情況下不顯示進度,這個設置用于顯示進度
效果:
[](http://img.blog.csdn.net/20141030162313762)
- 前言
- Appcompat_V7問題
- This Android SDK requires Android Developer Toolkit version 23.0.0 or above
- 創建Android項目不自動生成Activity,layout目錄為空
- 新建android項目gen目錄下未生成R文件
- 手機安全衛士02:splash界面ui
- 知識點:Android控件系列之Toast
- 手機安全衛士03:獲取更新的服務器配置,顯示更新對話框
- 異常處理:android.os.NetworkOnMainThreadException--多線程問題
- 知識點:Android控件系列之對話框AlertDialog.Builder
- 手機安全衛士04_01:界面(Activity)之間的切換,Activity和任務棧
- 知識點:Android控件系列之ProgressDialog與ProgressBar
- 手機安全衛士04_02:從服務器下載并安裝新版本安裝包
- 知識點:Intent
- 知識點:Adapter適配器
- 手機安全衛士05_1:程序主界面
- 手機安全衛士05_2:程序主界面,為每個條目添加事件
- 知識點:動態設置布局LayoutInflater
- 知識點:SharedPreferences
- 手機安全衛士06-手機防盜之自定義對話框
- 手機安全衛士07-手機防盜之進入限制
- 手機安全衛士08-一些布局和顯示的細節:State List
- 手機安全衛士09-手機防盜界面設置向導1
- 手機安全衛士10-設置向導之綁定SIM卡