Android-Service系列之断点续传下载

PS:本文代码来自慕课网同名课程Android-Service系列之断点续传下载,这里只是为了Mark下来方便自己以后使用。

首先是布局文件,因为只是demo,所以使用图形化工具生成。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.tk.download.MainActivity" >

    <TextView
        android:id="@+id/tv"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

    <ProgressBar
        android:id="@+id/pb_progress"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/tv"
        android:layout_marginTop="36dp"
        android:layout_toRightOf="@+id/tv" />

    <Button
        android:id="@+id/btn_start"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/pb_progress"
        android:layout_marginTop="31dp"
        android:layout_alignParentLeft="true"
       
        android:text="start" />

    <Button
        android:id="@+id/btn_stop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBaseline="@+id/btn_start"
        android:layout_alignBottom="@+id/btn_start"
        android:layout_alignParentRight="true"
        android:text="stop" />

</RelativeLayout>
MainActivity.java

public class MainActivity extends Activity implements OnClickListener{
    private TextView mTvFileName;
    private ProgressBar mProgressBar;
    private Button btnStart;
    private Button btnStop;
    private final FileInfo fileInfo = new FileInfo(0,"http://down.qingkan.net/32/32033.txt","翡翠之塔.txt",0,0);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        init();
        
    }
    
    private void init(){
    	mTvFileName = (TextView) findViewById(R.id.tv);
    	
    	mProgressBar = (ProgressBar) findViewById(R.id.pb_progress);
    	btnStart = (Button) findViewById(R.id.btn_start);
    	btnStop = (Button) findViewById(R.id.btn_stop);
    	mProgressBar.setMax(100);
        btnStart.setOnClickListener(this);
        btnStop.setOnClickListener(this);
        
        IntentFilter filter = new IntentFilter();
        filter.addAction(DownloadService.ACTION_UPDATE);
        registerReceiver(mReceiver,filter);
    }
    
    @Override
    protected void onDestroy(){
    	super.onDestroy();
    	unregisterReceiver(mReceiver);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
    /**
     * 更新ui的广播接收器
     */
    BroadcastReceiver mReceiver = new BroadcastReceiver(){

		@Override
		public void onReceive(Context context, Intent intent) {
			if(DownloadService.ACTION_UPDATE.equals(intent.getAction())){
				int finished = intent.getIntExtra("finished",0);
				mProgressBar.setProgress(finished);
			}
			
		}
    	
    };

	@Override
	public void onClick(View v) {
		
		switch(v.getId()){
		case R.id.btn_start:
			Intent intent = new Intent(MainActivity.this, DownloadService.class);
			intent.setAction( DownloadService.ACTION_START);
			intent.putExtra("fileInfo", fileInfo);
			startService(intent);
			break;
		case R.id.btn_stop:
			Intent mIntent = new Intent(MainActivity.this, DownloadService.class);
			mIntent.setAction( DownloadService.ACTION_STOP);
			mIntent.putExtra("fileInfo", fileInfo);
			startService(mIntent);
			break;	
		}
	}
}
数据库相关代码

DBHelper.java

/**
 * 创建数据库语句
 * 
 *
 */
public class DBHelper extends SQLiteOpenHelper {
	private static final String DB_NAME = "download.db";
    private static final int VERSION = 1;
    private static final String SQL_CREATE = "create table thread_info(_id integer primary key autoincrement," +
    		"thread_id integer,url text,start integer,end integer,finished integer)";
    private static final String SQL_DROP = "drop table if exists thread_info";
	public DBHelper(Context context) {
		super(context, DB_NAME, null, VERSION);
		// TODO Auto-generated constructor stub
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL(SQL_CREATE);

	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		db.execSQL(SQL_DROP);
		db.execSQL(SQL_CREATE);

	}

}
DAO相关

public interface ThreadDAO {
	/**
	 * 插入线程信息
	 * 
	 * @param threadInfo
	 */
	public void insertThread(ThreadInfo threadInfo);

	/**
	 * 删除线程
	 * 
	 * @param url
	 * @param threadId
	 */
	public void deleteThread(String url, int threadId);

	/**
	 * 更新线程
	 * 
	 * @param url
	 * @param threadId
	 */
	public void updateThread(String url, int threadId,int finished);

	/**
	 * 查找线程
	 * 
	 * @param url
	 * @return
	 */
	public List<ThreadInfo> getThreads(String url);
	/**
	 * 线程消息是否存在
	 * @param url
	 * @param threadId
	 * @return
	 */
	public boolean isExtists(String url,int threadId);
}
public class ThreadDAOImpl implements ThreadDAO{
private DBHelper mHelper = null;
public ThreadDAOImpl(Context context){
	mHelper = new DBHelper(context);
}
	@Override
	public void insertThread(ThreadInfo threadInfo) {
		SQLiteDatabase db = mHelper.getWritableDatabase();
		db.execSQL("insert into thread_info(thread_id,url,start,end,finished) values(?,?,?,?,?)",
				new Object[]{threadInfo.getId(),threadInfo.getUrl(),threadInfo.getStart(),threadInfo.getEnd(),threadInfo.getEnd()});
		db.close();
	}
	
