实现下载功能
compile 'com.squareup.okhttp3:okhttp:3.8.0'
定义一个接口:DownloadListener
public interface DownloadListener {
void onProgress(int progress);//下载进度
void onSuccess();//成功
void onFailed();//失败
void onPaused();//暂停
void onCanceled();//取消
}
利用多线程来实现下载功能
public class DownloadTask extends AsyncTask<String,Integer,Integer> {
public static final int TYPE_SUCCESS = 0;
public static final int TYPE_FAILED = 1;
public static final int TYPE_PASUED = 2;
public static final int TYPE_CANCELED =3;
private DownloadListener mListener;
private boolean isCanceled = false;
private boolean isPasued = false;
private int lastProgress;
public DownloadTask(DownloadListener downloadListener){
this.mListener = downloadListener;
}
@Override
protected Integer doInBackground(String... params) {
InputStream is = null;
//利用RandomAccessFile实现文件的下载
RandomAccessFile saveFile = null;
File file = null;
try{
long downloadLength = 0;//下载的实际大小
String downloadUrl = params[0];
String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.getPath();
file = new File(directory+fileName);
if (file.exists()){
downloadLength = file.length();
}
long contentLength = getContentLength(downloadUrl);//文件的实际大小
if (contentLength == 0){
//下载失败
return TYPE_FAILED;
}else if (contentLength == downloadLength){
//下载成功
return TYPE_SUCCESS;
}
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
//断点下载
.addHeader("RANGE","bytes="+downloadLength+"-")
.url(downloadUrl)
.build();
Response response = client.newCall(request).execute();
if (response != null){
is = response.body().byteStream();
saveFile = new RandomAccessFile(file,"rw");
saveFile.seek(downloadLength);//跳过已下载的字节
byte[] b = new byte[1024];
int total = 0;
int len;
while ((len=is.read(b))!=-1){
if (isCanceled){
return TYPE_CANCELED;
}else if (isPasued){
return TYPE_PASUED;
}else {
total = len+total;
saveFile.write(b,0,len);
//计算下载百分比
int progress = (int)((total+downloadLength)*100/contentLength);
//从子线程切换到UI线程
publishProgress(progress);
}
}
response.body().close();
return TYPE_SUCCESS;
}
}catch (Exception e){
e.printStackTrace();
}finally {
try{
if (is != null){
is.close();
}
if (saveFile != null){
saveFile.close();
}
if (isCanceled && file!=null){
file.delete();
}
}catch (Exception e){
e.printStackTrace();
}
}
return TYPE_FAILED;
}
@Override
protected void onProgressUpdate(Integer... values) {
int progress = values[0];
if (progress>lastProgress){
mListener.onProgress(progress);//传递下载进度
lastProgress = progress;//更新下载进度
}
}
@Override
protected void onPostExecute(Integer integer) {
switch (integer){
case TYPE_SUCCESS:
mListener.onSuccess();
break;
case TYPE_FAILED:
mListener.onFailed();
break;
case TYPE_PASUED:
mListener.onPaused();
break;
case TYPE_CANCELED:
mListener.onCanceled();
break;
default:
break;
}
}
public void pauseDownload(){
isPasued = true;
}
public void cancelDownload(){
isCanceled = true;
}
private long getContentLength(String downloadUrl)throws IOException{
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(downloadUrl)
.build();
Response response = client.newCall(request).execute();
if (response != null && response.isSuccessful()){
long contentLength = response.body().contentLength();
response.body().close();
return contentLength;
}
return 0;
}
}
重写doInBackground(),onProgressUpdate(),和onPostExecute();
doInBackground():来执行具体的下载操作。
onProgressUpdate():来更新界面上的下载进度。
onPostExecute():用于通知最后的下载结果。
分析:
doInBackground():首先我们在参数里获取了下载的地址,并且根据地址。解析出来文件的名字,然后将文件下载到Environment.DIRECTORY_DOWNLOADS里,也就是SD卡的下载目录,在这之前我们要判断是不是存在已经下载的文件了,如果存在就读取文件已经下载的字节数,这样就启动断点的下载功能。
onProgressUpdate():获取当前的下载进度,然后和上一次的下载进度进行对比,如果变化就调用DownloadListener的onProgress()来通知更新下载进度。
onPostExecute():根据参数中传入的下载进度,进行回调。
下载的服务:
为了确保下载在后台运行。
public class DownloadService extends Service {
private DownloadTask mDownloadTask;
private String downloadUrl;
private DownloadListener mDownloadListener = new DownloadListener() {
@Override
public void onProgress(int progress) {
mNotificationManager().notify(1,getNotification("Downloading...",progress));
}
@Override
public void onSuccess() {
mDownloadTask = null;
//下载成功将前台服务关闭,并创建一个下载通知
stopForeground(true);
mNotificationManager().notify(1,getNotification("下载成功",-1));
Toast.makeText(DownloadService.this,"下载成功",Toast.LENGTH_SHORT).show();
}
@Override
public void onFailed() {
mDownloadTask = null;
//类似上面
mNotificationManager().notify(1,getNotification("下载失败",-1));
Toast.makeText(DownloadService.this,"下载失败",Toast.LENGTH_SHORT).show();
}
@Override
public void onPaused() {
mDownloadTask = null;
Toast.makeText(DownloadService.this,"暂停",Toast.LENGTH_SHORT).show();
}
@Override
public void onCanceled() {
mDownloadTask = null;
stopForeground(true);
Toast.makeText(DownloadService.this,"下载取消",Toast.LENGTH_SHORT).show();
}
};
private DownloadBinder mDownloadBinder = new DownloadBinder();
@Override
public IBinder onBind(Intent intent) {
return mDownloadBinder;
}
class DownloadBinder extends Binder{
public void startDownload(String url){
if (mDownloadTask == null){
downloadUrl = url;
mDownloadTask = new DownloadTask(mDownloadListener);
mDownloadTask.execute(downloadUrl);
startForeground(1,getNotification("Download...",0));
Toast.makeText(DownloadService.this,"下载中...",Toast.LENGTH_SHORT).show();
}
}
public void pauseDownload(){
if (mDownloadTask!=null){
mDownloadTask.pauseDownload();
}
}
public void cancelDownload(){
if (mDownloadTask!=null){
mDownloadTask.cancelDownload();
}else {
if (mDownloadTask!=null){
//取消下载要删除文件
String filename = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.getPath();
File file = new File(directory+filename);
if (file.exists()){
file.delete();
}
mNotificationManager().cancel(1);
stopForeground(true);
Toast.makeText(DownloadService.this,"取消下载",Toast.LENGTH_SHORT).show();
}
}
}
}
private NotificationManager mNotificationManager(){
return (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
}
private Notification getNotification(String title,int progress){
Intent intent = new Intent(this,MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,intent,0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher_round));
builder.setContentIntent(pendingIntent);
builder.setContentTitle(title);
if (progress>0){
builder.setContentText(progress+"%");
builder.setProgress(100,progress,false);
}
return builder.build();
}
}
让服务和活动进行通信,创建了一个DownloadBinder类,在startDownload()方法里,创建了DownloadTask的实例,把DownloadListener的实例传入然后调用execute()来进行下载,并且进URL传入。其余的都蛮简单就不需赘述了。
修改activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/start_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="开始下载"/>
<Button
android:id="@+id/pause_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="暂停下载"/>
<Button
android:id="@+id/cancel_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="取消下载"/>
</LinearLayout>
MainActivity:
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private DownloadService.DownloadBinder mDownloadBinder;
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mDownloadBinder = (DownloadService.DownloadBinder)service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button start_download = (Button)findViewById(R.id.start_download);
Button pause_download = (Button)findViewById(R.id.pause_download);
Button cancel_download = (Button)findViewById(R.id.cancel_download);
start_download.setOnClickListener(this);
pause_download.setOnClickListener(this);
cancel_download.setOnClickListener(this);
//启动服务
Intent intent = new Intent(this,DownloadService.class);
startService(intent);
bindService(intent,mServiceConnection,BIND_ABOVE_CLIENT);
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
}
}
@Override
public void onClick(View v) {
if (mDownloadBinder == null){
return;
}
switch (v.getId()){
case R.id.start_download:
//下载QQ
String url = "http://p.gdown.baidu.com/f0c57eb4a1f400f93ec32dc86c49802a08bc43d6208169d3d8b85f482de492224dd4e14dc8d793100fb5d245b7e2cb55f0a3e114cd46ac707b819c87ff3dd652677b1f669036271811fa48439815e351fd8b56c07d81c340b2417e9282bf4fbedc7d9f04d722c7db";
mDownloadBinder.startDownload(url);
break;
case R.id.pause_download:
mDownloadBinder.pauseDownload();
break;
case R.id.cancel_download:
mDownloadBinder.cancelDownload();
break;
default:
break;
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
switch (requestCode){
case 1:
if (grantResults.length>0&&grantResults[0]!=PackageManager.PERMISSION_GRANTED){
Toast.makeText(this,"拒绝权限无法使用功能",Toast.LENGTH_SHORT).show();
finish();
}
break;
default:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unbindService(mServiceConnection);
}
}
最后在AndroidMainfest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.eason.downtest">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<service
android:name=".DownloadService"
android:enabled="true"
android:exported="true">
</service>
</application>
</manifest>
效果图: