很多时候我们需要在Android设备上下载远程服务器上的文件进安装,前两天晚上我看到一个视频,写了两个晚上,大概理解一下。   直接通过Android提供的Http类访问远程服务器,这里AndroidHttpClient是SDK 2.2中新出的方法,

下载断点文件。

下面让我们看一下图先。

 

 
  
  1. 让我们看一下代码的实现方法。 
  2. package com.smart.db; 
  3. import java.util.HashMap; 
  4. import java.util.Map; 
  5. import android.content.Context; 
  6. import android.database.Cursor; 
  7. import android.database.sqlite.SQLiteDatabase; 
  8. /** 
  9. * 业务bean 
  10. * 
  11. */ 
  12. public class FileService { 
  13. private DBOpenHelper openHelper; 
  14. public FileService(Context context) { 
  15.   openHelper = new DBOpenHelper(context); 
  16. } 
  17. /** 
  18.   * 获取每条线程已经下载的文件长度 
  19.   * @param path 
  20.   * @return 
  21.   */ 
  22. public Map<Integer, Integer> getData(String path){ 
  23.   SQLiteDatabase db = openHelper.getReadableDatabase(); 
  24.   Cursor cursor = db.rawQuery("select threadid, downlength from SmartFileDownlog where downpath=?", new String[]{path}); 
  25.   Map<Integer, Integer> data = new HashMap<Integer, Integer>(); 
  26.   while(cursor.moveToNext()){ 
  27.    data.put(cursor.getInt(0), cursor.getInt(1)); 
  28.   } 
  29.   cursor.close(); 
  30.   db.close(); 
  31.   return data; 
  32. } 
  33. /** 
  34.   * 保存每条线程已经下载的文件长度 
  35.   * @param path 
  36.   * @param map 
  37.   */ 
  38. public void save(String path,  Map<Integer, Integer> map){//int threadid, int position 
  39.   SQLiteDatabase db = openHelper.getWritableDatabase(); 
  40.   db.beginTransaction(); 
  41.   try{ 
  42.    for(Map.Entry<Integer, Integer> entry : map.entrySet()){ 
  43.     db.execSQL("insert into SmartFileDownlog(downpath, threadid, downlength) values(?,?,?)", 
  44.       new Object[]{path, entry.getKey(), entry.getValue()}); 
  45.    } 
  46.    db.setTransactionSuccessful(); 
  47.   }finally{ 
  48.    db.endTransaction(); 
  49.   } 
  50.   db.close(); 
  51. } 
  52. /** 
  53.   * 实时更新每条线程已经下载的文件长度 
  54.   * @param path 
  55.   * @param map 
  56.   */ 
  57. public void update(String path, Map<Integer, Integer> map){ 
  58.   SQLiteDatabase db = openHelper.getWritableDatabase(); 
  59.   db.beginTransaction(); 
  60.   try{ 
  61.    for(Map.Entry<Integer, Integer> entry : map.entrySet()){ 
  62.     db.execSQL("update SmartFileDownlog set downlength=? where downpath=? and threadid=?", 
  63.       new Object[]{entry.getValue(), path, entry.getKey()}); 
  64.    } 
  65.    db.setTransactionSuccessful(); 
  66.   }finally{ 
  67.    db.endTransaction(); 
  68.   } 
  69.   db.close(); 
  70. } 
  71. /** 
  72.   * 当文件下载完成后,删除对应的下载记录 
  73.   * @param path 
  74.   */ 
  75. public void delete(String path){ 
  76.   SQLiteDatabase db = openHelper.getWritableDatabase(); 
  77.   db.execSQL("delete from SmartFileDownlog where downpath=?", new Object[]{path}); 
  78.   db.close(); 
  79. } 
  80.  
  81. } 
  82.  
  83.  
  84. package com.smart.impl; 
  85. import java.io.File; 
  86. import java.io.RandomAccessFile; 
  87. import java.net.HttpURLConnection; 
  88. import java.net.URL; 
  89. import java.util.LinkedHashMap; 
  90. import java.util.Map; 
  91. import java.util.UUID; 
  92. import java.util.concurrent.ConcurrentHashMap; 
  93. import java.util.regex.Matcher; 
  94. import java.util.regex.Pattern; 
  95. import android.content.Context; 
  96. import android.util.Log; 
  97. import com.smart.db.FileService; 
  98. /** 
  99. * 文件下载器 
  100. * @author lihuoming@sohu.com 
  101. */ 
  102. public class SmartFileDownloader { 
  103. private static final String TAG = "SmartFileDownloader"; 
  104. private Context context; 
  105. private FileService fileService; 
  106. /* 已下载文件长度 */ 
  107. private int downloadSize = 0; 
  108. /* 原始文件长度 */ 
  109. private int fileSize = 0; 
  110. /* 线程数 */ 
  111. private SmartDownloadThread[] threads; 
  112. /* 本地保存文件 */ 
  113. private File saveFile; 
  114. /* 缓存各线程下载的长度*/ 
  115. private Map<Integer, Integer> data = new ConcurrentHashMap<Integer, Integer>(); 
  116. /* 每条线程下载的长度 */ 
  117. private int block; 
  118. /* 下载路径  */ 
  119. private String downloadUrl; 
  120. /** 
  121.   * 获取线程数 
  122.   */ 
  123. public int getThreadSize() { 
  124.   return threads.length; 
  125. } 
  126. /** 
  127.   * 获取文件大小 
  128.   * @return 
  129.   */ 
  130. public int getFileSize() { 
  131.   return fileSize; 
  132. } 
  133. /** 
  134.   * 累计已下载大小 
  135.   * @param size 
  136.   */ 
  137. protected synchronized void append(int size) { 
  138.   downloadSize += size; 
  139. } 
  140. /** 
  141.   * 更新指定线程最后下载的位置 
  142.   * @param threadId 线程id 
  143.   * @param pos 最后下载的位置 
  144.   */ 
  145. protected void update(int threadId, int pos) { 
  146.   this.data.put(threadId, pos); 
  147. } 
  148. /** 
  149.   * 保存记录文件 
  150.   */ 
  151. protected synchronized void saveLogFile() { 
  152.   this.fileService.update(this.downloadUrl, this.data); 
  153. } 
  154. /** 
  155.   * 构建文件下载器 
  156.   * @param downloadUrl 下载路径 
  157.   * @param fileSaveDir 文件保存目录 
  158.   * @param threadNum 下载线程数 
  159.   */ 
  160. public SmartFileDownloader(Context context, String downloadUrl, File fileSaveDir, int threadNum) { 
  161.   try { 
  162.    this.context = context; 
  163.    this.downloadUrl = downloadUrl; 
  164.    fileService = new FileService(this.context); 
  165.    URL url = new URL(this.downloadUrl); 
  166.    if(!fileSaveDir.exists()) fileSaveDir.mkdirs(); 
  167.    this.threads = new SmartDownloadThread[threadNum];      
  168.    HttpURLConnection conn = (HttpURLConnection) url.openConnection(); 
  169.    conn.setConnectTimeout(5*1000); 
  170.    conn.setRequestMethod("GET"); 
  171.    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, */*"); 
  172.    conn.setRequestProperty("Accept-Language", "zh-CN"); 
  173.    conn.setRequestProperty("Referer", downloadUrl); 
  174.    conn.setRequestProperty("Charset", "UTF-8"); 
  175.    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)"); 
  176.    conn.setRequestProperty("Connection", "Keep-Alive"); 
  177.    conn.connect(); 
  178.    printResponseHeader(conn); 
  179.    if (conn.getResponseCode()==200) { 
  180.     this.fileSize = conn.getContentLength();//根据响应获取文件大小 
  181.     if (this.fileSize <= 0) throw new RuntimeException("Unkown file size "); 
  182.        
  183.     String filename = getFileName(conn); 
  184.     this.saveFile = new File(fileSaveDir, filename);/* 保存文件 */ 
  185.     Map<Integer, Integer> logdata = fileService.getData(downloadUrl); 
  186.     if(logdata.size()>0){ 
  187.      for(Map.Entry<Integer, Integer> entry : logdata.entrySet()) 
  188.       data.put(entry.getKey(), entry.getValue()); 
  189.     } 
  190.     this.block = (this.fileSize % this.threads.length)==0? this.fileSize / this.threads.length : this.fileSize / this.threads.length + 1; 
  191.     if(this.data.size()==this.threads.length){ 
  192.      for (int i = 0; i < this.threads.length; i++) { 
  193.       this.downloadSize += this.data.get(i+1); 
  194.      } 
  195.      print("已经下载的长度"+ this.downloadSize); 
  196.     }    
  197.    }else{ 
  198.     throw new RuntimeException("server no response "); 
  199.    } 
  200.   } catch (Exception e) { 
  201.    print(e.toString()); 
  202.    throw new RuntimeException("don't connection this url"); 
  203.   } 
  204. } 
  205. /** 
  206.   * 获取文件名 
  207.   */ 
  208. private String getFileName(HttpURLConnection conn) { 
  209.   String filename = this.downloadUrl.substring(this.downloadUrl.lastIndexOf('/') + 1); 
  210.   if(filename==null || "".equals(filename.trim())){//如果获取不到文件名称 
  211.    for (int i = 0;; i++) { 
  212.     String mine = conn.getHeaderField(i); 
  213.     if (mine == null) break; 
  214.     if("content-disposition".equals(conn.getHeaderFieldKey(i).toLowerCase())){ 
  215.      Matcher m = Pattern.compile(".*filename=(.*)").matcher(mine.toLowerCase()); 
  216.      if(m.find()) return m.group(1); 
  217.     } 
  218.    } 
  219.    filename = UUID.randomUUID()+ ".tmp";//默认取一个文件名 
  220.   } 
  221.   return filename; 
  222. } 
  223.  
  224. /** 
  225.   *  开始下载文件 
  226.   * @param listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null 
  227.   * @return 已下载文件大小 
  228.   * @throws Exception 
  229.   */ 
  230. public int download(SmartDownloadProgressListener listener) throws Exception{ 
  231.   try { 
  232.    RandomAccessFile randOut = new RandomAccessFile(this.saveFile, "rw"); 
  233.    if(this.fileSize>0) randOut.setLength(this.fileSize); 
  234.    randOut.close(); 
  235.    URL url = new URL(this.downloadUrl); 
  236.    if(this.data.size() != this.threads.length){ 
  237.     this.data.clear();//清除数据 
  238.     for (int i = 0; i < this.threads.length; i++) { 
  239.      this.data.put(i+1, 0); 
  240.     } 
  241.    } 
  242.    for (int i = 0; i < this.threads.length; i++) { 
  243.     int downLength = this.data.get(i+1); 
  244.     if(downLength < this.block && this.downloadSize<this.fileSize){ //该线程未完成下载时,继续下载       
  245.      this.threads = new SmartDownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1); 
  246.      this.threads.setPriority(7); 
  247.      this.threads.start(); 
  248.     }else{ 
  249.      this.threads = null; 
  250.     } 
  251.    } 
  252.    this.fileService.save(this.downloadUrl, this.data); 
  253.    boolean notFinish = true;//下载未完成 
  254.    while (notFinish) {// 循环判断是否下载完毕 
  255.     Thread.sleep(900); 
  256.     notFinish = false;//假定下载完成 
  257.     for (int i = 0; i < this.threads.length; i++){ 
  258.      if (this.threads != null && !this.threads.isFinish()) { 
  259.       notFinish = true;//下载没有完成 
  260.       if(this.threads.getDownLength() == -1){//如果下载失败,再重新下载 
  261.        this.threads = new SmartDownloadThread(this, url, this.saveFile, this.block, this.data.get(i+1), i+1); 
  262.        this.threads.setPriority(7); 
  263.        this.threads.start(); 
  264.       } 
  265.      } 
  266.     }    
  267.     if(listener!=null) listener.onDownloadSize(this.downloadSize); 
  268.    } 
  269.    fileService.delete(this.downloadUrl); 
  270.   } catch (Exception e) { 
  271.    print(e.toString()); 
  272.    throw new Exception("file download fail"); 
  273.   } 
  274.   return this.downloadSize; 
  275. } 
  276. /** 
  277.   * 获取Http响应头字段 
  278.   * @param http 
  279.   * @return 
  280.   */ 
  281. public static Map<String, String> getHttpResponseHeader(HttpURLConnection http) { 
  282.   Map<String, String> header = new LinkedHashMap<String, String>(); 
  283.   for (int i = 0;; i++) { 
  284.    String mine = http.getHeaderField(i); 
  285.    if (mine == null) break; 
  286.    header.put(http.getHeaderFieldKey(i), mine); 
  287.   } 
  288.   return header; 
  289. } 
  290. /** 
  291.   * 打印Http头字段 
  292.   * @param http 
  293.   */ 
  294. public static void printResponseHeader(HttpURLConnection http){ 
  295.   Map<String, String> header = getHttpResponseHeader(http); 
  296.   for(Map.Entry<String, String> entry : header.entrySet()){ 
  297.    String key = entry.getKey()!=null ? entry.getKey()+ ":" : ""; 
  298.    print(key+ entry.getValue()); 
  299.   } 
  300. } 
  301. //打印日志 
  302. private static void print(String msg){ 
  303.   Log.i(TAG, msg); 
  304. } 
  305.  
  306.  
  307. } 
  308. package com.smart.impl; 
  309. import java.io.File; 
  310. import java.io.InputStream; 
  311. import java.io.RandomAccessFile; 
  312. import java.net.HttpURLConnection; 
  313. import java.net.URL; 
  314. import android.util.Log; 
  315. public class SmartDownloadThread extends Thread { 
  316. private static final String TAG = "SmartDownloadThread"; 
  317. private File saveFile; 
  318. private URL downUrl; 
  319. private int block; 
  320. /* *下载开始位置  */ 
  321. private int threadId = -1; 
  322. private int downLength; 
  323. private boolean finish = false; 
  324. private SmartFileDownloader downloader; 
  325. public SmartDownloadThread(SmartFileDownloader downloader, URL downUrl, File saveFile, int block, int downLength, int threadId) { 
  326.   this.downUrl = downUrl; 
  327.   this.saveFile = saveFile; 
  328.   this.block = block; 
  329.   this.downloader = downloader; 
  330.   this.threadId = threadId; 
  331.   this.downLength = downLength; 
  332. } 
  333.  
  334. @Override 
  335. public void run() { 
  336.   if(downLength < block){//未下载完成 
  337.    try { 
  338.     HttpURLConnection http = (HttpURLConnection) downUrl.openConnection(); 
  339.     http.setConnectTimeout(5 * 1000); 
  340.     http.setRequestMethod("GET"); 
  341.     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, */*"); 
  342.     http.setRequestProperty("Accept-Language", "zh-CN"); 
  343.     http.setRequestProperty("Referer", downUrl.toString()); 
  344.     http.setRequestProperty("Charset", "UTF-8"); 
  345.     int startPos = block * (threadId - 1) + downLength;//开始位置 
  346.     int endPos = block * threadId -1;//结束位置 
  347.     http.setRequestProperty("Range", "bytes=" + startPos + "-"+ endPos);//设置获取实体数据的范围 
  348.     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)"); 
  349.     http.setRequestProperty("Connection", "Keep-Alive"); 
  350.     
  351.     InputStream inStream = http.getInputStream(); 
  352.     byte[] buffer = new byte[1024]; 
  353.     int offset = 0; 
  354.     print("Thread " + this.threadId + " start download from position "+ startPos); 
  355.     RandomAccessFile threadfile = new RandomAccessFile(this.saveFile, "rwd"); 
  356.     threadfile.seek(startPos); 
  357.     while ((offset = inStream.read(buffer, 0, 1024)) != -1) { 
  358.      threadfile.write(buffer, 0, offset); 
  359.      downLength += offset; 
  360.      downloader.update(this.threadId, downLength); 
  361.      downloader.saveLogFile(); 
  362.      downloader.append(offset); 
  363.     } 
  364.     threadfile.close(); 
  365.     inStream.close();    
  366.     print("Thread " + this.threadId + " download finish"); 
  367.     this.finish = true; 
  368.    } catch (Exception e) { 
  369.     this.downLength = -1; 
  370.     print("Thread "+ this.threadId+ ":"+ e); 
  371.    } 
  372.   } 
  373. } 
  374. private static void print(String msg){ 
  375.   Log.i(TAG, msg); 
  376. } 
  377. /** 
  378.   * 下载是否完成 
  379.   * @return 
  380.   */ 
  381. public boolean isFinish() { 
  382.   return finish; 
  383. } 
  384. /** 
  385.   * 已经下载的内容大小 
  386.   * @return 如果返回值为-1,代表下载失败 
  387.   */ 
  388. public long getDownLength() { 
  389.   return downLength; 
  390. } 
  391. } 
  392. package com.smart.activoty.download; 
  393. import java.io.File; 
  394. import android.app.Activity; 
  395. import android.os.Bundle; 
  396. import android.os.Environment; 
  397. import android.os.Handler; 
  398. import android.os.Message; 
  399. import android.view.View; 
  400. import android.widget.Button; 
  401. import android.widget.EditText; 
  402. import android.widget.ProgressBar; 
  403. import android.widget.TextView; 
  404. import android.widget.Toast; 
  405. import com.smart.impl.SmartDownloadProgressListener; 
  406. import com.smart.impl.SmartFileDownloader; 
  407. public class SmartDownloadActivity extends Activity { 
  408.     private ProgressBar downloadbar; 
  409.     private EditText pathText; 
  410.     private TextView resultView; 
  411.     private Handler handler = new Handler(){ 
  412.   @Override//信息 
  413.   public void handleMessage(Message msg) { 
  414.    switch (msg.what) { 
  415.    case 1: 
  416.     int size = msg.getData().getInt("size"); 
  417.     downloadbar.setProgress(size); 
  418.     float result = (float)downloadbar.getProgress()/ (float)downloadbar.getMax(); 
  419.     int p = (int)(result*100); 
  420.     resultView.setText(p+"%"); 
  421.     if(downloadbar.getProgress()==downloadbar.getMax()) 
  422.      Toast.makeText(SmartDownloadActivity.this, R.string.success, 1).show(); 
  423.     break; 
  424.    case -1: 
  425.     Toast.makeText(SmartDownloadActivity.this, R.string.error, 1).show(); 
  426.     break; 
  427.    } 
  428.     
  429.   }      
  430.     }; 
  431.     
  432.     @Override 
  433.     public void onCreate(Bundle savedInstanceState) { 
  434.         super.onCreate(savedInstanceState); 
  435.         setContentView(R.layout.main); 
  436.          
  437.         Button button = (Button)this.findViewById(R.id.button); 
  438.         downloadbar = (ProgressBar)this.findViewById(R.id.downloadbar); 
  439.         pathText = (EditText)this.findViewById(R.id.path); 
  440.         resultView = (TextView)this.findViewById(R.id.result); 
  441.         button.setOnClickListener(new View.OnClickListener() {    
  442.    @Override 
  443.    public void onClick(View v) { 
  444.     String path = pathText.getText().toString(); 
  445.     if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ 
  446.      File dir = Environment.getExternalStorageDirectory();//文件保存目录 
  447.      download(path, dir); 
  448.     }else{ 
  449.      Toast.makeText(SmartDownloadActivity.this, R.string.sdcarderror, 1).show(); 
  450.     } 
  451.    } 
  452.   }); 
  453.     } 
  454.     //对于UI控件的更新只能由主线程(UI线程)负责,如果在非UI线程更新UI控件,更新的结果不会反映在屏幕上,某些控件还会出错 
  455.     private void download(final String path, final File dir){ 
  456.      new Thread(new Runnable() { 
  457.    @Override 
  458.    public void run() { 
  459.     try { 
  460.      SmartFileDownloader loader = new SmartFileDownloader(SmartDownloadActivity.this, path, dir, 3); 
  461.      int length = loader.getFileSize();//获取文件的长度 
  462.      downloadbar.setMax(length); 
  463.      loader.download(new SmartDownloadProgressListener(){ 
  464.       @Override 
  465.       public void onDownloadSize(int size) {//可以实时得到文件下载的长度 
  466.        Message msg = new Message(); 
  467.        msg.what = 1; 
  468.        msg.getData().putInt("size", size);       
  469.        handler.sendMessage(msg); 
  470.       }}); 
  471.     } catch (Exception e) { 
  472.      Message msg = new Message();//信息提示 
  473.      msg.what = -1; 
  474.      msg.getData().putString("error", "下载失败");//如果下载错误,显示提示失败! 
  475.      handler.sendMessage(msg); 
  476.     } 
  477.    } 
  478.   }).start();//开始 
  479.       
  480.     } 
  481. }