	@Override
	public void deleteThread(String url, int threadId) {
		SQLiteDatabase db = mHelper.getWritableDatabase();
		db.execSQL("delete from thread_info where url=? and thread_id=?",
				new Object[]{url,threadId});
		db.close();
		
	}

	@Override
	public void updateThread(String url, int threadId,int finished) {
		SQLiteDatabase db = mHelper.getWritableDatabase();
		db.execSQL("update thread_info set finished=? where url=? and thread_id=?",
				new Object[]{finished,url,threadId});
		db.close();
		
	}

	@Override
	public List<ThreadInfo> getThreads(String url) {
		List<ThreadInfo> list = new ArrayList<ThreadInfo>();
		SQLiteDatabase db = mHelper.getWritableDatabase();
		Cursor cursor = db.rawQuery("select * from thread_info where url=?", new String[]{url});
		while(cursor.moveToNext()){
			ThreadInfo thread = new ThreadInfo();
			thread.setId(cursor.getInt(cursor.getColumnIndex("thread_id")));
			thread.setUrl(cursor.getString(cursor.getColumnIndex("url")));
			thread.setStart(cursor.getInt(cursor.getColumnIndex("start")));
			thread.setEnd(cursor.getInt(cursor.getColumnIndex("end")));
			thread.setFinished(cursor.getInt(cursor.getColumnIndex("finished")));
		    list.add(thread);
		}
		cursor.close();
		db.close();
		return list;
	}

	@Override
	public boolean isExtists(String url, int threadId) {
		
		SQLiteDatabase db = mHelper.getWritableDatabase();
		Cursor cursor = db.rawQuery("select * from thread_info where url=? and thread_id=?", new String[]{url,threadId+""});
		boolean extists = cursor.moveToNext();
		cursor.close();
		db.close();
	
		return extists;
	}
}
实体类两个

public class ThreadInfo implements Serializable{
	private int id;
	private String url;
	private int start;
	private int end;//文件长度
	private int finished;//任务中断时的进度

	public ThreadInfo() {
		super();
	}

	public ThreadInfo(int id, String url, int start, int end, int finished) {
		super();
		this.id = id;
		this.url = url;
		this.start = start;
		this.end = end;
		this.finished = finished;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public int getStart() {
		return start;
	}

	public void setStart(int start) {
		this.start = start;
	}

	public int getEnd() {
		return end;
	}

	public void setEnd(int end) {
		this.end = end;
	}

	public int getFinished() {
		return finished;
	}

	public void setFinished(int finished) {
		this.finished = finished;
	}

	@Override
	public String toString() {
		return "ThreadInfo [id=" + id + ", url=" + url + ", start=" + start
				+ ", end=" + end + ", finished=" + finished + "]";
	}

}
public class FileInfo implements Serializable{
	private int id;
	private String url;
	private String fileName;
	private int length;
	private int finished;

	
	public FileInfo() {
		
	}

	public FileInfo(int id, String url, String fileName, int length,
			int finished) {
		super();
		this.id = id;
		this.url = url;
		this.fileName = fileName;
		this.length = length;
		this.finished = finished;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getUrl() {
		return url;
	}

	public void setUrl(String url) {
		this.url = url;
	}

	public String getFileName() {
		return fileName;
	}

	public void setFileName(String fileName) {
		this.fileName = fileName;
	}

	public int getLength() {
		return length;
	}

	public void setLength(int length) {
		this.length = length;
	}

	public int getFinished() {
		return finished;
	}

	public void setFinished(int finished) {
		this.finished = finished;
	}

	@Override
	public String toString() {
		return "FileInfo [id=" + id + ", url=" + url + ", fileName=" + fileName
				+ ", length=" + length + ", finished=" + finished + "]";
	}

}
下载核心类

public class DownloadService extends Service {
    private final String tag = getClass().getName();
	public static final String ACTION_START = "ACTION_START";
	public static final String ACTION_STOP = "ACTION_STOP";
	public static final String ACTION_UPDATE = "ACTION_UPDATE";
	public static final String DOWNLOAD_PATH = 
			Environment.getExternalStorageDirectory().getAbsolutePath()+"/mDownLoads/";
	public static final int MSG_INIT = 0;
	
	private DownloadTask task = null;
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		//获得Activity传来的参数
		if(ACTION_START.equals(intent.getAction())){
			FileInfo fileInfo = (FileInfo)intent.getSerializableExtra("fileInfo");
			Log.i(tag,"Start+"+fileInfo.toString());
			new InitThread(fileInfo).start();
		}else if(ACTION_STOP.equals(intent.getAction())){
			FileInfo fileInfo = (FileInfo)intent.getSerializableExtra("fileInfo");
			Log.i(tag,"Stop+"+fileInfo.toString());
			if(task!=null){
				task.setPause(true);
			}
		}
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	
	Handler mHandler = new Handler(){
		public void handleMessage(Message msg){
			switch(msg.what){
			case MSG_INIT:
				FileInfo fileInfo = (FileInfo)msg.obj;
				Log.i(tag,"INIT"+fileInfo.toString());
				//启动下载任务
				task = new DownloadTask(DownloadService.this,fileInfo);
				task.download();
				break;
			}
		}
	};
	
