我們主要指的是下載一個文件讲逛,不考慮斷點續(xù)傳亏吝。
主要的三種方式AsyncTask、Service和使用DownloadManager
一盏混、如果想要在后臺下載任務(wù)的同時可以更新進(jìn)度條UI----使用AsyncTask
忽略安裝需要密碼這個細(xì)節(jié)蔚鸥,用oppo手機的應(yīng)該知道,這個是只有oppo測試機會這樣括饶,別的手機就可以直接安裝了株茶。
要點:
-
一個url下載鏈接,一個ProgressDialog用來顯示進(jìn)度图焰,一個按鈕(點擊升級)按鈕監(jiān)聽的方法為onUpdateClick()
-
接著放入主要的代碼
//關(guān)于進(jìn)度顯示
private ProgressDialog progressDialog;
//相關(guān)屬性
progressDialog =new ProgressDialog(UpdateDialogActivity.this);
progressDialog.setMessage("正在下載...");
progressDialog.setIndeterminate(true);
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
progressDialog.setCancelable(true);
//升級下載按鈕點擊事件
private void onUpdateClick() {
// TODO: 2017/10/11 三種方式實現(xiàn)apk下載
//第一種 asynctask
//onProgressUpdate和onPreExecute是運行在UI線程中的启盛,
// 所以我們應(yīng)該在這兩個方法中更新progress。
final DownloadTask downloadTask = new DownloadTask(UpdateDialogActivity.this);
//execute 執(zhí)行一個異步任務(wù),通過這個方法觸發(fā)異步任務(wù)的執(zhí)行僵闯。這個方法要在主線程調(diào)用卧抗。
downloadTask.execute(url);
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
downloadTask.cancel(true);
}
});
}
-
最主要的是內(nèi)部類 DownloadTask ,具體的方法說明參考注釋以及文章AsyncTask機制原理分析
private class DownloadTask extends AsyncTask<String,Integer,String> {
private Context context;
private PowerManager.WakeLock mWakeLock;
public DownloadTask(Context context) {
this.context = context;
}
//onPreExecute(),在execute(Params... params)方法被調(diào)用后立即執(zhí)行,執(zhí)行在ui線程鳖粟,
// 一般用來在執(zhí)行后臺任務(wù)前會UI做一些標(biāo)記
@Override
protected void onPreExecute() {
super.onPreExecute();
// take CPU lock to prevent CPU from going off if the user
// presses the power button during download
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
getClass().getName());
mWakeLock.acquire();
progressDialog.show();
}
// doInBackground這個方法在onPreExecute()完成后立即執(zhí)行社裆,
// 用于執(zhí)行較為耗時的操作,
// 此方法接受輸入?yún)?shù)
// 和返回計算結(jié)果(返回的計算結(jié)果將作為參數(shù)在任務(wù)完成是傳遞到onPostExecute(Result result)中)向图,
// 在執(zhí)行過程中可以調(diào)用publishProgress(Progress... values)來更新進(jìn)度信息
//后臺任務(wù)的代碼塊
@Override
protected String doInBackground(String... url) {
InputStream input = null;
OutputStream output = null;
HttpURLConnection connection = null;
try {
URL urll=new URL(url[0]);
Log.d("upgrade","url1:"+urll+"http:////url:"+url);
connection = (HttpURLConnection) urll.openConnection();
connection.connect();
// expect HTTP 200 OK, so we don't mistakenly save error report
// instead of the file
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Server returned HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage();
}
// this will be useful to display download percentage
// might be -1: server did not report the length
int fileLength = connection.getContentLength();
// download the file
input = connection.getInputStream();
output = new FileOutputStream("/sdcard/new.apk");
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
if (isCancelled()) {
input.close();
return null;
}
total += count;
// publishing the progress....
if (fileLength > 0) // only if total length is known
//在調(diào)用這個方法后泳秀,執(zhí)行onProgressUpdate(Progress... values),
//運行在主線程榄攀,用來更新pregressbar
publishProgress((int) (total * 100 / fileLength));
output.write(data, 0, count);
}
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return null;
}
//onProgressUpdate(Progress... values),
// 執(zhí)行在UI線程嗜傅,在調(diào)用publishProgress(Progress... values)時,此方法被執(zhí)行檩赢。
@Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
// if we get here, length is known, now set indeterminate to false
progressDialog.setIndeterminate(false);
progressDialog.setMax(100);
progressDialog.setProgress(progress[0]);
}
//onPostExecute(Result result),
// 執(zhí)行在UI線程吕嘀,當(dāng)后臺操作結(jié)束時,此方法將會被調(diào)用贞瞒。
@Override
protected void onPostExecute(String result) {
mWakeLock.release();
progressDialog.dismiss();
if (result != null)
Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
else
{Toast.makeText(context,"File downloaded", Toast.LENGTH_SHORT).show();}
//這里主要是做下載后自動安裝的處理
File file=new File("/sdcard/new.apk");
Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(installIntent);
}
}
二偶房、使用DownloadManager
每個Android App都會有版本更新的功能,而下載功能Google官方推薦使用 DownloadManager服務(wù)
使用最簡單的一種
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setDescription("下載中");
request.setTitle("我的下載");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
}
request.allowScanningByMediaScanner();//設(shè)置可以被掃描到
request.setVisibleInDownloadsUi(true);// 設(shè)置下載可見
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);//下載完成后通知欄任然可見
request.setDestinationInExternalPublicDir(
Environment.DIRECTORY_DOWNLOADS, "my.apk");
manager = (DownloadManager) getActivity().getSystemService(Context.DOWNLOAD_SERVICE);
// manager.enqueue(request);
long Id = manager.enqueue(request);
//listener(Id);
SharedPreferences sPreferences = getActivity().getSharedPreferences(
"downloadapk", 0);
sPreferences.edit().putLong("apk",Id).commit();//保存此次下載ID
Log.d("shengji", "開始下載任務(wù):" + Id + " ...");
如果想同樣實現(xiàn)下載完安裝军浆,要使用廣播.當(dāng)DownloadManager下載完成后會發(fā)出一個廣播 android.intent.action.DOWNLOAD_COMPLETE棕洋,創(chuàng)建一個廣播接收者,處理自動提示安裝:
public class DownLoadBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
long completeId = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
Log.d("shengji","下載完成后的ID:"+completeId);
SharedPreferences sPreferences =context.getSharedPreferences(
"downloadapk", 0);
long Id = sPreferences.getLong("apk", 0);
if (Id==completeId){
DownloadManager manager =
(DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
Intent installIntent=new Intent(Intent.ACTION_VIEW);
Uri downloadFileUri = manager
.getUriForDownloadedFile(completeId);
installIntent.setDataAndType(downloadFileUri,
"application/vnd.android.package-archive");
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(installIntent);
}
}
}
在AndroidManifet中進(jìn)行注冊
<receiver android:name=".receiver.DownLoadBroadcastReceiver">
<intent-filter android:priority="20" >
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
</intent-filter>
</receiver>
還要加權(quán)限:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
雖說代碼量很少瘾敢,但是也確實會有問題遇到
問題1:No Activity found to handle Intent
解決:首先不要單獨設(shè)置data和type拍冠, 要同時setDataAndType(data, "application/vnd.android.package-archive")尿这。其次最多的可能是下載文件路徑的問題簇抵,好好檢查文件路徑是否錯誤或是否不可讀。最簡單的方法就是把apk的路徑固定死
問題2:權(quán)限問題射众,targetSdkVersion >=23需要獲取權(quán)限才能自動安裝
解決:
方法一:把build.gradle 文件中的targetSdkVersion < 23碟摆。這種方式也是最簡單的。
方法二:動態(tài)的獲取權(quán)限:代碼如下
// getPersimmions();方法
@TargetApi(23)
private void getPersimmions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
ArrayList<String> permissions = new ArrayList<String>();
/*
* 讀寫權(quán)限和電話狀態(tài)權(quán)限非必要權(quán)限(建議授予)只會申請一次叨橱,用戶同意或者禁止典蜕,只會彈一次
*/
// 讀寫權(quán)限
if (addPermission(permissions, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
permissionInfo += "Manifest.permission.WRITE_EXTERNAL_STORAGE Deny \n";
}
if (permissions.size() > 0) {
requestPermissions(permissions.toArray(new String[permissions.size()]), SDK_PERMISSION_REQUEST);
}
}
}
@TargetApi(23)
private boolean addPermission(ArrayList<String> permissionsList, String permission) {
if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { // 如果應(yīng)用沒有獲得對應(yīng)權(quán)限,則添加到列表中,準(zhǔn)備批量申請
if (shouldShowRequestPermissionRationale(permission)){
return true;
}else{
permissionsList.add(permission);
return false;
}
}else{
return true;
}
}
@TargetApi(23)
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
// TODO Auto-generated method stub
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
三、使用service(IntentService和ResultReceiver)
IntentService繼承自service罗洗,在IntentService中我們開啟一個線程執(zhí)行下載任務(wù)(service和你的app其實是在一個線程中愉舔,因此不想阻塞主線程的話必須開啟新的線程。
//在這里根據(jù)url進(jìn)行下載文件伙菜,并通過receiver把需要更新的progressbar的值放在bundle傳過去
public class DownloadService extends IntentService {
public static final int UPDATE_PROGRESS = 8344;
public DownloadService() {
super("DownloadService");
}
@Override
protected void onHandleIntent(Intent intent) {
String urlToDownload = intent.getStringExtra("url");
ResultReceiver receiver = (ResultReceiver) intent.getParcelableExtra("receiver");
HttpURLConnection connection ;
try {
URL url = new URL(urlToDownload);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
// this will be useful so that you can show a typical 0-100% progress bar
int fileLength = connection.getContentLength();
Log.d("test","fileLength:"+fileLength);
// download the file
InputStream input = connection.getInputStream();
OutputStream output = new FileOutputStream("/sdcard/new.apk");
byte data[] = new byte[2048];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
total += count;
// publishing the progress....
Bundle resultData = new Bundle();
resultData.putInt("progress" ,(int) (total * 100 / fileLength));
receiver.send(UPDATE_PROGRESS, resultData);
output.write(data, 0, count);
}
output.flush();
output.close();
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
//記得注冊<service android:name=".DownloadService"/>
activity中這樣調(diào)用DownloadService
progressDialog.show();
Intent intent = new Intent(this, DownloadService.class);
intent.putExtra("url",url);
intent.putExtra("receiver", new DownloadReceiver(new Handler()));
startService(intent);
activity中定義一個廣播接收器繼承ResultReceiver轩缤,ResultReceiver允許我們接收來自service中發(fā)出的廣播
//使用ResultReceiver接收來自DownloadService的下載進(jìn)度通知
private class DownloadReceiver extends ResultReceiver {
public DownloadReceiver(Handler handler) {
super(handler);
}
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
if (resultCode == DownloadService.UPDATE_PROGRESS) {
int progress = resultData.getInt("progress");
//(true)就是根據(jù)你的進(jìn)度可以設(shè)置現(xiàn)在的進(jìn)度值。
//(false)就是滾動條的當(dāng)前值自動在最小到最大值之間來回移動,形成這樣一個動畫效果
progressDialog.setIndeterminate(false);
progressDialog.setProgress(progress);
if (progress == 100) {
progressDialog.dismiss();
//自動安裝下載的apk
File file=new File("/sdcard/new.apk");
Intent installIntent = new Intent(Intent.ACTION_VIEW);
installIntent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(installIntent);
}
}
}
}
如果對您有用火的,給個贊鼓勵一下唄~