android后台下载服务的完成及事项

本文介绍了如何在Android中实现后台下载服务,利用OkHttp3和ButterKnife框架。首先在build.gradle中添加依赖,然后定义回调接口,使用AsyncTask处理异步下载,创建DownloadService继承Service。在MainActivity中绑定服务,通过按钮控制下载的开始、暂停和取消。服务的声明和生命周期管理,特别是onDestroy()中的解绑操作,防止内存泄漏。此外,还提到了PendingIntent在通知栏点击进入应用的功能以及如何正确停止服务。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


android后台下载功能的完成

首先是基本框架的搭建,用到的是okhttp3已经注入框架butterknife,所以在build.gradle里面添加
compile ‘com.jakewharton:butterknife:7.0.1’
compile ‘com.squareup.okhttp3:okhttp:3.4.1’
两个依赖包。

  1. 第一步:定义一个回调接口
  public interface DownloadListener {

    void onProgress(int progress);`//`通知当前进度

    void onSuccess();

    void onFailed();

    void onPaused();

    void onCanceled();
}

2:使用继承AsyncTask来处理异步消息,注意指定三个泛型参数<Params,Progress,Result>
.Params:执行AsyncTask时需要传入的参数,在后台任务中使用。
.Progress : 需要显示进度的时候用这里指定的泛型作单位。
.Result : 用这里指定的泛型作为结果的返回值。

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_PAUSED = 2;//下载暂停
    public static final int TYPE_CANCELED = 3;//下载取消

   //在构造函数中通过这个参数回调
    private DownloadListener downloadListener;

    private boolean isCanceled = false;
    private boolean isPaused = false;

    private int lastProgress;

    public DownloadTask(DownloadListener Listener) {
        this.downloadListener = Listener;
    }

   //后台执行具体的下载逻辑
    @Override
    protected Integer doInBackground(String... strings) {
        InputStream is = null;
        RandomAccessFile savedFile = null;
        File file = null;
        try {
            long downloadedLength = 0;//记录下载文件的长度
            String downloadUrl = strings[0];
            String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
            String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
            file = new File(directory + fileName);
            if (file.exists()) {
                downloadedLength = file.length();
            }
            long contentLength = getContentLength(downloadUrl);
            if (contentLength == 0) {
                return TYPE_FAILED;
            } else if (contentLength == downloadedLength) {              //已下载字节和总文件字节长度相等,则下载成功
                return TYPE_SUCCESS;
            }
            OkHttpClient client = new OkHttpClient();
            //断点下载,指定从哪个字节开始上一次的下载
            Request request = new Request.Builder().addHeader("RANGE", "bytes = " + downloadedLength + "-").url(downloadUrl).build();
            Response response = client.
                           newCall(request).execute();
            if (response != null) {
                is = response.body().byteStream();
                savedFile = new RandomAccessFile(file, "rw");
                savedFile.seek(downloadedLength);//跳过已下载字节
                byte[] b = new byte[1024];
                int total = 0;
                int len;
                while ((len = is.read(b)) != -1) {
                    if (isCanceled) {
                        return TYPE_CANCELED;
                    } else if (isPaused) {
                        return TYPE_PAUSED;
                    } else {
                        total += len;
                        savedFile.write(b, 0, len);
                        //计算已下载的百分比
                        int progress = (int) ((total + downloadedLength) * 100 / contentLength);
                        publishProgress(progress);
                    }
                }
                response.body().close();
                return TYPE_SUCCESS;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (savedFile != null) {
                    savedFile.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) {
           //回调方法中的onProgress
            downloadListener.onProgress(progress);
            lastProgress = progress;
        }
    }

    //通知最终的下载结果
    //用的listener来回调方法。
    @Override
    protected void onPostExecute(Integer status) {
        switch (status) {
            case TYPE_SUCCESS:
                downloadListener.onSuccess();
                break;
            case TYPE_FAILED:
                downloadListener.onFailed();
                break;
            case TYPE_PAUSED:
                downloadListener.onPaused();
                break;
            case TYPE_CANCELED:
                downloadListener.onCanceled();
                break;
            default:
                break;
        }
    }

    public void pauseDownload(){
        isPaused = 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.close();
            return contentLength;
        }
        return 0;
    }
}

-3.:写服务DownloadService 继承Service
public class DownloadService extends Service {

    private DownloadTask downloadTask;

    private String downloadUrl;

