Android 断点下载Demo

本文代码例子:http://pan.baidu.com/s/1imeY1

DownloadService.java

package com.tianlei;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.text.format.DateFormat;
import android.util.Log;
import android.widget.Toast;

public class DownloadService extends Service implements DownLoadThreadListener
{
    
    private Mybindler mybindler;

    private Messenger messenger;
    
    private DownLoadThread dThread ;
    
    @Override
    public boolean onUnbind(Intent intent)
    {
        Log.v("MaidlService", "onUnbind");
        
        return super.onUnbind(intent);
    }

    public IBinder onBind(Intent intent)
    {

        Log.v("MaidlService", "onBind");
        return mybindler = new Mybindler();
    }

    @Override
    public void onDestroy()
    {
        Log.v("MaidlService", "onDestroy");
        super.onDestroy();
    }

    @Override
    public void onCreate()
    {
        // TODO Auto-generated method stub
        super.onCreate();
        Log.v("MaidlService", "onCreate");
    }

    public void onStart(Intent intent, int startId)
    {

        super.onStart(intent, startId);
        switch (intent.getFlags())
        {
            case 12:
                Log.v("MaidlService", "11111111111222222222222");
                break;

        }

    }

   
    public void setHandler(Handler handler)
    {
        messenger = new Messenger(handler);
    }
    public class Mybindler extends Binder
    {
        public void speak()
        {
            Log.v("MaidlService", "Mybindler  speak");
        }