    /**
     * 初始化子线程
     */
class InitThread extends Thread{
	private FileInfo fileInfo;

	public InitThread(FileInfo fileInfo) {
		super();
		this.fileInfo = fileInfo;
	}
	public void run(){
		HttpURLConnection coon = null;
	    RandomAccessFile raf = null;
		try{
			//连接网络文件
			URL url = new URL(fileInfo.getUrl());
			coon = (HttpURLConnection)url.openConnection();
			coon.setConnectTimeout(3000);
			coon.setRequestMethod("GET");
			int length = -1;
			if (coon.getResponseCode()==HttpStatus.SC_OK){
				//获取文件长度
				length = coon.getContentLength();
			}
			if (length<0){
				return;
			}
			
			
			File dir = new File(DOWNLOAD_PATH);
			if(!dir.exists()){
				dir.mkdir();
			}
			//本地创建文件
		    File file = new File(dir,fileInfo.getFileName());
		    raf = new  RandomAccessFile(file,"rwd");
		    //设置文件长度
		    raf.setLength(length);
		    fileInfo.setLength(length);
		    //利用handler将信息从线程中回传给service
		    mHandler.obtainMessage(MSG_INIT,fileInfo).sendToTarget();
		}catch(Exception e){
			e.printStackTrace();
		}finally{
			
			try {
				coon.disconnect();
				raf.close();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
}

public class DownloadTask {
private Context mContext = null;
private FileInfo mFileInfo = null;
private ThreadDAO mDao = null;
private int mFinished = 0;
private boolean isPause = false;
public DownloadTask(Context mContext, FileInfo mFileInfo) {
	super();
	this.mContext = mContext;
	this.mFileInfo = mFileInfo;
	mDao = new ThreadDAOImpl(mContext);
}

public void setPause(boolean isPause) {
	this.isPause = isPause;
}

public void download(){
	//读取数据库线程信息
	List<ThreadInfo> threadInfos = mDao.getThreads(mFileInfo.getUrl());
	ThreadInfo threadInfo = null;
	if(threadInfos.size()==0){
		//初始化线程信息对象
		threadInfo = new ThreadInfo(0,mFileInfo.getUrl(),0,mFileInfo.getLength(),0);
	}else{
		threadInfo = threadInfos.get(0);
	}
	//创建子线程下载
	new DownloadThread(threadInfo).start();
}
class DownloadThread extends Thread{
	private ThreadInfo mThreadInfo = null;
    
	public DownloadThread(ThreadInfo mThreadInfo) {
		super();
		this.mThreadInfo = mThreadInfo;
	}
	public void run(){
		//数据库插入线程信息
		if(!mDao.isExtists(mThreadInfo.getUrl(),mThreadInfo.getId())){
			mDao.insertThread(mThreadInfo);
		}
		
		HttpURLConnection conn = null;
		RandomAccessFile raf = null;
		InputStream input = null;
		try {
			URL url = new URL(mThreadInfo.getUrl());
			conn = (HttpURLConnection)url.openConnection();
			conn.setConnectTimeout(3000);
			conn.setRequestMethod("GET");
			//设置下载位置
			int start = mThreadInfo.getStart()+mThreadInfo.getFinished();
			conn.setRequestProperty("Range", "bytes="+start+"-"+mThreadInfo.getEnd());
		   //写入位置
			File file = new File(DownloadService.DOWNLOAD_PATH,mFileInfo.getFileName());
			raf = new RandomAccessFile(file,"rwd");
			raf.seek(start);
			Intent intent = new Intent(DownloadService.ACTION_UPDATE);
			mFinished+=mThreadInfo.getFinished();
			//开始下载,返回值206
			if(conn.getResponseCode()==HttpStatus.SC_PARTIAL_CONTENT){
				//读取数据
				input = conn.getInputStream();
				byte[] bytes = new byte[1024];
				int len = -1;
				long time = System.currentTimeMillis();//减慢进度条刷新时间,减少ui负载
				while((len = input.read(bytes))!=-1){
				    //写入文件	
					raf.write(bytes,0,len);
					//把下载进度发送广播给Activity
					mFinished += len;
					if(System.currentTimeMillis()-time>500){
					time = System.currentTimeMillis();
					intent.putExtra("finished", mFinished*100/mFileInfo.getLength());
					mContext.sendBroadcast(intent);
					}
					//下载暂停时,保存下载进度
					if(isPause){
						mDao.updateThread(mThreadInfo.getUrl(), mThreadInfo.getId(), mFinished);
					    return;
					}
				}
				//删除数据库中的线程信息
				mDao.deleteThread(mThreadInfo.getUrl(), mThreadInfo.getId());
			}
			
			
			
			
			
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}finally{
			
			try {conn.disconnect();
			    input.close();
				raf.close();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		
	}
}
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值