目錄
實(shí)現(xiàn)效果
按照慣例充坑,效果奉上
Kapture 2018-12-01 at 16.44.09.gif
前言
日更啊染突!日更捻爷!,上一篇比較劃水的給大家分享了一下份企,在做這個(gè)功能時(shí)候的一點(diǎn)Java基礎(chǔ)也榄,IO流,在這個(gè)過(guò)程中順便再講一下另一個(gè)IO流中的RandomAccessFile類司志,那么今天分享一下甜紫。
- OKHttp依賴:使用的是OKhttp來(lái)連接網(wǎng)絡(luò)
- RandomAccessFile 來(lái)進(jìn)行IO的讀寫
- BroadcastReceiver:用來(lái)接受和更新UI的
正文
OKManger
OKManger
類中實(shí)現(xiàn)了 網(wǎng)絡(luò)連接方法,下載文件方法骂远,啟用線程方法囚霸;
public class OkManager {
private File rootFile;//文件的路徑
private File file;//文件
private long downLoadSize;//下載文件的長(zhǎng)度
private final ThreadPoolExecutor executor;// 線程池
private boolean isDown = false; //是否已經(jīng)下載過(guò)了(下載后點(diǎn)擊暫停) 默認(rèn)為false
private String name; //名稱
private String path;// 下載的網(wǎng)址
private RandomAccessFile raf; // 讀取寫入IO方法
private long totalSize = 0;
private MyThread thread;//線程
private Handler handler;//Handler 方法
private DownLoad.IProgress progress;// 下載進(jìn)度方法,內(nèi)部定義的抽象方法
/**
* 構(gòu)造方法 OKhttp
* @param path 網(wǎng)絡(luò)連接路徑
* @param progress 更新路徑
*/
public OkManager(String path, DownLoad.IProgress progress) {
this.path = path;
this.progress = progress;
this.handler = new Handler();
this.name = path.substring(path.lastIndexOf("/") + 1);
rootFile = FileUtils.getRootFile();
executor = new ThreadPoolExecutor(5, 5, 50, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3000));
//executor.execute(new MyThread());
}
/**
* 自定義線程
*/
class MyThread extends Thread {
@Override
public void run() {
super.run();
downLoadFile();
}
}
/**
* 這就是下載方法
*/
private void downLoadFile() {
try {
if (file == null) {//判斷是否擁有相應(yīng)的文件
file = new File(rootFile, name); //很正常的File() 方法
raf = new RandomAccessFile(file, "rwd");//實(shí)例化一下我們的RandomAccessFile()方法
} else {
downLoadSize = file.length();// 文件的大小
if (raf == null) {//判斷讀取是否為空
raf = new RandomAccessFile(file, "rwd");
}
raf.seek(downLoadSize);
}
totalSize = getContentLength(path);//獲取文件的大小
if (downLoadSize == totalSize) {// 判斷是否下載完成
//已經(jīng)下載完成
return;
}
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(path).
addHeader("Range", "bytes=" + downLoadSize + "-" + totalSize).build();
Response response = client.newCall(request).execute();
InputStream ins = response.body().byteStream();
//上面的就是簡(jiǎn)單的OKHttp連接網(wǎng)絡(luò)激才,通過(guò)輸入流進(jìn)行寫入到本地
int len = 0;
byte[] by = new byte[1024];
long endTime = System.currentTimeMillis();
while ((len = ins.read(by)) != -1 && isDown) {//如果下載沒(méi)有出錯(cuò)并且已經(jīng)開(kāi)始下載拓型,循環(huán)進(jìn)行以下方法
raf.write(by, 0, len);
downLoadSize += len;
if (System.currentTimeMillis() - endTime > 1000) {
final double dd = downLoadSize / (totalSize * 1.0);
DecimalFormat format = new DecimalFormat("#0.00");
String value = format.format((dd * 100)) + "%";//計(jì)算百分比
Log.i("tag", "==================" + value);
handler.post(new Runnable() {//通過(guò)Handler發(fā)送消息到UI線程,更新
@Override
public void run() {
progress.onProgress((int) (dd * 100));
}
});
}
}
response.close();//最后要把response關(guān)閉
} catch (Exception e) {
e.getMessage();
}
}
/**
* 線程開(kāi)啟方法
*/
public void start() {
if (thread == null) {
thread = new MyThread();
isDown = true;
executor.execute(thread);
}
}
/**
* 線程停止方法
*/
public void stop() {
if (thread != null) {
isDown = false;
executor.remove(thread);
thread = null;
}
}
//通過(guò)OkhttpClient獲取文件的大小
public long getContentLength(String url) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(url).build();
Response response = client.newCall(request).execute();
long length = response.body().contentLength();
response.close();
return length;
}
public interface IProgress {
void onProgress(int progress);
}
}
以上的方法瘸恼,大家仔細(xì)的看一看劣挫,這里面有詳細(xì)的注釋,大家主要關(guān)注downLoadFile()
方法东帅,
FileUtils
public class FileUtils {
//判斷是否安裝SDCard
public static boolean isSdOk(){
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
return true;
}
return false;
}
//創(chuàng)建一個(gè)文件夾压固,用來(lái)存放下載的文件
public static File getRootFile(){
File sd = Environment.getExternalStorageDirectory();
File rootFile = new File(sd,"TEMPFILE");
if (!rootFile.exists()){
rootFile.mkdirs();
}
return rootFile;
}
}
這個(gè)就是文件寫入的路徑,封裝的方法靠闭,第一個(gè)就是判斷是否安裝SDCard
帐我,第二個(gè)方法創(chuàng)建文件夾,存放下載的文件阎毅;
activity_main.xml
布局文件焚刚,視圖,用了兩個(gè)按鈕和progressBar
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="us.mifeng.downloader.MainActivity">
<ProgressBar
android:layout_margin="10dp"
android:layout_marginTop="20dp"
android:id="@+id/progress"
android:layout_width="match_parent"
android:layout_height="5dp"
style="@android:style/Widget.ProgressBar.Horizontal"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/progress"
android:orientation="horizontal">
<Button
android:id="@+id/start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="開(kāi)始" />
<Button
android:id="@+id/stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止"/>
</LinearLayout>
</RelativeLayout>
主方法 Mainactivity()
public class MainActivity extends AppCompatActivity implements View.OnClickListener, DownLoad.IProgress {
private String path = "https://download.alicdn.com/wireless/taobao4android/latest/702757.apk";
//private DownLoad downLoad;
private ProgressBar pBar;
private OkManager downLoad;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//downLoad = new DownLoad(path,this);
downLoad = new OkManager(path,this);
initView();
}
private void initView() {
Button start = (Button) findViewById(R.id.start);
Button stop = (Button) findViewById(R.id.stop);
pBar = (ProgressBar) findViewById(R.id.progress);
pBar.setMax(100);
start.setOnClickListener(this);
stop.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.start:
downLoad.start();
break;
case R.id.stop:
downLoad.stop();
break;
}
}
//顯示進(jìn)度條
@Override
public void onProgress(int progress) {
pBar.setProgress(progress);
}
}
很簡(jiǎn)單就是調(diào)用了之前封裝的方法扇调,大家好好地看響應(yīng)的方法就可以了
總結(jié)
- 斷點(diǎn)續(xù)傳的關(guān)鍵是斷點(diǎn)矿咕,所以在制定傳輸協(xié)議的時(shí)候要設(shè)計(jì)好,如上圖,我自定義了一個(gè)交互協(xié)議碳柱,每次下載請(qǐng)求都會(huì)帶上下載的起始點(diǎn)捡絮,這樣就可以支持從斷點(diǎn)下載了, 其實(shí)HTTP里的斷點(diǎn)續(xù)傳也是這個(gè)原理莲镣,在HTTP的頭里有個(gè)可選的字段RANGE福稳,表示下載的范圍。
- Range : 用于客戶端到服務(wù)器端的請(qǐng)求瑞侮,可通過(guò)該字段指定下載文件的某一段大小的圆,及其單位。典型的格式如:
Range: bytes=0-499 下載第0-499字節(jié)范圍的內(nèi)容
Range: bytes=500-999 下載第500-999字節(jié)范圍的內(nèi)容
Range: bytes=-500 下載最后500字節(jié)的內(nèi)容
Range: bytes=500- 下載從第500字節(jié)開(kāi)始到文件結(jié)束部分的內(nèi)容
感謝
這個(gè)代碼其實(shí)是我在Github上面找到的響應(yīng)的方法半火,比較簡(jiǎn)單越妈,所以可以更好的放入項(xiàng)目中,希望大家多去大神github上面star