今天还是看着慕课网,把昨天的断点续传代码做了一些修改,用列表的形式,可以自定义下载一个文件需要的线程数,但是可能是由于线程的创建和销毁,还有多个线程同时操作数据库,所以感觉体验不是很好,虽然对方法声明了同步,也安排对数据库的操作在线程之外,同时也利用了线程池,来尽量减少对资源的消耗,,但是觉得不是很流畅,,,还有万恶的进度条,,,,
同样先是我的代码目录结构
adapter很简洁
List<FileInfos> list;
Context mContext;
LayoutInflater inflater;
public MyAdapter(List<FileInfos> list, Context mContext) {
this.list = list;
this.mContext = mContext;
inflater=LayoutInflater.from(mContext);
}
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View view, ViewGroup parent) {
MyViewHolder holder=null;
if (view==null)
{
view = inflater.inflate(R.layout.item_layout, parent, false);
holder = new MyViewHolder();
holder.textView= (TextView) view.findViewById(R.id.textView);
holder.start_bt= (Button) view.findViewById(R.id.start_bt);
holder.pause_bt= (Button) view.findViewById(R.id.pause_bt);
holder.progressBar= (ProgressBar) view.findViewById(R.id.progressBar);
view.setTag(holder);
}
else
{
holder= (MyViewHolder) view.getTag();
}
final MyViewHolder holderBuff=holder;
holder.start_bt.setTag(position);
holder.pause_bt.setTag(position);
//设置视图中的控件
final FileInfos fileInfos = list.get(position);
holder.textView.setText(fileInfos.getFileName());
holder.progressBar.setMax(100);
holder.progressBar.setProgress(fileInfos.getFinished());
holderBuff.start_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (position==(int)holderBuff.start_bt.getTag())
{
//通知service开始下载
Intent intentStart = new Intent(mContext, DownloadService.class);
intentStart.setAction(DownloadService.ACTION_START);
intentStart.putExtra("fileInfo",fileInfos);
mContext.startService(intentStart);
}
}
});
holderBuff.pause_bt.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (position==(int)holderBuff.pause_bt.getTag())
{
//通知server暂停下载
Intent intentStop = new Intent(mContext, DownloadService.class);
intentStop.setAction(DownloadService.ACTION_STOP);
intentStop.putExtra("fileInfo",fileInfos);
mContext.startService(intentStop);
}
}
});
return view;
}
static class MyViewHolder{
TextView textView;
Button start_bt,pause_bt;
ProgressBar progressBar;
}
public class FileInfos implements Serializable{
private int id;
private String url;
private String fileName;
private int length;
private int finished;
}
public class ThreadInfos implements Serializable{
private int id;
private String url;
private int start;
private int end;
private int finished;
}
public class DBHelper extends SQLiteOpenHelper{
private static final String DB_NAME="download.db";
public static DBHelper helper;
private static String SQL_CREATE="create table if not exists thread_info(_id integer primary key autoincrement,thread_id integer," +
"url text,start integer,end integer,finished integer)";
private static String SQL_DROP="drop table if exists thread_info";
public static synchronized DBHelper getInstance(Context mContext)
{
if (helper==null)
{
helper=new DBHelper(mContext,DB_NAME,null,1);
}
return helper;
}
private DBHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(SQL_CREATE);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
/**数据访问接口的实现
* Created by Administrator on 2017/7/19.
*/
public class ThreadDaoImp implements ThreadDao {
private DBHelper helper;
public ThreadDaoImp(Context context) {
helper=DBHelper.getInstance(context);
}
@Override
public synchronized void insertThread(ThreadInfos threadInfos) {
SQLiteDatabase db = helper.getReadableDatabase();
db.execSQL("insert into thread_info(thread_id,url,start,end,finished) values (?,?,?,?,?)",
new Object[]{threadInfos.getId(),threadInfos.getUrl(),threadInfos.getStart(),threadInfos.getEnd(),threadInfos.getFinished()});
db.close();
}
@Override
public synchronized void deleteThread(String url, int thredId) {
SQLiteDatabase db = helper.getReadableDatabase();
db.execSQL("delete from thread_info where url= ? and thread_id= ?",
new Object[]{url,thredId});
db.close();
}
@Override
public synchronized void deleteThread(String url) {
SQLiteDatabase db = helper.getReadableDatabase();
db.execSQL("delete from thread_info where url= ? ",
new Object[]{url});
db.close();
}
@Override
public synchronized void updateThread(String url, int threadId, int finished) {
SQLiteDatabase db = helper.getReadableDatabase();
db.execSQL("update thread_info set finished = ? where url= ? and thread_id= ?",
new Object[]{finished,url,threadId});
db.close();
}
@Override
public synchronized List<ThreadInfos> getThreads(String url) {
SQLiteDatabase db = helper.getReadableDatabase();
List<ThreadInfos> list=new ArrayList<>();
Cursor cursor = db.rawQuery("select * from thread_info where url= ? ",
new String[]{url});
while (cursor.moveToNext())
{
ThreadInfos threadInfos = new ThreadInfos();
threadInfos.setId(cursor.getInt(cursor.getColumnIndex("thread_id")));
threadInfos.setUrl(cursor.getString(cursor.getColumnIndex("url")));
threadInfos.setStart(cursor.getInt(cursor.getColumnIndex("start")));
threadInfos.setEnd(cursor.getInt(cursor.getColumnIndex("end")));
threadInfos.setFinished(cursor.getInt(cursor.getColumnIndex("finished")));
list.add(threadInfos);
}
cursor.close();
db.close();
return list;
}
@Override
public synchronized boolean isExists(String url, int threadId) {
SQLiteDatabase db = helper.getReadableDatabase();
Cursor cursor = db.rawQuery("select * from thread_info where url= ? and thread_id= ?",
new String[]{url,threadId+""});
boolean exists=cursor.moveToNext();
cursor.close();
db.close();
return exists;
}
}
/**下载任务类
* Created by Administrator on 2017/7/19.
*/
public class DownloadTask {
private Context context;
private FileInfos fileInfos;
private ThreadDaoImp threadDaoImp;
private int mFinished=0;
public boolean isPaused;
//下载一个文件需要的线程个数
private int threadCount;
private List<DownloadThread> downloadThreadList=new ArrayList<>();
//带缓存数量的线程池
public static ExecutorService executorService= Executors.newCachedThreadPool();
public DownloadTask(Context context, FileInfos fileInfos,int threadCount) {
this.context = context;
this.fileInfos = fileInfos;
this.threadCount=threadCount;
threadDaoImp=new ThreadDaoImp(context);
}
public void download()
{
//读取数据库的线程信息
List<ThreadInfos> list = threadDaoImp.getThreads(fileInfos.getUrl());
ThreadInfos threadInfos=null;
if (list.size()==0)
{
//获得每个线程需要下载的长度
int length = fileInfos.getLength() / threadCount;
for (int i = 0; i < threadCount; i++) {
//初始化线程信息对象
threadInfos = new ThreadInfos(i,fileInfos.getUrl(),length*i,(i+1)*length-1,0);
if (i==threadCount-1)
{
threadInfos.setEnd(fileInfos.getLength());
}
list.add(threadInfos);
//先数据库插入线程信息
/*if (!threadDaoImp.isExists(threadInfos.getUrl(),threadInfos.getId()))
{
threadDaoImp.insertThread(threadInfos);
}*/
threadDaoImp.insertThread(threadInfos);
}
}
//启动多个线程进行下载
for (int i = 0; i < list.size(); i++) {
threadInfos=list.get(i);
DownloadThread downloadThread = new DownloadThread(threadInfos);
//downloadThread.start();
//通过线程池启动线程
executorService.execute(downloadThread);
downloadThreadList.add(downloadThread);
}
}
//判断是否所有线程都执行完毕
public synchronized void checkAllThreadsFinished()
{
boolean allFinished=true;
for (DownloadThread downloadThread:downloadThreadList)
{
if (!downloadThread.isFinished)
{
allFinished=false;
break;
}
}
//下载完毕时,通知页面更新,并传递当前文件信息
if (allFinished)
{
threadDaoImp.deleteThread(fileInfos.getUrl());
Intent intent = new Intent();
intent.setAction(DownloadService.ACTION_FINISH);
intent.putExtra("fileInfo",fileInfos);
intent.putExtra("finish",0);
context.sendBroadcast(intent);
}
}
//下载线程
class DownloadThread extends Thread{
private ThreadInfos threadInfos;
//标志是否完成
public boolean isFinished=false;
public DownloadThread(ThreadInfos threadInfos) {
this.threadInfos = threadInfos;
}
@Override
public void run() {
super.run();
RandomAccessFile raf=null;
try {
URL url = new URL(threadInfos.getUrl());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//conn.connect();
int length=-1;
//设置下载位置
int start=threadInfos.getStart()+threadInfos.getFinished();
conn.setRequestProperty("Range","bytes="+start+"-"+threadInfos.getEnd());
//设置文件写入位置
File file = new File(DownloadService.DOWNLOAD_PATH,fileInfos.getFileName());
raf = new RandomAccessFile(file, "rwd");
raf.seek(start);
Intent intent = new Intent();
intent.setAction(DownloadService.ACTION_UPDATE);
mFinished+=threadInfos.getFinished();
if (conn.getResponseCode()==206)
{
//开始下载
//读取数据
InputStream input = conn.getInputStream();
byte[] bytes = new byte[1024*4];
long time=0;
while ((length=input.read(bytes))!=-1)
{
//写入文件
raf.write(bytes,0,length);
//累加完成进度,把下载进度通过广播发送给activity
mFinished+=length;
//设置每个线程的完成进度
threadInfos.setFinished(threadInfos.getFinished()+length);
if (mFinished>=fileInfos.getLength())
{
mFinished=fileInfos.getLength();
}
if (System.currentTimeMillis()-time>1000)
{
time=System.currentTimeMillis();
intent.putExtra("progress",mFinished*100/fileInfos.getLength());
intent.putExtra("position",fileInfos.getId());
context.sendBroadcast(intent);
}
//在下载暂停时,保存下载进度
if (isPaused)
{
threadDaoImp.updateThread(threadInfos.getUrl(),threadInfos.getId(),threadInfos.getFinished()+length);
return;
}
}
//当前线程下载完毕
isFinished=true;
//检查下载任务是否执行完毕
checkAllThreadsFinished();
//下载完成后,删除线程信息
//threadDaoImp.deleteThread(threadInfos.getUrl(),threadInfos.getId());
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (raf!=null)
{
raf.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public interface ThreadDao {
//插入线程信息
public void insertThread(ThreadInfos threadInfos);
//删除线程
public void deleteThread(String url, int thredId);
//删除线程
public void deleteThread(String url);
//更新线程下载进度
public void updateThread(String url, int threadId, int finished);
//查询线程信息
public List<ThreadInfos> getThreads(String url);
//线程信息是否存在
public boolean isExists(String url, int threadId);
}
public class DownloadService extends Service{
public static final String DOWNLOAD_PATH= Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getAbsolutePath()
+ File.separator+"demo";
public static final String ACTION_START="ACTION_START";
public static final String ACTION_STOP="ACTION_STOP";
public static final String ACTION_FINISH="ACTION_FINISH";
public static final String ACTION_UPDATE="ACTION_UPDATE";
public static final int MSG_INIT=0;
DownloadTask downloadTask;
//下载任务的集合
private Map<Integer,DownloadTask> mTasks=new LinkedHashMap<>();
public Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what)
{
case 0:
FileInfos fileInfos= (FileInfos) msg.obj;
Log.d("==handler====","======="+fileInfos.toString());
//启动下载任务
downloadTask = new DownloadTask(DownloadService.this, fileInfos,1);
downloadTask.download();
mTasks.put(fileInfos.getId(),downloadTask);
break;
default:
break;
}
}
};
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//获取从activity中传递过来的信息
if (ACTION_START.equals(intent.getAction()))
{
FileInfos fileInfo = (FileInfos) intent.getSerializableExtra("fileInfo");
Log.d("==start====","======"+fileInfo.toString());
InitThread initThread = new InitThread(fileInfo);
//通过线程池启动线程
DownloadTask.executorService.execute(initThread);
}
else if (ACTION_STOP.equals(intent.getAction()))
{
FileInfos fileInfo = (FileInfos) intent.getSerializableExtra("fileInfo");
Log.d("==stop====","======"+fileInfo.toString());
//从集合中取出下载任务
downloadTask=mTasks.get(fileInfo.getId());
if (downloadTask!=null)
{
downloadTask.isPaused=true;
}
}
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
class InitThread extends Thread{
FileInfos fileInfos;
public InitThread(FileInfos fileInfos) {
this.fileInfos = fileInfos;
}
@Override
public void run() {
super.run();
RandomAccessFile raf=null;
try {
//连接网络文件
URL url = new URL(fileInfos.getUrl());
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
int length=-1;
if (conn.getResponseCode()==200)
{
//获得文件长度
length=conn.getContentLength();
if (length<=0)
{
return;
}
File dir=new File(DOWNLOAD_PATH);
if (!dir.exists())
{
dir.mkdirs();
}
//在本地创建文件
File file=new File(dir,fileInfos.getFileName());
raf = new RandomAccessFile(file, "rwd");
//设置文件长度
raf.setLength(length);
}
fileInfos.setLength(length);
Message message = handler.obtainMessage();
message.what=MSG_INIT;
message.obj=fileInfos;
handler.sendMessage(message);
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (raf!=null)
{
raf.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class MainActivity extends AppCompatActivity {
ListView listView;
List<FileInfos> list=new ArrayList<>();
MyAdapter adapter;
MyBroadCastReceiver receiver;
String urlKoWo="http://down.shouji.kuwo.cn/star/mobile/KuwoPlayerV3_ar_8.5.0.2_kw.apk";
String urlWangYiYun="http://wap.sogou.com/app/redir.jsp?appdown=1&u=b-5xxbPWjTJoeTuIzmwjONmoKoIYNgdlPOVy7fun_559rBQsnOOI53wn2loT2DsynmZKhKgICw9cvo3pwsuRdnnjD1ytUFis2XKdOfrva1NqaQuikpu3jJQKXph_jOiUllzWmh1shCpefgbQw2K0EZJD7JV3Lhf7&docid=-7472260854952176859&sourceid=-8776534065371990068&w=1906&stamp=20170720";
String urlQqYinYue="http://wap.sogou.com/app/redir.jsp?appdown=1&u=0Gd8piB6093-W-QPHXpNcQJvLH6U5CYdhuq4q4hqPTzLhN43WuAaT3n4Y-s0ktNHy6rGmVCuxuQ.&docid=-4411786374463855878&sourceid=-232956208690215432&w=1906&stamp=20170720";
String urlWangYiXinWen="http://wap.sogou.com/app/redir.jsp?appdown=1&u=b-5xxbPWjTJoeTuIzmwjONmoKoIYNgdlkJxIVeG_E-J5MlgzcFdg_6OaWQcQGiJV0ZeNYiLRI5ox-6Vb9Ht3p3njD1ytUFis2XKdOfrva1OAw54MZ2sYE4mD7zyni3p_HmD2WrN3oRsz6M6Y2evgE5cXC2PaxNpfD-pdxvmeHec.&docid=1984396447247278048&sourceid=4426688076946271253&w=1906&stamp=20170720";
String urlAiQiYi="http://wap.sogou.com/app/redir.jsp?appdown=1&u=0Gd8piB6093-W-QPHXpNcQJvLH6U5CYd5Zr5JWfuiTzw3lCzNhGrNbDfVRQLpRJX&docid=2339797260441192979&sourceid=7064725457117163301&w=1906&stamp=20170720";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView= (ListView) findViewById(R.id.listView);
FileInfos fileInfos = new FileInfos(0, urlKoWo, "kuwo.apk", 0, 0);
FileInfos fileInfos1 = new FileInfos(1, urlWangYiYun, "wangyiyun.apk", 0, 0);
FileInfos fileInfos2 = new FileInfos(2, urlQqYinYue, "qq.apk", 0, 0);
FileInfos fileInfos3 = new FileInfos(3, urlWangYiXinWen, "wangyixinwen.apk", 0, 0);
FileInfos fileInfos4 = new FileInfos(4, urlAiQiYi, "aiqiyi.apk", 0, 0);
list.add(fileInfos);
list.add(fileInfos1);
list.add(fileInfos2);
list.add(fileInfos3);
list.add(fileInfos4);
adapter=new MyAdapter(list,this);
listView.setAdapter(adapter);
//注册广播
receiver = new MyBroadCastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(DownloadService.ACTION_UPDATE);
intentFilter.addAction(DownloadService.ACTION_FINISH);
registerReceiver(receiver,intentFilter);
}
class MyBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (DownloadService.ACTION_UPDATE.equals(intent.getAction()))
{
int progress = intent.getIntExtra("progress", -1);
int position = intent.getIntExtra("position", -1);
if (-1!=progress)
{
list.get(position).setFinished(progress);
adapter.notifyDataSetChanged();
}
}
else if (DownloadService.ACTION_FINISH.equals(intent.getAction()))
{
FileInfos fileInfo = (FileInfos) intent.getSerializableExtra("fileInfo");
list.get(fileInfo.getId()).setFinished(0);
adapter.notifyDataSetChanged();
Toast.makeText(MainActivity.this,fileInfo.getFileName()+"下载完成",Toast.LENGTH_SHORT).show();
}
}
}
}
我这么贴代码是为了方便我自己以后用的,,如果你们感兴趣还是自己敲一遍吧,有什么其他的优化方法记得和我说一声!