流程:
/** * 断点续传:就是下载如果暂停后,下次下载时候,继续从上次下载的位置开始下载即可 * 大致流程: * 点击下载后,开始一个service,在onStartCommand中进行 * InitThread开始分线程进行获取网络资源的大小,获取完毕发送消息MSG_INIT, * 进行downLoad,下载前判断,是否下载过,如果么有就初始化 * threadInfo = new ThreadInfo(0, mFileInfo.getUrl(), 0, mFileInfo.getLength(), 0); 如果又下载就进行获取, 然后开启分线程进行下载 new DownloadThread(threadInfo).start(); 下载操作:向数据库插入线程信息,设置下载位置,设置文件写入位置,开始进行下载 下载时候,通过handler发送广播(传递下载进度值), 在广播接收器中进行mProgressBar.setProgress(finised);设置进度即可 下载完毕,发送消息,进行Toast.makeText(mMainActivity, "下载完毕", 0).show(); * */
activity_main.xml
<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=".MainActivity" > <TextView android:id="@+id/tv_fileName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="imooc.apk" /> <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_fileName" /> <Button android:id="@+id/btn_stop" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/pb_progress" android:layout_alignParentRight="true" android:text="暂停" /> <Button android:id="@+id/btn_start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/pb_progress" android:layout_toLeftOf="@id/btn_stop" android:text="下载" /> </RelativeLayout>
package com.download.entities; import java.io.Serializable; public class FileInfo implements Serializable { private int id; private String url; private String fileName; private int length; private int finished; public FileInfo(int id, String url, String fileName, int length, int finished) { 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 + "]"; } }
package com.download.entities; public class ThreadInfo { /** * 对应数据库的字段即可 */ private int id; private String url; private int start; private int end; private int finished; public ThreadInfo() { } public ThreadInfo(int id, String url, int start, int end, int finished) { 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 + "]"; } }
package com.download.db; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; /** * 数据库帮助类 */ public class DBHelper extends SQLiteOpenHelper { private static final String DB_NAME = "download.db"; private static final int VERSION = 1; //字段:id、thread_id、url、start、end、finished 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); } @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); } }ThreadDAO
package com.download.db; import java.util.List; import com.download.entities.ThreadInfo; public interface ThreadDAO { /** * 插入线程信息 */ public void insertThread(ThreadInfo threadInfo); /** * 删除线程信息 */ public void deleteThread(String url, int thread_id); /** * 更新线程下载进度 */ public void updateThread(String url, int thread_id, int finished); /** * 查询文件的线程信息 */ public List<ThreadInfo> getThreads(String url); /** * 线程信息是否存在 */ public boolean isExists(String url, int thread_id); }
package com.download.db; import java.util.ArrayList; import java.util.List; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import com.download.entities.ThreadInfo; /** * 数据访问接口实现 */ 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.getFinished() }); db.close(); } @Override public void deleteThread(String url, int thread_id) { SQLiteDatabase db = mHelper.getWritableDatabase(); db.execSQL("delete from thread_info where url = ? and thread_id = ?", new Object[] { url, thread_id }); db.close(); } @Override public void updateThread(String url, int thread_id, int finished) { SQLiteDatabase db = mHelper.getWritableDatabase(); db.execSQL( "update thread_info set finished = ? where url = ? and thread_id = ?", new Object[] { finished, url, thread_id }); 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 threadInfo = new ThreadInfo(); threadInfo.setId(cursor.getInt(cursor.getColumnIndex("thread_id"))); threadInfo.setUrl(cursor.getString(cursor.getColumnIndex("url"))); threadInfo.setStart(cursor.getInt(cursor.getColumnIndex("start"))); threadInfo.setEnd(cursor.getInt(cursor.getColumnIndex("end"))); threadInfo.setFinished(cursor.getInt(cursor .getColumnIndex("finished"))); list.add(threadInfo); } cursor.close(); db.close(); return list; } @Override public boolean isExists(String url, int thread_id) { SQLiteDatabase db = mHelper.getWritableDatabase(); Cursor cursor = db.rawQuery( "select * from thread_info where url = ? and thread_id = ?", new String[] { url, thread_id + "" }); boolean exists = cursor.moveToNext(); cursor.close(); db.close(); return exists; } }
package com.download.app; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.os.Handler; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; import com.download.entities.FileInfo; import com.download.services.DownloadService; import com.imooc.DownLoad.R; public class MainActivity extends Activity { // 进度条 private ProgressBar mProgressBar = null; // 开始 private Button mStartBtn = null; // 暂停 private Button mStopBtn = null; public static MainActivity mMainActivity = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 初始化控件 mProgressBar = (ProgressBar) findViewById(R.id.pb_progress); mStartBtn = (Button) findViewById(R.id.btn_start); mStopBtn = (Button) findViewById(R.id.btn_stop); // 将进度条设置为最大 mProgressBar.setMax(100); /** * 参数:(int id, String url, String fileName, int length,int finished) * */ final FileInfo fileInfo = new FileInfo(0, "http://www.imooc.com/mobile/imooc.apk", "imooc.apk", 0, 0); // 添加事件监听 mStartBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { /** * 通过Intent传递参数给Service */ Intent intent = new Intent(MainActivity.this, DownloadService.class); //设置intent的标记 intent.setAction(DownloadService.ACTION_START); intent.putExtra("fileInfo", fileInfo); startService(intent); } }); mStopBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 通过Intent传递参数给Service Intent intent = new Intent(MainActivity.this, DownloadService.class); intent.setAction(DownloadService.ACTION_STOP); intent.putExtra("fileInfo", fileInfo); startService(intent); } }); // 注册广播接收器 IntentFilter filter = new IntentFilter(); filter.addAction(DownloadService.ACTION_UPDATE); registerReceiver(mReceiver, filter); mMainActivity = this; } /** * 更新UI的广播接收器 */ BroadcastReceiver mReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (DownloadService.ACTION_UPDATE.equals(intent.getAction())) { int finised = intent.getIntExtra("finished", 0); mProgressBar.setProgress(finised); } } }; /** * 监听返回键 * * @see android.app.Activity#onKeyUp(int, android.view.KeyEvent) */ @Override public boolean onKeyUp(int keyCode, KeyEvent event) { if (KeyEvent.KEYCODE_BACK == keyCode && mStartBtn != null) // 按了返回键时应暂停下载 { mStopBtn.performClick(); // 模拟按下暂停按钮 } return super.onKeyUp(keyCode, event); } public Handler handler = new Handler() { public void handleMessage(android.os.Message msg) { Toast.makeText(mMainActivity, "下载完毕", 0).show(); } }; /** * 退出后,注销广播接收器 */ protected void onDestroy() { super.onDestroy(); unregisterReceiver(mReceiver); } }
package com.download.services; import java.io.File; import java.io.IOException; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.URL; import org.apache.http.HttpStatus; import com.download.entities.FileInfo; import android.app.Service; import android.content.Intent; import android.os.Environment; import android.os.Handler; import android.os.IBinder; import android.util.Log; public class DownloadService extends Service { 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 int MSG_INIT = 0; private DownloadTask mTask = null; private String TAG = "DownloadService"; @Override public int onStartCommand(Intent intent, int flags, int startId) { // 获得Activity传过来的参数 if (ACTION_START.equals(intent.getAction())) { //获取intent传递过来的,携带的FileInfo信息 FileInfo fileInfo = (FileInfo) intent .getSerializableExtra("fileInfo"); // 启动初始化线程 new InitThread(fileInfo).start(); } else if (ACTION_STOP.equals(intent.getAction())) { FileInfo fileInfo = (FileInfo) intent .getSerializableExtra("fileInfo"); if (mTask != null) { mTask.isPause = true; } } return super.onStartCommand(intent, flags, startId); } private Handler mHandler = new Handler() { public void handleMessage(android.os.Message msg) { switch (msg.what) { case MSG_INIT: FileInfo fileInfo = (FileInfo) msg.obj; Log.i(TAG, "Init:" + fileInfo); // 启动下载任务 mTask = new DownloadTask(DownloadService.this, fileInfo); mTask.downLoad(); break; default: break; } }; }; /** * 创建线程进行下载操作 * */ public static final String DOWNLOAD_PATH = Environment .getExternalStorageDirectory().getAbsolutePath() + "/downloads/"; private class InitThread extends Thread { private FileInfo mFileInfo = null; public InitThread(FileInfo mFileInfo) { this.mFileInfo = mFileInfo; } @Override public void run() { //获取conn HttpURLConnection connection = null; RandomAccessFile raf = null; try { // 连接网络文件 URL url = new URL(mFileInfo.getUrl()); //打开conn链接 connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(5000); connection.setRequestMethod("GET"); int length = -1; if (connection.getResponseCode() == HttpStatus.SC_OK) { // 获得文件的长度 length = connection.getContentLength(); } if (length <= 0) { return; } File dir = new File(DOWNLOAD_PATH); if (!dir.exists()) { dir.mkdir(); } // 在本地创建文件 File file = new File(dir, mFileInfo.getFileName()); raf = new RandomAccessFile(file, "rwd"); // 设置文件长度 raf.setLength(length); mFileInfo.setLength(length); //obtainMessage(int what, Object obj),准备完毕发送消息通知进行下载操作 mHandler.obtainMessage(MSG_INIT, mFileInfo).sendToTarget(); } catch (Exception e) { e.printStackTrace(); } finally { if (connection != null) { connection.disconnect(); } if (raf != null) { try { raf.close(); } catch (IOException e) { e.printStackTrace(); } } } } } @Override public IBinder onBind(Intent intent) { return null; } }
package com.download.services; import java.io.File; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.List; import org.apache.http.HttpStatus; import android.R.integer; import android.content.Context; import android.content.Intent; import android.util.Log; import android.widget.Toast; import com.download.app.MainActivity; import com.download.db.ThreadDAO; import com.download.db.ThreadDAOImpl; import com.download.entities.FileInfo; import com.download.entities.ThreadInfo; public class DownloadTask { private Context mContext = null; private FileInfo mFileInfo = null; private ThreadDAO mDao = null; private int mFinised = 0; public boolean isPause = false; public DownloadTask(Context mContext, FileInfo mFileInfo) { this.mContext = mContext; this.mFileInfo = mFileInfo; mDao = new ThreadDAOImpl(mContext); } public void downLoad() { // 读取数据库的线程信息 List<ThreadInfo> threads = mDao.getThreads(mFileInfo.getUrl()); ThreadInfo threadInfo = null; if (0 == threads.size()) { // 初始化线程信息对象---ThreadInfo(int id, String url, int start, int end, int finished) threadInfo = new ThreadInfo(0, mFileInfo.getUrl(), 0, mFileInfo.getLength(), 0); } else { threadInfo = threads.get(0); } // 创建子线程进行下载 new DownloadThread(threadInfo).start(); } /** * 下载线程 */ private class DownloadThread extends Thread { private ThreadInfo mThreadInfo = null; public DownloadThread(ThreadInfo mInfo) { this.mThreadInfo = mInfo; } @Override public void run() { // 向数据库插入线程信息 if (!mDao.isExists(mThreadInfo.getUrl(), mThreadInfo.getId())) { mDao.insertThread(mThreadInfo); } HttpURLConnection connection = null; RandomAccessFile raf = null; InputStream inputStream = null; try { URL url = new URL(mThreadInfo.getUrl()); connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(5000); connection.setRequestMethod("GET"); // 设置下载位置 int start = mThreadInfo.getStart() + mThreadInfo.getFinished(); connection.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(); intent.setAction(DownloadService.ACTION_UPDATE); mFinised += mThreadInfo.getFinished(); // 开始下载 if (connection.getResponseCode() == HttpStatus.SC_PARTIAL_CONTENT) { // 读取数据 inputStream = connection.getInputStream(); byte buf[] = new byte[1024 << 2]; int len = -1; long time = System.currentTimeMillis(); while ((len = inputStream.read(buf)) != -1) { // 写入文件 raf.write(buf, 0, len); // 把下载进度发送广播给Activity mFinised += len; if (System.currentTimeMillis() - time > 500) { time = System.currentTimeMillis(); intent.putExtra("finished", mFinised * 100 / mThreadInfo.getEnd()); //发送广播 mContext.sendBroadcast(intent); } // 在下载暂停时,保存下载进度 if (isPause) { mDao.updateThread(mThreadInfo.getUrl(), mThreadInfo.getId(), mFinised); return; } } // 删除线程信息 mDao.deleteThread(mThreadInfo.getUrl(), mThreadInfo.getId()); Log.i("DownloadTask", "下载完毕"); //发送消息,通知下载完毕即可 MainActivity.mMainActivity.handler.sendEmptyMessage(0); } } catch (Exception e) { e.printStackTrace(); } finally { try { if (connection != null) { connection.disconnect(); } if (raf != null) { raf.close(); } if (inputStream != null) { inputStream.close(); } } catch (Exception e2) { e2.printStackTrace(); } } } } }