首页 > 学院 > 开发设计 > 正文

断点后台下载

2019-11-09 17:59:21
字体:
来源:转载
供稿:网友

首先看效果图: 效果图1 效果图2

实现思路:使用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>
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表