码迷,mamicode.com
首页 > 移动开发 > 详细

Android多线程断点续传

时间:2016-05-13 15:02:19      阅读:321      评论:0      收藏:0      [点我收藏+]

标签:

麻雀虽小五脏俱全,本文我是从慕课网学习的感谢慕课提供了那么免费优秀的资源

这里巧妙的把Android的四大主键紧密的联系了起来

Activity—Service—Broadcast Receiver—-SQLite

不多啰嗦 直接上代码

注释很详细

慢慢参透

技术分享

技术分享

public class MainActivity extends Activity {

private ListView mListView;
private List<FileInfo> mFileList;
private ListViewAdapter mAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initList();

    mListView = (ListView) findViewById(R.id.lv_listView);
    mAdapter = new ListViewAdapter(MainActivity.this, mFileList);
    mListView.setAdapter(mAdapter);

    IntentFilter filter = new IntentFilter();
    filter.addAction(DownloadService.ACTION_FINISHED);
    filter.addAction(DownloadService.ACTION_UPDATE);
    registerReceiver(receiver, filter);
}

private void initList() {
    mFileList = new ArrayList<FileInfo>();
    FileInfo mFileInfo = new FileInfo("mukewang0.apk", 0, 0,
            "http://www.imooc.com/mobile/mukewang.apk", 0);
    FileInfo mFileInfo1 = new FileInfo(
            "知乎.apk",
            0,
            1,
            "http://zhstatic.zhihu.com/pkg/store/zhihu/zhihu-android-app-zhihu-release-2.4.4-244.apk",
            0);
    FileInfo mFileInfo2 = new FileInfo(
            "知乎日报.apk",
            0,
            2,
            "http://zhstatic.zhihu.com/pkg/store/daily/zhihu-daily-zhihu-2.5.3(390).apk",
            0);
    mFileList.add(mFileInfo);
    mFileList.add(mFileInfo1);
    mFileList.add(mFileInfo2);
}

BroadcastReceiver receiver = new BroadcastReceiver() {

    long time = System.currentTimeMillis();

    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();

        FileInfo mFileInfo = (FileInfo) intent
                .getSerializableExtra("fileInfo");
        if (DownloadService.ACTION_UPDATE.equals(action)) {
            // 更新UI
            int finished = intent.getIntExtra("finished", -1);
            int fileId = intent.getIntExtra("fileId", -1);
            // 调用Adpter中的更新UI方法;
            if ((System.currentTimeMillis() - time) > 1000) {
                time = System.currentTimeMillis();
                mAdapter.updateProgress(fileId, finished);
            }
        } else if (DownloadService.ACTION_FINISHED.equals(action)) {
            // 下载结束后重置,并弹出Toast提示
            FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
            mAdapter.updateProgress(fileInfo.getId(), fileInfo.getLength());
            Toast.makeText(MainActivity.this,
                    mFileInfo.getFileName() + " 下载完成!", Toast.LENGTH_SHORT)
                    .show();
        }

    }
};

@Override
protected void onDestroy() {
    super.onDestroy();
    unregisterReceiver(receiver);
}

}

