首先看效果图:
实现思路:使用HttpURLConnection和AsyncTask(便于及时取消任务,实现暂停下载的功能)实现断点下载,将下载进度保存到数据库中,每次打开程序时遍历数据库,设置进度调的初始状态。
数据库操作:
DownloadContract类,包含表名,字段名
public class DownloadContract {
// 为了防止有人意外地实例化合同类,使构造函数私有。
private DownloadContract() {
}
/* 内部类定义表的内容 */
public static class DownloadEntry implements BaseColumns {
public static final String TABLE_NAME = "downloadentry";
public static final String COLUMN_NAME_TID = "tid";
public static final String COLUMN_NAME_JINDU = "jindu";
public static final String COLUMN_NAME_YIXIAZAI = "yixiazai";
public static final String COLUMN_NAME_PATH = "path";
}
}
SQLTool类,包含创建,删除表的语句
public class SQLTool {
private static final String TEXT_TYPE = " TEXT";
private static final String INTEGER_TYPE = " INTEGER";
private static final String COMMA_SEP = ",";
public static final String SQL_CREATE_ENTRIES =
"CREATE TABLE " + DownloadContract.DownloadEntry.TABLE_NAME + " (" +
DownloadContract.DownloadEntry._ID + " INTEGER PRIMARY KEY," +
DownloadContract.DownloadEntry.COLUMN_NAME_TID + INTEGER_TYPE + COMMA_SEP +
DownloadContract.DownloadEntry.COLUMN_NAME_JINDU + INTEGER_TYPE + COMMA_SEP +
DownloadContract.DownloadEntry.COLUMN_NAME_YIXIAZAI + INTEGER_TYPE +COMMA_SEP +
DownloadContract.DownloadEntry.COLUMN_NAME_PATH + TEXT_TYPE + " )";
public static final String SQL_DELETE_ENTRIES =
"DROP TABLE IF EXISTS " + DownloadContract.DownloadEntry.TABLE_NAME;
}
DownloadDbHelper类,继承SQLiteOpenHelper
public class DownloadDbHelper extends SQLiteOpenHelper {
// 如果更改数据库模式,则必须增加数据库版本。
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "dl.db";
public DownloadDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase sqLiteDatabase) {
sqLiteDatabase.execSQL(SQL_CREATE_ENTRIES);
}
@Override
public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
// 这个数据库只是一个用于在线数据的缓存,因此其升级策略是简单地丢弃数据并重新开始
sqLiteDatabase.execSQL(SQL_DELETE_ENTRIES);
onCreate(sqLiteDatabase);
}
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
}
DAOHelper类,包含增删改查操作
public class DAOHelper {
private DownloadDbHelper downloadDbHelper;
public DAOHelper(Context context) {
downloadDbHelper = new DownloadDbHelper(context);
}
public long add(Download download) {
// 以写入模式获取数据存储库
SQLiteDatabase db = downloadDbHelper.getWritableDatabase();
//创建一个新的值映射,其中列名称是键
ContentValues values = new ContentValues();
values.put(DownloadContract.DownloadEntry.COLUMN_NAME_TID, download.gettId());
values.put(DownloadContract.DownloadEntry.COLUMN_NAME_JINDU, download.getJindu());
values.put(DownloadContract.DownloadEntry.COLUMN_NAME_YIXIAZAI, download.getYixiazai());
values.put(DownloadContract.DownloadEntry.COLUMN_NAME_PATH, download.getPath());
// 插入新行,返回新行的主键值,-1表示插入失败
long newRowId = db.insert(DownloadContract.DownloadEntry.TABLE_NAME, null, values);
return newRowId;
}
public void delete(int tID) {
SQLiteDatabase db = downloadDbHelper.getWritableDatabase();
// 定义查询的“where”部分。
String selection = DownloadContract.DownloadEntry.COLUMN_NAME_TID + " LIKE ?";
// 在占位符顺序中指定参数。
String[] selectionArgs = {tID + ""};
// 发出SQL语句。
db.delete(DownloadContract.DownloadEntry.TABLE_NAME, selection, selectionArgs);
}
public int update(Download download) {
SQLiteDatabase db = downloadDbHelper.getReadableDatabase();
ContentValues values = new ContentValues();
values.put(DownloadContract.DownloadEntry.COLUMN_NAME_JINDU, download.getJindu());
values.put(DownloadContract.DownloadEntry.COLUMN_NAME_YIXIAZAI, download.getYixiazai());
values.put(DownloadContract.DownloadEntry.COLUMN_NAME_PATH, download.getPath());
// 要更新的行
String selection = DownloadContract.DownloadEntry.COLUMN_NAME_TID + " LIKE ?";
String[] selectionArgs = {download.gettId() + ""};
// 得到受影响的行数
int count = db.update(
DownloadContract.DownloadEntry.TABLE_NAME,
values,
selection,
selectionArgs);
return count;
}
public boolean select(int tID) {
SQLiteDatabase db = downloadDbHelper.getReadableDatabase();
String[] projection = {
DownloadContract.DownloadEntry.COLUMN_NAME_TID,
DownloadContract.DownloadEntry.COLUMN_NAME_JINDU,
DownloadContract.DownloadEntry.COLUMN_NAME_YIXIAZAI,
DownloadContract.DownloadEntry.COLUMN_NAME_PATH
};
String selection = DownloadContract.DownloadEntry.COLUMN_NAME_TID + " = ?";
String[] selectionArgs = {tID + ""};
String sortOrder =
DownloadContract.DownloadEntry.COLUMN_NAME_TID + " DESC";
Cursor c = db.query(
DownloadContract.DownloadEntry.TABLE_NAME,
projection,
selection,
selectionArgs,
null,
null,
sortOrder
);
if (c.moveToFirst()) {
return true;
} else {
return false;
}
}
public List<Download> selectAll() {
List<Download> dataList = new ArrayList<Download>();
SQLiteDatabase db = downloadDbHelper.getReadableDatabase();
Cursor c = db.query(
DownloadContract.DownloadEntry.TABLE_NAME,
null,
null,
null,
null,
null,
null
);
while (c.moveToNext()) {
Log.d("dl", "添加");
Download download = new Download();
download.settId(c.getInt(c.getColumnIndexOrThrow(DownloadContract.DownloadEntry.COLUMN_NAME_TID)));
download.setJindu(c.getInt(c.getColumnIndexOrThrow(DownloadContract.DownloadEntry.COLUMN_NAME_JINDU)));
download.setYixiazai(c.getInt(c.getColumnIndexOrThrow(DownloadContract.DownloadEntry.COLUMN_NAME_YIXIAZAI)));
download.setPath(c.getString(c.getColumnIndexOrThrow(DownloadContract.DownloadEntry.COLUMN_NAME_PATH)));
dataList.add(download);
}
return dataList;
}
}
Download实体类
public class Download {
private int tId;
private int jindu;
private int yixiazai;
private String path;
public int gettId() {
return tId;
}
public void settId(int tId) {
this.tId = tId;
}
public int getJindu() {
return jindu;
}
public void setJindu(int jindu) {
this.jindu = jindu;
}
public int getYixiazai() {
return yixiazai;
}
public void setYixiazai(int yixiazai) {
this.yixiazai = yixiazai;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
@Override
public String toString() {
return "Download{" +
"tId=" + tId +
", jindu=" + jindu +
", yixiazai=" + yixiazai +
", path='" + path + '\'' +
'}';
}
}
Service
DownloadSerivce类
public class DownloadSerivce extends Service {
private int jindu;
private File dlFile, file;
private DownloadTask downloadTask;
private long alreadySize;
private Timer timer;
private DAOHelper daoHelper;
private final IBinder mBinder = new DLBinder();
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1000:
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentTitle("自定义点击事件");
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.remoteview);
remoteViews.setProgressBar(R.id.progressBar, 100, jindu, false);
remoteViews.setTextViewText(R.id.textView, jindu + "%");
builder.setContent(remoteViews);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
startForeground(1, builder.build());
break;
}
}
};
public class DLBinder extends Binder {
public DownloadSerivce getService() {
return DownloadSerivce.this;
}
}
@Override
public void onCreate() {
daoHelper = new DAOHelper(this);
file = getTemporaryStorageDir(getApplicationContext(), "apk");
Log.d("dl", "path = " + file.getAbsolutePath());
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setContentTitle("自定义点击事件");
RemoteViews remoteViews = new RemoteViews(getPackageName(), R.layout.remoteview);
remoteViews.setProgressBar(R.id.progressBar, 100, jindu, false);
remoteViews.setTextViewText(R.id.textView, jindu + "%");
builder.setContent(remoteViews);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
startForeground(1, builder.build());
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
}
// 开始下载
public void startDL(int tID) {
if (downloadTask == null) {
downloadTask = new DownloadSerivce.DownloadTask();
downloadTask.execute("http://msoftdl.360.cn/mobilesafe/shouji360/360safe/6002520/360MobileSafe.apk");
if (timer != null) {
timer.cancel();
}
timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
Message msg = new Message();
msg.what = 1000;
handler.sendMessage(msg);
}
}, 0, 1000);
}
}
// 暂停下载
public void pauseDL(int tID) {
if (downloadTask != null) {
downloadTask.cancel(true);
downloadTask = null;
}
if (timer != null) {
timer.cancel();
}
Download download = new Download();
download.settId(1);
download.setJindu(jindu);
download.setYixiazai((int) alreadySize);
download.setPath(file.getAbsolutePath() + "/360.apk");
int i = daoHelper.update(download);
Log.d("dl", "updateline = " + i);
List<Download> list = new ArrayList<Download>();
list = daoHelper.selectAll();
Log.d("dl", "list = " + list.toString());
}
public File getTemporaryStorageDir(Context context, String albumName) {
// 获取临时文件夹
dlFile = new File(context.getExternalFilesDir(
Environment.DIRECTORY_DOWNLOADS), albumName);
if (dlFile.mkdirs() || dlFile.isDirectory()) {
Log.d("dl", "文件夹已存在");
} else {
Log.d("dl", "文件夹创建失败");
}
return dlFile;
}
// 获取下载进度
public int getJindu(int tID) {
Log.d("dl", "jindu = " + jindu);
return jindu;
}
public class DownloadTask extends AsyncTask<String, Integer, String> {
@Override
protected void onPreExecute() {
super.onPreExecute();
}
@Override
protected String doInBackground(String... strings) {
try {
//创建URL对象
URL url = new URL(strings[0]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
//已经下载的字节数
alreadySize = 0;
//将文件写到指定目录中
File file = new File(dlFile.getAbsolutePath(), "123.apk");
if (file.exists()) {
//如果文件存在,就获取当前文件的大小
alreadySize = file.length();
}
conn.addRequestProperty("range", "bytes=" + alreadySize + "-");
conn.connect();
// 获得返回结果
int code = conn.getResponseCode();
// 响应成功返回206
if (code == 206) {
// 获取未下载的文件的大小
long unfinishedSize = conn.getContentLength();
// 文件的大小
long size = alreadySize + unfinishedSize;
Log.d("dl", "size = " + size);
// 获取输入流
InputStream in = conn.getInputStream();
// 获取输出对象,在原文件后追加
OutputStream out = new BufferedOutputStream(new FileOutputStream(file, true));
// 开始下载
byte[] buff = new byte[2048];
int len;
StringBuilder sb = new StringBuilder();
while ((len = in.read(buff)) != -1) {
out.write(buff, 0, len);
// 累加已下载的大小
alreadySize += len;
// 更新进度
publishProgress((int) (alreadySize * 1.0 / size * 100));
}
out.close();
} else {
Toast.makeText(DownloadSerivce.this, "下载失败", Toast.LENGTH_SHORT).show();
}
conn.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
jindu = (values[0]);
}
}
}
以上是准备工作。
下面是具体的使用代码:
布局文件
remoteview.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.9" />
<TextView
android:id="@+id/textView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.1" />
</LinearLayout>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.jqk.backgrounddownload.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:orientation="horizontal">
<ProgressBar
android:id="@+id/progressBar1"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.7"
android:max="100" />
<TextView
android:id="@+id/jindu"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.1" />
<Button
android:id="@+id/start"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.1"
android:text="开始" />
<Button
android:id="@+id/pause"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.1"
android:text="暂停" />
</LinearLayout>
</LinearLayout>
Activity
MainActivity.java
public class MainActivity extends AppCompatActivity {
private Button start, pause;
private ProgressBar progressBar;
private File dlFile, file;
private TextView tvJindu;
private List<Download> all;
private DAOHelper daoHelper;
private DownloadSerivce mService;
private boolean mBound = false;
// 定时任务,更新下载进度
private Timer timer;
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
DownloadSerivce.DLBinder binder = (DownloadSerivce.DLBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
private Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case 1000:
int jindu = mService.getJindu(1);
setData(jindu);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start = (Button) findViewById(R.id.start);
pause = (Button) findViewById(R.id.pause);
progressBar = (ProgressBar) findViewById(R.id.progressBar1);
tvJindu = (TextView) findViewById(R.id.jindu);
file = getTemporaryStorageDir(getApplicationContext(), "apk");
Log.d("dl", "path = " + file.getAbsolutePath());
daoHelper = new DAOHelper(this);
if (!daoHelper.select(1)) {
Download download = new Download();
download.settId(1);
download.setJindu(0);
download.setYixiazai(0);
download.setPath(file.getAbsolutePath() + "/123.apk");
long i = daoHelper.add(download);
if (i != -1) {
Toast.makeText(this, "插入成功", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "插入失败", Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(this, "数据已存在", Toast.LENGTH_SHORT).show();
}
initData();
// 启动服务
Intent intent = new Intent(this, DownloadSerivce.class);
startService(intent);
// 绑定服务
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mService.startDL(1);
timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
Message msg = new Message();
msg.what = 1000;
handler.sendMessage(msg);
}
}, 0, 200);
}
});
pause.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mService.pauseDL(1);
if (timer != null) {
timer.cancel();
}
}
});
}
public void initData() {
all = new ArrayList<Download>();
all = daoHelper.selectAll();
Download download = all.get(0);
tvJindu.setText(download.getJindu() + "%");
progressBar.setProgress(download.getJindu());
}
public void setData(int jindu) {
if (jindu != 0) {
tvJindu.setText(jindu + "%");
progressBar.setProgress(jindu);
}
}
public File getTemporaryStorageDir(Context context, String albumName) {
dlFile = new File(context.getExternalFilesDir(
Environment.DIRECTORY_DOWNLOADS), albumName);
if (dlFile.mkdirs() || dlFile.isDirectory()) {
Log.d("dl", "文件夹已存在");
} else {
Log.d("dl", "文件夹创建失败");
}
return dlFile;
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mConnection);
Intent intent = new Intent(this, DownloadSerivce.class);
stopService(intent);
}
}
最后别忘了添加权限,声明Service
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<service android:name=".DownloadSerivce"></service>