首先看效果圖:
實作思路:使用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 = ;
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());
// 插入新行,傳回新行的主鍵值,-表示插入失敗
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 :
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, , jindu, false);
remoteViews.setTextViewText(R.id.textView, jindu + "%");
builder.setContent(remoteViews);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
startForeground(, 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, , jindu, false);
remoteViews.setTextViewText(R.id.textView, jindu + "%");
builder.setContent(remoteViews);
NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
startForeground(, 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 = ;
handler.sendMessage(msg);
}
}, , );
}
}
// 暫停下載下傳
public void pauseDL(int tID) {
if (downloadTask != null) {
downloadTask.cancel(true);
downloadTask = null;
}
if (timer != null) {
timer.cancel();
}
Download download = new Download();
download.settId();
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[]);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
//已經下載下傳的位元組數
alreadySize = ;
//将檔案寫到指定目錄中
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 == ) {
// 擷取未下載下傳的檔案的大小
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[];
int len;
StringBuilder sb = new StringBuilder();
while ((len = in.read(buff)) != -) {
out.write(buff, , len);
// 累加已下載下傳的大小
alreadySize += len;
// 更新進度
publishProgress((int) (alreadySize * / size * ));
}
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[]);
}
}
}
以上是準備工作。
下面是具體的使用代碼:
布局檔案
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 :
int jindu = mService.getJindu();
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()) {
Download download = new Download();
download.settId();
download.setJindu();
download.setYixiazai();
download.setPath(file.getAbsolutePath() + "/123.apk");
long i = daoHelper.add(download);
if (i != -) {
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();
timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
Message msg = new Message();
msg.what = ;
handler.sendMessage(msg);
}
}, , );
}
});
pause.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mService.pauseDL();
if (timer != null) {
timer.cancel();
}
}
});
}
public void initData() {
all = new ArrayList<Download>();
all = daoHelper.selectAll();
Download download = all.get();
tvJindu.setText(download.getJindu() + "%");
progressBar.setProgress(download.getJindu());
}
public void setData(int jindu) {
if (jindu != ) {
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" />