断点后台下载

来源:互联网 发布:域推送安装软件 编辑:程序博客网 时间:2024/05/17 09:18

首先看效果图:
效果图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>
2 0