    private DownloadListener listener = new DownloadListener() {
        @Override
        public void onProgress(int progress) {
            getNotificationManager().notify(1, getNotification("Downloading...", progress));
        }

        @Override
        public void onSuccess() {
            downloadTask = null;
            //下载成功将前台服务通知关闭,并创建一个下载成功的通知
            stopForeground(true);
            getNotificationManager().notify(1, getNotification("Download Success", -1));
            Toast.makeText(DownloadService.this, "下载成功.在binder里面回调方法里面", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onFailed() {
            downloadTask = null;
            //下载成功将前台服务通知关闭,并创建一个下载失败的通知
            stopForeground(true);
            getNotificationManager().notify(1, getNotification("Download Failed", -1));
            Toast.makeText(DownloadService.this, "Download Failed", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onPaused() {
            downloadTask = null;
            Toast.makeText(DownloadService.this, "Paused", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onCanceled() {
            downloadTask = null;
            stopForeground(true);
            Toast.makeText(DownloadService.this, "这里只取消不删除", Toast.LENGTH_SHORT).show();
        }
    };

    private DownloadBinder mBinder = new DownloadBinder();

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

   //为了让服务和活动之间联系通信,创建Binder
   //提供三个,开始,暂停,取消的方法
    class DownloadBinder extends Binder {
        public void startDownload(String url) {
            if(downloadTask == null){
                downloadUrl = url;
                downloadTask = new DownloadTask(listener);
                downloadTask.execute(downloadUrl);
   startForeground(1,getNotification("Downloading...",0));
   Toast.makeText(DownloadService.this, "Downloading...开始下载binder里面", Toast.LENGTH_SHORT).show();
            }
        }

        public void pauseDowload(){
            if(downloadTask != null){
                downloadTask.pauseDownload();
            }
        }

        public void cancelDownload(){
            if(downloadTask != null){
                downloadTask.cancelDownload();
            }else {
             // 取消下载时,需要将之前的文件都删除。
                if(downloadUrl!= 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();
                    }
                    getNotificationManager().cancel(1);
                    stopForeground(true);
                    Toast.makeText(DownloadService.this, "这里取消下载也删除了", Toast.LENGTH_SHORT).show();
                }
            }
        }

    }

    private Notification getNotification(String title, int progress) {
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pi = 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));
        builder.setContentIntent(pi);
        builder.setContentTitle(title);
        builder.setPriority(NotificationCompat.PRIORITY_HIGH);//设置通知重要程度,min,low,默认,high,max
        if (progress > 0) {
            builder.setContentText(progress + "%");
            builder.setProgress(100, progress, false);//三个参数,最大进度,当前进度,是否使用模糊进度条
        }
        return builder.build();
    }

    private NotificationManager getNotificationManager() {
        return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    }

    public DownloadService() {
    }


}

***

布局文件就是三个button,分别为开始,暂停,取消。

MainActivity中:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

@Bind(R.id.btn_start)
Button btnStart;
@Bind(R.id.btn_pause)
Button btnPause;
@Bind(R.id.btn_cancel)
Button btnCancel;
private DownloadService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
downloadBinder = (DownloadService.DownloadBinder) iBinder;
}

    @Override
    public void onServiceDisconnected(ComponentName componentName) {

    }
};

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);btnStart.setOnClickListener(this);
btnPause.setOnClickListener(this);
btnCancel.setOnClickListener(this);

    Intent intent = new Intent(this,DownloadService.class);
    startService(intent);//开始服务
    bindService(intent,connection,BIND_AUTO_CREATE);//绑定服务

    //查看权限,如果没有则申请权限
    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 view) {
if(downloadBinder ==null){
return;
}
switch (view.getId()){
case R.id.btn_start:
String url = “https://raw.githubusercontent.com/guolindev/eclipse/master/” +
“eclipse-inst-win64.exe”;
downloadBinder.startDownload(url);
break;
case R.id.btn_pause:
downloadBinder.pauseDowload();
break;
case R.id.btn_cancel:
downloadBinder.cancelDownload();
break;
default:
break;
}
}

进行到这一步,后台的通知绑定活动,也就可以实现功能了。

总结:首先,需要申明两个权限: 


<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.
WRITE_EXTERNAL_STORAGE"/>

服务也是需要申明的

 <service
            android:name=".DownloadService"
            android:enabled="true"
            android:exported="true"/>

在最后还要记得重写onDestroy()方法,解绑操作,否则会内存泄露。

@Override
    protected  void onDestroy(){
        super.onDestroy();
        unbindService(connection);
    }

PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);中这行代码的主要功能是点击通知里面能进入相应的app.然后在 builder.setContentIntent(pi);中完成。

stopSelf(1);这样才能停止一个服务,最好是传入参数id 就是创建服务的id,只要使用 stopSelf(1);方法 就会默认跳到onDestroy方法中。服务一定要记得在onDestroy()方法中解绑服务,在取消按钮上,要构建intent对象,stopService(intent);才行。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值