当前位置:操作系统 > 安卓/Android >>

android 多线程断点下载实现

原理:

在发出请求协议的时候加上参数Range,可以定位下载文件的开始位置和结束位置,下载数据的时候,记录每条线程已经下载的数据量和线程id,现在文件的url作为下载文件的一个标识,对下载信息做持久化,可以存入数据库,文件,网络等,本例一sqlite数据库做的,当开始下载文件的时候,检查数据库没有没这个下载地址的下载信息,没有的话,就开始下载,如果有这个下载地址的信息,就计算他的线程数,如果之前下载的线程数与当前下载任务的线程数一致的话.重新开始下载.如果有下载信息,并且线程数一样的话,发送请求的时候可以使用上面的Range参数,他的对应值就是开始下载的位置和结束下载的位置,如:conn.setRequestProperty("Range", "bytes=" + startPosition + "-" + endPosition);如果想尽快的读懂下面的代码,最好知道实现本例的原理,下面代码实现

主页面activity,注意主线程(ui线程)的画面数据有子线程提供的实现

[java]
package com.itcast.activity; 
 
import java.io.File; 
import java.io.IOException; 
 
import android.app.Activity; 
import android.os.Bundle; 
import android.os.Environment; 
import android.os.Handler; 
import android.os.Message; 
import android.view.Menu; 
import android.view.View; 
import android.widget.Button; 
import android.widget.EditText; 
import android.widget.ProgressBar; 
import android.widget.TextView; 
import android.widget.Toast; 
 
import com.itcast.download.DownloadPropressListener; 
import com.itcast.download.FileDownLoader; 
import com.itcast.download.R; 
 
public class MainActivity extends Activity { 
    private EditText downloadPath; 
    private ProgressBar progressBar; 
    private TextView resultText; 
    private Button download; 
    private Button stop; 
    private FileDownLoader downLoader; 
    // 当handler被创建时,会关联到创建他的当前线程的消息队列,该类用以往消息队列发送消息,消息由当前的线程内部进行处理, 
    // 消息的运行代码是运行在ui线程里的,r如果想让handler在处理消息的时候执行自己想要的代码,可以重写它的handleMessage方法 
    private Handler handler = new Handler() { 
        @Override 
        public void handleMessage(Message msg) { 
            switch (msg.what) {// 判断消息id 
            case 1:// 消息代號 
                int downloadSize = msg.getData().getInt("downloadSize");// 获取当前所有线程下载的数据量 
                progressBar.setProgress(downloadSize);// 设置进度条的进度值 
                float num = (float) progressBar.getProgress() / (float) progressBar.getMax();// 下载数据量的百分比 
                int result = (int) (num * 100); 
                resultText.setText(result + "%");// 用TextView显示下载的百分比 
                if (progressBar.getProgress() == progressBar.getMax()) {// 如果现在完成,提示下载完成 
                    Toast.makeText(MainActivity.this, R.string.success, Toast.LENGTH_SHORT).show(); 
                    download.setEnabled(true);// 下载完成后下载按钮处于活动妆太浓 
                    stop.setEnabled(false);// 停止按钮不能被点击,变灰 
                } 
                break; 
            case -1:// 消息id-1 
                Toast.makeText(MainActivity.this, R.string.error, Toast.LENGTH_SHORT).show();// 如果下载过程中出现错误 
                break; 
            } 
 
        } 
    }; 
 
    @Override 
    public void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main); 
        downloadPath = (EditText) findViewById(R.id.urlPath); 
        resultText = (TextView) findViewById(R.id.result); 
        progressBar = (ProgressBar) findViewById(R.id.progress); 
        download = (Button) findViewById(R.id.down); 
        stop = (Button) findViewById(R.id.stop); 
        ButtonClickListener buttonListener = new ButtonClickListener();// 点击事件的处理类 
        download.setOnClickListener(buttonListener); 
        stop.setOnClickListener(buttonListener); 
    } 
 
    @Override 
    public boolean onCreateOptionsMenu(Menu menu) { 
        getMenuInflater().inflate(R.menu.activity_main, menu); 
        return true; 
    } 
 
    /**
     * 主线程(ui线程) Responsive(应用程序响应)<br>
     * <br>
     * 1,在正常情况下,Android程序会在一条单线程里运行。如果Activity要处理一件比较耗时的工作,应该交给子线程完成,
     * 否侧会因为主线程长时间等待被阻塞 , 后面的用户输入事件因没能在5秒内响应,导致应用出现ANR对话框(ANR(Application No
     * Response) 对话框) <br>
     * <br>
     * 2,若果用了子线程,该方法编译没错,但是运行起来会有错,因为这个方法是执行的代码是另外开的子线程中,所有的任务都在子线程中完成 ,
     * 这个代码只是走了一遍代码, 可能不到一秒就跑完了,但是真正的下载任务还在子线程中进行,根据java规范,一旦方法运行完了,那么他的属性也就消失了,
     * 这样子线程中就没法在使用创建线程的方法传入的参数了,为了方便使用,应该把两个参数设置为final,这样传递的就是在是对象的引用了,而是直接传值<br>
     * <br>
     * 3,因为用了子线程,而主线程(ui线程)中的控件只能由主线程来控制,如实用

补充:移动开发 , Android ,
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,