多线程断点续传

来源:互联网 发布:日本清酒 知乎 编辑:程序博客网 时间:2024/05/16 07:32

1.首先是清单文件中的一些权限

    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET"/>

2 数据库操作工具类
 */
public class DownlaodSqlTool {
    private DownLoadHelper dbHelper;


    public DownlaodSqlTool(Context context) {
        dbHelper = new DownLoadHelper(context);
    }


    /**
     * 创建下载的具体信息
     */
    public void insertInfos(List<DownloadInfo> infos) {
        SQLiteDatabase database = dbHelper.getWritableDatabase();
        for (DownloadInfo info : infos) {
            String sql = "insert into download_info(thread_id,start_pos, end_pos,compelete_size,url) values (?,?,?,?,?)";
            Object[] bindArgs = { info.getThreadId(), info.getStartPos(),
                    info.getEndPos(), info.getCompeleteSize(), info.getUrl() };
            database.execSQL(sql, bindArgs);
        }
    }


    /**
     * 得到下载具体信息
     */
    public List<DownloadInfo> getInfos(String urlstr) {
        List<DownloadInfo> list = new ArrayList<DownloadInfo>();
        SQLiteDatabase database = dbHelper.getWritableDatabase();
        String sql = "select thread_id, start_pos, end_pos,compelete_size,url from download_info where url=?";
        Cursor cursor = database.rawQuery(sql, new String[] { urlstr });
        while (cursor.moveToNext()) {
            DownloadInfo info = new DownloadInfo(cursor.getInt(0),
                    cursor.getInt(1), cursor.getInt(2), cursor.getInt(3),
                    cursor.getString(4));
            list.add(info);
        }
        return list;
    }


    /**
     * 更新数据库中的下载信息
     */
    public void updataInfos(int threadId, int compeleteSize, String urlstr) {
        SQLiteDatabase database = dbHelper.getWritableDatabase();
        String sql = "update download_info set compelete_size=? where thread_id=? and url=?";
        Object[] bindArgs = { compeleteSize, threadId, urlstr };
        database.execSQL(sql, bindArgs);
    }
    /**
     * 关闭数据库
     */
    public void closeDb() {
        dbHelper.close();
    }


    /**
     * 下载完成后删除数据库中的数据
     */
    public void delete(String url) {
        SQLiteDatabase database = dbHelper.getWritableDatabase();
        database.delete("download_info", "url=?", new String[] { url });
    }
}


3.利用数据库来记录下载信息
 * @author acer
 */
public class DownLoadHelper extends SQLiteOpenHelper{


private static final String SQL_NAME = "download.db";
private static final int DOWNLOAD_VERSION=1;

public DownLoadHelper(Context context) {
super(context, SQL_NAME, null, DOWNLOAD_VERSION);
// TODO Auto-generated constructor stub
}

/**
     * 在download.db数据库下创建一个download_info表存储下载信息
     */
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("create table download_info(_id integer PRIMARY KEY AUTOINCREMENT, thread_id integer, "
                + "start_pos integer, end_pos integer, compelete_size integer,url char)");
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {


    }


}

4.利用Http协议进行多线程下载具体实践类

