断点续传---多线程下载进阶(三)

来源:互联网 发布:java 获取字符串下标 编辑:程序博客网 时间:2024/06/11 20:24

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:paddingBottom="@dimen/activity_vertical_margin"    android:paddingLeft="@dimen/activity_horizontal_margin"    android:paddingRight="@dimen/activity_horizontal_margin"    android:paddingTop="@dimen/activity_vertical_margin"    tools:context=".MainActivity" >    <ListView        android:id="@+id/lv_downLoad"        android:layout_width="match_parent"        android:layout_height="match_parent" >    </ListView></RelativeLayout>

item.xml

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:orientation="vertical" >    <TextView        android:id="@+id/tv_fileName"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:text="imooc.apk" />    <ProgressBar        android:id="@+id/pb_progress"        style="?android:attr/progressBarStyleHorizontal"        android:layout_width="match_parent"        android:layout_height="wrap_content"         android:layout_below="@id/tv_fileName"/>    <Button        android:id="@+id/btn_stop"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_alignParentRight="true"        android:layout_below="@id/pb_progress"        android:text="暂停" />    <Button        android:id="@+id/btn_start"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:layout_below="@+id/pb_progress"        android:layout_toLeftOf="@id/btn_stop"        android:text="下载" /></RelativeLayout>

FileInfo

