android斷點續傳實現應用程序更新下載

本文內容參考了CSDN博客博主Sodino的文章 地址:http://blog.csdn.net/sodino/archive/2011/06/09/6535278.aspx


這裏我對他的工具類NetworkTool進行了一些修改,自己用起來還可以,寫下主要爲自己以後看的,在此感謝他。

斷點續傳用到的知識點:
1.使用RandomAccessFile設定文件大小並於指定位置開始讀數據[randomAccessFile.seek(position)]。    
2.請求資源鏈接時指定所請求數據的返回範圍。
    httpURLConnection.setRequestProperty("Range", "bytes=" + start + "-" + (contentLength - 1));


直接上代碼:NetworkTool類

package com.example.yunmiweather.service;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.InetSocketAddress;
import java.net.MalformedURLException;
import java.net.URL;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Handler;

public class NetWorkTool {


public static final int CONNECTION_TIMEOUT = 1000 * 60 * 5;
public static final int READ_TIMEOUT = 1000 * 60 * 5;
/**
* 開啓一個HTTP鏈接。
*/
public static HttpURLConnection openUrl(Context context, String urlStr) {
URL urlURL = null;
HttpURLConnection httpConn = null;
try {
urlURL = new URL(urlStr);
// 需要android.permission.ACCESS_NETWORK_STATE
// 在沒有網絡的情況下,返回值爲null。
NetworkInfo networkInfo = ((ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE))
.getActiveNetworkInfo();
// 如果是使用的運營商網絡
if (networkInfo != null) {
if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {
// 獲取默認代理主機ip
String host = android.net.Proxy.getDefaultHost();
// 獲取端口
int port = android.net.Proxy.getDefaultPort();
if (host != null && port != -1) {
// 封裝代理連接主機IP與端口號。
InetSocketAddress inetAddress = new InetSocketAddress(
host, port);
// 根據URL鏈接獲取代理類型,本鏈接適用於TYPE.HTTP
java.net.Proxy.Type proxyType = java.net.Proxy.Type
.valueOf(urlURL.getProtocol().toUpperCase());
java.net.Proxy javaProxy = new java.net.Proxy(
proxyType, inetAddress);
httpConn = (HttpURLConnection) urlURL
.openConnection(javaProxy);
} else {
httpConn = (HttpURLConnection) urlURL.openConnection();
}
} else {
httpConn = (HttpURLConnection) urlURL.openConnection();
}
httpConn.setDoInput(true);
httpConn.setReadTimeout(READ_TIMEOUT);
httpConn.setConnectTimeout(CONNECTION_TIMEOUT);
} else {
// LogOut.out(this, "No Avaiable Network");
}
} catch (NullPointerException npe) {
npe.printStackTrace();
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return httpConn;
}


/** 啓動鏈接並將RespondCode值返回。 */
public static int connect(HttpURLConnection httpConn) {
int code = -1;
if (httpConn != null) {
try {
httpConn.connect();
code = httpConn.getResponseCode();
} catch (IOException e) {
e.printStackTrace();
}
}
return code;
}


/**
* 將指定的HTTP鏈接內容存儲到指定的的文件中。<br/>
* 返回值僅當參考。<br/>

* @param httpConn
* @param filePath
*            指定存儲的文件路徑。
*/
public static boolean download2File(HttpURLConnection httpConn, File file) {
boolean result = true;
FileOutputStream fos = null;
byte[] data = new byte[1024];
int readLength = -1;
InputStream is = null;
try {
fos = new FileOutputStream(file);
is = httpConn.getInputStream();
while ((readLength = is.read(data)) != -1) {
fos.write(data, 0, readLength);
fos.flush();
}
fos.flush();
} catch (IOException ie) {
result = false;
ie.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (fos != null) {
fos.close();
}
} catch (IOException ie) {
ie.printStackTrace();
}
}
return result;
}


/**
* 將bean資源下載。<br/>
* 支持斷點續傳。

*/
public static void download2File(Context context, BeanDownload bean,
DownloadCallable downloadCallable) {
HttpURLConnection httpConn = null;
File file = bean.file;
RandomAccessFile randomFile = null;
FileOutputStream fos = null;
int dataBlockLength = 2048;
byte[] data = new byte[dataBlockLength];
int readLength = -1;
InputStream is = null;
try {
if (file.getParentFile().exists() == false) {
file.getParentFile().mkdirs();
}
if (file.exists() == false) {
file.createNewFile();
}
randomFile = new RandomAccessFile(file, "rw");
bean.loadedSize = (int) randomFile.length();
httpConn = openUrl(context, bean.url);
bean.size = httpConn.getContentLength();
if (bean.loadedSize <= 0) {
System.out.println("普通方式");
// 採用普通的下載方式
int respondCode = connect(httpConn);
fos = new FileOutputStream(file);
if (respondCode == HttpURLConnection.HTTP_OK) {
is = httpConn.getInputStream();
while ((readLength = is.read(data)) != -1) {
fos.write(data, 0, readLength);
bean.loadedSize += readLength;
downloadCallable.downRefresh(bean);
System.out.println("普通刷新");


}
if (bean.loadedSize >= bean.size) {
downloadCallable.downFinish();
}
}
} else if (bean.loadedSize < bean.size) {
// 採用斷點續傳方式
httpConn = openUrl(context, bean.url);
httpConn.setRequestProperty("Range", "bytes=" + bean.loadedSize
+ "-" + (bean.size - 1));
int respondCode = connect(httpConn);
if (respondCode == HttpURLConnection.HTTP_PARTIAL) {
System.out.println("斷點續傳");
is = httpConn.getInputStream();
while ((readLength = is.read(data)) != -1) {
randomFile.seek(bean.loadedSize);
randomFile.write(data, 0, readLength);
bean.loadedSize += readLength;
downloadCallable.downRefresh(bean);
}
if (bean.loadedSize == bean.size) {
downloadCallable.downFinish();
}
}
} else {
System.out.println("下載完成");
downloadCallable.downFinish();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (httpConn != null) {
disconnect(httpConn);
}
if (fos != null) {
fos.close();
}
if (randomFile != null) {
randomFile.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

public static void disconnect(HttpURLConnection httpConn) {
if (httpConn != null) {
httpConn.disconnect();
}
}

//此處是我寫的一個回調接口,當然你也可以修改接口裏方法的聲明以滿足你的需求
public interface DownloadCallable {

public void downRefresh(BeanDownload bean);
public void downFinish();
public void downFaile();
}

//簡單的封裝的一個下載類
public static class BeanDownload {
public int size;// 下載的文件大小
public int loadedSize;// 已經下載的大小
public String url;// 下載地址
public File file;// 下載到本地的文件


public BeanDownload(String url, File file) {
this.file = file;
this.url = url;
}
}
}


在更新serivice裏開啓一個線程,調用這個類裏面的下載方法,自己實現相應的回調接口,如果是通知欄提示的話就可以用notification,如果是在activity中進行UI更新,那就用handler。這裏我用的是簡單的通知的形式,當然更復雜的通知,你可以去寫一個layout文件,用遠程視圖實現效果。

package com.example.yunmiweather.service;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.net.Uri;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;


import com.example.yunmiweather.R;
import com.example.yunmiweather.activity.MainActivity;
import com.example.yunmiweather.service.NetWorkTool.BeanDownload;
import com.example.yunmiweather.service.NetWorkTool.DownloadCallable;
import com.example.yunmiweather.util.UpdateVersion;


public class UpdateVersionService extends Service {

public static final int mUpdateNotificationId = 0;
private String appUrl;

private File mUpdateFile;
private File mUpdateDir;

private NetWorkTool.DownloadCallable mDownloadCallable;
private BeanDownload mBeanDown;

private NotificationManager mNotificationManager;
private Notification mNotification;

private PendingIntent mPendingIntent;
private Intent mUpdateIntent;


private Thread mDownLoadThread;


@Override
public IBinder onBind(Intent intent) {
return null;
}


@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
appUrl = intent.getStringExtra(UpdateVersion.APPDOWNLOADURL);


if (android.os.Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())) {
mUpdateDir = new File(
Environment.getExternalStorageDirectory(),
getPackageName());
mUpdateFile = new File(mUpdateDir.getPath(),
"yunmi_weather.apk");
}


createNotification();


createDownloadThread();

mDownloadCallable = new DownloadCallable() {


@Override
public void downRefresh(BeanDownload bean) {
mNotification.setLatestEventInfo(UpdateVersionService.this,
"正在下載", bean.loadedSize * 100 / bean.size + "%",
mPendingIntent);
mNotificationManager.notify(mUpdateNotificationId,
mNotification);
}


@Override
public void downFinish() {
mNotification.setLatestEventInfo(UpdateVersionService.this,
"yunmi", "下載完成",
mPendingIntent);
mNotificationManager.notify(mUpdateNotificationId,
mNotification);
instartAPK();
stopSelf();
}


@Override
public void downFaile() {
mNotification.setLatestEventInfo(UpdateVersionService.this,
"yunmi", "下載失敗", mPendingIntent);
mNotificationManager.notify(mUpdateNotificationId,
mNotification);
stopSelf();
}
};
}
return super.onStartCommand(intent, flags, startId);
}


private void createNotification() {
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
mNotification = new Notification();
mNotification.icon = R.drawable.ic_launcher;
mNotification.tickerText = "開始下載";


mUpdateIntent = new Intent(this, MainActivity.class);
mUpdateIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mPendingIntent = PendingIntent.getActivity(this, 0, mUpdateIntent, 0);
mNotification.setLatestEventInfo(this, "yunmi", "開始下載", mPendingIntent);


mNotificationManager.notify(mUpdateNotificationId, mNotification);


}


private void createDownloadThread() {
mDownLoadThread = new Thread(new DownLoadRunnable());
mDownLoadThread.start();
}


class DownLoadRunnable implements Runnable {


@Override
public void run() {
mBeanDown = new BeanDownload(appUrl,mUpdateFile);
NetWorkTool.download2File(UpdateVersionService.this, mBeanDown,
mDownloadCallable);
}
}


public void instartAPK() {
Uri uri = Uri.fromFile(mUpdateFile);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.addCategory("android.intent.category.DEFAULT");
intent.setDataAndType(uri, "application/vnd.android.package-archive");
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章