package com.yu.threadsdownload;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.w3c.dom.Text;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
import android.text.TextUtils;
import android.text.format.Formatter;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;
import com.yu.bean.DownloadInfo;
public class MainActivity extends Activity {
// 下载链接
public static final String QQ = "http://a5.pc6.com/cx3/QQmobile2016.pc6.apk";
public static final String WEIXIN = "http://a5.pc6.com/lffaz3/weixin2016.apk";
public static final String PC6 = "http://a3.pc6.com/cx/pc6shichang.pc6.apk";
// handler 消息
public static final int MSG_DATA_LOADED = 0; // 数据加载完成时
public static final int MSG_PROGRESS_UPDATE = 1;// 下载进度更新时
public static final int MSG_TASK_DONE = 2; // 下载完成时
public static final int MSG_ONCLICK = 3; // 点击下载时
static ListView lv;
static List<DownloadInfo> infos;
static DownloadAdapter adapter;
static MainActivity ma;
static Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MSG_DATA_LOADED:
infos.add((DownloadInfo) msg.obj);
if (adapter == null) {
adapter = ma.new DownloadAdapter();
}else{
adapter.notifyDataSetChanged();
}
lv.setAdapter(adapter);
break;
case MSG_PROGRESS_UPDATE :
DownloadInfo info =(DownloadInfo) msg.obj;
ProgressBar pb = (ProgressBar) lv.findViewWithTag(info.getUrl());
TextView tvProgress = (TextView) lv.findViewWithTag(info.getUrl()+"tvProgress");
if (pb != null) {
pb.setProgress(info.getPercent());
}
if (tvProgress != null) {
tvProgress.setText(info.getPercent()+"%");
}
if (info.getPercent() == 100) {
Toast.makeText(ma, info.getFileName()+"下载完成!", 1).show();
}
break;
}
};
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
}
private void initView() {
lv = (ListView) findViewById(R.id.lv_download);
infos = new ArrayList<DownloadInfo>();
ma = this;
adapter = new DownloadAdapter();
}
private void initData() {
String[] urls = {QQ,WEIXIN,PC6};
for (final String url : urls) {
newTask(url);
}
}
/** * 新建下载任务 * @param url */
public void newTask(final String url) {
new Thread(){
HttpURLConnection conn;
public void run() {
try {
conn = (HttpURLConnection) new URL(url).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
if (conn.getResponseCode() == 200) {
String fileSize = Formatter.formatFileSize(MainActivity.this, conn.getContentLength());
String fileName = getFileName(url);
DownloadInfo info = new DownloadInfo(fileName, fileSize, conn.getContentLength(),url,0);
Message msg = handler.obtainMessage();
msg.what = MSG_DATA_LOADED;
msg.obj = info;
handler.sendMessage(msg);
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
conn.disconnect();
}
};
}.start();
}
/** * 获取下载软件的文件名 * @param url * @return */
public String getFileName(String url) {
int index = url.lastIndexOf("/");
return url.substring(index+1);
}
// 适配器类
class DownloadAdapter extends BaseAdapter{
@Override
public int getCount() {
return infos.size();
}
@Override
public Object getItem(int position) {
return infos.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder ;
if (convertView == null) {
holder = new ViewHolder();
convertView = View.inflate(MainActivity.this, R.layout.item_threads_download, null);
holder.tvFileName = (TextView) convertView.findViewById(R.id.tv_fileName);
holder.tvProgress = (TextView) convertView.findViewById(R.id.tv_progress);
holder.pb = (ProgressBar) convertView.findViewById(R.id.pb_progress);
holder.pb.setMax(100);
holder.btDownload = (Button) convertView.findViewById(R.id.bt_download);
convertView.setTag(holder);
}else{
holder = (ViewHolder) convertView.getTag();
}
final DownloadInfo info = infos.get(position);
holder.tvFileName.setText(info.getFileName()+"/"+info.getFileSize());
holder.tvProgress.setText(info.getProgress()+"%");
holder.pb.setTag(info.getUrl());
holder.tvProgress.setTag(info.getUrl()+"tvProgress");
holder.btDownload.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
startService(new Intent(MainActivity.this,DownloadService.class));
Message msg = DownloadService.handler.obtainMessage();
msg.what = MSG_ONCLICK;
msg.obj = info;
DownloadService.handler.sendMessage(msg );
}
});
return convertView;
}
}
class ViewHolder{
TextView tvFileName,tvProgress;
ProgressBar pb;
Button btDownload;
}
/** * 新建下载任务 * @param view */
public void addTask(View view) {
EditText et = (EditText) findViewById(R.id.et_url);
String url = et.getText().toString().trim();
if (TextUtils.isEmpty(url)) {
return;
}
newTask(url);
}
}
package com.yu.threadsdownload;
import java.util.ArrayList;
import java.util.List;
import com.yu.bean.DownloadInfo;
import com.yu.db.DownloadDBImpl;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
public class DownloadService extends Service {
static DownloadService dls ;
static DownloadTask task;
static List<DownloadTask> tasks; // 任务队列
static DownloadDBImpl db ;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
dls = DownloadService.this;
tasks = new ArrayList<DownloadTask>();
db = new DownloadDBImpl(dls);// 仅在service创建时,得到db对象实例
}
public static Handler handler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case MainActivity.MSG_ONCLICK: // 当点击开启下载任务时
DownloadInfo info = (DownloadInfo) msg.obj;
// Log.e("", "info:"+info.toString());
task = new DownloadTask(info, 3,dls, db);
if (tasks.size() >=2) {
Toast.makeText(dls, "已加入下载任务队列!", 0).show();
}
tasks.add(task);
info.setTaskId(tasks.indexOf(task));
for (DownloadTask task : tasks) {
if (tasks.indexOf(task)<2 && !task.isRunning) { // 超过两个的任务需要等待
task.start();
}
}
break;
case MainActivity.MSG_TASK_DONE: // 当有任务完成时
int taskId = msg.arg1;
if (taskId < tasks.size()) {
DownloadTask task2 = tasks.remove(taskId);
Log.e("", "移除"+task2);
}
for (DownloadTask currTask : tasks) { // 开启排队等待的任务
if (tasks.indexOf(task)<2 && !currTask.isRunning) {
task.start();
}
}
break;
}
};
};
}
package com.yu.threadsdownload;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Environment;
import android.os.Message;
import android.util.Log;
import com.yu.bean.DownloadInfo;
import com.yu.bean.ThreadInfo;
import com.yu.db.DownloadDBImpl;
/** * 下载任务 * @author Administrator * */
public class DownloadTask {
DownloadInfo info;
int threadCount;
DownloadDBImpl db ;
long progress = 0;
boolean isPurse;
boolean isRunning;
List<DownloadThread> taskThreads;
public DownloadTask(DownloadInfo info,int threadCount,Context context,DownloadDBImpl db) {
super();
this.info = info;
this.threadCount = threadCount;
this.db = db;
taskThreads = new ArrayList<DownloadTask.DownloadThread>();
isPurse = false;
}
/** * 初始化下载 * @param url */
public void start(){
isRunning = true;
long length = info.getFileLength();
long part = length / threadCount;
for (int i = 0; i < threadCount; i++) {
long start = part*i;
long end = start+part-1;
if (i == threadCount - 1) {
end = length;
}
int threadId = i;
Log.e("", "start:"+start+"end:"+end);
ThreadInfo threadInfo = new ThreadInfo(threadId, 0, info.getUrl() , start, end);
db.insertThread(threadInfo );
DownloadThread thread = new DownloadThread(start, end, info.getUrl(),threadId);
thread.start();
taskThreads.add(thread);
}
}
/** * 下载线程 * @author Administrator * */
class DownloadThread extends Thread{
long start,end;
int threadId;
String url;
ThreadInfo threadInfo;
long threadProgress = 0;
boolean finished;
public DownloadThread(long start, long end, String url, int threadId) {
super();
finished = false;
this.start = start;
this.end = end;
this.url = url;
this.threadId = threadId;
threadInfo = new ThreadInfo( start, end, url,threadId);
}
@Override
public void run() {
super.run();
HttpURLConnection conn = null;
RandomAccessFile raf = null;
try {
long threadProgressed = db.getThreadProgress(threadInfo);
final long currStart = start + threadProgressed;
progress = db.getTaskProgress(url);
// Log.e("","threadProgressed:"+ threadProgressed+",taskProgress"+progress);
conn = (HttpURLConnection) new URL(info.getUrl()).openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);
// 请求部分网络数据 响应码为206
conn.addRequestProperty("Range", "bytes="+currStart+"-"+end);
File file = new File(Environment.getExternalStorageDirectory(), info.getFileName());
// 写入SD卡 开启写入权限
raf = new RandomAccessFile(file,"rw");
// 跳到分段下载起始位置
raf.seek(currStart);
raf.setLength(info.getFileLength());
if (conn.getResponseCode() == 206) {
InputStream is = conn.getInputStream();
byte[] buff = new byte[1024];
int len = -1;
long currTime = System.currentTimeMillis();
while((len = is.read(buff)) != -1){
raf.write(buff, 0, len);
progress+=len;
threadProgress+=len;
if (System.currentTimeMillis() - currTime > 1000) {
currTime = System.currentTimeMillis();
threadInfo.setThreadProgress(threadProgress);
info.setProgress(progress);
db.addThreadProgress(threadInfo);
db.addTaskProgress(info);
Message msg = MainActivity.handler.obtainMessage();
msg.what = MainActivity.MSG_PROGRESS_UPDATE;
info.setPercent((int) (progress * 100/info.getFileLength()));
msg.obj = info;
MainActivity.handler.sendMessage(msg);
}
}
finished = true;
checkFinfish();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
conn.disconnect();
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
};
}
/** * 检查下载任务是否完成 */
public void checkFinfish() {
for (DownloadThread thread : taskThreads) {
if (!thread.finished) {
return;
}
}
// 通知任务完成
Message msg = MainActivity.handler.obtainMessage();
msg.what = MainActivity.MSG_PROGRESS_UPDATE;
info.setPercent(100);
msg.obj = info;
MainActivity.handler.sendMessage(msg);
// 通知任务队列变化
Message msg2 = DownloadService.handler.obtainMessage();
msg2.what = MainActivity.MSG_TASK_DONE;
msg2.arg1 = info.getTaskId();
DownloadService.handler.sendMessage(msg2);
// 清除数据库下载记录
db.deleteThread(info.getUrl());
}
}
package com.yu.db;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
public class ThreadsDBHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "download.db";
private static ThreadsDBHelper sDbHelper;
private ThreadsDBHelper(Context context) {
super(context, DB_NAME, null, 1);
}
/** * 单例 * @param context * @return */
public static ThreadsDBHelper getInstance(Context context) {
if (sDbHelper == null) {
synchronized (ThreadsDBHelper.class) {
if (sDbHelper == null) {
sDbHelper = new ThreadsDBHelper(context);
}
}
}
return sDbHelper;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table downloadInfo(_id integer primary key autoincrement,threadId integer,taskProgress integer,finished integer,start integer,end integer,url text)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
package com.yu.db;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import com.yu.bean.DownloadInfo;
import com.yu.bean.ThreadInfo;
public class DownloadDBImpl implements DownloadDBInterface{
ThreadsDBHelper helper;
SQLiteDatabase db;
public DownloadDBImpl(Context context) {
super();
helper = ThreadsDBHelper.getInstance(context);
}
@Override
public synchronized boolean insertThread(ThreadInfo threadInfo) {
if (isTheradExist(threadInfo.getUrl(),threadInfo.getThreadId())) {
return false;
}else{
db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("threadId", threadInfo.getThreadId());
values.put("url", threadInfo.getUrl());
values.put("start", threadInfo.getStart());
values.put("end", threadInfo.getEnd());
long raw = db.insert("downloadInfo", null, values);
if (raw != -1) {
db.close();
return true;
}
db.close();
}
return false;
}
@Override
public synchronized boolean deleteThread(String url) {
db = helper.getWritableDatabase();
int raw = db.delete("downloadInfo", "url=?", new String[]{url});
if (raw != 0) {
db.close();
return true;
}
db.close();
return false;
}
@Override
public synchronized ThreadInfo findThread(String url,int threadId) {
db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select threadProgress,start,end from downloadInfo where url=? and threadId=?", new String[]{url,threadId+""});
if (cursor.moveToNext()) {
int threadProgress = cursor.getInt(0);
int start = cursor.getInt(1);
int end = cursor.getInt(2);
ThreadInfo threadInfo = new ThreadInfo(threadProgress, start, end);
return threadInfo;
}
return null;
}
@Override
public synchronized boolean isTheradExist(String url, int threadId) {
db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select * from downloadInfo where url=? and threadId=?", new String[]{url,threadId+""});
boolean exist = cursor.moveToNext();
cursor.close();
db.close();
return exist;
}
@Override
public synchronized long getTaskProgress(String url) {
db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select taskProgress from downloadInfo where url=?", new String[]{url});
if(cursor.moveToNext()){
long taskProgress = cursor.getInt(0);
cursor.close();
db.close();
return taskProgress;
}
cursor.close();
db.close();
return 0;
}
@Override
public synchronized boolean addTaskProgress(DownloadInfo downloadInfo) {
if (isTaskExist(downloadInfo.getUrl())) {
db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("taskProgress", downloadInfo.getProgress());
values.put("url", downloadInfo.getUrl());
long raw = db.update("downloadInfo", values, "url=?", new String[]{downloadInfo.getUrl()});
if (raw != -1){
db.close();
return true;
}
db.close();
}else{
db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("taskProgress", downloadInfo.getProgress());
values.put("url", downloadInfo.getUrl());
long raw = db.insert("downloadInfo", null, values );
if (raw != -1){
db.close();
return true;
}
db.close();
}
return false;
}
@Override
public synchronized long getThreadProgress(ThreadInfo info) {
db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select finished from downloadInfo where url=? and threadId=?", new String[]{info.getUrl(),info.getThreadId()+""});
if(cursor.moveToNext()){
long threadProgress = cursor.getInt(0);
cursor.close();
db.close();
Log.e("", "threadProgress----"+threadProgress);
return threadProgress;
}
cursor.close();
db.close();
return 0;
}
@Override
public synchronized boolean addThreadProgress(ThreadInfo threadInfo) {
if (isTheradExist(threadInfo.getUrl(), threadInfo.getThreadId())) {
db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("finished", threadInfo.getThreadProgress());
int raw = db.update("downloadInfo", values , "url=? and threadId=?", new String[]{threadInfo.getUrl(),threadInfo.getThreadId()+""});
if (raw > 0) {
db.close();
return true;
}
db.close();
}else{
db = helper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("finished", threadInfo.getThreadProgress());
values.put("threadId", threadInfo.getThreadId());
values.put("url", threadInfo.getUrl());
long raw = db.insert("downloadInfo", null ,values);
if (raw != -1) {
db.close();
return true;
}
db.close();
}
return false;
}
@Override
public boolean isTaskExist(String url) {
db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select taskProgress from downloadInfo where url=?", new String[]{url});
if(cursor != null) {
if (cursor.moveToNext()) {
cursor.close();
db.close();
return true;
}
}
cursor.close();
db.close();
return false;
}
}
数据库操作接口
package com.yu.db;
import com.yu.bean.DownloadInfo;
import com.yu.bean.ThreadInfo;
/** * 线程下载接口 * @author Administrator * */
public abstract interface DownloadDBInterface {
/** * 插入线程信息 * @param threadInfo * @return */
public abstract boolean insertThread(ThreadInfo threadInfo);
/** * 根据url删除线程信息 * @param url * @return */
public abstract boolean deleteThread(String url);
/** * 根据url查找线程信息 * @param url * @return */
public abstract ThreadInfo findThread(String url,int threadId);
/** * 根据url threadId判断线程信息是否存在 * @param url * @param threadId * @return */
public abstract boolean isTheradExist(String url,int threadId);
/** * 根据url判断任务信息是否存在 * @param url * @return */
public abstract boolean isTaskExist(String url);
/** * 获取任务进度 * @return */
public abstract long getTaskProgress(String url);
/** * 添加任务进度 * @return */
public abstract boolean addTaskProgress(DownloadInfo downloadInfo);
/** * 添加线程进度 * @param url * @return */
public abstract boolean addThreadProgress(ThreadInfo threadInfo);
/** * 获取线程进度 * @return */
public abstract long getThreadProgress(ThreadInfo threadInfo);
}