package com.download.entities;import java.io.Serializable;public class FileInfo implements Serializable {private int id;private String url;private String fileName;private int length;private int finished;public FileInfo(int id, String url, String fileName, int length,int finished) {this.id = id;this.url = url;this.fileName = fileName;this.length = length;this.finished = finished;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public String getFileName() {return fileName;}public void setFileName(String fileName) {this.fileName = fileName;}public int getLength() {return length;}public void setLength(int length) {this.length = length;}public int getFinished() {return finished;}public void setFinished(int finished) {this.finished = finished;}@Overridepublic String toString() {return "FileInfo [id=" + id + ", url=" + url + ", fileName=" + fileName+ ", length=" + length + ", finished=" + finished + "]";}}

ThreadInfo

package com.download.entities;import android.R.integer;public class ThreadInfo {private int id;private String url;private int start;private int end;private int finished;public ThreadInfo() {}public ThreadInfo(int id, String url, int start, int end, int finished) {this.id = id;this.url = url;this.start = start;this.end = end;this.finished = finished;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getUrl() {return url;}public void setUrl(String url) {this.url = url;}public int getStart() {return start;}public void setStart(int start) {this.start = start;}public int getEnd() {return end;}public void setEnd(int end) {this.end = end;}public int getFinished() {return finished;}public void setFinished(int finished) {this.finished = finished;}@Overridepublic String toString() {return "ThreadInfo [id=" + id + ", url=" + url + ", start=" + start+ ", end=" + end + ", finished=" + finished + "]";}}

ThreadDAO

package com.download.db;import java.util.List;import com.download.entities.ThreadInfo;public interface ThreadDAO {public void insertThread(ThreadInfo threadInfo);public void deleteThread(String url);public void updateThread(String url, int thread_id, int finished);public List<ThreadInfo> getThreads(String url);public boolean isExists(String url, int thread_id);}


DBHelper

package com.download.db;import android.R.integer;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteDatabase.CursorFactory;import android.database.sqlite.SQLiteOpenHelper;/** * 数据库帮助类 */public class DBHelper extends SQLiteOpenHelper {private static final String DB_NAME = "download.db";private static final int VERSION = 1;private static final String SQL_CREATE = "create table thread_info(_id integer primary key autoincrement,"+ "thread_id integer, url text, start integer, end integer, finished integer)";private static final String SQL_DROP = "drop table if exists thread_info";private static DBHelper sDbHelper = null;private DBHelper(Context context) {super(context, DB_NAME, null, VERSION);}public static DBHelper getInstance(Context context) {if (null == sDbHelper) {sDbHelper = new DBHelper(context);}return sDbHelper;}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL(SQL_CREATE);}@Overridepublic void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {db.execSQL(SQL_DROP);db.execSQL(SQL_CREATE);}}


ThreadDAOImpl

package com.download.db;import java.util.ArrayList;import java.util.List;import android.content.Context;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import com.download.entities.ThreadInfo;/** * 数据访问接口实现 */public class ThreadDAOImpl implements ThreadDAO {private DBHelper mHelper = null;public ThreadDAOImpl(Context context) {mHelper = DBHelper.getInstance(context);}@Overridepublic synchronized void insertThread(ThreadInfo threadInfo) {SQLiteDatabase db = mHelper.getWritableDatabase();db.execSQL("insert into thread_info(thread_id,url,start,end,finished) values(?,?,?,?,?)",new Object[] { threadInfo.getId(), threadInfo.getUrl(),threadInfo.getStart(), threadInfo.getEnd(),threadInfo.getFinished() });db.close();}@Overridepublic synchronized void deleteThread(String url) {SQLiteDatabase db = mHelper.getWritableDatabase();db.execSQL("delete from thread_info where url = ?",new Object[] { url });db.close();}@Overridepublic synchronized void updateThread(String url, int thread_id,int finished) {SQLiteDatabase db = mHelper.getWritableDatabase();db.execSQL("update thread_info set finished = ? where url = ? and thread_id = ?",new Object[] { finished, url, thread_id });db.close();}@Overridepublic List<ThreadInfo> getThreads(String url) {List<ThreadInfo> list = new ArrayList<ThreadInfo>();SQLiteDatabase db = mHelper.getReadableDatabase();Cursor cursor = db.rawQuery("select * from thread_info where url = ?",new String[] { url });while (cursor.moveToNext()) {ThreadInfo threadInfo = new ThreadInfo();threadInfo.setId(cursor.getInt(cursor.getColumnIndex("thread_id")));threadInfo.setUrl(cursor.getString(cursor.getColumnIndex("url")));threadInfo.setStart(cursor.getInt(cursor.getColumnIndex("start")));threadInfo.setEnd(cursor.getInt(cursor.getColumnIndex("end")));threadInfo.setFinished(cursor.getInt(cursor.getColumnIndex("finished")));list.add(threadInfo);}cursor.close();db.close();return list;}@Overridepublic boolean isExists(String url, int thread_id) {SQLiteDatabase db = mHelper.getReadableDatabase();Cursor cursor = db.rawQuery("select * from thread_info where url = ? and thread_id = ?",new String[] { url, thread_id + "" });boolean exists = cursor.moveToNext();cursor.close();db.close();return exists;}}

MainActivity

package com.download.app;import java.util.ArrayList;import java.util.List;import android.app.Activity;import android.content.BroadcastReceiver;import android.content.Context;import android.content.Intent;import android.content.IntentFilter;import android.os.Bundle;import android.util.Log;import android.view.KeyEvent;import android.widget.ListView;import android.widget.Toast;import com.download.entities.FileInfo;import com.download.services.DownloadService;import com.imooc.DownLoad.R;public class MainActivity extends Activity {public static MainActivity mMainActivity = null;private ListView mListView = null;private List<FileInfo> mFileInfoList = null;private FileListAdapter mAdapter = null;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// listView视图控件mListView = (ListView) findViewById(R.id.lv_downLoad);// data数据源mFileInfoList = new ArrayList<FileInfo>();// 初始化文件信息对象FileInfo fileInfo = null;// 为方便测试,用Tomcat作服务器for (int i = 0; i < 13; i++) {fileInfo = new FileInfo(i, "http://192.168.1.100:8080/imooc" + i+ ".apk", "imooc" + i + ".apk", 0, 0);mFileInfoList.add(fileInfo);}// 适配器mAdapter = new FileListAdapter(this, mFileInfoList);// 设置适配器mListView.setAdapter(mAdapter);// 注册广播接收器IntentFilter filter = new IntentFilter();filter.addAction(DownloadService.ACTION_UPDATE);filter.addAction(DownloadService.ACTION_FINISHED);registerReceiver(mReceiver, filter);mMainActivity = this;}protected void onDestroy() {super.onDestroy();unregisterReceiver(mReceiver);}/** * 更新UI的广播接收器 */BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {if (DownloadService.ACTION_UPDATE.equals(intent.getAction())) {int finised = intent.getIntExtra("finished", 0);int id = intent.getIntExtra("id", 0);mAdapter.updateProgress(id, finised);Log.i("mReceiver", id + "-finised = " + finised);} else if (DownloadService.ACTION_FINISHED.equals(intent.getAction())) {// 下载结束FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");mAdapter.updateProgress(fileInfo.getId(), 0);Toast.makeText(MainActivity.this,mFileInfoList.get(fileInfo.getId()).getFileName()+ "下载完毕", 0).show();}}};}

FileListAdapter

package com.download.app;import java.util.List;import android.content.Context;import android.content.Intent;import android.view.LayoutInflater;import android.view.View;import android.view.View.OnClickListener;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.Button;import android.widget.ProgressBar;import android.widget.TextView;import com.download.entities.FileInfo;import com.download.services.DownloadService;import com.imooc.DownLoad.R;public class FileListAdapter extends BaseAdapter {private Context mContext;private List<FileInfo> mList;public FileListAdapter(Context context, List<FileInfo> fileInfos) {this.mContext = context;this.mList = fileInfos;}@Overridepublic int getCount() {return mList.size();}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder viewHolder = null;final FileInfo fileInfo = mList.get(position);if (convertView != null) {viewHolder = (ViewHolder) convertView.getTag();if (!viewHolder.mFileName.getTag().equals(Integer.valueOf(fileInfo.getId()))) {convertView = null;}}if (null == convertView) {LayoutInflater inflater = LayoutInflater.from(mContext);convertView = inflater.inflate(R.layout.item, null);viewHolder = new ViewHolder((TextView) convertView.findViewById(R.id.tv_fileName),(ProgressBar) convertView.findViewById(R.id.pb_progress),(Button) convertView.findViewById(R.id.btn_start),(Button) convertView.findViewById(R.id.btn_stop));convertView.setTag(viewHolder);viewHolder.mFileName.setText(fileInfo.getFileName());viewHolder.mProgressBar.setMax(100);viewHolder.mStartBtn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// 通知Service开始下载Intent intent = new Intent(mContext, DownloadService.class);intent.setAction(DownloadService.ACTION_START);intent.putExtra("fileInfo", fileInfo);mContext.startService(intent);}});viewHolder.mStopBtn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {Intent intent = new Intent(mContext, DownloadService.class);intent.setAction(DownloadService.ACTION_STOP);intent.putExtra("fileInfo", fileInfo);mContext.startService(intent);}});// 将viewHolder.mFileName的Tag设为fileInfo的ID,用于唯一标识viewHolder.mFileNameviewHolder.mFileName.setTag(Integer.valueOf(fileInfo.getId()));}viewHolder.mProgressBar.setProgress(fileInfo.getFinished());return convertView;}public void updateProgress(int id, int progress) {FileInfo fileInfo = mList.get(id);fileInfo.setFinished(progress);notifyDataSetChanged();}private static class ViewHolder {TextView mFileName;ProgressBar mProgressBar;Button mStartBtn;Button mStopBtn;public ViewHolder(TextView mFileName, ProgressBar mProgressBar,Button mStartBtn, Button mStopBtn) {this.mFileName = mFileName;this.mProgressBar = mProgressBar;this.mStartBtn = mStartBtn;this.mStopBtn = mStopBtn;}}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}}

DownloadService

package com.download.services;import java.io.File;import java.io.IOException;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.URL;import java.util.LinkedHashMap;import java.util.Map;import org.apache.http.HttpStatus;import com.download.entities.FileInfo;import android.app.Service;import android.content.Intent;import android.os.Environment;import android.os.Handler;import android.os.IBinder;import android.util.Log;import android.webkit.WebView.FindListener;public class DownloadService extends Service {public static final String DOWNLOAD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/downloads/";public static final String ACTION_START = "ACTION_START";public static final String ACTION_STOP = "ACTION_STOP";public static final String ACTION_UPDATE = "ACTION_UPDATE";public static final String ACTION_FINISHED = "ACTION_FINISHED";public static final int MSG_INIT = 0;private String TAG = "DownloadService";private Map<Integer, DownloadTask> mTasks = new LinkedHashMap<Integer, DownloadTask>();@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 获得Activity传过来的参数if (ACTION_START.equals(intent.getAction())) {FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");Log.i(TAG, "Start:" + fileInfo.toString());// 启动初始化线程new InitThread(fileInfo).start();} else if (ACTION_STOP.equals(intent.getAction())) {FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");Log.i(TAG, "Stop:" + fileInfo.toString());// 从集合中取出下载任务DownloadTask task = mTasks.get(fileInfo.getId());if (task != null) {task.isPause = true;}}return super.onStartCommand(intent, flags, startId);}private Handler mHandler = new Handler() {public void handleMessage(android.os.Message msg) {switch (msg.what) {case MSG_INIT:FileInfo fileInfo = (FileInfo) msg.obj;Log.i(TAG, "Init:" + fileInfo);// 启动下载任务DownloadTask task = new DownloadTask(DownloadService.this,fileInfo, 3);task.downLoad();// 把下载任务添加到集合中mTasks.put(fileInfo.getId(), task);break;default:break;}};};private class InitThread extends Thread {private FileInfo mFileInfo = null;public InitThread(FileInfo mFileInfo) {this.mFileInfo = mFileInfo;}/** * @see java.lang.Thread#run() */@Overridepublic void run() {HttpURLConnection connection = null;RandomAccessFile raf = null;try {// 连接网络文件URL url = new URL(mFileInfo.getUrl());connection = (HttpURLConnection) url.openConnection();connection.setConnectTimeout(5000);connection.setRequestMethod("GET");int length = -1;if (connection.getResponseCode() == HttpStatus.SC_OK) {// 获得文件的长度length = connection.getContentLength();}if (length <= 0) {return;}File dir = new File(DOWNLOAD_PATH);if (!dir.exists()) {dir.mkdir();}// 在本地创建文件File file = new File(dir, mFileInfo.getFileName());raf = new RandomAccessFile(file, "rwd");// 设置文件长度raf.setLength(length);mFileInfo.setLength(length);mHandler.obtainMessage(MSG_INIT, mFileInfo).sendToTarget();} catch (Exception e) {e.printStackTrace();} finally {if (connection != null) {connection.disconnect();}if (raf != null) {try {raf.close();} catch (IOException e) {e.printStackTrace();}}}}}@Overridepublic IBinder onBind(Intent intent) {return null;}}

DownloadTask

package com.download.services;import java.io.File;import java.io.InputStream;import java.io.RandomAccessFile;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.net.URLConnection;import java.util.ArrayList;import java.util.List;import org.apache.http.HttpStatus;import android.R.integer;import android.content.Context;import android.content.Intent;import android.nfc.Tag;import android.util.Log;import android.widget.Toast;import com.download.app.MainActivity;import com.download.db.ThreadDAO;import com.download.db.ThreadDAOImpl;import com.download.entities.FileInfo;import com.download.entities.ThreadInfo;/** * 下载任务类 */public class DownloadTask {private Context mContext = null;private FileInfo mFileInfo = null;private ThreadDAO mDao = null;private int mFinised = 0;public boolean isPause = false;private int mThreadCount = 1; // 线程数量private List<DownloadThread> mDownloadThreadList = null; // 线程集合public DownloadTask(Context mContext, FileInfo mFileInfo, int count) {this.mContext = mContext;this.mFileInfo = mFileInfo;this.mThreadCount = count;mDao = new ThreadDAOImpl(mContext);}public void downLoad() {// 读取数据库的线程信息List<ThreadInfo> threads = mDao.getThreads(mFileInfo.getUrl());ThreadInfo threadInfo = null;if (0 == threads.size()) {// 计算每个线程下载长度int len = mFileInfo.getLength() / mThreadCount;for (int i = 0; i < mThreadCount; i++) {// 初始化线程信息对象threadInfo = new ThreadInfo(i, mFileInfo.getUrl(), len * i,(i + 1) * len - 1, 0);if (mThreadCount - 1 == i) // 处理最后一个线程下载长度不能整除的问题{threadInfo.setEnd(mFileInfo.getLength());}// 添加到线程集合中threads.add(threadInfo);mDao.insertThread(threadInfo);}}mDownloadThreadList = new ArrayList<DownloadTask.DownloadThread>();// 启动多个线程进行下载for (ThreadInfo info : threads) {DownloadThread thread = new DownloadThread(info);thread.start();// 添加到线程集合中mDownloadThreadList.add(thread);}}/** * 下载线程 */private class DownloadThread extends Thread {private ThreadInfo mThreadInfo = null;public boolean isFinished = false; // 线程是否执行完毕public DownloadThread(ThreadInfo mInfo) {this.mThreadInfo = mInfo;}@Overridepublic void run() {HttpURLConnection connection = null;RandomAccessFile raf = null;InputStream inputStream = null;try {URL url = new URL(mThreadInfo.getUrl());connection = (HttpURLConnection) url.openConnection();connection.setConnectTimeout(5000);connection.setRequestMethod("GET");// 设置下载位置int start = mThreadInfo.getStart() + mThreadInfo.getFinished();connection.setRequestProperty("Range", "bytes=" + start + "-"+ mThreadInfo.getEnd());// 设置文件写入位置File file = new File(DownloadService.DOWNLOAD_PATH,mFileInfo.getFileName());raf = new RandomAccessFile(file, "rwd");raf.seek(start);Intent intent = new Intent();intent.setAction(DownloadService.ACTION_UPDATE);mFinised += mThreadInfo.getFinished();Log.i("mFinised", mThreadInfo.getId() + "finished = "+ mThreadInfo.getFinished());// 开始下载if (connection.getResponseCode() == HttpStatus.SC_PARTIAL_CONTENT) {// 读取数据inputStream = connection.getInputStream();byte buf[] = new byte[1024 << 2];int len = -1;long time = System.currentTimeMillis();while ((len = inputStream.read(buf)) != -1) {// 写入文件raf.write(buf, 0, len);// 累加整个文件完成进度mFinised += len;// 累加每个线程完成的进度mThreadInfo.setFinished(mThreadInfo.getFinished() + len);if (System.currentTimeMillis() - time > 1000) {time = System.currentTimeMillis();int f = mFinised * 100 / mFileInfo.getLength();if (f > mFileInfo.getFinished()) {intent.putExtra("finished", f);intent.putExtra("id", mFileInfo.getId());mContext.sendBroadcast(intent);}}// 在下载暂停时,保存下载进度if (isPause) {mDao.updateThread(mThreadInfo.getUrl(),mThreadInfo.getId(),mThreadInfo.getFinished());Log.i("mThreadInfo", mThreadInfo.getId()+ "finished = " + mThreadInfo.getFinished());return;}}// 标识线程执行完毕isFinished = true;checkAllThreadFinished();}} catch (Exception e) {e.printStackTrace();} finally {try {if (connection != null) {connection.disconnect();}if (raf != null) {raf.close();}if (inputStream != null) {inputStream.close();}} catch (Exception e2) {e2.printStackTrace();}}}}/** * 判断所有的线程是否执行完毕 */private synchronized void checkAllThreadFinished() {boolean allFinished = true;// 遍历线程集合,判断线程是否都执行完毕for (DownloadThread thread : mDownloadThreadList) {if (!thread.isFinished) {allFinished = false;break;}}if (allFinished) {// 删除下载记录mDao.deleteThread(mFileInfo.getUrl());// 发送广播知道UI下载任务结束Intent intent = new Intent(DownloadService.ACTION_FINISHED);intent.putExtra("fileInfo", mFileInfo);mContext.sendBroadcast(intent);}}}



0 0
原创粉丝点击