断点续传

今天还是看着慕课网,把昨天的断点续传代码做了一些修改,用列表的形式,可以自定义下载一个文件需要的线程数,但是可能是由于线程的创建和销毁,还有多个线程同时操作数据库,所以感觉体验不是很好,虽然对方法声明了同步,也安排对数据库的操作在线程之外,同时也利用了线程池,来尽量减少对资源的消耗,,但是觉得不是很流畅,,,还有万恶的进度条,,,,

同样先是我的代码目录结构

这里写图片描述

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();
            }
        }
    }
}

我这么贴代码是为了方便我自己以后用的,,如果你们感兴趣还是自己敲一遍吧,有什么其他的优化方法记得和我说一声!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值