public class DownloadHttpTool {

private static final String TAG = DownloadHttpTool.class.getSimpleName();


private int threadCount;//线程数量
private String urlstr;//URL地址
private Context mContext;
private Handler mHandler;
private List<DownloadInfo> downloadInfos;//保存下载信息的类


private String localPath;//目录
private String fileName;//文件名
private int fileSize;
private DownlaodSqlTool sqlTool;//文件信息保存的数据库操作类


private enum Download_State {
Downloading, Pause, Ready;//利用枚举表示下载的三种状态
}


private Download_State state = Download_State.Ready;//当前下载状态


private int globalCompelete = 0;//所有线程下载的总数


public DownloadHttpTool(int threadCount, String urlString,
String localPath, String fileName, Context context, Handler handler) {
super();
this.threadCount = threadCount;
this.urlstr = urlString;
this.localPath = localPath;
this.mContext = context;
this.mHandler = handler;
this.fileName = fileName;
sqlTool = new DownlaodSqlTool(mContext);
}


//在开始下载之前需要调用ready方法进行配置
public void ready() {
Log.w(TAG, "ready");
globalCompelete = 0;
downloadInfos = sqlTool.getInfos(urlstr);
if (downloadInfos.size() == 0) {
initFirst();
} else {
File file = new File(localPath + "/" + fileName);
if (!file.exists()) {
sqlTool.delete(urlstr);
initFirst();
} else {
fileSize = downloadInfos.get(downloadInfos.size() - 1)
.getEndPos();
for (DownloadInfo info : downloadInfos) {
globalCompelete += info.getCompeleteSize();
}
Log.w(TAG, "globalCompelete:::" + globalCompelete);
}
}
}


public void start() {
Log.w(TAG, "start");
if (downloadInfos != null) {
if (state == Download_State.Downloading) {
return;
}
state = Download_State.Downloading;
for (DownloadInfo info : downloadInfos) {
Log.v(TAG, "startThread");
new DownloadThread(info.getThreadId(), info.getStartPos(),
info.getEndPos(), info.getCompeleteSize(),
info.getUrl()).start();
}
}
}


public void pause() {
state = Download_State.Pause;
sqlTool.closeDb();
}

public void delete(){
compelete();
File file = new File(localPath + "/" + fileName);
file.delete();
}

public void compelete() {
sqlTool.delete(urlstr);
sqlTool.closeDb();
}


public int getFileSize() {
return fileSize;
}


public int getCompeleteSize() {
return globalCompelete;
}


//第一次下载初始化
private void initFirst() {
Log.w(TAG, "initFirst");
try {
URL url = new URL(urlstr);
HttpURLConnection connection = (HttpURLConnection) url
.openConnection();
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
fileSize = connection.getContentLength();
Log.w(TAG, "fileSize::" + fileSize);
File fileParent = new File(localPath);
if (!fileParent.exists()) {
fileParent.mkdir();
}
File file = new File(fileParent, fileName);
if (!file.exists()) {
file.createNewFile();
}
// 本地访问文件
RandomAccessFile accessFile = new RandomAccessFile(file, "rwd");
accessFile.setLength(fileSize);
accessFile.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
int range = fileSize / threadCount;
downloadInfos = new ArrayList<DownloadInfo>();
for (int i = 0; i < threadCount - 1; i++) {
DownloadInfo info = new DownloadInfo(i, i * range, (i + 1) * range
- 1, 0, urlstr);
downloadInfos.add(info);
}
DownloadInfo info = new DownloadInfo(threadCount - 1, (threadCount - 1)
* range, fileSize - 1, 0, urlstr);
downloadInfos.add(info);
sqlTool.insertInfos(downloadInfos);
}


//自定义下载线程
private class DownloadThread extends Thread {


private int threadId;
private int startPos;
private int endPos;
private int compeleteSize;
private String urlstr;
private int totalThreadSize;


public DownloadThread(int threadId, int startPos, int endPos,
int compeleteSize, String urlstr) {
this.threadId = threadId;
this.startPos = startPos;
this.endPos = endPos;
totalThreadSize = endPos - startPos + 1;
this.urlstr = urlstr;
this.compeleteSize = compeleteSize;
}


@Override
public void run() {
HttpURLConnection connection = null;
RandomAccessFile randomAccessFile = null;
InputStream is = null;
try {
randomAccessFile = new RandomAccessFile(localPath + "/"
+ fileName, "rwd");
randomAccessFile.seek(startPos + compeleteSize);
URL url = new URL(urlstr);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
connection.setRequestProperty("Range", "bytes="
+ (startPos + compeleteSize) + "-" + endPos);
is = connection.getInputStream();
byte[] buffer = new byte[1024];
int length = -1;
while ((length = is.read(buffer)) != -1) {
randomAccessFile.write(buffer, 0, length);
compeleteSize += length;
Message message = Message.obtain();
message.what = threadId;
message.obj = urlstr;
message.arg1 = length;
mHandler.sendMessage(message);
sqlTool.updataInfos(threadId, compeleteSize, urlstr);
Log.w(TAG, "Threadid::" + threadId + "    compelete::"
+ compeleteSize + "    total::" + totalThreadSize);
if (compeleteSize >= totalThreadSize) {
break;
}
if (state != Download_State.Downloading) {
break;
}
}


} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
randomAccessFile.close();
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}

5. bean类

public class DownloadInfo {
/**
* 保存每个下载线程下载信息类

*/

private int threadId;// 下载器id
private int startPos;// 开始点
private int endPos;// 结束点
private int compeleteSize;// 完成度
private String url;// 下载文件的URL地址

public DownloadInfo(int threadId, int startPos, int endPos,
int compeleteSize, String url) {
this.threadId = threadId;
this.startPos = startPos;
this.endPos = endPos;
this.compeleteSize = compeleteSize;
this.url = url;
}


public DownloadInfo() {
}


public String getUrl() {
return url;
}


public void setUrl(String url) {
this.url = url;
}


public int getThreadId() {
return threadId;
}


public void setThreadId(int threadId) {
this.threadId = threadId;
}


public int getStartPos() {
return startPos;
}


public void setStartPos(int startPos) {
this.startPos = startPos;
}


public int getEndPos() {
return endPos;
}


public void setEndPos(int endPos) {
this.endPos = endPos;
}


public int getCompeleteSize() {
return compeleteSize;
}


public void setCompeleteSize(int compeleteSize) {
this.compeleteSize = compeleteSize;
}


@Override
public String toString() {
return "DownloadInfo [threadId=" + threadId + ", startPos=" + startPos
+ ", endPos=" + endPos + ", compeleteSize=" + compeleteSize
+ "]";
}
}

6. 将下载方法封装在此类 提供下载,暂停,删除,以及重置的方法
 
public class DownloadUtil {


private DownloadHttpTool mDownloadHttpTool;
private OnDownloadListener onDownloadListener;


private int fileSize;
private int downloadedSize = 0;


@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {


@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
int length = msg.arg1;
synchronized (this) {//加锁保证已下载的正确性
downloadedSize += length;
}
if (onDownloadListener != null) {
onDownloadListener.downloadProgress(downloadedSize);
}
if (downloadedSize >= fileSize) {
mDownloadHttpTool.compelete();
if (onDownloadListener != null) {
onDownloadListener.downloadEnd();
}
}
}


};


public DownloadUtil(int threadCount, String filePath, String filename,
String urlString, Context context) {


mDownloadHttpTool = new DownloadHttpTool(threadCount, urlString,
filePath, filename, context, mHandler);
}


//下载之前首先异步线程调用ready方法获得文件大小信息,之后调用开始方法
public void start() {
new AsyncTask<Void, Void, Void>() {


@Override
protected Void doInBackground(Void... arg0) {
// TODO Auto-generated method stub
mDownloadHttpTool.ready();
return null;
}


@Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
fileSize = mDownloadHttpTool.getFileSize();
downloadedSize = mDownloadHttpTool.getCompeleteSize();
Log.w("Tag", "downloadedSize::" + downloadedSize);
if (onDownloadListener != null) {
onDownloadListener.downloadStart(fileSize);
}
mDownloadHttpTool.start();
}
}.execute();
}


public void pause() {
mDownloadHttpTool.pause();
}

public void delete(){
mDownloadHttpTool.delete();
}


public void reset(){
mDownloadHttpTool.delete();
start();
}

public void setOnDownloadListener(OnDownloadListener onDownloadListener) {
this.onDownloadListener = onDownloadListener;
}


//下载回调接口
public interface OnDownloadListener {
public void downloadStart(int fileSize);


public void downloadProgress(int downloadedSize);


public void downloadEnd();
}
}

7.Activity

public class MainActivity extends FragmentActivity {


private static final String TAG = MainActivity.class.getSimpleName();


private ProgressBar mProgressBar;
private Button start;
private Button pause;
private Button delete;
private Button reset;
private TextView total;


private int max;


private DownloadUtil mDownloadUtil;


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
start = (Button) findViewById(R.id.button_start);
pause = (Button) findViewById(R.id.button_pause);
delete = (Button) findViewById(R.id.button_delete);
reset = (Button) findViewById(R.id.button_reset);
total = (TextView) findViewById(R.id.textView_total);
String urlString = "http://bbra.cn/Uploadfiles/imgs/20110303/fengjin/013.jpg";
String localPath = Environment.getExternalStorageDirectory()
.getAbsolutePath() + "/local";
mDownloadUtil = new DownloadUtil(2, localPath, "abc.jpg", urlString,
this);
mDownloadUtil.setOnDownloadListener(new OnDownloadListener() {


@Override
public void downloadStart(int fileSize) {
// TODO Auto-generated method stub
Log.w(TAG, "fileSize::" + fileSize);
max = fileSize;
mProgressBar.setMax(fileSize);
}


@Override
public void downloadProgress(int downloadedSize) {
// TODO Auto-generated method stub
Log.w(TAG, "Compelete::" + downloadedSize);
mProgressBar.setProgress(downloadedSize);
total.setText((int) downloadedSize * 100 / max + "%");
}


@Override
public void downloadEnd() {
// TODO Auto-generated method stub
Log.w(TAG, "ENd");
}
});
start.setOnClickListener(new OnClickListener() {


@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
mDownloadUtil.start();
}
});
pause.setOnClickListener(new OnClickListener() {


@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
mDownloadUtil.pause();
}
});
delete.setOnClickListener(new OnClickListener() {


@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
mDownloadUtil.delete();
}
});
reset.setOnClickListener(new OnClickListener() {


@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
mDownloadUtil.reset();
}
});
}

}

8.布局文件

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:padding="30dp" >


    <ProgressBar
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />


    <Button
        android:id="@+id/button_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignRight="@+id/progressBar1"
        android:layout_below="@+id/progressBar1"
        android:layout_marginTop="32dp"
        android:text="开始" />


    <Button
        android:id="@+id/button_pause"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/button_start"
        android:layout_alignBottom="@+id/button_start"
        android:layout_toLeftOf="@+id/button_start"
        android:text="暂停" />


    <TextView
        android:id="@+id/textView_total"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/button_pause"
        android:layout_alignLeft="@+id/progressBar1"
        android:text="0%" />


    <Button
        android:id="@+id/button_delete"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/button_pause"
        android:layout_alignBottom="@+id/button_pause"
        android:layout_toLeftOf="@+id/button_pause"
        android:text="删除" />


    <Button
        android:id="@+id/button_reset"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignRight="@+id/button_start"
        android:layout_below="@+id/button_start"
        android:layout_marginTop="18dp"
        android:text="重新下载" />


</RelativeLayout>



原创粉丝点击