public class ListViewAdapter extends BaseAdapter {

private Context mContext;
private List<FileInfo> fileList;

public ListViewAdapter(Context mContext, List<FileInfo> fileList) {
    this.mContext = mContext;
    this.fileList = fileList;
}

@Override
public int getCount() {
    return fileList.size();
}

@Override
public Object getItem(int position) {
    return fileList.get(position);
}

@Override
public long getItemId(int position) {
    return position;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    ViewHolder holder = null;
    final FileInfo mFileInfo = fileList.get(position);
    if (convertView == null) {
        holder = new ViewHolder();
        convertView = LayoutInflater.from(mContext).inflate(
                R.layout.list_item, null);
        holder.tv_fileName = (TextView) convertView
                .findViewById(R.id.tv_fileName);
        holder.btn_start = (Button) convertView
                .findViewById(R.id.btn_start);
        holder.btn_stop = (Button) convertView.findViewById(R.id.btn_stop);
        holder.pb_download = (ProgressBar) convertView
                .findViewById(R.id.pb_download);

        holder.tv_fileName.setText(mFileInfo.getFileName());
        holder.btn_start.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(mContext, DownloadService.class);
                intent.setAction(DownloadService.ACTION_START);
                intent.putExtra("fileInfo", mFileInfo);
                mContext.startService(intent);

            }
        });
        holder.btn_stop.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                Intent intent = new Intent(mContext, DownloadService.class);
                intent.setAction(DownloadService.ACTION_STOP);
                intent.putExtra("fileInfo", mFileInfo);
                mContext.startService(intent);

            }
        });
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }
    holder.pb_download.setProgress(mFileInfo.getFinished());

    return convertView;
}

/**
 * 更新进度条
 * 
 * @param id
 * @param progress
 */
public void updateProgress(int fileId, int finished) {
    FileInfo fileInfo = fileList.get(fileId);
    fileInfo.setFinished(finished);
    notifyDataSetChanged();// 调用此方法后系统会重新调用getView方法,完成刷新UI的操作
}

static class ViewHolder {
    TextView tv_fileName;
    Button btn_start, btn_stop;
    ProgressBar pb_download;
}

}

/**
* 文件信息
*
* @author Lang Junping
*
*/

public class FileInfo implements Serializable {
private static final long serialVersionUID = 1L;
private String fileName; // 文件名
private int length; // 文件长度
private int id; // 文件ID
private String url; // 文件在网络上的url
private int finished; // 文件进度

public FileInfo() {
    super();
}

public FileInfo(String fileName, int length, int id, String url,
        int finished) {
    super();
    this.fileName = fileName;
    this.length = length;
    this.id = id;
    this.url = url;
    this.finished = finished;
}

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 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 getFinished() {
    return finished;
}

public void setFinished(int finished) {
    this.finished = finished;
}

@Override
public String toString() {
    return "FileInfo [fileName=" + fileName + ", length=" + length
            + ", id=" + id + ", url=" + url + ", finished=" + finished
            + "]";
}

}

/**
* 线程信息
*
* @author Lang Junping
*
*/

public class ThreadInfo {
private int id;
private String url;
private int start; // 开始下载的位置
private int stop; // 停止下载的位置
private int finished; // 下载进度

public ThreadInfo() {
    super();
}

public ThreadInfo(int id, String url, int start, int stop, int finished) {
    super();
    this.id = id;
    this.url = url;
    this.start = start;
    this.stop = stop;
    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 getStop() {
    return stop;
}

public void setStop(int stop) {
    this.stop = stop;
}

public int getFinished() {
    return finished;
}

public void setFinished(int finished) {
    this.finished = finished;
}

@Override
public String toString() {
    return "TheadInfo [id=" + id + ", url=" + url + ", start=" + start
            + ", stop=" + stop + ", finished=" + finished + "]";
}

}

public class DownloadService extends Service {

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 String DOWNLOAD_PATH = Environment
        .getExternalStorageDirectory().getAbsolutePath() + "/downloads/";
public static final int THREAD_COUNT = 3; //下载线程数

public static final int MSG_INIT = 0x001;

private InitThread mInitThread = null;


private Map<Integer,DownloadTask> mTasks = new LinkedHashMap<Integer,DownloadTask>();

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");
    if (ACTION_START.equals(intent.getAction())) {
        //启动下载
        mInitThread = new InitThread(fileInfo);

        DownloadTask.sExecutorService.execute(mInitThread);

    } else if (ACTION_STOP.equals(intent.getAction())) {
        //暂停下载
        int id = fileInfo.getId();
        if(mTasks!=null){
            DownloadTask mTask = mTasks.get(id);
            mTask.setPause(true);
        }

    }
    return super.onStartCommand(intent, flags, startId);
}

@Override
public IBinder onBind(Intent intent) {
    return null;
}

Handler mHandler = new Handler() {
    public void handleMessage(android.os.Message msg) {
        switch (msg.what) {
        case MSG_INIT:
            FileInfo mFileInfo = (FileInfo) msg.obj;
            //启动下载
            DownloadTask mTask= new DownloadTask(DownloadService.this, mFileInfo,THREAD_COUNT);
            mTask.download();
            mTasks.put(mFileInfo.getId(), mTask);
            break;
        }
    }
};

/**
 * 初始化线程子类
 * 
 * @author Lang Junping
 *
 */

class InitThread extends Thread {
    private FileInfo mFileInfo;

