diff --git a/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/DBHelper.java b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/DBHelper.java new file mode 100644 index 00000000..73cfce6e --- /dev/null +++ b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/DBHelper.java @@ -0,0 +1,31 @@ +package single_thread_download.skyward.com.db; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteOpenHelper; + +/** + * Created by skyward on 2016/6/15. + */ +public class DBHelper extends SQLiteOpenHelper { + public static final int VERSION = 1; + public static final String SQL_CREATE = "create table thread_info(_id integer primary key," + + "thread_id integer,url text,start integer,end integer,finished integer)"; + public static final String SQL_DROP = "drop table if exists thread_info"; + public static final String DB_NAME = "download.db"; + + 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); + } +} diff --git a/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/DownloadService.java b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/DownloadService.java new file mode 100644 index 00000000..3fe2c904 --- /dev/null +++ b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/DownloadService.java @@ -0,0 +1,118 @@ +package single_thread_download.skyward.com.service; + +import android.app.Service; +import android.content.Intent; +import android.os.Bundle; +import android.os.Environment; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.text.LoginFilter; +import android.util.Log; + +import java.io.File; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; + +import single_thread_download.skyward.com.bean.FileInfo; + +public class DownloadService extends Service { + DownloadTask downloadTask = null; + Handler mHandler = new Handler() { + @Override + public void handleMessage(Message msg) { + switch (MSG_INIT) { + case MSG_INIT: + FileInfo fileInfo = (FileInfo) msg.obj; + Log.i(TAG, "handleMessage: " + fileInfo); + downloadTask = new DownloadTask(DownloadService.this, fileInfo); + downloadTask.download(); + break; + } + } + }; + private static final String TAG = "DownloadService"; + 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; + public static final String DOWNLOAD_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() + "/download/"; + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + String action = intent.getAction(); + //获得Activity传来的参数 + FileInfo fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo"); + if(action.equals(ACTION_START)) { + Log.i(TAG, "Start: " + fileInfo.toString()); + new InitThread(fileInfo).start(); + } else if(action.equals(ACTION_STOP)) { + Log.i(TAG, "Stop: " +fileInfo.toString()); + downloadTask.isPause = true; + } + return super.onStartCommand(intent, flags, startId); + } + + public DownloadService() { + } + + @Override + public IBinder onBind(Intent intent) { + return null; + } + + class InitThread extends Thread { + private FileInfo mFileInfo; + + public InitThread(FileInfo mFileInfo) { + this.mFileInfo = mFileInfo; + } + + @Override + public void run() { + HttpURLConnection conn = null; + RandomAccessFile raf = null; + try { + URL url = new URL(mFileInfo.getUrl()); + conn = (HttpURLConnection) url.openConnection(); + conn.setConnectTimeout(3000); + conn.setRequestMethod("GET"); + int length = -1; + if(conn.getResponseCode() == 200) { + //获取文件长度 + length = conn.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); + mHandler.obtainMessage(MSG_INIT, mFileInfo).sendToTarget(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + if(raf != null) { + raf.close(); + } + conn.disconnect(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } +} diff --git a/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/DownloadTask.java b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/DownloadTask.java new file mode 100644 index 00000000..6022baa9 --- /dev/null +++ b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/DownloadTask.java @@ -0,0 +1,126 @@ +package single_thread_download.skyward.com.service; + +import android.content.Context; +import android.content.Intent; +import android.widget.ProgressBar; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.RandomAccessFile; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.List; + +import single_thread_download.skyward.com.bean.FileInfo; +import single_thread_download.skyward.com.bean.ThreadInfo; +import single_thread_download.skyward.com.db.ThreadDao; +import single_thread_download.skyward.com.db.ThreadDaoImpl; + +/** + * 下载任务类 + * Created by skyward on 2016/6/16. + */ +public class DownloadTask { + private Context mContext = null; + private FileInfo mFileInfo = null; + private ThreadDao mDao = null; + private int mFinished = 0; + public boolean isPause = false; + + public void download() { + //读取数据库的线程信息 + List 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(); + } + + public DownloadTask(Context mContext, FileInfo mFileInfo) { + this.mContext = mContext; + this.mFileInfo = mFileInfo; + mDao = new ThreadDaoImpl(mContext); + } + + /** + * 下载线程 + */ + class DownloadThread extends Thread { + private ThreadInfo mThreadInfo = null; + + public DownloadThread(ThreadInfo mThreadInfo){ + this.mThreadInfo = mThreadInfo; + } + + @Override + public void run() { + //向数据库插入线程信息 + if(!mDao.isExists(mThreadInfo.getUrl(), mThreadInfo.getId())) { + mDao.insertThread(mThreadInfo); + } + HttpURLConnection conn = null; + RandomAccessFile raf = null; + InputStream in = 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 f = new File(DownloadService.DOWNLOAD_PATH, mFileInfo.getFileName()); + raf = new RandomAccessFile(f, "rwd"); + raf.seek(start); + Intent intent = new Intent(DownloadService.ACTION_UPDATE); + mFinished += mThreadInfo.getFinished(); + //开始下载 + if(conn.getResponseCode() == 206) { + //读取数据 + in = conn.getInputStream(); + byte[] buffer = new byte[1024 * 4]; + int len = -1; + long time = System.currentTimeMillis(); + while((len = in.read(buffer)) != -1) { + //写入文件 + raf.write(buffer, 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 (MalformedURLException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } finally { + try { + conn.disconnect(); + raf.close(); + in.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + } +} diff --git a/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/FileInfo.java b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/FileInfo.java new file mode 100644 index 00000000..5c866396 --- /dev/null +++ b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/FileInfo.java @@ -0,0 +1,81 @@ +package single_thread_download.skyward.com.bean; + +import java.io.Serializable; + +/** + * Created by skyward on 2016/5/30. + */ +public class FileInfo implements Serializable{ + //编号 + private int id; + //文件名 + private String fileName; + //文件大小 + private int length; + //下载链接 + private String url; + //已下载文件大小 + private int finished; + + public FileInfo(int id, String url, String fileName, int length, int finished) { + this.id = id; + this.fileName = fileName; + this.length = length; + this.url = url; + this.finished = finished; + } + + public FileInfo() { + } + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + 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 String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public int getFinished() { + return finished; + } + + public void setFinished(int finished) { + this.finished = finished; + } + + @Override + public String toString() { + return "FileInfo{" + + "id=" + id + + ", fileName='" + fileName + '\'' + + ", length=" + length + + ", url='" + url + '\'' + + ", finished=" + finished + + '}'; + } +} diff --git a/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/MainActivity.java b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/MainActivity.java new file mode 100644 index 00000000..1edaedd5 --- /dev/null +++ b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/MainActivity.java @@ -0,0 +1,125 @@ +package single_thread_download.skyward.com; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.util.List; + +import single_thread_download.skyward.com.bean.FileInfo; +import single_thread_download.skyward.com.bean.ThreadInfo; +import single_thread_download.skyward.com.db.ThreadDao; +import single_thread_download.skyward.com.db.ThreadDaoImpl; +import single_thread_download.skyward.com.service.DownloadService; + +public class MainActivity extends AppCompatActivity { + private ProgressBar pb_progress; + private Button bt_operation; + private TextView tv_fileName; + private TextView tv_state; + //默认值为初态 + private int state = 0; + + /** + * 下载状态 + * 根据下载状态改变按钮文本内容,并执行不同操作 + * 0:初态 + * 按钮的文本内容为“下载” + * 当用户按下按钮后,根据url开始下载文件,同时将按钮的文本内容改为“停止”,bt_state改为1,进入正在下载状态 + * 1:正在下载状态 + * 按钮文本内容为“停止” + * 当用户按下按钮后,执行停止下载操作,同时将按钮的文本内容改为“继续下载”,bt_state改为2,进入停止下载状态 + * 2:停止下载状态 + * 按钮文本内容“继续下载” + * 当用户按下按钮后,从上一次下载的位置开始下载,同时将按钮的文本内容改为“停止”,bt_state改为1,进入正在下载状态 + * 3.下载完成 + * 当文件下载完成时,将按钮内容改为“下载已完成”。同时将按钮设为不可点击。 + */ + public static final int START = 0; + public static final int DOWNLOADING = 1; + public static final int STOP = 2; + public static final int FINISH = 3; + + 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); + pb_progress.setProgress(finished); + } + } + }; + + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_net1314080903223); + pb_progress = (ProgressBar) findViewById(R.id.pb_progress); + bt_operation = (Button) findViewById(R.id.bt_operation); + tv_state = (TextView) findViewById(R.id.tv_state); + tv_fileName = (TextView) findViewById(R.id.tv_fileName); + + final FileInfo fileInfo = new FileInfo(0, "http://dl.ad-safe.com/adsafe_v4/download/adsafe.v4.2.507.1201.exe", "adsafe.exe",0, 0); + Intent intent = new Intent(MainActivity.this, DownloadService.class); + bt_operation.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + Intent intent = new Intent(MainActivity.this, DownloadService.class); + switch (state) { + //初态:开始下载操作, + case START: + bt_operation.setText("停止"); + tv_state.setText("正在下载"); + intent.setAction(DownloadService.ACTION_START); + intent.putExtra("fileInfo", fileInfo); + startService(intent); + state = DOWNLOADING; + break; + //正在下载状态:停止下载操作,进入停止下载状态 + case DOWNLOADING: + bt_operation.setText("继续下载"); + tv_state.setText("停止下载"); + intent.setAction(DownloadService.ACTION_STOP); + intent.putExtra("fileInfo", fileInfo); + startService(intent); + state = STOP; + break; + //停止下载状态:从上次下载位置继续下载文件,进入正在下载状态 + case STOP: + bt_operation.setText("停止"); + tv_state.setText("正在下载"); + intent.setAction(DownloadService.ACTION_START); + intent.putExtra("fileInfo", fileInfo); + startService(intent); + state = DOWNLOADING; + break; + case FINISH: + bt_operation.setText("下载已完成"); + bt_operation.setEnabled(false); + tv_state.setText("下载已完成"); + break; + } + } + }); + //注册广播接收者 + IntentFilter filter = new IntentFilter(); + filter.addAction(DownloadService.ACTION_UPDATE); + registerReceiver(mReceiver, filter); + } + + @Override + protected void onDestroy() { + super.onDestroy(); + unregisterReceiver(mReceiver); + } +} diff --git a/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/Net1314080903223Activity.java b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/Net1314080903223Activity.java new file mode 100644 index 00000000..0dbd3ede --- /dev/null +++ b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/Net1314080903223Activity.java @@ -0,0 +1,37 @@ +package single_thread_download.skyward.com; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.support.v7.app.AppCompatActivity; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.widget.Button; +import android.widget.ProgressBar; +import android.widget.TextView; + +import java.util.List; + +import single_thread_download.skyward.com.bean.FileInfo; +import single_thread_download.skyward.com.bean.ThreadInfo; +import single_thread_download.skyward.com.db.ThreadDao; +import single_thread_download.skyward.com.db.ThreadDaoImpl; +import single_thread_download.skyward.com.service.DownloadService; + +public class Net1314080903223Activity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_net1314080903223); + pb_progress = (ProgressBar) findViewById(R.id.pb_progress); + bt_operation = (Button) findViewById(R.id.bt_operation); + tv_state = (TextView) findViewById(R.id.tv_state); + tv_fileName = (TextView) findViewById(R.id.tv_fileName); + + + } + +} diff --git a/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/ThreadDao.java b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/ThreadDao.java new file mode 100644 index 00000000..cec0cb33 --- /dev/null +++ b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/ThreadDao.java @@ -0,0 +1,46 @@ +package single_thread_download.skyward.com.db; + +import java.util.List; + +import single_thread_download.skyward.com.bean.ThreadInfo; + +/** + * 数据访问接口 + * Created by skyward on 2016/6/15. + */ +public interface ThreadDao { + /** + * 插入线程信息 + */ + public void insertThread(ThreadInfo threadInfo); + + /** + * 删除线程 + * @param url + * @param thread_id + */ + public void deleteThread(String url, int thread_id); + + /** + * 更新线程下载进度 + * @param url + * @param thread_id + * @param finished + */ + public void updateThread(String url, int thread_id, int finished); + + /** + * 查询线程信息 + * @param url + * @return + */ + public List getThreads(String url); + + /** + * 线程信息是否已存在 + * @param url + * @param thread_id + * @return + */ + public boolean isExists(String url, int thread_id); +} diff --git a/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/ThreadDaoImpl.java b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/ThreadDaoImpl.java new file mode 100644 index 00000000..2ffbe42c --- /dev/null +++ b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/ThreadDaoImpl.java @@ -0,0 +1,78 @@ +package single_thread_download.skyward.com.db; + +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + +import java.util.ArrayList; +import java.util.List; + +import single_thread_download.skyward.com.bean.ThreadInfo; + +/** + * 数据库访问接口的实现 + * Created by skyward on 2016/6/15. + */ +public class ThreadDaoImpl implements ThreadDao { + private DBHelper mHelper = null; + + public ThreadDaoImpl(Context context) { + mHelper = new DBHelper(context); + } + + @Override + public void insertThread(ThreadInfo threadInfo) { + if (mHelper == null) { + Log.i("jkf", "insertThread: " + "坑爹的空指针"); + return; + } + 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 getThreads(String url) { + SQLiteDatabase db = mHelper.getWritableDatabase(); + List list = new ArrayList<>(); + 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(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); + } + 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 false; + } +} diff --git a/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/ThreadInfo.java b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/ThreadInfo.java new file mode 100644 index 00000000..a2cfbafe --- /dev/null +++ b/app/src/main/java/edu/hzuapps/androidworks/homeworks/net1314080903223/ThreadInfo.java @@ -0,0 +1,81 @@ +package single_thread_download.skyward.com.bean; + +/** + * 线程信息实体类 + * Created by skyward on 2016/5/30. + */ +public class ThreadInfo { + //编号 + private int id; + //下载链接 + private String url; + //下载的起始位置(字节) + private int start; + //下载的结束位置 + private int end; + //已下载完成的大小 + private int finished; + + @Override + public String toString() { + return "ThreadInfo{" + + "id=" + id + + ", url='" + url + '\'' + + ", start=" + start + + ", end=" + end + + ", 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; + } + + 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; + } +} diff --git a/app/src/main/res/layout/activity_net1314080903223.xml b/app/src/main/res/layout/activity_net1314080903223.xml new file mode 100644 index 00000000..2fad9d51 --- /dev/null +++ b/app/src/main/res/layout/activity_net1314080903223.xml @@ -0,0 +1,39 @@ + + + + + + + +