        public DownloadService getService()
        {
            return DownloadService.this;
        }
    }
    private boolean isHavingSDC()
    {
        // 增加是否有SD卡的判断,如果没有SD卡,则对任务进行操作前给用户提示
        // 以防用户点击继续下载,客户端会出异常
        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
        {
            Toast.makeText(this,
                "没有SDCARD",
                Toast.LENGTH_SHORT).show();
            return false;
        }
        return true;
    }
    public void startDownloadMOTA(String uri)
    {
        isHavingSDC();
        if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
        {
            dThread = new DownLoadThread(uri,this);
            dThread.start();
        }
        else 
        {
            Toast.makeText(this,
                "没有SDCARD",
                Toast.LENGTH_SHORT).show();
        }
    }
    public void changeDownsuspendAndStart()
    {
          dThread.ChangeNeedRun();
    }
    public void onDownFinished(boolean result, byte[] b, Object other)
    {
        Log.v("MaidlService", "  onDownFinished  result " + result)  ;
      
        
        if(result)
        {
            Message message = new Message();
            message.what = 1001;
            try
            {
                messenger.send(message);
            }
            catch (RemoteException e)
            {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
           
        }
      
    }

    public void onError(int errorCode)
    {
        Log.v("MaidlService", "  onDownFinished  result " + errorCode)  ;
    }

    public void onProcess(int percent, long usedTime)
    {
        Log.v("MaidlService", "  onProcess  percent " + percent)  ;
        
        Log.v("MaidlService", "  onProcess  usedTime " + usedTime)  ;
        
        Message message = new Message();
        message.what = 1000;
        Bundle bundle = new Bundle();
        bundle.putInt("percent", percent);
        message.setData(bundle);
//        bundle.putString("usedTime", usedTime);
        
        try
        {
            messenger.send(message);
        }
        catch (RemoteException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
    

}

DownLoadThread.java

package com.tianlei;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.os.Environment;
import android.util.Log;
public class DownLoadThread extends Thread 
{
  
    public static final String TAG = "DownLoadThread";

    /**
     * 文件临时的名字
     */
    private String fileName;

  
    /**
     * 写文件使用
     */
    private String tempFileName;

    /**
     * 防止来回改变下载地址,使用该临时变量来下�?
     */
    private String tempUrl;

 
    private long startTime = System.currentTimeMillis();
    
    private  long percent = 0;

    /**
     * 此次下载的结束时
     */
    private long endTime = 0;

    /**
     * 下载线程监听
     * 
     */
    private DownLoadThreadListener listener;

    private Object other;

    /**
     * 根据HTTP消息头中的contentType字段映射为文件扩展名
     */
    private String postfixfromHTTP;

    private long usedTime = 0;

    /**
     * 暂停 时候使用的标志位
     */
    private boolean isNeedRun = true;

  
    public boolean isNeedRun()
    {
        return isNeedRun;
    }

    public void ChangeNeedRun()
    {
        this.isNeedRun = !isNeedRun;
    }

    public DownLoadThread(String url, DownLoadThreadListener listener)
    {
     
        this.tempUrl = url;
      
//        this.fileName = startTime+"_";
        this.fileName = "1327104616137_";
        // 如果下载路径不存在,新建该下载路径
        File file = new File(Environment.getExternalStorageDirectory()+"/mota/");
        if (!file.exists())
        {
            try
            {
                // Log.d(TAG, file.getName().toString());
                boolean isSuccess = file.mkdirs();
                if (!isSuccess)
                {
                    Log.d(TAG, "mkdirs failed");
                }
            }
            catch (Exception e)
            {
                Log.e(TAG, e.toString());
            }
        }
        // 该临时变量用于写文件使用
        this.tempFileName =file.getAbsolutePath()+"/" + this.fileName + ".mc";
        Log.v("MaidlService", "tempFileName:: "+tempFileName);
        this.listener = listener;
      
    }

    /**
     * run()
     */
    public void run()
    {
        this.setPriority(Thread.NORM_PRIORITY);
        int result = 0;
        // 纪录本次下载的开始时间,用于统计下载使用时间�?
        startTime = System.currentTimeMillis();
        // 因为下载方式的不确定,在这做控制
        while (true)
        {
            result = download();

            // 只有大于0需要重新下载,0为下载成功,小于0为下载异常
            if (result > 0)
            {
                continue;
            }
            else
            {
                //异常和成功都退出
                break;
            }
        }
    }

    /**
     * 实际下载方法
     */
    private int download()
    {
        if (!isNeedRun)
        {
            return 1;
        }
        Log.d(TAG, "download begin");
        // 运行结果
        boolean isSuccess = false;

        // 下载为字节时的缓冲区
        byte[] resultData = new byte[0];

        File file = null;
        FileOutputStream outputStream = null;
        InputStream is = null;
        HttpURLConnection conn = null;
        URL myURL = null;

        // 使用临时变量进行计算,效率高,此次任务之前已使用的时间
        long tempUsedTime =usedTime ;
        // 内容大小,必须有大小才能返回下载进度
        long remainSize = -1;
        try
        {
            Log.d(TAG, "download() URL = " + tempUrl);
            // 下载地址为空,直接报错
            if (tempUrl != null && "".equals(tempUrl.trim()))
            {
                listener.onDownFinished(false, null, null);
                return -1;
            }
            myURL = new URL(tempUrl);
            conn = (HttpURLConnection) myURL.openConnection();
            conn.setDoInput(true);
            conn.setDoOutput(true);
            conn.setRequestProperty("Accept", "*/*");
            // 设置超时时间
            conn.setConnectTimeout(60000);
            conn.setReadTimeout(60000);
            // 已下载文件大�?
            long fileSize = 0;

            // 如果是下载到文件,需要打目录文件
                file = new File(tempFileName);
                //文件存在则 认为是上次是暂停的下载,这时候要进行续传操作
                if (file.exists())
                {
                    fileSize = file.length();
//                    Log.d(TAG, taskInfo.taskID + "  file.length() = " + fileSize);
                    outputStream = new FileOutputStream(file, true);
                   //特为了是断点续传的
                    conn.setRequestProperty("RANGE", "bytes=" + fileSize + "-");
                }
                else
                {
                    boolean bo = file.createNewFile();
                    if (!bo)
                    {
                        Log.d(TAG, "create new file false");
                    }
                    outputStream = new FileOutputStream(file, true);
                }
            int code = conn.getResponseCode();
            Log.d(TAG, "getResponseCode() = " + code);
            if (code != 200 && code != 206)
            {
                return errorCode(conn, code);
            }
            is = conn.getInputStream();
            // 内容大小,必须有大小才能返回下载进度
            remainSize = conn.getContentLength();
           
           //content_disposition  内容是文件名字
           //Accepte_length 是文件长度大小 字节
            Log.d(TAG, "conn.getContentLength() = " + conn.getContentLength());
            // 如果实在没有返回内容大小,通过Content-Range字段来获得文件大小
            getSize(conn, remainSize);
           
            // 已经下载的文件长度
            long index = fileSize;
            // 只在第一次下载文件时求出文件总
            if (remainSize > 0)
            {
               percent = fileSize * 100 / remainSize;
               //断点续传的时候 返回的remainsize 是剩余的大小 即是总大小减去上次下载大小
               fileSize = remainSize + fileSize;
            }
            // 下载进度
            long tmpPercent = 0;
            // 先提前告诉外面一下,刷新下显�?
            if (listener != null)
            {
                listener.onProcess((int)percent,usedTime);
            }
            // 下载缓冲区大小 可以根据实际情况在设置
            int chunkSize = 512;
          
            // 每次下载的缓冲区
            byte[] data = new byte[chunkSize];
            int length = 0;
            byte[] tempData;
            // 设置临时变量在循环里使用会比较快�?
           long tempFileSize = fileSize;

            do
            {
                // 读取内容
                length = is.read(data, 0, chunkSize);
                if (length != -1)
                {
                    // 下载到文件的处理
                  
                        // 写入到文件
                        outputStream.write(data, 0, length);
                        outputStream.flush();
                        // 必须知道报文大小才能设置进度
                        if (tempFileSize > 0)
                        {
                            index += length;
                            // 更新进度
                            tmpPercent = index * 100 / tempFileSize;
                            if (tmpPercent != percent)
                            {
                                listener.onProcess((int)tmpPercent,usedTime);
                               
                            }
                            percent = tmpPercent;
                        }
                }
            }
             while (length != -1&& isNeedRun );
            isSuccess = true;

            //  如果是客户端暂停操作 则不反回结果
            if (isNeedRun && getResult(tempUsedTime, isSuccess, resultData, file) == -1)
            {
                return -1;
            }
            else if(!isNeedRun)
            {
                return 1;
            }

        }
        catch (Exception e)
        {
            
        }
        finally
        {
            file = null;
            try
            {
                if (outputStream != null)
                {
                    outputStream.close();
                    outputStream = null;
                }
                if (is != null)
                {
                    is.close();
                    is = null;
                }
                if (conn != null)
                {
                    conn.disconnect();
                    conn = null;
                }
            }
            catch (Exception e)
            {
                Log.e(TAG, e.getMessage());
            }
        }
        return 0;
    }

    private int getResult(long tempUsedTime, boolean isSuccess, byte[] resultData, File file)
    {
        Log.d(TAG, "getResult");
        endTime = System.currentTimeMillis();
        usedTime = tempUsedTime + (endTime - startTime);
      
            // 判断下到文件是否是百分之百,如果不是报错(服务器中断服务�?
            if (percent != 100)
            {
                Log.d(TAG, "percent 111" + percent);
                listener.onDownFinished(false, null, null);
                return -1;
            }
            else if(percent == 100)
            {
                listener.onDownFinished(true, null, null);
            }
            // 判断临时文件是否存在,如果不存在下载失败。
            File f = new File(tempFileName);
            if (!f.exists())
            {
                percent = 0;
                Log.d(TAG, "percent 222" + percent);
                listener.onDownFinished(false, null, null);
                return -1;
            }
 
         reName(fileName, file);
        return 0;
    }

    /**
     * 错误返回码处理
     * 
     * @param conn HttpURLConnection
     * @param code int
     * @return int
     */
    private int errorCode(HttpURLConnection conn, int code)
    {
       
        endTime = System.currentTimeMillis();
        long time = endTime - startTime;
        usedTime += time;
        if (listener != null)
        {
            listener.onDownFinished(false, null, null);
        }
        // 其它返回码,先暂都做失败处理
        return -1;
    }

    /**
     * 如果实在没有返回内容大小,�?过Content-Range字段来获得文件大�?
     * 
     * @param conn HttpURLConnection
     * @param remainSize long
     */
    private void getSize(HttpURLConnection conn, long remainSize)
    {
        // 如果实在没有返回内容大小,可同过Content-Range字段来获得文件大小
        if (remainSize < 0)
        {
            // 没有返回content-length,取Content-Range
            String contentRange = conn.getHeaderField("Content-Range");
            if (null != contentRange)
            {
                // 有Content-Range,取长度
                // Content-Range: bytes 1230319-3098402/3098403
                int index = contentRange.indexOf('/');
                if (index != -1)
                {
                    String length = contentRange.substring(index + 1, contentRange.length());
                    Log.e(TAG, "length = " + length);
                    try
                    {
                        remainSize = Long.parseLong(length);
                    }
                    catch (Exception e)
                    {
                        Log.e(TAG, e.toString());
                    }
                }
            }
        }
    }

    /**
     * 下载完成后重命名
     * 
     * @param fileName1 名称
     * @param file 正式名称
     */
    private synchronized void reName(String fileName1, File file)
    {
        File reNameFile = new File(Environment.getExternalStorageDirectory()+"/mota/" + "62a9ead9jw1dp8r1o2cwdj.jpg");
        reNameFile.exists();
        boolean bo = file.renameTo(reNameFile);
    }
  
}

DownLoadThreadListener.java

package com.tianlei;
public interface DownLoadThreadListener
{
    /**
     * onDownFinished
     * @param result boolean
     * @param taskID int
     * @param b byte[]
     * @param other Object
     */
    void onDownFinished(boolean result, byte[] b, Object other);
    
    /**
     * onProcess
     * @param taskID int
     * @param percent int
     * @param usedTime long
     */
    void onProcess(int percent, long usedTime);
    
    void onError(int errorCode);
}

TestANDROID.java

package com.tianlei;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class TestANDROID extends Activity implements OnClickListener
{
    /** Called when the activity is first created. */

    private Handler handler = new Handler()
    {
        @Override
        public void handleMessage(Message msg)
        {
            switch (msg.what)
            {
                 case 1000:
                    
                    int percent = msg.getData().getInt("percent");
                    Log.v("percent", "  onProcess  percent " + percent)  ;
                    textView.setText(String.valueOf(percent));
                    
                    break;
                  case 1001:
                 Toast.makeText(TestANDROID.this, "下载完成", Toast.LENGTH_LONG).show();
                    break;
                default:
                  
                    break;
            }
            super.handleMessage(msg);
        }
    };

    private Button bindServiceButton;

    private Button unbindService;

    private Button downloadMota;

    /**
     * 下载进度显示
     */
    private TextView textView;

    /**
     * 开始 暂停
     */
    private Button startandsuspend;

    private DownloadService.Mybindler mybindler;

    /**
     * 后天下载服务
     */
    private DownloadService downloadService;

    private ServiceConnection serConnection = new ServiceConnection()
    {

        public void onServiceConnected(ComponentName name, IBinder service)
        {

            mybindler = (DownloadService.Mybindler) service;
            downloadService = mybindler.getService();
            downloadService.setHandler(handler);
            Log.v("MaidlService", "onServiceConnected");
        }

        public void onServiceDisconnected(ComponentName name)
        {

            mybindler = null;

            Log.v("MaidlService", "onServiceDisconnected");
        }

    };

    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        bindServiceButton = (Button) findViewById(R.id.startservice);
        unbindService = (Button) findViewById(R.id.stopservice);
        downloadMota = (Button) findViewById(R.id.downloadMOTA);
        startandsuspend = (Button) findViewById(R.id.suspend);

        bindServiceButton.setOnClickListener(this);
        unbindService.setOnClickListener(this);
        downloadMota.setOnClickListener(this);
        startandsuspend.setOnClickListener(this);

        textView = (TextView) findViewById(R.id.testview01);

    }

    public void onClick(View v)
    {
        switch (v.getId())
        {
            case R.id.startservice:
                Intent intent = new Intent(TestANDROID.this, DownloadService.class);
                // intent.setAction("com.tianlei.MaidlService");
                // intent.addFlags(12);
                intent.addFlags(12);
                bindService(intent, serConnection, Context.BIND_AUTO_CREATE);
                // Intent intent = new Intent(TestANDROID.this, MaidlService.class);
                // intent.addFlags(12);
                startService(intent);
                break;
            case R.id.stopservice:
                unbindService(serConnection);
                Intent intent2 = new Intent("com.tianlei.MaidlService");
                stopService(intent2);

                break;
            case R.id.downloadMOTA:
                mybindler.speak();
                String urlString = "http://files.cnblogs.com/hanyonglu/AndroidFile/MulThreadDownloader.rar";
                downloadService.startDownloadMOTA(urlString);

                break;
            case R.id.suspend:
                downloadService.changeDownsuspendAndStart();
                break;

            default:
                break;
        }

    }

}






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杜甲同学

感谢打赏,我会继续努力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值