GreenDao+多线程和断点续传实现下载
阅读原文时间:2021年04月20日阅读:2

先看下效果图:

点击下载进度条开始动,实现下载

点击下载后下载按钮变成不可按的状态

暂停按钮变成可按状态

点击暂停后下载按钮再次变成可按状态,点击下载继续续传

下载完成后,下载完成按钮变成可点击,

下载完成后点击下载完成播放下载的视频

下面开始粘代码:

首先我们生成GreenDao

首先我们需要在项目build.gradle中导入包`

    dependencies {     
        classpath 'org.greenrobot:greendao-gradle-plugin:3.2.1'
    }

在model倒入依赖:

compile 'org.greenrobot:greendao:3.2.0' compile 'org.greenrobot:greendao-generator:3.2.0'

接着在模块build.gradle的顶部添加
apply plugin: ‘org.greenrobot.greendao’
以及

  greendao {
        schemaVersion 1
        daoPackage 'com.xxxxx.xxx'//这个是生成代码保存的包名
        targetGenDir 'src/main/java'//保存到java代码路径
    }

同步后就可以使用greenDao。

生成bean

@Entity
public class User {
    @Id
    private Long id;
    private Integer thread_id;
    private Integer start_pos;
    private Integer end_pos;
    private Integer compelete_size;
    private String url;

//下面的都是自动生成
@Generated(hash = 2041931179)
**public** User(Long id, Integer thread\_id, Integer start\_pos, Integer end\_pos,
        Integer compelete\_size, String url) {
    **this**.**id** \= id;
    **this**.**thread\_id** \= thread\_id;
    **this**.**start\_pos** \= start\_pos;
    **this**.**end\_pos** \= end\_pos;
    **this**.**compelete\_size** \= compelete\_size;
    **this**.**url** \= url;
}
@Generated(hash = 586692638)
**public** User() {
}
**public** Long getId() {
    **return this**.**id**;
}
**public void** setId(Long id) {
    **this**.**id** \= id;
}
**public** Integer getThread\_id() {
    **return this**.**thread\_id**;
}
**public void** setThread\_id(Integer thread\_id) {
    **this**.**thread\_id** \= thread\_id;
}
**public** Integer getStart\_pos() {
    **return this**.**start\_pos**;
}
**public void** setStart\_pos(Integer start\_pos) {
    **this**.**start\_pos** \= start\_pos;
}
**public** Integer getEnd\_pos() {
    **return this**.**end\_pos**;
}
**public void** setEnd\_pos(Integer end\_pos) {
    **this**.**end\_pos** \= end\_pos;
}
**public** Integer getCompelete\_size() {
    **return this**.**compelete\_size**;
}
**public void** setCompelete\_size(Integer compelete\_size) {
    **this**.**compelete\_size** \= compelete\_size;
}
**public** String getUrl() {
    **return this**.**url**;
}
**public void** setUrl(String url) {
    **this**.**url** \= url;
}

}

自动生成是点击build

就能自动的生成你需要的Dao类和helper类

那么使用之前需要初始化一下

public class App extends Application{
public static UserDao userDao;

@Override

public void onCreate() {
super.onCreate();
DaoMaster.DevOpenHelper devOpenHelper = new DaoMaster.DevOpenHelper(this, "lenvess.db", null);
DaoMaster daoMaster = new DaoMaster(devOpenHelper.getWritableDb());
DaoSession daoSession = daoMaster.newSession();
userDao = daoSession.getUserDao();

}

}

GreenDao注解:

@Entity 用于标识这是一个需要Greendao帮我们生成代码的bean

@Id 标明主键,括号里可以指定是否自增

@Property 用于设置属性在数据库中的列名(默认不写就是保持一致)

@NotNull 非空

@Transient 标识这个字段是自定义的不会创建到数据库表里

@Unique 添加唯一约束

@ToOne 是将自己的一个属性与另一个表建立关联(外键)

@ToMany的属性referencedJoinProperty,类似于外键约束。

@JoinProperty 对于更复杂的关系,可以使用这个注解标明目标属性的源属性。

------------------------------------------------------------------------------------------------下面说下载的----------------------------------------------------------------------------------------------------------------------

public class DownlaodSqlTool {
/** * 创建下载的具体信息 */ public void insertInfos(List infos) {
for (DownloadInfo info : infos) {
User user = new User(null, info.getThreadId(), info.getStartPos(), info.getEndPos(), info.getCompeleteSize(), info.getUrl());
userDao.insert(user);
}
}

_/\*\*_  _\* 得到下载具体信息_ _\*/_ **public** List<DownloadInfo> getInfos(String urlstr) {
    List<DownloadInfo> list = **new** ArrayList<DownloadInfo>();
    List<User> list1 = _userDao_.queryBuilder().where(UserDao.Properties.**_Url_**.eq(urlstr)).build().list();
    **for** (User user : list1) {
        DownloadInfo infoss = **new** DownloadInfo(
                user.getThread\_id(), user.getStart\_pos(), user.getEnd\_pos(),
                user.getCompelete\_size(), user.getUrl());
        Log.d(**"main-----"**, infoss.toString());
        list.add(infoss);
    }

    **return** list;
}

_/\*\*_  _\* 更新数据库中的下载信息_ _\*/_ **public void** updataInfos(**int** threadId, **int** compeleteSize, String urlstr) {
    User user = _userDao_.queryBuilder()
            .where(UserDao.Properties.**_Thread\_id_**.eq(threadId), UserDao.Properties.**_Url_**.eq(urlstr)).build().unique();
    user.setCompelete\_size(compeleteSize);
    _userDao_.update(user);
}

_/\*\*_  _\* 下载完成后删除数据库中的数据_ _\*/_ **public void** delete(String url) {
    _userDao_.deleteAll();
}

}

public class DownloadHttpTool {
/** * 利用Http协议进行多线程下载具体实践类 */ private static final String TAG = DownloadHttpTool.class.getSimpleName();
private int threadCount;//线程数量 private String urlstr;//URL地址 private Context mContext;
private Handler mHandler;
private List downloadInfos;//保存下载信息的类 private String localPath;//目录 private String fileName;//文件名 private int fileSize;
private DownlaodSqlTool sqlTool;//文件信息保存的数据库操作类 private enum Download_State {
Downloading, Pause, Ready;//利用枚举表示下载的三种状态 }

**private** Download\_State **state** \= Download\_State.**_Ready_**;_//当前下载状态_   **private int** **globalCompelete** \= 0;_//所有线程下载的总数_   **public** DownloadHttpTool(**int** threadCount, String urlString,
                        String localPath, String fileName, Context context, Handler handler) {
    **super**();
    **this**.**threadCount** \= threadCount;
    **this**.**urlstr** \= urlString;
    **this**.**localPath** \= localPath;
    **this**.**mContext** \= context;
    **this**.**mHandler** \= handler;
    **this**.**fileName** \= fileName;
    **sqlTool** \= **new** DownlaodSqlTool();
}

_//在开始下载之前需要调用ready方法进行配置_  **public void** ready() {
    Log.w(**_TAG_**, **"ready"**);
    **globalCompelete** \= 0;
    **downloadInfos** \= **sqlTool**.getInfos(**urlstr**);
    **if** (**downloadInfos**.size() == 0) {
        initFirst();
    } **else** {
        File file = **new** File(**localPath** \+ **"/"** \+ **fileName**);
        **if** (!file.exists()) {
            **sqlTool**.delete(**urlstr**);
            initFirst();
        } **else** {
            **fileSize** \= **downloadInfos**.get(**downloadInfos**.size() - 1)
                    .getEndPos();
            **for** (DownloadInfo info : **downloadInfos**) {
                **globalCompelete** += info.getCompeleteSize();
            }
            Log.w(**_TAG_**, **"globalCompelete:::"** \+ **globalCompelete**);
        }
    }
}

**public void** start() {
    Log.w(**_TAG_**, **"start"**);
    **if** (**downloadInfos** != **null**) {
        **if** (**state** \== Download\_State.**_Downloading_**) {
            **return**;
        }
        **state** \= Download\_State.**_Downloading_**;
        **for** (DownloadInfo info : **downloadInfos**) {
            Log.v(**_TAG_**, **"startThread"**);
            **new** DownloadThread(info.getThreadId(), info.getStartPos(),
                    info.getEndPos(), info.getCompeleteSize(),
                    info.getUrl()).start();
        }
    }
}

**public void** pause() {
    **state** \= Download\_State.**_Pause_**;
}

**public void** delete() {
    compelete();
    File file = **new** File(**localPath** \+ **"/"** \+ **fileName**);
    file.delete();
}

**public void** compelete() {
    **sqlTool**.delete(**urlstr**);
}
_//加载完成_  **public void** loadEnd(){
    Toast.makeText(**mContext**,**"加载完成"**,Toast.**_LENGTH\_SHORT_**).show();
}

**public int** getFileSize() {
    **return** **fileSize**;
}

**public int** getCompeleteSize() {
    **return** **globalCompelete**;
}

_//第一次下载初始化_  **private void** initFirst() {
    Log.w(**_TAG_**, **"initFirst"**);
    **try** {
        URL url = **new** URL(**urlstr**);
        HttpURLConnection connection = (HttpURLConnection) url
                .openConnection();
        connection.setConnectTimeout(5000);
        connection.setRequestMethod(**"GET"**);
        **fileSize** \= connection.getContentLength();
        Log.w(**_TAG_**, **"fileSize::"** \+ **fileSize**);
        File fileParent = **new** File(**localPath**);
        **if** (!fileParent.exists()) {
            fileParent.mkdir();
        }
        File file = **new** File(fileParent, **fileName**);
        **if** (!file.exists()) {
            file.createNewFile();
        }
        _// 本地访问文件_  RandomAccessFile accessFile = **new** RandomAccessFile(file, **"rwd"**);
        accessFile.setLength(**fileSize**);
        accessFile.close();
        connection.disconnect();
    } **catch** (Exception e) {
        e.printStackTrace();
    }
    **int** range = **fileSize** / **threadCount**;
    **downloadInfos** \= **new** ArrayList<DownloadInfo>();
    **for** (**int** i = 0; i < **threadCount** \- 1; i++) {
        DownloadInfo info = **new** DownloadInfo(i, i \* range, (i + 1) \* range
                - 1, 0, **urlstr**);
        **downloadInfos**.add(info);
    }
    DownloadInfo info = **new** DownloadInfo(**threadCount** \- 1, (**threadCount** \- 1)
            \* range, **fileSize** \- 1, 0, **urlstr**);
    **downloadInfos**.add(info);
    **sqlTool**.insertInfos(**downloadInfos**);
}

_//自定义下载线程_  **private class** DownloadThread **extends** Thread {

    **private int** **threadId**;
    **private int** **startPos**;
    **private int** **endPos**;
    **private int** **compeleteSize**;
    **private** String **urlstr**;
    **private int** **totalThreadSize**;

    **public** DownloadThread(**int** threadId, **int** startPos, **int** endPos,
                          **int** compeleteSize, String urlstr) {
        **this**.**threadId** \= threadId;
        **this**.**startPos** \= startPos;
        **this**.**endPos** \= endPos;
        **totalThreadSize** \= endPos - startPos + 1;
        **this**.**urlstr** \= urlstr;
        **this**.**compeleteSize** \= compeleteSize;
    }

    @Override

public void run() {
HttpURLConnection connection = null;
RandomAccessFile randomAccessFile = null;
InputStream is = null;
try {
randomAccessFile = new RandomAccessFile(localPath + "/" + fileName, "rwd");
randomAccessFile.seek(startPos + compeleteSize);
URL url = new URL(urlstr);
connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setRequestMethod("GET");
connection.setRequestProperty("Range", "bytes=" + (startPos + compeleteSize) + "-" + endPos);
is = connection.getInputStream();
byte[] buffer = new byte[1024];
int length = -1;
while ((length = is.read(buffer)) != -1) {
randomAccessFile.write(buffer, 0, length);
compeleteSize += length;
Message message = Message.obtain();
message.what = threadId;
message.obj = urlstr;
message.arg1 = length;
mHandler.sendMessage(message);
sqlTool.updataInfos(threadId, compeleteSize, urlstr);
Log.w(TAG, "Threadid::" + threadId + " compelete::" + compeleteSize + " total::" + totalThreadSize);
if (compeleteSize >= totalThreadSize) {
break;
}
if (state != Download_State.Downloading) {
break;
}
}

        } **catch** (Exception e) {
            e.printStackTrace();
        } **finally** {
            **try** {
                **if** (is != **null**) {
                    is.close();
                }
                randomAccessFile.close();
                connection.disconnect();
            } **catch** (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

}

public class DownloadInfo {
/** * 保存每个下载线程下载信息类 * */ private int threadId;// 下载器id private int startPos;// 开始点 private int endPos;// 结束点 private int compeleteSize;// 完成度 private String url;// 下载文件的URL地址 public DownloadInfo(int threadId, int startPos, int endPos,
int compeleteSize, String url) {
this.threadId = threadId;
this.startPos = startPos;
this.endPos = endPos;
this.compeleteSize = compeleteSize;
this.url = url;
}

**public** DownloadInfo() {
}

**public** String getUrl() {
    **return** **url**;
}

**public void** setUrl(String url) {
    **this**.**url** \= url;
}

**public int** getThreadId() {
    **return** **threadId**;
}

**public void** setThreadId(**int** threadId) {
    **this**.**threadId** \= threadId;
}

**public int** getStartPos() {
    **return** **startPos**;
}

**public void** setStartPos(**int** startPos) {
    **this**.**startPos** \= startPos;
}

**public int** getEndPos() {
    **return** **endPos**;
}

**public void** setEndPos(**int** endPos) {
    **this**.**endPos** \= endPos;
}

**public int** getCompeleteSize() {
    **return** **compeleteSize**;
}

**public void** setCompeleteSize(**int** compeleteSize) {
    **this**.**compeleteSize** \= compeleteSize;
}

@Override

public String toString() {
return "DownloadInfo [threadId=" + threadId + ", startPos=" + startPos + ", endPos=" + endPos + ", compeleteSize=" + compeleteSize + "]";
}

}

public class DownloadUtil {
private DownloadHttpTool mDownloadHttpTool;
private OnDownloadListener onDownloadListener;

**private int** **fileSize**;
**private int** **downloadedSize** \= 0;

@SuppressLint(**"HandlerLeak"**)
**private** Handler **mHandler** \= **new** Handler() {

    @Override

public void handleMessage(Message msg) {
// TODO Auto-generated method stub super.handleMessage(msg);
int length = msg.arg1;
synchronized (this) {//加锁保证已下载的正确性 downloadedSize += length;
}
if (onDownloadListener != null) {
onDownloadListener.downloadProgress(downloadedSize);
}
if (downloadedSize >= fileSize) {
mDownloadHttpTool.compelete();
if (onDownloadListener != null) {
onDownloadListener.downloadEnd();
}
}
}

};

**public** DownloadUtil(**int** threadCount, String filePath, String filename,
                    String urlString, Context context) {

    **mDownloadHttpTool** \= **new** DownloadHttpTool(threadCount, urlString,
            filePath, filename, context, **mHandler**);
}

_//下载之前首先异步线程调用ready方法获得文件大小信息,之后调用开始方法_  **public void** start() {
    **new** AsyncTask<Void,Void,Void>() {

        @Override

protected Void doInBackground(Void… arg0) {
// TODO Auto-generated method stub mDownloadHttpTool.ready();
return null;
}

        @Override

protected void onPostExecute(Void result) {
// TODO Auto-generated method stub super.onPostExecute(result);
fileSize = mDownloadHttpTool.getFileSize();
downloadedSize = mDownloadHttpTool.getCompeleteSize();
Log.w("Tag", "downloadedSize::" + downloadedSize);
if (onDownloadListener != null) {
onDownloadListener.downloadStart(fileSize);
}
mDownloadHttpTool.start();
}
}.execute();
}

**public void** pause() {
    **mDownloadHttpTool**.pause();
}

**public void** delete(){
    **mDownloadHttpTool**.delete();
}
**public void** loadend(){
    **mDownloadHttpTool**.loadEnd();
}

**public void** reset(){
    **mDownloadHttpTool**.delete();
    start();
}

**public void** setOnDownloadListener(OnDownloadListener onDownloadListener) {
    **this**.**onDownloadListener** \= onDownloadListener;
}

_//下载回调接口_  **public interface** OnDownloadListener {
    **public void** downloadStart(**int** fileSize);

    **public void** downloadProgress(**int** downloadedSize);_//记录当前所有线程下总和_   **public void** downloadEnd();
}

}

activity使用

public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
@BindView(R.id.start)
Button start;
@BindView(R.id.delete)
Button pause;
@BindView(R.id.loadAll)
Button loadAll;
@BindView(R.id.progressBar)
ProgressBar mProgressBar;
@BindView(R.id.textView)
TextView total;
@BindView(R.id.videoview)
VideoView videoview;

**private int** **max**;
**private** DownloadUtil **mDownloadUtil**;

@Override

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);

    **total**.setText(**"加载:0%"**);


    String urlString = **"http://mirror.aarnet.edu.au/pub/TED-talks/911Mothers\_2010W-480p.mp4"**;
    String localPath = Environment.getExternalStorageDirectory()
            .getAbsolutePath() + **"/local"**;
    **mDownloadUtil** \= **new** DownloadUtil(4, localPath, **"adc.mp4"**, urlString,
            **this**);
    **mDownloadUtil**.setOnDownloadListener(**new** DownloadUtil.OnDownloadListener() {

        @Override

public void downloadStart(int fileSize) {
// TODO Auto-generated method stub Log.w(TAG, "fileSize::" + fileSize);
max = fileSize;
mProgressBar.setMax(fileSize);

        }

        @Override

public void downloadProgress(int downloadedSize) {
// TODO Auto-generated method stub Log.w(TAG, "Compelete::" + downloadedSize);
mProgressBar.setProgress(downloadedSize);

            _//textView进度_  **total**.setText((**long**) downloadedSize \* 100 / **mProgressBar**.getMax() + **"%"**);

        }

        @Override

public void downloadEnd() {
// TODO Auto-generated method stub Log.w(TAG, "ENd");
loadAll.setEnabled(true);
total.setText("加载完成");
}
});

    **start**.setOnClickListener(**new** View.OnClickListener() {

        @Override

public void onClick(View arg0) {
// TODO Auto-generated method stub //开始 start.setEnabled(false);
pause.setEnabled(true);
loadAll.setEnabled(false);
mDownloadUtil.start();

        }
    });
    **pause**.setOnClickListener(**new** View.OnClickListener() {

        @Override

public void onClick(View arg0) {
// TODO Auto-generated method stub //按钮可点击状态 start.setEnabled(true);
pause.setEnabled(true);
//暂停 mDownloadUtil.pause();

        }
    });

    **loadAll**.setOnClickListener(**new** View.OnClickListener() {
        @Override

public void onClick(View view) {

            **mDownloadUtil**.loadend();
            _//获取sd卡目录adc.map4文件_  File file = **new** File(Environment.getExternalStorageDirectory(), **"/local/adc.mp4"**);
            _//判断目录文件是否存在_  **if** (file.exists()) {
                _//视频播放路径_  **videoview**.setVideoPath(file.getPath());
                **if** (!**videoview**.isPlaying()) {
                    _//开始播放_  **videoview**.start();
                }

            } **else** {
                Toast.makeText(MainActivity.**this**, **"文件不存在"**, Toast.**_LENGTH\_SHORT_**).show();

            }
        }
    });


}

}

最后布局也粘出来

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context="com.bwie.wudan20180115.MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:orientation="horizontal" > <Button android:id="@+id/start" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="下载" /> <Button android:id="@+id/delete" android:layout_width="wrap_content" android:layout_height="wrap_content" android:enabled="false" android:text="暂停" /> <Button android:id="@+id/loadAll" android:layout_width="wrap_content" android:layout_height="wrap_content" android:enabled="false" android:text="下载完成" /> LinearLayout>

<**ProgressBar**  **android****:id=****"@+id/progressBar"**  **style=****"?android:attr/progressBarStyleHorizontal"**  **android****:layout\_width=****"fill\_parent"**  **android****:layout\_height=****"7.5dp"**  **android****:layout\_marginRight=****"8dp"**  **android****:layout\_marginLeft=****"8dp"**  **android****:max=****"100"**  **android****:progress=****"100"**  **android****:visibility=****"visible"** />

<**TextView**  **android****:id=****"@+id/textView"**  **android****:layout\_width=****"wrap\_content"**  **android****:layout\_height=****"wrap\_content"**  **android****:layout\_gravity=****"center"**  **android****:layout\_marginTop=****"8dp"**  **android****:text=****"TextView"** />



<**VideoView**  **android****:id=****"@+id/videoview"**  **android****:layout\_marginTop=****"30dp"**  **android****:layout\_width=****"match\_parent"**  **android****:layout\_height=****"200dp"** />

</LinearLayout>