本文代码例子: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;
}
}
}