    public InitThread(FileInfo mFileInfo) {
        this.mFileInfo = mFileInfo;
    }

    @Override
    public void run() {
        // 开启网络,查询长度,创建文件,返回信息
        HttpURLConnection conn = null;
        RandomAccessFile raf = null;
        try {
            // 开启网络
            URL url = new URL(mFileInfo.getUrl());
            conn = (HttpURLConnection) url.openConnection();
            conn.setRequestMethod("GET");
            conn.setReadTimeout(5000);
            // 获取文件长度
            int length = -1;
            if (conn.getResponseCode() == HttpURLConnection.HTTP_OK) {
                length = conn.getContentLength();
            }
            if (length <= 0) {
                return;
            }
            // 创建文件夹
            File dir = new File(DOWNLOAD_PATH);
            if (!dir.exists()) {
                dir.mkdir();
            }
            // 创建文件
            File mFile = new File(dir, mFileInfo.getFileName());
            // 创建RAF文件
            raf = new RandomAccessFile(mFile, "rwd");
            raf.setLength(length);
            // 设置文件长度,并通知主线程
            mFileInfo.setLength(length);

            mHandler.obtainMessage(MSG_INIT, mFileInfo).sendToTarget();

        } catch (MalformedURLException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            conn.disconnect();
            try {
                raf.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

    }
}

}

/**
* 下载任务类
*
*/
public class DownloadTask {
private Context mContext;
private FileInfo mFileInfo;
private ThreadDAO mThreadDAO;
private boolean isPause = false;
private int threadsCount = 1;
private List mThreadList;
private long mFinished = 0;
//线程池
public static ExecutorService sExecutorService = Executors.newCachedThreadPool();

public DownloadTask(Context mContext, FileInfo mFileInfo, int threadsCount) {
    this.mContext = mContext;
    this.mFileInfo = mFileInfo;
    this.threadsCount = threadsCount;
    mThreadDAO = new ThreadDAOimpl(mContext);
}

public boolean isPause() {
    return isPause;
}

public void setPause(boolean isPause) {
    this.isPause = isPause;
}

/**
 * 下载方法
 */
public void download() {

    // 读取数据库的线程信息
    List<ThreadInfo> threads = mThreadDAO.getThread(mFileInfo.getUrl());
    // 判断线程信息是否为空
    if (threads.size() == 0) {
        // 如果为空,则创建线程信息
        // 先计算文件分段长度
        int length = mFileInfo.getLength() / threadsCount;
        for (int i = 0; i < threadsCount; i++) {
            ThreadInfo mThreadInfo = new ThreadInfo(i, mFileInfo.getUrl(),
                    i * length, (i + 1) * length - 1, 0);
            // 不能整除的情况
            if (i == threadsCount - 1) {
                mThreadInfo.setStop(mFileInfo.getLength());
            }
            threads.add(mThreadInfo);
            // 向数据库插入线程信息
            mThreadDAO.insert(mThreadInfo);
        }

    }
    // 启动线程,开始下载
    mThreadList = new ArrayList<DownloadThread>();
    for (ThreadInfo info : threads) {
        DownloadThread dt = new DownloadThread(info);
        // 启动线程

// new Thread(dt).start();
//用线程池启动线程
DownloadTask.sExecutorService.execute(dt);
// 将线程添加到集合中,方便管理
mThreadList.add(dt);
}
}

/**
 * 下载线程
 *
 */
class DownloadThread implements Runnable {

