博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android(五)数据存储之五网络多线程断点下载
阅读量:4068 次
发布时间:2019-05-25

本文共 31170 字,大约阅读时间需要 103 分钟。

<p>们编写的是Andorid的HTTP多线程断点下载应用程序。因为之间我们学习的学习积累,直接使用单线程下载HTTP文件对我们来说是一件非常简单的事。那么,多线程断点下载的难点在哪里?1.多线程下载,2.支持断点。 </p>
<p><strong>多线程下载:</strong> <br><img title="2010-03-03 传智播客—Android(五)数据存储之五网络多线程断点下载 - 长城 - 长城" src="http://lh4.ggpht.com/_FMPy3IVGHrs/S49SK45xqaI/AAAAAAAABms/B6bk9kJo_vU/632BF46FE91E9326216CA04E22AF3F9E09350A1C.jpg?imgmax=800" border="0" alt="2010-03-03 传智播客—Android(五)数据存储之五网络多线程断点下载 - 长城 - 长城" width="435" height="91"></p>
<p>如何才能从文件的指定位置处开始下载文件?(比如从50MB开始)这一点我们可以通过HTTP请求信息头来设置,还记得HTTP请求信息头的“Range”属性吗?</p>
<p><strong>断点:</strong></p>
<p>首要问题(多线程下载)已经被我们解决了,支持断点下载想必大家也已经想到了。就是将下载的进度保存到文件中,但在Android中却不能这么做。通过老黎的试验,在Android平台中,我们需要向文件中写出下载的文件数据,还需要向另一个文件中写出下载进度,这样会出错。这样会导致有一个文件的内容没有被写出。所以我们就不能以文件的方式来保存下载进度,但可以通过数据库的方式保存下载进度。</p>
<p>这两大问题我们已经有了解决思路,那么就开始动手编写吧!</p>
<p><strong>1.</strong><strong>创建Android工程</strong></p>
<p>Project name:MulThreadDownloader</p>
<p>BuildTarget:Android2.1</p>
<p>Application name:多线程断点下载</p>
<p>Package name:com.changcheng.download</p>
<p>Create Activity:MulThreadDownloader</p>
<p>Min SDK Version:7</p>
<p><strong>2.AndroidManifest.xml</strong></p>
<p><?xml version=<em>"1.0"</em> encoding=<em>"utf-8"</em>?></p>
<p><manifest xmlns:android=<em>"http://schemas.android.com/apk/res/android"</em></p>
<p>package=<em>"com.changcheng.download"</em></p>
<p>android:versionCode=<em>"1"</em></p>
<p>android:versionName=<em>"1.0"</em>></p>
<p><application android:icon=<em>"@drawable/icon"</em> android:label=<em>"@string/app_name"</em>></p>
<p><activity android:name=<em>".MulThreadDownloader"</em></p>
<p>android:label=<em>"@string/app_name"</em>></p>
<p><intent-filter></p>
<p><action android:name=<em>"android.intent.action.MAIN"</em> /></p>
<p><category android:name=<em>"android.intent.category.LAUNCHER"</em> /></p>
<p></intent-filter></p>
<p></activity></p>
<p></application></p>
<p><uses-sdk android:minSdkVersion=<em>"7"</em> /></p>
<p><!-- 在SDCard中创建与删除文件权限 --></p>
<p><uses-permission android:name=<em>"android.permission.MOUNT_UNMOUNT_FILESYSTEMS"</em>/></p>
<p><!-- 往SDCard写入数据权限 --></p>
<p><uses-permission android:name=<em>"android.permission.WRITE_EXTERNAL_STORAGE"</em>/></p>
<p><!-- 访问<span style="text-decoration: underline;">internet</span>权限 --></p>
<p><uses-permission android:name=<em>"android.permission.INTERNET"</em>/></p>
<p></manifest></p>
<p><strong>3.strings.xml</strong></p>
<p><?xml version=<em>"1.0"</em> encoding=<em>"utf-8"</em>?></p>
<p><resources></p>
<p><string name=<em>"hello"</em>>Hello World, DownloadActivity!</string></p>
<p><string name=<em>"app_name"</em>>多线程断点下载</string></p>
<p><string name=<em>"path"</em>>下载路径</string></p>
<p><string name=<em>"downloadbutton"</em>>下载</string></p>
<p><string name=<em>"sdcarderror"</em>>SDCard不存在或者写保护</string></p>
<p></resources></p>
<p><strong>4.main.xml</strong></p>
<p><?xml version=<em>"1.0"</em> encoding=<em>"utf-8"</em>?></p>
<p><LinearLayout xmlns:android=<em>"http://schemas.android.com/apk/res/android"</em></p>
<p>android:orientation=<em>"vertical"</em></p>
<p>android:layout_width=<em>"fill_parent"</em></p>
<p>android:layout_height=<em>"fill_parent"</em></p>
<p>></p>
<p><!-- 下载路径 --></p>
<p><TextView</p>
<p>android:layout_width=<em>"fill_parent"</em></p>
<p>android:layout_height=<em>"wrap_content"</em></p>
<p>android:text=<em>"@string/path"</em></p>
<p>/></p>
<p><EditText</p>
<p>android:layout_width=<em>"fill_parent"</em></p>
<p>android:layout_height=<em>"wrap_content"</em></p>
<p>android:text=<em>"http://www.winrar.com.cn/download/wrar380sc.exe"</em></p>
<p>android:id=<em>"@+id/path"</em></p>
<p>/></p>
<p><!-- 下载按钮 --></p>
<p><Button</p>
<p>android:layout_width=<em>"wrap_content"</em></p>
<p>android:layout_height=<em>"wrap_content"</em></p>
<p>android:text=<em>"@string/downloadbutton"</em></p>
<p>android:id=<em>"@+id/button"</em></p>
<p>/></p>
<p><!-- 进度条 --></p>
<p><ProgressBar</p>
<p>android:layout_width=<em>"fill_parent"</em></p>
<p>android:layout_height=<em>"20dip"</em></p>
<p>style=<em>"?android:attr/progressBarStyleHorizontal"</em></p>
<p>android:id=<em>"@+id/downloadbar"</em>/></p>
<p><!-- 进度% --></p>
<p><TextView</p>
<p>android:layout_width=<em>"fill_parent"</em></p>
<p>android:layout_height=<em>"wrap_content"</em></p>
<p>android:gravity=<em>"center"</em></p>
<p>android:id=<em>"@+id/resultView"</em></p>
<p>/></p>
<p></LinearLayout></p>
<p><strong>5.MulThreadDownloader</strong></p>
<p><strong>package</strong> com.changcheng.download;</p>
<p><strong>import</strong> java.io.File;</p>
<p><strong>import</strong> com.changcheng.net.download.DownloadProgressListener;</p>
<p><strong>import</strong> com.changcheng.net.download.FileDownloader;</p>
<p><strong>import</strong> com.changcheng.download.R;</p>
<p><strong>import</strong> android.app.Activity;</p>
<p><strong>import</strong> android.os.Bundle;</p>
<p><strong>import</strong> android.os.Environment;</p>
<p><strong>import</strong> android.os.Handler;</p>
<p><strong>import</strong> android.os.Message;</p>
<p><strong>import</strong> android.view.View;</p>
<p><strong>import</strong> android.widget.Button;</p>
<p><strong>import</strong> android.widget.EditText;</p>
<p><strong>import</strong> android.widget.ProgressBar;</p>
<p><strong>import</strong> android.widget.TextView;</p>
<p><strong>import</strong> android.widget.Toast;</p>
<p><strong>public</strong> <strong>class</strong> MulThreadDownloader <strong>extends</strong> Activity {</p>
<p><strong>private</strong> EditText pathText;</p>
<p><strong>private</strong> ProgressBar progressBar;</p>
<p><strong>private</strong> TextView resultView;</p>
<p><strong>private</strong> Handler handler = <strong>new</strong> Handler(){</p>
<p>@Override</p>
<p><strong>public</strong> <strong>void</strong> handleMessage(Message msg) {</p>
<p><strong>if</strong>(!Thread.<em>currentThread</em>().isInterrupted()){</p>
<p><strong>switch</strong> (msg.what) {</p>
<p><strong>case</strong> 1:</p>
<p>// 获取当前文件下载的进度</p>
<p><strong>int</strong> size = msg.getData().getInt("size");</p>
<p>progressBar.setProgress(size);</p>
<p><strong>int</strong> result = (<strong>int</strong>)(((<strong>float</strong>)size/(<strong>float</strong>)progressBar.getMax()) * 100);</p>
<p>resultView.setText(result+ "%");</p>
<p><strong>if</strong>(progressBar.getMax() == size){</p>
<p>Toast.<em>makeText</em>(MulThreadDownloader.<strong>this</strong>, "文件下载完成", 1).show();</p>
<p>}</p>
<p><strong>break</strong>;</p>
<p><strong>case</strong> -1:</p>
<p>String error = msg.getData().getString("error");</p>
<p>Toast.<em>makeText</em>(MulThreadDownloader.<strong>this</strong>, error, 1).show();</p>
<p><strong>break</strong>;</p>
<p>}</p>
<p>}</p>
<p><strong>super</strong>.handleMessage(msg);</p>
<p>}</p>
<p>};</p>
<p>@Override</p>
<p><strong>public</strong> <strong>void</strong> onCreate(Bundle savedInstanceState) {</p>
<p><strong>super</strong>.onCreate(savedInstanceState);</p>
<p>setContentView(R.layout.<em>main</em>);</p>
<p>pathText = (EditText)<strong>this</strong>.findViewById(R.id.<em>path</em>);</p>
<p>progressBar = (ProgressBar)<strong>this</strong>.findViewById(R.id.<em>downloadbar</em>);</p>
<p>resultView = (TextView)<strong>this</strong>.findViewById(R.id.<em>resultView</em>);</p>
<p>Button button = (Button)<strong>this</strong>.findViewById(R.id.<em>button</em>);</p>
<p>button.setOnClickListener(<strong>new</strong> View.OnClickListener() {</p>
<p>@Override</p>
<p><strong>public</strong> <strong>void</strong> onClick(View v) {</p>
<p>String path = pathText.getText().toString();</p>
<p><strong>if</strong>(Environment.<em>getExternalStorageState</em>().equals(Environment.<em>MEDIA_MOUNTED</em>)){</p>
<p>//下载文件需要很长的时间,主线程是不能够长时间被阻塞,如果主线程被长时间阻塞, 那么<span style="text-decoration: underline;">Android</span>被回收应用</p>
<p>download(path, Environment.<em>getExternalStorageDirectory</em>());</p>
<p>}<strong>else</strong>{</p>
<p>Toast.<em>makeText</em>(MulThreadDownloader.<strong>this</strong>, R.string.<em>sdcarderror</em>, 1).show();</p>
<p>}</p>
<p>}</p>
<p>});</p>
<p>}</p>
<p>/**</p>
<p>* 下载文件</p>
<p>* <strong>@param</strong> path 下载路径</p>
<p>* <strong>@param</strong> saveDir 文件保存目录</p>
<p>*/</p>
<p>//对于<span style="text-decoration: underline;">Android</span>的UI控件,只能由主线程负责显示界面的更新,其他线程不能直接更新UI控件的显示</p>
<p><strong>public</strong> <strong>void</strong> download(<strong>final</strong> String path, <strong>final</strong> File saveDir){</p>
<p><strong>new</strong> Thread(<strong>new</strong> Runnable() {</p>
<p>@Override</p>
<p><strong>public</strong> <strong>void</strong> run() {</p>
<p>FileDownloader downer = <strong>new</strong> FileDownloader(MulThreadDownloader.<strong>this</strong>, path, saveDir, 3);</p>
<p>progressBar.setMax(downer.getFileSize());//设置进度条的最大刻度</p>
<p><strong>try</strong> {</p>
<p>downer.download(<strong>new</strong> DownloadProgressListener(){</p>
<p>@Override</p>
<p><strong>public</strong> <strong>void</strong> onDownloadSize(<strong>int</strong> size) {</p>
<p>Message msg = <strong>new</strong> Message();</p>
<p>msg.what = 1;</p>
<p>msg.getData().putInt("size", size);</p>
<p>handler.sendMessage(msg);//发送消息</p>
<p>}});</p>
<p>} <strong>catch</strong> (Exception e) {</p>
<p>Message msg = <strong>new</strong> Message();</p>
<p>msg.what = -1;</p>
<p>msg.getData().putString("error", "下载失败");</p>
<p>handler.sendMessage(msg);</p>
<p>}</p>
<p>}</p>
<p>}).start();</p>
<p>}</p>
<p>}</p>
<p><strong>6.FileDownload</strong></p>
<p><strong>package</strong> com.changcheng.net.download;</p>
<p><strong>import</strong> java.io.File;</p>
<p><strong>import</strong> java.io.RandomAccessFile;</p>
<p><strong>import</strong> java.net.HttpURLConnection;</p>
<p><strong>import</strong> java.net.URL;</p>
<p><strong>import</strong> java.util.LinkedHashMap;</p>
<p><strong>import</strong> java.util.Map;</p>
<p><strong>import</strong> java.util.UUID;</p>
<p><strong>import</strong> java.util.concurrent.ConcurrentHashMap;</p>
<p><strong>import</strong> java.util.regex.Matcher;</p>
<p><strong>import</strong> java.util.regex.Pattern;</p>
<p><strong>import</strong> com.changcheng.download.service.FileService;</p>
<p><strong>import</strong> android.content.Context;</p>
<p><strong>import</strong> android.util.Log;</p>
<p>/**</p>
<p>* 文件下载器</p>
<p>* <strong>@author</strong> <span style="text-decoration: underline;">lihuoming@sohu.com</span></p>
<p>*</p>
<p>*/</p>
<p><strong>public</strong> <strong>class</strong> FileDownloader {</p>
<p><strong>private</strong> Context <span style="text-decoration: underline;">context</span>;</p>
<p><strong>private</strong> FileService fileService;</p>
<p><strong>private</strong> <strong>static</strong> <strong>final</strong> String <em>TAG</em> = "FileDownloader";</p>
<p>/* 已下载文件大小 */</p>
<p><strong>private</strong> <strong>int</strong> downloadSize = 0;</p>
<p>/* 原始文件大小 */</p>
<p><strong>private</strong> <strong>int</strong> fileSize = 0;</p>
<p>/* 线程数 */</p>
<p><strong>private</strong> DownloadThread[] threads;</p>
<p>/* 下载路径 */</p>
<p><strong>private</strong> URL url;</p>
<p>/* 本地保存文件 */</p>
<p><strong>private</strong> File saveFile;</p>
<p>/* 下载记录文件 */</p>
<p><strong>private</strong> File <span style="text-decoration: underline;">logFile</span>;</p>
<p>/* 缓存各线程最后下载的位置*/</p>
<p><strong>private</strong> Map<Integer, Integer> data = <strong>new</strong> ConcurrentHashMap<Integer, Integer>();</p>
<p>/* 每条线程下载的大小 */</p>
<p><strong>private</strong> <strong>int</strong> block;</p>
<p><strong>private</strong> String downloadUrl;//下载路径</p>
<p>/**</p>
<p>* 获取线程数</p>
<p>*/</p>
<p><strong>public</strong> <strong>int</strong> getThreadSize() {</p>
<p><strong>return</strong> threads.length;</p>
<p>}</p>
<p>/**</p>
<p>* 获取文件大小</p>
<p>* <strong>@return</strong></p>
<p>*/</p>
<p><strong>public</strong> <strong>int</strong> getFileSize() {</p>
<p><strong>return</strong> fileSize;</p>
<p>}</p>
<p>/**</p>
<p>* 累计已下载大小</p>
<p>* <strong>@param</strong> size</p>
<p>*/</p>
<p><strong>protected</strong> <strong>synchronized</strong> <strong>void</strong> append(<strong>int</strong> size) {</p>
<p>downloadSize += size;</p>
<p>}</p>
<p>/**</p>
<p>* 更新指定线程最后下载的位置</p>
<p>* <strong>@param</strong> threadId 线程id</p>
<p>* <strong>@param</strong> pos 最后下载的位置</p>
<p>*/</p>
<p><strong>protected</strong> <strong>void</strong> update(<strong>int</strong> threadId, <strong>int</strong> pos) {</p>
<p><strong>this</strong>.data.put(threadId, pos);</p>
<p>}</p>
<p>/**</p>
<p>* 保存记录文件</p>
<p>*/</p>
<p><strong>protected</strong> <strong>synchronized</strong> <strong>void</strong> saveLogFile() {</p>
<p><strong>this</strong>.fileService.update(<strong>this</strong>.downloadUrl, <strong>this</strong>.data);</p>
<p>}</p>
<p>/**</p>
<p>* 构建文件下载器</p>
<p>* <strong>@param</strong> downloadUrl 下载路径</p>
<p>* <strong>@param</strong> fileSaveDir 文件保存目录</p>
<p>* <strong>@param</strong> threadNum 下载线程数</p>
<p>*/</p>
<p><strong>public</strong> FileDownloader(Context context, String downloadUrl, File fileSaveDir, <strong>int</strong> threadNum) {</p>
<p><strong>try</strong> {</p>
<p><strong>this</strong>.context = context;</p>
<p><strong>this</strong>.downloadUrl = downloadUrl;</p>
<p>fileService = <strong>new</strong> FileService(context);</p>
<p><strong>this</strong>.url = <strong>new</strong> URL(downloadUrl);</p>
<p><strong>if</strong>(!fileSaveDir.exists()) fileSaveDir.mkdirs();</p>
<p><strong>this</strong>.threads = <strong>new</strong> DownloadThread[threadNum];</p>
<p>HttpURLConnection conn = (HttpURLConnection) url.openConnection();</p>
<p>conn.setConnectTimeout(6*1000);</p>
<p>conn.setRequestMethod("GET");</p>
<p>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, */*");</p>
<p>conn.setRequestProperty("Accept-Language", "zh-CN");</p>
<p>conn.setRequestProperty("Referer", downloadUrl);</p>
<p>conn.setRequestProperty("Charset", "UTF-8");</p>
<p>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)");</p>
<p>conn.setRequestProperty("Connection", "Keep-Alive");</p>
<p>conn.connect();</p>
<p><em>printResponseHeader</em>(conn);</p>
<p><strong>if</strong> (conn.getResponseCode()==200) {</p>
<p><strong>this</strong>.fileSize = conn.getContentLength();//根据响应获取文件大小</p>
<p><strong>if</strong> (<strong>this</strong>.fileSize <= 0) <strong>throw</strong> <strong>new</strong> RuntimeException("无法获知文件大小 ");</p>
<p>String filename = getFileName(conn);</p>
<p><strong>this</strong>.saveFile = <strong>new</strong> File(fileSaveDir, filename);/* 保存文件 */</p>
<p>Map<Integer, Integer> logdata = fileService.getData(downloadUrl);</p>
<p><strong>if</strong>(logdata.size()>0){</p>
<p>data.putAll(logdata);</p>
<p>}</p>
<p><strong>this</strong>.block = <strong>this</strong>.fileSize / <strong>this</strong>.threads.length + 1;</p>
<p><strong>if</strong>(<strong>this</strong>.data.size()==<strong>this</strong>.threads.length){</p>
<p><strong>for</strong> (<strong>int</strong> i = 0; i < <strong>this</strong>.threads.length; i++) {</p>
<p><strong>this</strong>.downloadSize += <strong>this</strong>.data.get(i+1)-(<strong>this</strong>.block * i);</p>
<p>}</p>
<p><em>print</em>("已经下载的长度"+ <strong>this</strong>.downloadSize);</p>
<p>}</p>
<p>}<strong>else</strong>{</p>
<p><strong>throw</strong> <strong>new</strong> RuntimeException("服务器响应错误 ");</p>
<p>}</p>
<p>} <strong>catch</strong> (Exception e) {</p>
<p><em>print</em>(e.toString());</p>
<p><strong>throw</strong> <strong>new</strong> RuntimeException("连接不到下载路径 ");</p>
<p>}</p>
<p>}</p>
<p>/**</p>
<p>* 获取文件名</p>
<p>*/</p>
<p><strong>private</strong> String getFileName(HttpURLConnection conn) {</p>
<p>String filename = <strong>this</strong>.url.toString().substring(<strong>this</strong>.url.toString().lastIndexOf('/') + 1);</p>
<p><strong>if</strong>(filename==<strong>null</strong> || "".equals(filename.trim())){//如果获取不到文件名称</p>
<p><strong>for</strong> (<strong>int</strong> i = 0;; i++) {</p>
<p>String mine = conn.getHeaderField(i);</p>
<p><strong>if</strong> (mine == <strong>null</strong>) <strong>break</strong>;</p>
<p><strong>if</strong>("content-disposition".equals(conn.getHeaderFieldKey(i).toLowerCase())){</p>
<p>Matcher m = Pattern.<em>compile</em>(".*filename=(.*)").matcher(mine.toLowerCase());</p>
<p><strong>if</strong>(m.find()) <strong>return</strong> m.group(1);</p>
<p>}</p>
<p>}</p>
<p>filename = UUID.<em>randomUUID</em>()+ ".tmp";//默认取一个文件名</p>
<p>}</p>
<p><strong>return</strong> filename;</p>
<p>}</p>
<p>/**</p>
<p>* 开始下载文件</p>
<p>* <strong>@param</strong> listener 监听下载数量的变化,如果不需要了解实时下载的数量,可以设置为null</p>
<p>* <strong>@return</strong> 已下载文件大小</p>
<p>* <strong>@throws</strong> Exception</p>
<p>*/</p>
<p><strong>public</strong> <strong>int</strong> download(DownloadProgressListener listener) <strong>throws</strong> Exception{</p>
<p><strong>try</strong> {</p>
<p><strong>if</strong>(<strong>this</strong>.data.size() != <strong>this</strong>.threads.length){</p>
<p><strong>this</strong>.data.clear();</p>
<p><strong>for</strong> (<strong>int</strong> i = 0; i < <strong>this</strong>.threads.length; i++) {</p>
<p><strong>this</strong>.data.put(i+1, <strong>this</strong>.block * i);</p>
<p>}</p>
<p>}</p>
<p><strong>for</strong> (<strong>int</strong> i = 0; i < <strong>this</strong>.threads.length; i++) {</p>
<p><strong>int</strong> downLength = <strong>this</strong>.data.get(i+1) - (<strong>this</strong>.block * i);</p>
<p><strong>if</strong>(downLength < <strong>this</strong>.block && <strong>this</strong>.data.get(i+1)<<strong>this</strong>.fileSize){ //该线程未完成下载时,继续下载</p>
<p>RandomAccessFile randOut = <strong>new</strong> RandomAccessFile(<strong>this</strong>.saveFile, "rw");</p>
<p><strong>if</strong>(<strong>this</strong>.fileSize>0) randOut.setLength(<strong>this</strong>.fileSize);</p>
<p>randOut.seek(<strong>this</strong>.data.get(i+1));</p>
<p><strong>this</strong>.threads[i] = <strong>new</strong> DownloadThread(<strong>this</strong>, <strong>this</strong>.url, randOut, <strong>this</strong>.block, <strong>this</strong>.data.get(i+1), i+1);</p>
<p><strong>this</strong>.threads[i].setPriority(7);</p>
<p><strong>this</strong>.threads[i].start();</p>
<p>}<strong>else</strong>{</p>
<p><strong>this</strong>.threads[i] = <strong>null</strong>;</p>
<p>}</p>
<p>}</p>
<p><strong>this</strong>.fileService.save(<strong>this</strong>.downloadUrl, <strong>this</strong>.data);</p>
<p><strong>boolean</strong> notFinish = <strong>true</strong>;//下载未完成</p>
<p><strong>while</strong> (notFinish) {// 循环判断是否下载完毕</p>
<p>Thread.<em>sleep</em>(900);</p>
<p>notFinish = <strong>false</strong>;//假定下载完成</p>
<p><strong>for</strong> (<strong>int</strong> i = 0; i < <strong>this</strong>.threads.length; i++){</p>
<p><strong>if</strong> (<strong>this</strong>.threads[i] != <strong>null</strong> && !<strong>this</strong>.threads[i].isFinish()) {</p>
<p>notFinish = <strong>true</strong>;//下载没有完成</p>
<p><strong>if</strong>(<strong>this</strong>.threads[i].getDownLength() == -1){//如果下载失败,再重新下载</p>
<p>RandomAccessFile randOut = <strong>new</strong> RandomAccessFile(<strong>this</strong>.saveFile, "rw");</p>
<p>randOut.seek(<strong>this</strong>.data.get(i+1));</p>
<p><strong>this</strong>.threads[i] = <strong>new</strong> DownloadThread(<strong>this</strong>, <strong>this</strong>.url, randOut, <strong>this</strong>.block, <strong>this</strong>.data.get(i+1), i+1);</p>
<p><strong>this</strong>.threads[i].setPriority(7);</p>
<p><strong>this</strong>.threads[i].start();</p>
<p>}</p>
<p>}</p>
<p>}</p>
<p><strong>if</strong>(listener!=<strong>null</strong>) listener.onDownloadSize(<strong>this</strong>.downloadSize);</p>
<p>}</p>
<p>fileService.delete(<strong>this</strong>.downloadUrl);</p>
<p>} <strong>catch</strong> (Exception e) {</p>
<p><em>print</em>(e.toString());</p>
<p><strong>throw</strong> <strong>new</strong> Exception("下载失败");</p>
<p>}</p>
<p><strong>return</strong> <strong>this</strong>.downloadSize;</p>
<p>}</p>
<p>/**</p>
<p>* 获取<span style="text-decoration: underline;">Http</span>响应头字段</p>
<p>* <strong>@param</strong> http</p>
<p>* <strong>@return</strong></p>
<p>*/</p>
<p><strong>public</strong> <strong>static</strong> Map<String, String> getHttpResponseHeader(HttpURLConnection http) {</p>
<p>Map<String, String> header = <strong>new</strong> LinkedHashMap<String, String>();</p>
<p><strong>for</strong> (<strong>int</strong> i = 0;; i++) {</p>
<p>String mine = http.getHeaderField(i);</p>
<p><strong>if</strong> (mine == <strong>null</strong>) <strong>break</strong>;</p>
<p>header.put(http.getHeaderFieldKey(i), mine);</p>
<p>}</p>
<p><strong>return</strong> header;</p>
<p>}</p>
<p>/**</p>
<p>* 打印<span style="text-decoration: underline;">Http</span>头字段</p>
<p>* <strong>@param</strong> http</p>
<p>*/</p>
<p><strong>public</strong> <strong>static</strong> <strong>void</strong> printResponseHeader(HttpURLConnection http){</p>
<p>Map<String, String> header = <em>getHttpResponseHeader</em>(http);</p>
<p><strong>for</strong>(Map.Entry<String, String> entry : header.entrySet()){</p>
<p>String key = entry.getKey()!=<strong>null</strong> ? entry.getKey()+ ":" : "";</p>
<p><em>print</em>(key+ entry.getValue());</p>
<p>}</p>
<p>}</p>
<p><strong>private</strong> <strong>static</strong> <strong>void</strong> print(String msg){</p>
<p>Log.<em>i</em>(<em>TAG</em>, msg);</p>
<p>}</p>
<p>}</p>
<p><strong>7.DownloadProgressListener</strong></p>
<p><strong>package</strong> com.changcheng.net.download;</p>
<p><strong>public</strong> <strong>interface</strong> DownloadProgressListener {</p>
<p><strong>public</strong> <strong>void</strong> onDownloadSize(<strong>int</strong> size);</p>
<p>}</p>
<p><strong>8.FileService</strong></p>
<p><strong>package</strong> com.changcheng.download.service;</p>
<p><strong>import</strong> java.util.HashMap;</p>
<p><strong>import</strong> java.util.Map;</p>
<p><strong>import</strong> android.content.Context;</p>
<p><strong>import</strong> android.database.Cursor;</p>
<p><strong>import</strong> android.database.sqlite.SQLiteDatabase;</p>
<p>/**</p>
<p>* 业务bean</p>
<p>*</p>
<p>*/</p>
<p><strong>public</strong> <strong>class</strong> FileService {</p>
<p><strong>private</strong> DBOpenHelper openHelper;</p>
<p><strong>public</strong> FileService(Context context) {</p>
<p>openHelper = <strong>new</strong> DBOpenHelper(context);</p>
<p>}</p>
<p>/**</p>
<p>* 获取线程最后下载位置</p>
<p>* <strong>@param</strong> path</p>
<p>* <strong>@return</strong></p>
<p>*/</p>
<p><strong>public</strong> Map<Integer, Integer> getData(String path){</p>
<p>SQLiteDatabase db = openHelper.getReadableDatabase();</p>
<p>Cursor cursor = db.rawQuery("select threadid, position from filedown where downpath=?", <strong>new</strong> String[]{path});</p>
<p>Map<Integer, Integer> data = <strong>new</strong> HashMap<Integer, Integer>();</p>
<p><strong>while</strong>(cursor.moveToNext()){</p>
<p>data.put(cursor.getInt(0), cursor.getInt(1));</p>
<p>}</p>
<p>cursor.close();</p>
<p>db.close();</p>
<p><strong>return</strong> data;</p>
<p>}</p>
<p>/**</p>
<p>* 保存下载线程初始位置</p>
<p>* <strong>@param</strong> path</p>
<p>* <strong>@param</strong> map</p>
<p>*/</p>
<p><strong>public</strong> <strong>void</strong> save(String path, Map<Integer, Integer> map){//<span style="text-decoration: underline;">int</span> <span style="text-decoration: underline;">threadid</span>, <span style="text-decoration: underline;">int</span> position</p>
<p>SQLiteDatabase db = openHelper.getWritableDatabase();</p>
<p>db.beginTransaction();</p>
<p><strong>try</strong>{</p>
<p><strong>for</strong>(Map.Entry<Integer, Integer> entry : map.entrySet()){</p>
<p>db.execSQL("insert into filedown(downpath, threadid, position) values(?,?,?)",</p>
<p><strong>new</strong> Object[]{path, entry.getKey(), entry.getValue()});</p>
<p>}</p>
<p>db.setTransactionSuccessful();</p>
<p>}<strong>finally</strong>{</p>
<p>db.endTransaction();</p>
<p>}</p>
<p>db.close();</p>
<p>}</p>
<p>/**</p>
<p>* 实时更新线程的最后下载位置</p>
<p>* <strong>@param</strong> path</p>
<p>* <strong>@param</strong> map</p>
<p>*/</p>
<p><strong>public</strong> <strong>void</strong> update(String path, Map<Integer, Integer> map){</p>
<p>SQLiteDatabase db = openHelper.getWritableDatabase();</p>
<p>db.beginTransaction();</p>
<p><strong>try</strong>{</p>
<p><strong>for</strong>(Map.Entry<Integer, Integer> entry : map.entrySet()){</p>
<p>db.execSQL("update filedown set position=? where downpath=? and threadid=?",</p>
<p><strong>new</strong> Object[]{entry.getValue(), path, entry.getKey()});</p>
<p>}</p>
<p>db.setTransactionSuccessful();</p>
<p>}<strong>finally</strong>{</p>
<p>db.endTransaction();</p>
<p>}</p>
<p>db.close();</p>
<p>}</p>
<p>/**</p>
<p>* 当文件下载完成后,清掉该文件对应的下载记录</p>
<p>* <strong>@param</strong> path</p>
<p>*/</p>
<p><strong>public</strong> <strong>void</strong> delete(String path){</p>
<p>SQLiteDatabase db = openHelper.getWritableDatabase();</p>
<p>db.execSQL("delete from filedown where downpath=?", <strong>new</strong> Object[]{path});</p>
<p>db.close();</p>
<p>}</p>
<p>}</p>
<p><strong>9.DownloadThread</strong></p>
<p><strong>package</strong> com.changcheng.net.download;</p>
<p><strong>import</strong> java.io.InputStream;</p>
<p><strong>import</strong> java.io.RandomAccessFile;</p>
<p><strong>import</strong> java.net.HttpURLConnection;</p>
<p><strong>import</strong> java.net.URL;</p>
<p><strong>import</strong> android.util.Log;</p>
<p><strong>public</strong> <strong>class</strong> DownloadThread <strong>extends</strong> Thread {</p>
<p><strong>private</strong> <strong>static</strong> <strong>final</strong> String <em>TAG</em> = "DownloadThread";</p>
<p><strong>private</strong> RandomAccessFile saveFile;</p>
<p><strong>private</strong> URL downUrl;</p>
<p><strong>private</strong> <strong>int</strong> block;</p>
<p>/* 下载开始位置 */</p>
<p><strong>private</strong> <strong>int</strong> threadId = -1;</p>
<p><strong>private</strong> <strong>int</strong> startPos;</p>
<p><strong>private</strong> <strong>int</strong> downLength;</p>
<p><strong>private</strong> <strong>boolean</strong> finish = <strong>false</strong>;</p>
<p><strong>private</strong> FileDownloader downloader;</p>
<p><strong>public</strong> DownloadThread(FileDownloader downloader, URL downUrl, RandomAccessFile saveFile, <strong>int</strong> block, <strong>int</strong> startPos, <strong>int</strong> threadId) {</p>
<p><strong>this</strong>.downUrl = downUrl;</p>
<p><strong>this</strong>.saveFile = saveFile;</p>
<p><strong>this</strong>.block = block;</p>
<p><strong>this</strong>.startPos = startPos;</p>
<p><strong>this</strong>.downloader = downloader;</p>
<p><strong>this</strong>.threadId = threadId;</p>
<p><strong>this</strong>.downLength = startPos - (block * (threadId - 1));</p>
<p>}</p>
<p>@Override</p>
<p><strong>public</strong> <strong>void</strong> run() {</p>
<p><strong>if</strong>(downLength < block){//未下载完成</p>
<p><strong>try</strong> {</p>
<p>HttpURLConnection http = (HttpURLConnection) downUrl.openConnection();</p>
<p>http.setRequestMethod("GET");</p>
<p>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, */*");</p>
<p>http.setRequestProperty("Accept-Language", "zh-CN");</p>
<p>http.setRequestProperty("Referer", downUrl.toString());</p>
<p>http.setRequestProperty("Charset", "UTF-8");</p>
<p>http.setRequestProperty("Range", "bytes=" + <strong>this</strong>.startPos + "-");</p>
<p>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)");</p>
<p>http.setRequestProperty("Connection", "Keep-Alive");</p>
<p>InputStream inStream = http.getInputStream();</p>
<p><strong>int</strong> max = block>1024 ? 1024 : (block>10 ? 10 : 1);</p>
<p><strong>byte</strong>[] buffer = <strong>new</strong> <strong>byte</strong>[max];</p>
<p><strong>int</strong> offset = 0;</p>
<p><em>print</em>("线程 " + <strong>this</strong>.threadId + "从位置"+ <strong>this</strong>.startPos+ "开始下载 ");</p>
<p><strong>while</strong> (downLength < block && (offset = inStream.read(buffer, 0, max)) != -1) {</p>
<p>saveFile.write(buffer, 0, offset);</p>
<p>downLength += offset;</p>
<p>downloader.update(<strong>this</strong>.threadId, block * (threadId - 1) + downLength);</p>
<p>downloader.saveLogFile();</p>
<p>downloader.append(offset);</p>
<p><strong>int</strong> spare = block-downLength;//求剩下的字节数</p>
<p><strong>if</strong>(spare < max) max = (<strong>int</strong>) spare;</p>
<p>}</p>
<p>saveFile.close();</p>
<p>inStream.close();</p>
<p><em>print</em>("线程 " + <strong>this</strong>.threadId + "完成下载 ");</p>
<p><strong>this</strong>.finish = <strong>true</strong>;</p>
<p><strong>this</strong>.interrupt();</p>
<p>} <strong>catch</strong> (Exception e) {</p>
<p><strong>this</strong>.downLength = -1;</p>
<p><em>print</em>("线程"+ <strong>this</strong>.threadId+ ":"+ e);</p>
<p>}</p>
<p>}</p>
<p>}</p>
<p><strong>private</strong> <strong>static</strong> <strong>void</strong> print(String msg){</p>
<p>Log.<em>i</em>(<em>TAG</em>, msg);</p>
<p>}</p>
<p>/**</p>
<p>* 下载是否完成</p>
<p>* <strong>@return</strong></p>
<p>*/</p>
<p><strong>public</strong> <strong>boolean</strong> isFinish() {</p>
<p><strong>return</strong> finish;</p>
<p>}</p>
<p>/**</p>
<p>* 已经下载的内容大小</p>
<p>* <strong>@return</strong> 如果返回值为-1,代表下载失败</p>
<p>*/</p>
<p><strong>public</strong> <strong>long</strong> getDownLength() {</p>
<p><strong>return</strong> downLength;</p>
<p>}</p>
<p>}</p>
<p><strong>11.DBOpenHelper</strong></p>
<p><strong>package</strong> com.changcheng.download.service;</p>
<p><strong>import</strong> android.content.Context;</p>
<p><strong>import</strong> android.database.sqlite.SQLiteDatabase;</p>
<p><strong>import</strong> android.database.sqlite.SQLiteOpenHelper;</p>
<p><strong>public</strong> <strong>class</strong> DBOpenHelper <strong>extends</strong> SQLiteOpenHelper {</p>
<p><strong>private</strong> <strong>static</strong> <strong>final</strong> String <em>DBNAME</em> = "download.db";</p>
<p><strong>private</strong> <strong>static</strong> <strong>final</strong> <strong>int</strong> <em>VERSION</em> = 2;</p>
<p><strong>public</strong> DBOpenHelper(Context context) {</p>
<p><strong>super</strong>(context, <em>DBNAME</em>, <strong>null</strong>, <em>VERSION</em>);</p>
<p>}</p>
<p>@Override</p>
<p><strong>public</strong> <strong>void</strong> onCreate(SQLiteDatabase db) {</p>
<p>db.execSQL("CREATE TABLE IF NOT EXISTS filedown (id integer primary key autoincrement, downpath varchar(100), threadid INTEGER, position INTEGER)");</p>
<p>}</p>
<p>@Override</p>
<p><strong>public</strong> <strong>void</strong> onUpgrade(SQLiteDatabase db, <strong>int</strong> oldVersion, <strong>int</strong> newVersion) {</p>
<p>db.execSQL("DROP TABLE IF EXISTS filedown");</p>
<p>onCreate(db);</p>
<p>}</p>
<p>}</p>
<p>结束!</p>

转载地址:http://ucaji.baihongyu.com/

你可能感兴趣的文章
49. Group Anagrams (String, Map)
查看>>
139. Word Break (DP)
查看>>
23. Merge k Sorted Lists (Divide and conquer, Linked List) 以及java匿名内部类
查看>>
Tensorflow入门资料
查看>>
剑指_用两个栈实现队列
查看>>
剑指_顺时针打印矩阵
查看>>
剑指_栈的压入弹出序列
查看>>
剑指_复杂链表的复制
查看>>
服务器普通用户(非管理员账户)在自己目录下安装TensorFlow
查看>>
星环后台研发实习面经
查看>>
大数相乘不能用自带大数类型
查看>>
字节跳动后端开发一面
查看>>
CentOS Tensorflow 基础环境配置
查看>>
centOS7安装FTP
查看>>
FTP的命令
查看>>
CentOS操作系统下安装yum的方法
查看>>
ping 报name or service not known
查看>>
FTP 常见问题
查看>>
zookeeper单机集群安装
查看>>
do_generic_file_read()函数
查看>>