断点后台下载

首先看效果图:
效果图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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值