软件测试界面
功能介绍:输入下载地址后,点击下载,进度条显示下载的百分比,处于下载时下载按钮无法使用,未开始下载,停止按钮无法使用
完整代码分析:
1.界面创建:
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="fill_parent" 4 android:layout_height="fill_parent" 5 android:orientation="vertical" > 6 7 <TextView 8 android:layout_width="fill_parent" 9 android:layout_height="wrap_content" 10 android:text="@string/path" /> 11 12 <EditText 13 android:id="@+id/path" 14 android:layout_width="fill_parent" 15 android:layout_height="wrap_content" 16 android:text="http://192.168.0.117/testxml/xunlei.exe" /> 17 <LinearLayout 18 android:layout_width="fill_parent" 19 android:layout_height="wrap_content" 20 android:orientation="horizontal" 21 > 22 <!--下载按钮--> 23 <Button 24 android:id="@+id/downloadbutton" 25 android:layout_width="wrap_content" 26 android:layout_height="wrap_content" 27 android:text="@string/button" /> 28 <!--停止按钮--> 29 <Button 30 android:id="@+id/stopbutton" 31 android:layout_width="wrap_content" 32 android:layout_height="wrap_content" 33 android:text="@string/stopbutton" 34 android:enabled="false"/> 35 36 </LinearLayout> 37 38 <ProgressBar 39 android:id="@+id/progressBar" 40 style="?android:attr/progressBarStyleHorizontal" 41 android:layout_width="fill_parent" 42 android:layout_height="18dp" /> 43 <!--进度条下面的百分比--> 44 <TextView 45 android:id="@+id/resultview" 46 android:layout_width="fill_parent" 47 android:layout_height="wrap_content" 48 android:gravity="center" /> 49 50 </LinearLayout>
2.清单文件
1 <?xml version="1.0" encoding="utf-8"?> 2 <manifest xmlns:android="http://schemas.android.com/apk/res/android" 3 package="caicai.cn.multhreaddownloader" 4 android:versionCode="1" 5 android:versionName="1.0" > 6 7 <uses-sdk android:minSdkVersion="8" /> 8 <uses-permission android:name="android.permission.INTERNET"/> 9 <!--允许装备或解除可移除的存储仓库的文件系统 --> 10 <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> 11 <!--允许写入外存储设备即SDcard --> 12 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 13 14 <application 15 android:icon="@drawable/ic_launcher" 16 android:label="@string/app_name" > 17 <activity 18 android:label="@string/app_name" 19 android:name=".Cai_multhreaddownloaderActivity" > 20 <intent-filter > 21 <action android:name="android.intent.action.MAIN" /> 22 23 <category android:name="android.intent.category.LAUNCHER" /> 24 </intent-filter> 25 </activity> 26 </application> 27 28 </manifest>
3.界面操作
1 package caicai.cn.multhreaddownloader; 2 3 import java.io.File; 4 5 import net.download.DownloadProgressListener; 6 import net.download.FileDownloader; 7 8 import android.app.Activity; 9 import android.os.Bundle; 10 import android.os.Environment; 11 import android.os.Handler; 12 import android.os.Message; 13 import android.view.View; 14 import android.widget.Button; 15 import android.widget.EditText; 16 import android.widget.ProgressBar; 17 import android.widget.TextView; 18 import android.widget.Toast; 19 20 public class Cai_multhreaddownloaderActivity extends Activity { 21 private EditText pathtext; 22 private TextView resultview; 23 private Button downloadbutton; 24 private Button stopbutton; 25 private ProgressBar progressBar; 26 private Handler handler = new Uihandler(); 27 28 // handler用于往创建handler对象所在的线程所绑定的消息队列发送消息 29 30 private final class Uihandler extends Handler {// 接收从子线程传来的值,注意只有主线程才能对UI控件进行控制与更新 31 public void handleMessage(Message msg) { 32 switch (msg.what) { 33 case 1: 34 int size = msg.getData().getInt("size"); 35 progressBar.setProgress(size);// 当前刻度; 36 float num = (float) progressBar.getProgress() 37 / (float) progressBar.getMax(); 38 int result = (int) (num * 100); 39 resultview.setText(result + "%"); 40 if (progressBar.getProgress() == progressBar.getMax()) { 41 Toast.makeText(getApplicationContext(), "下载完成", 1).show(); 42 } 43 break; 44 case -1: 45 Toast.makeText(getApplicationContext(), R.string.error, 1) 46 .show(); 47 break; 48 } 49 50 } 51 } 52 53 public void onCreate(Bundle savedInstanceState) { 54 super.onCreate(savedInstanceState); 55 setContentView(R.layout.main); 56 pathtext = (EditText) this.findViewById(R.id.path); 57 resultview = (TextView) this.findViewById(R.id.resultview); 58 downloadbutton = (Button) this.findViewById(R.id.downloadbutton); 59 stopbutton = (Button) this.findViewById(R.id.stopbutton); 60 progressBar = (ProgressBar) this.findViewById(R.id.progressBar); 61 Buttonclicklistener listener = new Buttonclicklistener(); 62 downloadbutton.setOnClickListener(listener); 63 stopbutton.setOnClickListener(listener); 64 } 65 66 private final class Buttonclicklistener implements View.OnClickListener { 67 68 public void onClick(View v) { 69 switch (v.getId()) { 70 case R.id.downloadbutton: 71 String path = pathtext.getText().toString();// 获取下载路径 72 if (Environment.getExternalStorageState().equals( 73 Environment.MEDIA_MOUNTED)) {// 判断SDcard是否存在 或者可写数据 74 File savedir = Environment.getExternalStorageDirectory();// 获取sdcard跟目录,即保存路径 75 download(path, savedir); 76 }else{ 77 Toast.makeText(getApplicationContext(),R.string.sdcarderror, 1).show(); 78 } 79 downloadbutton.setEnabled(false); 80 stopbutton.setEnabled(true); 81 break; 82 case R.id.stopbutton: 83 exit(); 84 downloadbutton.setEnabled(true); 85 stopbutton.setEnabled(false); 86 break; 87 } 88 } 89 90 /* 91 * 由于用户的输入事件(点击button, 触摸屏幕....)是由主线程负责处理的,如果主线程处于工作状态, 92 * 此时用户产生的输入事件如果没能在5秒内得到处理,系统就会报“应用无响应”错误。 93 * 所以在主线程里不能执行一件比较耗时的工作,否则会因主线程阻塞而无法处理用户的输入事件, 94 * 导致“应用无响应”错误的出现。耗时的工作应该在子线程里执行。 95 */ 96 private DownloadTask task; 97 private void exit() {//退出下载 98 if (task!= null) { 99 task.exit(); 100 } 101 }; 102 103 private void download(String path, File savedir) { 104 task = new DownloadTask(path, savedir); 105 new Thread(task).start(); 106 } 107 108 private final class DownloadTask implements Runnable { 109 String path; 110 File savedir; 111 FileDownloader loader; 112 113 public DownloadTask(String path, File savedir) { 114 this.path = path; 115 this.savedir = savedir; 116 } 117 118 public void exit() { 119 if (loader != null) { 120 loader.exit();// 退出下载 121 } 122 } 123 124 @Override 125 public void run() { 126 try { 127 loader = new FileDownloader(getApplicationContext(), path, 128 savedir, 3); 129 progressBar.setMax(loader.getFileSize());// 设置进度条最大刻度 130 loader.download(new DownloadProgressListener() { 131 @Override 132 public void onDownloadSize(int size) { 133 Message msg = new Message(); 134 msg.what = 1;// 定义消息的ID,以便区别那个消息发过来的 135 msg.getData().putInt("size", size); 136 handler.sendMessage(msg); 137 138 } 139 }); 140 } catch (Exception e) { 141 e.printStackTrace(); 142 handler.sendMessage(handler.obtainMessage(-1));// 发送编号为-1的空消息 143 } 144 145 } 146 147 } 148 } 149 }
4.下载接口
1 package net.download; 2 3 public interface DownloadProgressListener { 4 public void onDownloadSize(int size); 5 }
5.下载线程类
1 package net.download; 2 3 import java.io.File; 4 import java.io.InputStream; 5 import java.io.RandomAccessFile; 6 import java.net.HttpURLConnection; 7 import java.net.URL; 8 9 import android.util.Log; 10 11 public class DownloadThread extends Thread { 12 private static final String TAG = "DownloadThread"; 13 private File saveFile; 14 private URL downUrl; 15 private int block; 16 /* 下载开始位置 */ 17 private int threadId = -1; 18 private int downLength; 19 private boolean finish = false; 20 private FileDownloader downloader; 21 22 public DownloadThread(FileDownloader downloader, URL downUrl, File saveFile, int block, int downLength, int threadId) { 23 this.downUrl = downUrl; 24 this.saveFile = saveFile; 25 this.block = block; 26 this.downloader = downloader; 27 this.threadId = threadId; 28 this.downLength = downLength; 29 } 30 31 @Override 32 public void run() { 33 if(downLength < block){//未下载完成 34 try { 35 HttpURLConnection http = (HttpURLConnection) downUrl.openConnection(); 36 http.setConnectTimeout(5 * 1000); 37 http.setRequestMethod("GET"); 38 http.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, */*"); 39 http.setRequestProperty("Accept-Language", "zh-CN"); 40 http.setRequestProperty("Referer", downUrl.toString()); 41 http.setRequestProperty("Charset", "UTF-8"); 42 int startPos = block * (threadId - 1) + downLength;//开始位置 43 int endPos = block * threadId -1;//结束位置 44 http.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);//设置获取实体数据的范围 45 http.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)"); 46 http.setRequestProperty("Connection", "Keep-Alive"); 47 48 InputStream inStream = http.getInputStream(); 49 byte[] buffer = new byte[1024]; 50 int offset = 0; 51 print("Thread " + this.threadId + " start download from position "+ startPos); 52 RandomAccessFile threadfile = new RandomAccessFile(this.saveFile, "rwd"); 53 threadfile.seek(startPos); 54 while (!downloader.getExit() && (offset = inStream.read(buffer, 0, 1024)) != -1) { 55 threadfile.write(buffer, 0, offset); 56 downLength += offset; 57 downloader.update(this.threadId, downLength); 58 downloader.append(offset); 59 } 60 threadfile.close(); 61 inStream.close(); 62 print("Thread " + this.threadId + " download finish"); 63 this.finish = true; 64 } catch (Exception e) { 65 this.downLength = -1; 66 print("Thread "+ this.threadId+ ":"+ e); 67 } 68 } 69 } 70 private static void print(String msg){ 71 Log.i(TAG, msg); 72 } 73 /** 74 * 下载是否完成 75 * @return 76 */ 77 public boolean isFinish() { 78 return finish; 79 } 80 /** 81 * 已经下载的内容大小 82 * @return 如果返回值为-1,代表下载失败 83 */ 84 public long getDownLength() { 85 return downLength; 86 } 87 }
6.文件下载器
1 package net.download; 2 3 import java.io.File; 4 import java.io.RandomAccessFile; 5 import java.net.HttpURLConnection; 6 import java.net.URL; 7 import java.util.LinkedHashMap; 8 import java.util.Map; 9 import java.util.UUID; 10 import java.util.concurrent.ConcurrentHashMap; 11 import java.util.regex.Matcher; 12 import java.util.regex.Pattern; 13 import service.FileService; 14 15 import android.content.Context; 16 import android.util.Log; 17 /** 18 * 文件下载器使用类案例 19 * 20 try { 21 FileDownloader loader = new FileDownloader(context, "http://browse.babasport.com/ejb3/ActivePort.exe", 22 new File("D:\androidsoft\test"), 2); 23 loader.getFileSize();//得到文件总大小 24 loader.download(new DownloadProgressListener(){ 25 public void onDownloadSize(int size) { 26 print("已经下载:"+ size); 27 } 28 }); 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } 32 */ 33 public class FileDownloader { 34 private static final String TAG = "FileDownloader"; 35 private Context context; 36 private FileService fileService; 37 /* 停止下载 */ 38 private boolean exit; 39 /* 已下载文件长度 */ 40 private int downloadSize = 0; 41 /* 原始文件长度 */ 42 private int fileSize = 0; 43 /* 线程数 */ 44 private DownloadThread[] threads; 45 /* 本地保存文件 */ 46 private File saveFile; 47 /* 缓存各线程下载的长度*/ 48 private Map<Integer, Integer> data = new ConcurrentHashMap<Integer, Integer>(); 49 /* 每条线程下载的长度 */ 50 private int block; 51 /* 下载路径 */ 52 private String downloadUrl; 53 /** 54 * 获取线程数 55 */ 56 public int getThreadSize() { 57 return threads.length; 58 } 59 /** 60 * 退出下载 61 */ 62 public void exit(){ 63 this.exit = true; 64 } 65 public boolean getExit(){ 66 return this.exit; 67 } 68 /** 69 * 获取文件大小 70 * @return 71 */ 72 public int getFileSize() { 73 return fileSize; 74 } 75 /** 76 * 累计已下载大小 77 * @param size 78 */ 79 protected synchronized void append(int size) { 80 downloadSize += size; 81 } 82 /** 83 * 更新指定线程最后下载的位置 84 * @param threadId 线程id 85 * @param pos 最后下载的位置 86 */ 87 protected synchronized void update(int threadId, int pos) { 88 this.data.put(threadId, pos); 89 this.fileService.update(this.downloadUrl, threadId, pos); 90 } 91 /** 92 * 构建文件下载器 93 * @param downloadUrl 下载路径 94 * @param fileSaveDir 文件保存目录 95 * @param threadNum 下载线程数 96 */ 97 public FileDownloader(Context context, String downloadUrl, File fileSaveDir, int threadNum) { 98 try { 99 this.context = context; 100 this.downloadUrl = downloadUrl; 101 fileService = new FileService(this.context); 102 URL url = new URL(this.downloadUrl); 103 if(!fileSaveDir.exists()) fileSaveDir.mkdirs(); 104 this.threads = new DownloadThread[threadNum]; 105 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 106 conn.setConnectTimeout(5*1000); 107 conn.setRequestMethod("GET"); 108 conn.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, */*"); 109 conn.setRequestProperty("Accept-Language", "zh-CN"); 110 conn.setRequestProperty("Referer", downloadUrl); 111 conn.setRequestProperty("Charset", "UTF-8"); 112 conn.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)"); 113 conn.setRequestProperty("Connection", "Keep-Alive"); 114 conn.connect(); 115 printResponseHeader(conn); 116 if (conn.getResponseCode()==200) { 117 this.fileSize = conn.getContentLength();//根据响应获取文件大小 118 if (this.fileSize <= 0) throw new RuntimeException("Unkown file size "); 119 120 String filename = getFileName(conn);//获取文件名称 121 this.saveFile = new File(fileSaveDir, filename);//构建保存文件 122 Map<Integer, Integer> logdata = fileService.getData(downloadUrl);//获取下载记录 123 if(logdata.size()>0){//如果存在下载记录 124 for(Map.Entry<Integer, Integer> entry : logdata.entrySet()) 125 data.put(entry.getKey(), entry.getValue());//把各条线程已经下载的数据长度放入data中 126 } 127 if(this.data.size()==this.threads.length){//下面计算所有线程已经下载的数据总长度 128 for (int i = 0; i < this.threads.length; i++) { 129 this.downloadSize += this.data.get(i+1); 130 } 131 print("已经下载的长度"+ this.downloadSize); 132 } 133 //计算每条线程下载的数据长度 134 this.block = (this.fileSize % this.threads.length)==0? this.fileSize / this.threads.length : this.fileSize / this.threads.length + 1; 135 }else{ 136 throw new RuntimeException("server no response "); 137 } 138 } catch (Exception e) { 139 print(e.toString()); 140 throw new RuntimeException("don't connection this url"); 141 } 142 } 143 /** 144 * 获取文件名 145 */ 146 private String getFileName(HttpURLConnection conn) { 147 String filename = this.downloadUrl.substring(this.downloadUrl.lastIndexOf('/') + 1); 148 if(filename==null || "".equals(filename.trim())){//如果获取不到文件名称 149 for (int i = 0;; i++) { 150 String mine = conn.getHeaderField(i); 151 if (mine == null) break; 152 if("content-disposition".equals(conn.getHeaderFieldKey(i).toLowerCase())){ 153 Matcher m = Pattern.compile(".*filename=(.*)").matcher(mine.toLowerCase()); 154 if(m.find()) return m.group(1); 155 } 156 } 157 filename = UUID.randomUUID()+ ".tmp";//默认取一个文件名 158 } 159 return filename; 160 } 161 162 /** 163 * 开始下载文件 164 * @param listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null 165 * @return 已下载文件大小 166 * @throws Exception 167 */ 168 public int download(DownloadProgressListener listener) throws Exception{ 169 try { 170 RandomAccessFile randOut = new RandomAccessFile(this.saveFile, "rw"); 171 if(this.fileSize>0) randOut.setLength(this.fileSize); 172 randOut.close(); 173 URL url = new URL(this.downloadUrl); 174 if(this.data.size() != this.threads.length){//如果原先未曾下载或者原先的下载线程数与现在的线程数不一致 175 this.data.clear(); 176 for (int i = 0; i < this.threads.length; i++) { 177 this.data.put(i+1, 0);//初始化每条线程已经下载的数据长度为0 178 } 179 this.downloadSize = 0; 180 } 181 for (int i = 0; i < this.threads.length; i++) {//开启线程进行下载 182 int downLength = this.data.get(i+1); 183 if(downLength < this.block && this.downloadSize<this.fileSize){//判断线程是否已经完成下载,否则继续下载 184 this.threads[i] = new DownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1); 185 this.threads[i].setPriority(7); 186 this.threads[i].start(); 187 }else{ 188 this.threads[i] = null; 189 } 190 } 191 fileService.delete(this.downloadUrl);//如果存在下载记录,删除它们,然后重新添加 192 fileService.save(this.downloadUrl, this.data); 193 boolean notFinish = true;//下载未完成 194 while (notFinish) {// 循环判断所有线程是否完成下载 195 Thread.sleep(900); 196 notFinish = false;//假定全部线程下载完成 197 for (int i = 0; i < this.threads.length; i++){ 198 if (this.threads[i] != null && !this.threads[i].isFinish()) {//如果发现线程未完成下载 199 notFinish = true;//设置标志为下载没有完成 200 if(this.threads[i].getDownLength() == -1){//如果下载失败,再重新下载 201 this.threads[i] = new DownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1); 202 this.threads[i].setPriority(7); 203 this.threads[i].start(); 204 } 205 } 206 } 207 if(listener!=null) listener.onDownloadSize(this.downloadSize);//通知目前已经下载完成的数据长度 208 } 209 if(downloadSize == this.fileSize) fileService.delete(this.downloadUrl);//下载完成删除记录 210 } catch (Exception e) { 211 print(e.toString()); 212 throw new Exception("file download error"); 213 } 214 return this.downloadSize; 215 } 216 /** 217 * 获取Http响应头字段 218 * @param http 219 * @return 220 */ 221 public static Map<String, String> getHttpResponseHeader(HttpURLConnection http) { 222 Map<String, String> header = new LinkedHashMap<String, String>(); 223 for (int i = 0;; i++) { 224 String mine = http.getHeaderField(i); 225 if (mine == null) break; 226 header.put(http.getHeaderFieldKey(i), mine); 227 } 228 return header; 229 } 230 /** 231 * 打印Http头字段 232 * @param http 233 */ 234 public static void printResponseHeader(HttpURLConnection http){ 235 Map<String, String> header = getHttpResponseHeader(http); 236 for(Map.Entry<String, String> entry : header.entrySet()){ 237 String key = entry.getKey()!=null ? entry.getKey()+ ":" : ""; 238 print(key+ entry.getValue()); 239 } 240 } 241 242 private static void print(String msg){ 243 Log.i(TAG, msg); 244 } 245 }
7.数据库操作类
1 package service; 2 3 import android.content.Context; 4 import android.database.sqlite.SQLiteDatabase; 5 import android.database.sqlite.SQLiteOpenHelper; 6 7 public class DBOpenHelper extends SQLiteOpenHelper { 8 private static final String DBNAME = "download.db"; 9 private static final int VERSION = 1; 10 11 public DBOpenHelper(Context context) { 12 super(context, DBNAME, null, VERSION); 13 } 14 15 @Override 16 public void onCreate(SQLiteDatabase db) { 17 db.execSQL("CREATE TABLE IF NOT EXISTS filedownlog (id integer primary key autoincrement, downpath varchar(100), threadid INTEGER, downlength INTEGER)"); 18 } 19 20 @Override 21 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 22 db.execSQL("DROP TABLE IF EXISTS filedownlog"); 23 onCreate(db); 24 } 25 26 }
8.下载的进度存入数据库操作类
1 package service; 2 3 import java.util.HashMap; 4 import java.util.Map; 5 6 import android.content.Context; 7 import android.database.Cursor; 8 import android.database.sqlite.SQLiteDatabase; 9 /** 10 * 业务bean 11 * 12 */ 13 public class FileService { 14 private DBOpenHelper openHelper; 15 16 public FileService(Context context) { 17 openHelper = new DBOpenHelper(context); 18 } 19 /** 20 * 获取每条线程已经下载的文件长度 21 * @param path 22 * @return 23 */ 24 public Map<Integer, Integer> getData(String path){ 25 SQLiteDatabase db = openHelper.getReadableDatabase(); 26 Cursor cursor = db.rawQuery("select threadid, downlength from filedownlog where downpath=?", new String[]{path}); 27 Map<Integer, Integer> data = new HashMap<Integer, Integer>(); 28 while(cursor.moveToNext()){ 29 data.put(cursor.getInt(0), cursor.getInt(1)); 30 } 31 cursor.close(); 32 db.close(); 33 return data; 34 } 35 /** 36 * 保存每条线程已经下载的文件长度 37 * @param path 38 * @param map 39 */ 40 public void save(String path, Map<Integer, Integer> map){//int threadid, int position 41 SQLiteDatabase db = openHelper.getWritableDatabase(); 42 db.beginTransaction(); 43 try{ 44 for(Map.Entry<Integer, Integer> entry : map.entrySet()){ 45 db.execSQL("insert into filedownlog(downpath, threadid, downlength) values(?,?,?)", 46 new Object[]{path, entry.getKey(), entry.getValue()}); 47 } 48 db.setTransactionSuccessful(); 49 }finally{ 50 db.endTransaction(); 51 } 52 db.close(); 53 } 54 /** 55 * 实时更新每条线程已经下载的文件长度 56 * @param path 57 * @param map 58 */ 59 public void update(String path, int threadId, int pos){ 60 SQLiteDatabase db = openHelper.getWritableDatabase(); 61 db.execSQL("update filedownlog set downlength=? where downpath=? and threadid=?", 62 new Object[]{pos, path, threadId}); 63 db.close(); 64 } 65 /** 66 * 当文件下载完成后,删除对应的下载记录 67 * @param path 68 */ 69 public void delete(String path){ 70 SQLiteDatabase db = openHelper.getWritableDatabase(); 71 db.execSQL("delete from filedownlog where downpath=?", new Object[]{path}); 72 db.close(); 73 } 74 75 }