Androidのweb開発 - HttpURLConnectionでマルチスレッドファイルダウンロード
HttpURLConnectionはURLConnectionのサブクラスである。
拡張されているメソッド
- int getResponseCode()
- String getResponseMessage()
- String getRequestMethod()
- void setRequestMethod()
マルチスレッドでイメージダウンロードサンプルコード
①ダウンロードユーティルクラス
MultiDownloadUtil.java
package com.example.liguofeng.httpurlconnectionsample; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; /** * ダウンロードユーティル */ public class MultiDownloadUtil { private String path; private String targetFile; private int threadNum; private int fileSize; private DownloadThread[] threads; /** * コンストラクター * @param path * @param targetFile * @param threadNum */ public MultiDownloadUtil(String path, String targetFile, int threadNum) { this.path = path; this.targetFile = targetFile; this.threadNum = threadNum; this.threads = new DownloadThread[threadNum]; } public void download() throws Exception{ URL url = new URL(this.path); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setConnectTimeout(5 * 1000); conn.setRequestMethod("GET"); conn.setRequestProperty("Accept", "*/*"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.setRequestProperty("user-agent", "Mozilla/5.0 (compatible; MSIE 6.0; WIndows NT 5.1; SV1)"); // ファイルサイズ取得 fileSize = conn.getContentLength(); // 一旦切断 conn.disconnect(); // 一スレッドでダウンロードするサイズ int currentPartSize = fileSize / threadNum + 1; RandomAccessFile file = new RandomAccessFile(targetFile, "rw"); // 空ファイルサイズ設定 file.setLength(fileSize); file.close(); for (int i = 0; i < threadNum; i++) { // スレッド毎のダウンロード位置 int startPos = i * currentPartSize; // スレッド毎にRandomAccessFile使用 RandomAccessFile currentPart = new RandomAccessFile(targetFile, "rw"); // ダウンロード位置に移動 currentPart.seek(startPos); // スレッド生成 threads[i] = new DownloadThread(startPos, currentPartSize, currentPart); System.out.println("-----------------------------------------------"); System.out.println("startPos:" + startPos + " currentPartSize:" + currentPartSize + " currentPart:" + currentPart); // スレッド起動 threads[i].start(); } } // ダウンロード割合 public double getCompleteRate() { // 全スレッドのダウンロードサイズ int sumSize = 0; System.out.println(sumSize); for (int i = 0; i < threadNum; i++) { sumSize += threads[i].length; System.out.println(sumSize); } // ダウンロード済み割合 System.out.println(sumSize * 1.0 / fileSize); return sumSize * 1.0 / fileSize; } // InputStreamスキップ public static void skipFully(InputStream in, long bytes) throws IOException { long remainning = bytes; long len = 0; while (remainning > 0) { len = in.skip(remainning); remainning -= len; } } private class DownloadThread extends Thread { // ダウンロード開始位置 private int startPos; // ダウンロードサイズ private int currentPartSize; // ダウンロードパート private RandomAccessFile currentPart; // 定义已经该线程已下载的字节数 public int length; public DownloadThread(int startPos, int currentPartSize, RandomAccessFile currentPart) { this.startPos = startPos; this.currentPartSize = currentPartSize; this.currentPart = currentPart; } @Override public void run() { try { URL url = new URL(path); HttpURLConnection conn = (HttpURLConnection)url .openConnection(); conn.setConnectTimeout(5 * 1000); conn.setRequestMethod("GET"); conn.setRequestProperty("accept", "*/*"); conn.setRequestProperty("Charset", "UTF-8"); conn.setRequestProperty("user-agent", "Mozilla/5.0 (compatible; MSIE 6.0; WIndows NT 5.1; SV1)"); InputStream inStream = conn.getInputStream(); // スキップ MultiDownloadUtil.skipFully(inStream, this.startPos); // inStream.skip(this.startPos); //このメソッドは正確でないらしい byte[] buffer = new byte[1024]; int hasRead = 0; // webリソース読み取り while (length < currentPartSize && (hasRead = inStream.read(buffer)) > 0) { currentPart.write(buffer, 0, hasRead); // ダウンロードサイズ length += hasRead; } currentPart.close(); inStream.close(); } catch (Exception e) { e.printStackTrace(); } } } }
② AndroidManifest.xml
<!-- SDカード --> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!-- SDカード書き込み --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- ネットワーク --> <uses-permission android:name="android.permission.INTERNET"/>
③ activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="ダウンロードリソースのURL:" /> <EditText android:id="@+id/url" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="http://f.st-hatena.com/images/fotolife/l/liguofeng29/20160113/20160113215930.jpg" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="ターゲットファイル" /> <EditText android:id="@+id/target" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="/mnt/sdcard/a.jpg" /> <Button android:id="@+id/down" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Download" /> <!-- ダウンロード進捗 --> <ProgressBar android:id="@+id/bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" style="?android:attr/progressBarStyleHorizontal" /> </LinearLayout>
④ MainActivity.java
package com.example.liguofeng.httpurlconnectionsample; import android.os.Message; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.widget.Button; import android.widget.EditText; import android.widget.ProgressBar; import android.os.Handler; import android.view.View; import android.view.View.OnClickListener; import java.util.Timer; import java.util.TimerTask; public class MainActivity extends AppCompatActivity { EditText url; EditText target; Button downBn; ProgressBar bar; MultiDownloadUtil downUtil; private int mDownStatus; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // VIEW取得 url = (EditText) findViewById(R.id.url); target = (EditText) findViewById(R.id.target); downBn = (Button) findViewById(R.id.down); bar = (ProgressBar) findViewById(R.id.bar); // Handler生成 final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { if (msg.what == 0x123) { bar.setProgress(mDownStatus); } } }; downBn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // DownloadのURL、スレッド数指定 downUtil = new MultiDownloadUtil(url.getText().toString(), target.getText().toString(), 2); new Thread() { @Override public void run() { try { // ダウンロード開始 downUtil.download(); } catch (Exception e) { e.printStackTrace(); } // 進捗更新 final Timer timer = new Timer(); timer.schedule(new TimerTask() { @Override public void run() { // 完成度 double completeRate = downUtil .getCompleteRate(); mDownStatus = (int) (completeRate * 100); // メッセージ送信 handler.sendEmptyMessage(0x123); // ダウンロード完了後タイマー停止 if (mDownStatus >= 100) { timer.cancel(); } } }, 0, 100); } }.start(); } }); } }