MainActivity如下:
package cc.activity; import java.io.File; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import cc.download.DownloadProgressListener; import cc.download.FileDownloader; import cn.com.downloadActivity.R; /** * Demo描述: * 多线程断点下载文件的实现 * * 思路梳理: * 1 将待下载文件切分成几部分,每部分开启一条线程进行下载 * 2 将每条线程下载的部分利用RandomAccessFile写入本地文件 * 3 在下载过程中不断将该线程已经下载的数据位置保存至数据库 * 4 若下载过程中突然断电,下次下载时则从数据库中取出每条线程 * 的断点值,继续下载即可 * * 注意事项: * 1 示例中的图片链接选自凤凰网,图片版权属于凤凰网(http://www.ifeng.com/) * 2 该图片以后可能会被凤凰网删除,所以在测试时可选择其他网络图片 */ public class MainActivity extends Activity { private Context mContext; private EditText mUrlEditText; private ProgressBar mProgressBar; private Button mDownLoadButton; private TextView mPercentTextView; private UIHandler mHandler=new UIHandler(); private final int NORMAL=9527; private final int ERROR=250; private final String MESSAGE_KEY="size"; private DownloadProgressListener mDownloadProgressListener; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); init(); } private void init() { mContext = this; mUrlEditText = (EditText) findViewById(R.id.urlEditText); mProgressBar = (ProgressBar) findViewById(R.id.progressBar); mPercentTextView = (TextView) findViewById(R.id.percentTextView); mDownLoadButton = (Button) findViewById(R.id.downloadButton); mDownLoadButton.setOnClickListener(new ClickListenerImpl()); mDownloadProgressListener=new DownloadProgressListenerImpl(); } private class ClickListenerImpl implements OnClickListener{ @Override public void onClick(View v) { if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ String path=mUrlEditText.getText().toString(); download(path,Environment.getExternalStorageDirectory()); }else{ Toast.makeText(mContext, R.string.SDCardError, Toast.LENGTH_SHORT).show(); } } } private void download(String path,File saveDir){ new Thread(new DownloadRunnableImpl(path, saveDir)).start(); } //下载文件的子线程 private class DownloadRunnableImpl implements Runnable{ private String path; private File SaveDir; public DownloadRunnableImpl(String path, File saveDir) { this.path = path; this.SaveDir = saveDir; } public void run(){ try { FileDownloader fileDownloader=new FileDownloader(getApplicationContext(), path, 4, SaveDir); mProgressBar.setMax(fileDownloader.getFileRawSize()); //置显示进度的回调 fileDownloader.setDownloadProgressListener(mDownloadProgressListener); //开始下载 fileDownloader.startDownload(); } catch (Exception e) { Message message=new Message(); message.what=ERROR; mHandler.sendMessage(message); e.printStackTrace(); } } } private class DownloadProgressListenerImpl implements DownloadProgressListener { @Override public void onDownloadSize(int size) { Message message = new Message(); message.what = NORMAL; message.getData().putInt(MESSAGE_KEY, size); mHandler.sendMessage(message); } } // 显示下载进度 private class UIHandler extends Handler { public void handleMessage(Message msg) { switch (msg.what) { case NORMAL: int size = msg.getData().getInt(MESSAGE_KEY); mProgressBar.setProgress(size); float percentFloat = (float) (mProgressBar.getProgress() / (float) mProgressBar.getMax()); int percentInt = (int) (percentFloat * 100); mPercentTextView.setText(percentInt + "%"); System.out.println("size="+size+",mProgressBar.getProgress()="+mProgressBar.getProgress()+",mProgressBar.getMax()="+mProgressBar.getMax()); if (mProgressBar.getProgress() == mProgressBar.getMax()) { Toast.makeText(mContext, R.string.success,Toast.LENGTH_SHORT).show(); } break; case ERROR: Toast.makeText(mContext, R.string.error,Toast.LENGTH_SHORT).show(); break; default: break; } } } }
DownloadProgressListener如下:
package cc.download; /** * 下载的监听接口 */ public interface DownloadProgressListener { public void onDownloadSize(int size); }
FileDownloader如下:
package cc.download; import java.io.File; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.http.HttpStatus; import android.content.Context; import cc.helper.DownloadThreadHelper; /** * 该类用于下载文件 * 思路梳理: * 1 在FileDownloader的构造方法里做一些下载的准备操作 * 1.1 获取源文件的大小 * 1.2 计算每条线程需要下载的数据长度 * 1.3 生成本地文件用于存储下载的文件并设置本地RandomAccessFile的大小 * 1.4 取出每条线程的上次下载的情况和已下载的总长度 * * 2 利用startDownload()方法执行文件下载 * * 分析说明: * 关于1.2 计算每条线程需要下载的数据长度 的原理及影响的详细分析: * everyThreadNeedDownloadLength=rawFileSize%threadNum==0 ? rawFileSize/threadNum : rawFileSize/threadNum+1; * 1 如果资源大小模于线程数时结果为0,那么表示每条线程需要下载的大小恰好将原大小等分 * 2 当然更多的情况是有余数的(即不能整除).那么此时该怎么办呢?每条线程该下载的长度是多少呢? * 我们可以这么做: * 2.1 原大小/除以线程的条数 * 2.2 在2.1的基础上+1 * 即代码rawFileSize/threadNum+1 * 这样就表示每条线程要下载的大小长度 * * 带来的问题: * 按照上面的方式,我们期望每条线程下载相同的数据量.但是存在个小问题: * 这样各线程累加起来的的下载总量是要大于原大小的.一般会多几个字节. * 这几个字节是多余的(redundant). * 而且这些几个多余的字节是出现在最后一条下载线程中,它的终止位置已经 * 超过了原文件的末尾. * 这样就造成了下载的文件与原文件大小不一致. * 所以在下载过程中需要处理该情况,处理方式参见DownloadThread类 * */ public class FileDownloader { private Context mContext; //下载路径 private String mDownloadPath; //待下载文件的原始长度 private int rawFileSize=0; //保存下载文件的本地文件 private File mLocalFile; //已下载大小 private int downloadTotalSize=0; //下载此文件需要的各个线程 private DownloadThread [] downloadThreadsArray; //每条线程需下载的长度 private int everyThreadNeedDownloadLength; //存放目前每条线程的信息包含其id和已下载大小 private Map<Integer,Integer> mCurrentEveryThreadInfoMap; //用于对各个线程进行操作 private DownloadThreadHelper mDownloadThreadHelper; private DownloadProgressListener mDownloadProgressListener; public FileDownloader(Context context,String downloadPath,int threadNum,File fileSaveDir){ System.out.println("源文件路径 downloadPath= "+downloadPath); System.out.println("下载开启的线程数 threadNum="+threadNum); try { mContext=context; mDownloadPath=downloadPath; mCurrentEveryThreadInfoMap=new HashMap<Integer,Integer>(); mDownloadThreadHelper=new DownloadThreadHelper(context); downloadThreadsArray=new DownloadThread[threadNum]; URL downloadUrl=new URL(downloadPath); HttpURLConnection httpURLConnection=(HttpURLConnection) downloadUrl.openConnection(); httpURLConnection.setReadTimeout(5*1000); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*"); httpURLConnection.setRequestProperty("Accept-Language", "zh-CN"); httpURLConnection.setRequestProperty("Referer", downloadPath); httpURLConnection.setRequestProperty("Charset", "UTF-8"); httpURLConnection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"); httpURLConnection.setRequestProperty("Connection", "Keep-Alive"); httpURLConnection.connect(); if(httpURLConnection.getResponseCode()==HttpStatus.SC_OK){ //第一步:获得源文件大小 rawFileSize=httpURLConnection.getContentLength(); System.out.println("源文件大小 rawFileSize="+rawFileSize); //第二步:计算每条线程需要下载的数据长度 everyThreadNeedDownloadLength=rawFileSize%threadNum==0 ? rawFileSize/threadNum : rawFileSize/threadNum+1; System.out.println("每条线程应下载大小 everyThreadNeedDownloadLength="+everyThreadNeedDownloadLength); if(rawFileSize<=0){ throw new RuntimeException("file is not found"); } //第三步:建立本地文件并设置本地RandomAccessFile的大小 String rawFileName=getFileName(httpURLConnection); if(!fileSaveDir.exists()){ fileSaveDir.mkdirs(); } mLocalFile=new File(fileSaveDir, rawFileName); System.out.println("本地文件路径 mLocalFile.getAbsolutePath()="+mLocalFile.getAbsolutePath()); RandomAccessFile randomAccessFile=new RandomAccessFile(mLocalFile, "rw"); if(rawFileSize>0){ randomAccessFile.setLength(rawFileSize); } randomAccessFile.close(); //第四步:取出每条线程的上次下载的情况和已下载的总长度 /** * 以下操作围绕断点进行的: * 1 从数据库取出每条线程上一次的下载情况,存入everyThreadLastDownloadLengthMap * 2 判断上次下载时开启的线程数是否和本次下载开启线程数一致 * 若不一致则无法在原基础上继续断点下载,则将mCurrentEveryThreadInfoMap中各条线程下载量设置为0 * 若一致则取出已下载的数据总量 */ //若以前下载过,则取出每条线程以前的情况存入mCurrentEveryThreadInfoMap Map<Integer,Integer> everyThreadLastDownloadLengthMap=mDownloadThreadHelper.getEveryThreadDownloadLength(downloadPath); if(everyThreadLastDownloadLengthMap.size()>0){ for(Map.Entry<Integer, Integer> entry:everyThreadLastDownloadLengthMap.entrySet()){ mCurrentEveryThreadInfoMap.put(entry.getKey(), entry.getValue()); System.out.println("--> 断点回复处 --> threadID="+entry.getKey()+",已下载数据量 length="+entry.getValue()); } } //若以往的线程条数和现在的线程条数不一致,则无法按照 //断点继续下载.所以将每条已下载的数据量置为0,并更新数据库 //若以往的线程条数和现在的线程条数一致,则取出已下载的数据总量 if(downloadThreadsArray.length!=mCurrentEveryThreadInfoMap.size()){ mCurrentEveryThreadInfoMap.clear(); for(int i=1;i<=downloadThreadsArray.length;i++){ mCurrentEveryThreadInfoMap.put(i, 0); } mDownloadThreadHelper.saveEveryThreadDownloadLength(mDownloadPath, mCurrentEveryThreadInfoMap); }else{ for(int i=1;i<=threadNum;i++){ downloadTotalSize=downloadTotalSize+mCurrentEveryThreadInfoMap.get(i); } // 更新已经下载的数据量 if (mDownloadProgressListener != null) { mDownloadProgressListener.onDownloadSize(downloadTotalSize); } System.out.println("--> 断点回复处 --> 已经下载 downloadTotalSize="+downloadTotalSize); } }else{ throw new RuntimeException("The HttpURLConnection is fail"); } } catch (Exception e) { throw new RuntimeException("Init FileDownloader is fail"); } } public int startDownload( ){ try { URL downloadURL=new URL(mDownloadPath); /** * 对每条线程的下载情况进行判断 * 如果没有下载完,则继续下载 * 否则将该线程置为空 */ for(int i=1;i<=downloadThreadsArray.length;i++){ //取出此线程已经下载的大小 int existDownloadSize=mCurrentEveryThreadInfoMap.get(i); if(existDownloadSize<everyThreadNeedDownloadLength && downloadTotalSize<rawFileSize){ downloadThreadsArray[i-1]=new DownloadThread(this, i, everyThreadNeedDownloadLength, mCurrentEveryThreadInfoMap.get(i), downloadURL, mLocalFile); //设置优先级 downloadThreadsArray[i-1].setPriority(7); //开始下载,注意数组的开始角标是从零开始的 downloadThreadsArray[i-1].start(); }else{ downloadThreadsArray[i-1]=null; } } /** * 注意: * 对于下载失败的线程(-1)重新开始下载 */ Boolean isAllFinish=true; while (isAllFinish) { isAllFinish = false; for (int i = 1; i <= downloadThreadsArray.length; i++) { if (downloadThreadsArray[i - 1] != null && !downloadThreadsArray[i - 1].isFinish()) { isAllFinish = true; if (downloadThreadsArray[i - 1].getDownloadSize() == -1) { downloadThreadsArray[i - 1] = new DownloadThread(this, i, everyThreadNeedDownloadLength,mCurrentEveryThreadInfoMap.get(i), downloadURL,mLocalFile); downloadThreadsArray[i - 1].setPriority(7); downloadThreadsArray[i - 1].start(); } } } } //下载完成,删除记录 mDownloadThreadHelper.deleteEveryThreadDownloadRecord(mDownloadPath); } catch (Exception e) { throw new RuntimeException("the download is fail"); } return downloadTotalSize; } //获取线程数 public int getThreadsNum(){ return downloadThreadsArray.length; } //获取原始文件大小 public int getFileRawSize(){ return rawFileSize; } //更新已经下载的总数据量 protected synchronized void appendDownloadTotalSize(int newSize){ downloadTotalSize=downloadTotalSize+newSize; if (mDownloadProgressListener!=null) { mDownloadProgressListener.onDownloadSize(downloadTotalSize); } System.out.println("当前总下载量 downloadTotalSize="+downloadTotalSize); } //更新每条线程已经下载的数据量 protected synchronized void updateEveryThreadDownloadLength(int threadid,int position){ mCurrentEveryThreadInfoMap.put(threadid, position); mDownloadThreadHelper.updateEveryThreadDownloadLength(mDownloadPath, mCurrentEveryThreadInfoMap); } //获取文件名 public String getFileName(HttpURLConnection conn){ String filename = mDownloadPath.substring(mDownloadPath.lastIndexOf('/') + 1); if(filename==null || "".equals(filename.trim())){ for (int i = 0;; i++) { String mine = conn.getHeaderField(i); if (mine == null) break; if("content-disposition".equals(conn.getHeaderFieldKey(i).toLowerCase())){ Matcher m = Pattern.compile(".*filename=(.*)").matcher(mine.toLowerCase()); if(m.find()) return m.group(1); } } filename = UUID.randomUUID()+ ".tmp"; } return filename; } public void setDownloadProgressListener(DownloadProgressListener downloadProgressListener){ mDownloadProgressListener=downloadProgressListener; } }
DownloadThread如下:
package cc.download; import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; /** * 该类表示每一条进行下载的线程 * 在下载的过程中不断地实时更新该线程的已下载量,从而刷新每条线程的已下载量 * 亦实时更新已经下载的总数据量 * * 分析说明 * 1 在FileDownloader中提到最后一条线程下载的数据可能有redundant数据的问题 * 在此描述解决办法如下: * 在计算每条线程下载终止位置(endPosition)时,需要对最后一条线程做特殊的处理 * 当endPosition大于了原文件的大小,即代码endPosition>fileDownloader.getFileRawSize() * 此时需要修改该线程的结束位置endPosition和应该下载的数据量everyThreadNeedDownloadLength * * 2 遇到的一个问题 * 在此处我们为每条线程设置了读取网络数据的范围,即代码: * httpURLConnection.setRequestProperty("Range","bytes="+startPosition+"-"+endPosition); * 然后在InputStreamwhile不断读取数据的时候,采用的方式是: * ((len=inputStream.read(b))!=-1)来判断是否已经读到了endPosition. * 之所以这么做是依据以往的经验且以为设置了Range,所以在读到endPosition的时候就应该返回-1 * 但是这么做是错误的!!!!!!!!!! * 在读到endPosition的时候并没有返回-1,会一直往下读取数据导致错误. * 所以: * 需要给while()多加一个判断downLength<everyThreadNeedDownloadLength * 表示当前该线程已经下载的数据量小于它本该读取的数据量,此时才可以继续读数据. * * 请注意另外一个重要问题: * 每条线程最后一次读取可能会多读数据的问题. * 比如:还有1000个字节就完成了该线程本该操作的数据,但是inputStream在接下来的最后一次 * 读数据时候却读了缓存byte [] b=new byte[1024]大小的字节数,造成了redundant数据的问题 * 产生这个问题的原因还是因为在读到endPosition的时候并没有返回-1,所以会一直往下读取数. * 所以在此需要特殊处理: * 2.1 修正downLength即代码:downLength=everyThreadNeedDownloadLength; * 2.2 修正实际下载有用的数据的长度即代码: * fileDownloader.appendDownloadTotalSize(len-redundant); * 而不是fileDownloader.appendDownloadTotalSize(len) * 从而确保了DownloadTotalSize值的准确 */ public class DownloadThread extends Thread{ private FileDownloader fileDownloader; private int threadId; private int everyThreadNeedDownloadLength; private int downLength; private URL downUrl; private File localFile; private Boolean isFinish=false; public DownloadThread(FileDownloader fileDownloader, int threadId, int everyThreadNeedDownloadLength,int downLength, URL downUrl, File localFile) { this.fileDownloader = fileDownloader; this.threadId = threadId; this.everyThreadNeedDownloadLength = everyThreadNeedDownloadLength; this.downLength = downLength; this.downUrl = downUrl; this.localFile = localFile; } @Override public void run() { try { //当此线程已下载量小于应下载量 if(downLength<everyThreadNeedDownloadLength){ HttpURLConnection httpURLConnection=(HttpURLConnection) downUrl.openConnection(); httpURLConnection.setConnectTimeout(5*1000); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setRequestProperty("Accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*"); httpURLConnection.setRequestProperty("Accept-Language", "zh-CN"); httpURLConnection.setRequestProperty("Referer", downUrl.toString()); httpURLConnection.setRequestProperty("Charset", "UTF-8"); //开始下载位置 int startPosition=everyThreadNeedDownloadLength*(threadId-1)+downLength; //结束下载位置 int endPosition=everyThreadNeedDownloadLength*threadId-1; //处理最后一条下载线程的特殊情况 if (endPosition>fileDownloader.getFileRawSize()) { int redundant=endPosition-(fileDownloader.getFileRawSize()-1); endPosition=fileDownloader.getFileRawSize()-1; everyThreadNeedDownloadLength=everyThreadNeedDownloadLength-redundant; } //设置下载的起止位置 httpURLConnection.setRequestProperty("Range","bytes="+startPosition+"-"+endPosition); System.out.println("====> 每条线程的下载起始情况 threadId="+threadId+",startPosition="+startPosition+",endPosition="+endPosition); httpURLConnection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.2; Trident/4.0; .NET CLR 1.1.4322; .NET CLR 2.0.50727; .NET CLR 3.0.04506.30; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)"); httpURLConnection.setRequestProperty("Connection", "Keep-Alive"); RandomAccessFile randomAccessFile=new RandomAccessFile(localFile, "rwd"); randomAccessFile.seek(startPosition); InputStream inputStream=httpURLConnection.getInputStream(); int len=0; byte [] b=new byte[1024]; while((len=inputStream.read(b))!=-1 && downLength<everyThreadNeedDownloadLength){ downLength=downLength+len; int redundant =0; //处理每条线程最后一次读取可能会多读数据的问题 if (downLength>everyThreadNeedDownloadLength) { redundant =downLength-everyThreadNeedDownloadLength; downLength=everyThreadNeedDownloadLength; } randomAccessFile.write(b, 0, len-redundant); //实时更新该线程的已下载量,从而刷新每条线程的已下载量 fileDownloader.updateEveryThreadDownloadLength(threadId, downLength); //实时更新已经下载的总量 fileDownloader.appendDownloadTotalSize(len-redundant); } inputStream.close(); randomAccessFile.close(); //改变标志位 isFinish=true; } } catch (Exception e) { downLength=-1; e.printStackTrace(); } } //判断是否已经下载完成 public Boolean isFinish(){ return isFinish; } //返回该线程已经下载的数据量,若-1则代表失败 public int getDownloadSize(){ return downLength; } }
DBOpenHelper如下:
package cc.helper; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; /** * 总结: * 1 继承自SQLiteOpenHelper * 2 完成构造方法 */ public class DBOpenHelper extends SQLiteOpenHelper { private final static String DBName = "download.db"; private final static int VERSION = 1; public DBOpenHelper(Context context) { super(context, DBName, null, VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL("CREATE TABLE IF NOT EXISTS filedownload(id integer primary key autoincrement, downpath varchar(100), threadid INTEGER, downlength INTEGER)"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { db.execSQL("DROP TABLE IF EXISTS filedownload"); onCreate(db); } }
DownloadThreadHelper如下:
package cc.helper; import java.util.HashMap; import java.util.Map; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; /** * 该类主要用来操作每条线程对应的保存在数据库中的下载信息 * 比如:某条线程已经下载的数据量 */ public class DownloadThreadHelper { private DBOpenHelper dbOpenHelper; public DownloadThreadHelper(Context context) { dbOpenHelper = new DBOpenHelper(context); } /** * 保存每条线程已经下载的长度 */ public void saveEveryThreadDownloadLength(String path, Map<Integer, Integer> map) { SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); db.beginTransaction(); try { for (Map.Entry<Integer, Integer> entry : map.entrySet()) { db.execSQL("insert into filedownload(downpath, threadid, downlength) values(?,?,?)", new Object[] { path, entry.getKey(), entry.getValue() }); } db.setTransactionSuccessful(); } finally { db.endTransaction(); db.close(); } } /** * 获取每条线程已经下载的长度 */ public Map<Integer, Integer> getEveryThreadDownloadLength(String path){ SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); Cursor cursor=db.rawQuery("select threadid,downlength from filedownload where downpath=?", new String[]{path}); Map<Integer, Integer> threadsMap=new HashMap<Integer, Integer>(); while(cursor.moveToNext()){ int threadid=cursor.getInt(0); int downlength=cursor.getInt(1); threadsMap.put(threadid, downlength); } cursor.close(); db.close(); return threadsMap; } /** * 实时更新每条线程已经下载的数据长度 * 利用downPath和threadid来确定其已下载长度 */ public void updateEveryThreadDownloadLength(String path, Map<Integer, Integer> map) { SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); db.beginTransaction(); try { for (Map.Entry<Integer, Integer> entry : map.entrySet()) { db.execSQL("update filedownload set downlength=? where threadid=? and downpath=?",new Object[] { entry.getValue(), entry.getKey(), path}); System.out.println("更新该线程下载情况 threadID="+entry.getKey()+",length="+entry.getValue()); } db.setTransactionSuccessful(); } finally { db.endTransaction(); db.close(); } } /** * 下载完成后,删除每条线程的记录 */ public void deleteEveryThreadDownloadRecord(String path){ SQLiteDatabase db = dbOpenHelper.getWritableDatabase(); db.execSQL("delete from filedownload where downpath=?", new String[]{path}); db.close(); } }
main.xml如下:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="文件下载路径:" /> <EditText android:id="@+id/urlEditText" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="http://y1.ifengimg.com/dc14f57c79882c4a/2013/1003/re_524cb964403c1.jpg" /> <Button android:id="@+id/downloadButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="下载" /> <ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyleHorizontal" android:layout_width="fill_parent" android:layout_height="18dip" /> <TextView android:id="@+id/percentTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" /> </LinearLayout>
时间: 2024-11-02 08:07:03