    public boolean isThreadFinished = false;
    private ThreadInfo mThreadInfo;

    public DownloadThread(ThreadInfo mThreadInfo) {
        this.mThreadInfo = mThreadInfo;
    }

    @Override
    public void run() {
        HttpURLConnection conn = null;
        RandomAccessFile raf = null;
        InputStream input = null;
        try {
            // 开启网络
            URL url = new URL(mThreadInfo.getUrl());
            conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(3000);
            conn.setRequestMethod("GET");
            // 设置下载位置
            int start = mThreadInfo.getStart() + mThreadInfo.getFinished();
            // 下载范围
            String range = "bytes=" + start + "-" + mThreadInfo.getStop();

            conn.setRequestProperty("Range", range);
            // 设置写入位置
            File file = new File(DownloadService.DOWNLOAD_PATH,
                    mFileInfo.getFileName());
            raf = new RandomAccessFile(file, "rwd");
            raf.seek(start);
            // 累加完成进度
            mFinished += mThreadInfo.getFinished();
            // 开始下载
            if (conn.getResponseCode() == HttpStatus.SC_PARTIAL_CONTENT) {
                // 读取数据
                input = conn.getInputStream();
                byte[] buffer = new byte[1024 * 4];
                int len = -1;
                while ((len = input.read(buffer)) != -1) {
                    // 写入文件
                    raf.write(buffer, 0, len);
                    // 累加整个文件完成的进度
                    mFinished += len;
                    // 累加每个线程的完成进度
                    mThreadInfo
                            .setFinished(mThreadInfo.getFinished() + len);
                    int progress = (int) (mFinished * 100 / mFileInfo
                            .getLength());
                    // 发送更新UI广播
                    Intent intent = new Intent(
                            DownloadService.ACTION_UPDATE);
                    intent.putExtra("finished", progress);
                    intent.putExtra("fileId", mFileInfo.getId());
                    mContext.sendBroadcast(intent);
                    // 下载暂停时保存线程下载进度
                    if (isPause()) {
                        mThreadDAO.updateThread(mThreadInfo.getUrl(),
                                mThreadInfo.getId(),
                                mThreadInfo.getFinished());
                        return;
                    }
                }
                isThreadFinished = true;
                // 判断所有的线程是否下载完毕
                checkAllThreadIsFinished();
            } else {
                Toast.makeText(mContext, "网络请求失败", Toast.LENGTH_SHORT)
                        .show();
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                conn.disconnect();
                raf.close();
                if (input != null) {
                    input.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private synchronized void checkAllThreadIsFinished() {
        boolean allFinished = true;
        // 遍历集合
        for (DownloadThread dt : mThreadList) {
            if (!dt.isThreadFinished) {
                allFinished = false;
                return;
            }
        }

        if (allFinished) {
            Intent intent = new Intent(DownloadService.ACTION_FINISHED);
            intent.putExtra("fileInfo", mFileInfo);
            mContext.sendBroadcast(intent);
            mThreadDAO.delete(mFileInfo.getUrl());
        }
    }

}

}

public class DBHelper extends SQLiteOpenHelper {

public static DBHelper sHelper = null;
public static final String DB_NAME = "download.db";
public static final int VERSION = 1;
// 创建数据库的SQL语法
public static final String SQL_CREATE = "create table if not exists thread_info(_id integer primary key autoincrement,"
        + "thread_id integer,url text,start integer,stop integer,finished integer)";
// 删除数据库的语法
public static final String SQL_DROP = "drop table if exists thread_info";

private DBHelper(Context context) {
    super(context, DB_NAME, null, VERSION);
}
/**
 * 单例模式
 */
public static DBHelper getInstance(Context context){
    if(sHelper==null){
        sHelper = new DBHelper(context);
    }

    return sHelper;
}

@Override
public void onCreate(SQLiteDatabase db) {
    db.execSQL(SQL_CREATE);
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    db.execSQL(SQL_DROP);
    db.execSQL(SQL_CREATE);
}

}

/**
 * 数据库访问接口
 * @author Lang Junping
 *
 */

public interface ThreadDAO {
/**
* 插入线程信息
* @param threadInfo
*/
public void insert(ThreadInfo threadInfo);
/**
* 删除线程信息
* @param url filepath
* @param thread_id threadId
*/
public void delete(String url);
/**
* 更新线程下载进度
* @param url
* @param thread_id
*/
public void updateThread(String url,int thread_id,long finished);
/**
* 查询线程的文件信息
* @param url
* @return 返回一个含有线程信息的List
*/
public List getThread(String url);
/**
* 判断线程信息是否
* @param url
* @param thread_id
* @return
*/
public boolean isExists(String url,int thread_id);
}

public class ThreadDAOimpl implements ThreadDAO {

private DBHelper mHelper = null;

public ThreadDAOimpl(Context context) {
    mHelper = DBHelper.getInstance(context);
}

@Override
public synchronized void insert(ThreadInfo threadInfo) {
    // 创建数据库
    SQLiteDatabase db = mHelper.getWritableDatabase();

    db.execSQL(
            "insert into thread_info(thread_id,url,start,stop,finished) values(?,?,?,?,?)",
            new Object[] { threadInfo.getId(), threadInfo.getUrl(),
                    threadInfo.getStart(), threadInfo.getStop(),
                    threadInfo.getFinished() });
    db.close();

}

@Override
public synchronized void delete(String url) {

    SQLiteDatabase db = mHelper.getWritableDatabase();

    db.execSQL("delete from thread_info where url=?", new Object[] { url });
    db.close();

}

@Override
public synchronized void updateThread(String url, int thread_id,
        long 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();

}

@Override
public List<ThreadInfo> getThread(String url) {
    SQLiteDatabase db = mHelper.getReadableDatabase();

    Cursor cur = db.rawQuery("select * from thread_info where url=?",
            new String[] { url });
    List<ThreadInfo> list = new ArrayList<ThreadInfo>();
    ThreadInfo mThread = null;
    while (cur.moveToNext()) {
        mThread = new ThreadInfo();
        mThread.setId(cur.getInt(cur.getColumnIndex("thread_id")));
        mThread.setStart(cur.getInt(cur.getColumnIndex("start")));
        mThread.setStop(cur.getInt(cur.getColumnIndex("stop")));
        mThread.setFinished(cur.getInt(cur.getColumnIndex("finished")));
        mThread.setUrl(cur.getString(cur.getColumnIndex("url")));

        list.add(mThread);
    }
    cur.close();
    db.close();

    return list;
}

@Override
public boolean isExists(String url, int thread_id) {
    SQLiteDatabase db = mHelper.getReadableDatabase();
    Cursor cur = db.rawQuery(
            "select * from thread_info where url=? and thread_id=?",
            new String[] { url, thread_id + "" });
    boolean exists = cur.moveToNext();
    cur.close();
    db.close();
    return exists;
}

}

也许大伙把代码搬完了,然而程序并没有达到预期的效果

老鸟应该都知道,既然有网络访问,网络权限是必须要加的

《uses-permission android:name=”android.permission.INTERNET”/》

这里还涉及到文件的读写权限

《uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”/》

这里要启动Service,Service一定要在配置文件中注册了才能使用

《service android:name=”包名.DownloadService”/》

万恶的编辑器会吃代码

Android多线程断点续传

标签:

原文地址:http://blog.csdn.net/qq_23976141/article/details/51383940

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!