Android 下载模块,使用Xutils3 的下载功能,Activity和services数据交互,listview显示下载进度

来源:互联网 发布:mac电脑重装系统 编辑:程序博客网 时间:2024/05/16 09:12

临近过年,今天将花了两天时间写的下载模块贡献出来,以前我也是天天需要什么功能,就天天查百度,现在自己也能开源一点东西了,也是非常很开心的,hahaha。

这里写图片描述

离职前写了音乐播放器的音乐下载界面,自己封装了下载线程,并使用多线程断点续传下载音乐,感觉多线程下载音乐也并不比单线程下载快多少,就一个4M的音乐来说,开3条线程下了46s,单线程下了60s,如果同时现在3个音乐,那么多线程下载相当于开了9 个线程,速度尽然和单线程的速度持平,有时候还会慢于3条线程,于是我放弃了多线程断点续传,还是老老实实写单线程的吧(- -!我觉得应该是我的下载线程的毛病,哈哈)。。。

于是今天就用了xutils3的网络访问模块,实现的需求是。
1、APP关闭后(kill),重新开启时,能记录当前下载进度,断点续传。
2、音乐能增删,暂停,显示下载速度和下载进度。

其实都写好了哈,主要说说编码思路。

通过Acitivity的bindService,可以调用service的bind来传递需要下载的文件信息到service,service通过调用xutils的带进度的下载模块,来回调下载信息,同时service通过下载观察者不断观察下载进度,将进度传递给Activity,之后setAdapter即可。

里面最主要的东西就是DownloadService,所有的代码逻辑都是这个类处理的。

传递下载文件信息的类MainActivity

这个类有个问题
就是文件名的获取是根据Url倒叙截取/后面的文件来命名的,如果你的url不符合规则,请自行定义。

    private String getfileName(String url) {//根据下载地址给下载文件命名        try{            return url.substring(url.lastIndexOf("/") + 1);        }catch (Exception ex){            return mBinder.getCurrentListSize()+"";//返回一个数字        }    }

权限问题
代码中加了6.0的动态权限判断,因为要操作SD卡,所以这里也要注意。
因为我没有6.0的测试手机,所以这里虽然都写了,但是只是作为提示使用,需要你自己更改逻辑位置,如果是6.0以下的,并没有问题的 。

Handler handler = new Handler(){        @Override        public void handleMessage(Message msg) {            super.handleMessage(msg);            switch (msg.what){                case 0:                    Toast.makeText(getBaseContext(),"已获取SD卡权限",Toast.LENGTH_SHORT).show();                    //6.0系统在这里进行service的初始化                    break;                case 1:                    Toast.makeText(getBaseContext(),"SD卡权限未开启",Toast.LENGTH_SHORT).show();                    break;            }        }    };    //判断权限方法    private void initCheckSelfPermission() {        //判断是否有内存卡权限        CheckSelfPermission.getSDCard(this);    }    //动态权限回调    @Override    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {        super.onRequestPermissionsResult(requestCode, permissions, grantResults);        doNext(requestCode,grantResults);    }    //动态权限回调,yes和no点击    private void doNext(int requestCode, int[] grantResults) {        if (requestCode == DownLoadConstant.WRITE_EXTERNAL_STORAGE_REQUEST_CODE) {            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {                LogUtils.d("权限获取成功");                handler.sendEmptyMessage(0);            } else {                handler.sendEmptyMessage(1);            }        }    }

最关键的是DownloadService ,bind里有对外提供的增删改查方法
在下载的过程中,有缓存GetFileSharePreance 不断在保存下载的urls下载地址列表和文件下载进度,为APP下次重启提供断点,里面有详细注释。

/** * 作者:朱亮 on 2017/1/17 15:36 * 邮箱:171422696@qq.com * 下载服务类,执行下载任务,并将进度传递到Activity中(这里用一句话描述这个方法的作用) */public class DownloadService extends Service {    public static final String ACTION_START = "ACTION_START";    public static final String ACTION_STOP = "ACTION_STOP";    public static final String ACTION_DELETE = "ACTION_DELETE";    private List<OnDownLoadBackListener> loadBackListeners = new ArrayList<OnDownLoadBackListener>();//注册一个下载观察者    private static final int DOWNLOADSERVICEID = 1;//下载监听传递值    private FileInfo fileInfo;//下载对象    private MyBinder myBinder = new MyBinder();    private ArrayList<FileInfo> mCurretList;//当前服务中的正在下载列表    private ArrayList<FileInfo> mCurretListOut;//输出的下载列表    private GetFileSharePreance preance;    private HashMap<Integer,DownloanThread> map;//创建动态对象    @Override    public IBinder onBind(Intent intent) {        return myBinder;    }    @Override    public void onCreate() {        mCurretList = new ArrayList<>();        mCurretListOut = new ArrayList<>();        map = new HashMap<Integer,DownloanThread>();        preance = BaseApplication.getFileSharePreance();//获取缓存实例,这里的缓存主要用在APP关闭后,重新进入下载列表后显示使用        super.onCreate();    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        if (intent != null) {            // 获得Activity穿来的参数            fileInfo = (FileInfo) intent.getSerializableExtra("fileInfo");            if (ACTION_START.equals(intent.getAction())) {                startDownLoad(fileInfo);//开始下载            } else if (ACTION_STOP.equals(intent.getAction())) {                stopDownLoad(fileInfo);//停止下载            } else if (ACTION_DELETE.equals(intent.getAction())) {                deleteDownLoad(fileInfo);//删除下载            }        }        return super.onStartCommand(intent, flags, startId);    }    //开始下载逻辑处理方法    private void startDownLoad(FileInfo fileInfo) {        if (fileInfo != null && !TextUtils.isEmpty(fileInfo.getUrl())) {            preance.setUrlList(mCurretList);//从缓存地址集合中移除这一条(保存最新的地址集合)            map.put(fileInfo.getId(),new DownloanThread());            map.get(fileInfo.getId()).Download(fileInfo);//开始下载            SPUtils.setLong(getApplicationContext(), SPKey.CURRENTLIST_SIZE, fileInfo.getId());//设置下载ID自增            sendHandler();        }    }    //停止下载逻辑处理方法    private void stopDownLoad(FileInfo fileInfo) {        if (fileInfo != null && !TextUtils.isEmpty(fileInfo.getUrl())) {            if (map.get(fileInfo.getId()) != null) {                map.get(fileInfo.getId()).downLoadCancel();//下载线程停止            }            sendHandler();        }    }    //删除下载逻辑处理方法    private void deleteDownLoad(FileInfo fileInfo) {        if (fileInfo != null && !TextUtils.isEmpty(fileInfo.getUrl())) {            for (int x = 0; x < mCurretList.size(); x++) {//如果是正在下载的列表,先暂停下载。                if (mCurretList.get(x).getUrl().equals(fileInfo.getUrl())) {//如果匹配到了                    preance.delete(mCurretList.get(x).getUrl());//删除下载缓存                    if(map.get(fileInfo.getId()) != null){                        map.get(fileInfo.getId()).downLoadCancel();//从下载线程停止                    }                    mCurretList.remove(mCurretList.get(x));//从下载列表移除这条                    preance.setUrlList(mCurretList);//从缓存地址集合中移除这一条(保存最新的地址集合)                    preance.delete(fileInfo.getUrl());//删除缓存数据                }            }            sendHandler();        }    }    public class MyBinder extends Binder {        //注册一个下载观察者        public void registDownLoadListener(OnDownLoadBackListener loadBackListener) {            loadBackListeners.add(loadBackListener);        }        //取消一个观察者        public void unregistDownLoadListener(OnDownLoadBackListener loadBackListener) {            loadBackListeners.remove(loadBackListener);        }        //设置当前的下载列表(会清空当前的下载列表哦,慎用,如果需要请用  appendToCurrentList())        public void setCurrentList(ArrayList<FileInfo> list) {            mCurretList.clear();            if (list != null) {                mCurretList.addAll(list);            }        }        //获取当前的正在下载的所有下载信息列表        public ArrayList<FileInfo> getCurrentList() {            return mCurretList;        }        //从缓存中获取下载信息        public ArrayList<FileInfo> getCurrentListFShareP(){            ArrayList<FileInfo> infos = new ArrayList<>();            ArrayList<String> list = preance.getUrlList();//先获取下载url集合            for(int x = 0 ; x < list.size() ; x++){//根据url地址查询下载的缓存数据                infos.add(preance.getFilePre(list.get(x)));            }            setCurrentList(infos);            return infos;        }        //下载次数自增        public int getCurrentListSize() {            long l = SPUtils.getLong(getApplicationContext(), SPKey.CURRENTLIST_SIZE);            return (int) l + 1;        }        //添加当前的下载列表(相同下载地址文件过滤)        public void appendToCurrentList(FileInfo info) {            if (info != null) {                // 只添加当前下载列表中没有的                boolean existed = false;                for (int j = 0; j < mCurretList.size(); j++) {                    if ((mCurretList.get(j).getUrl()).equals(info.getUrl())) {                        existed = true;                    }                }                if (!existed) {                    mCurretList.add(info);                    startDownLoad(info);//开始下载                }            }        }        //停止下载        public void stopDownLoad(FileInfo fileInfo) {            if (fileInfo != null && !TextUtils.isEmpty(fileInfo.getUrl())) {                stopDownLoad(fileInfo);            }        }        //删除当前的某一个列表        public void deleyeCurrentList(FileInfo fileInfo) {            deleteDownLoad(fileInfo);        }        //删除所有的下载列表信息(未编写)        public void deleteAll(){        }    }    private Handler handler = new Handler() {        @Override        public void handleMessage(Message msg) {            switch (msg.what) {                case DOWNLOADSERVICEID:                    for (int x = 0; x < loadBackListeners.size(); x++) {//有多少个界面在观察下载数据                        mCurretListOut.clear();                        for(int j = 0 ; j < mCurretList.size() ;j++){//正在下载列表中的数据                            if(map.get(mCurretList.get(j).getId()) == null){//假如下载线程没有开启                                mCurretListOut.add(mCurretList.get(j));                            }else {//从下载线程中取数据                                mCurretListOut.add(map.get(mCurretList.get(j).getId()).getFileInfo());                            }                        }                        loadBackListeners.get(x).onDownloadSize(mCurretListOut);//传递给不同的观察者                    }                    //当下载的时候,延迟不断执行这句话,让所有的观察者不至于失去下载的各种信息,这里控制视图更新频率                    handler.sendEmptyMessageDelayed(                            DOWNLOADSERVICEID, 1000);                    break;                default:                    super.handleMessage(msg);            }        }    };    //发送hanlde    private void sendHandler(){        if (!handler.hasMessages(DOWNLOADSERVICEID)) {//如果hanlde的观察者没有了,重新发送一个            handler.sendEmptyMessage(DOWNLOADSERVICEID);        }    }}

下载的实际类DownloanThread,名字看起来是个线程,其实不是,只是将实际下载类分离出来,调用xutils的下载方法和取消下载方法,提供一个获取下载文件详细信息方法。

//获取下载中的文件信息    public FileInfo getFileInfo(){        return new FileInfo(fileInfo.getId(),fileInfo.getUrl(),fileInfo.getFileName(),length,downlenth,downType);    }

在这里回调文件的下载信息

  cancelable = x.http().get(params, new Callback.ProgressCallback<File>() {            @Override            public void onWaiting() {                LogUtils.e("等待中...");                downType = 0;            }            @Override            public void onStarted() {                LogUtils.e("开始下载。。。");                downType = 1;            }            @Override            public void onLoading(long total, long current, boolean isDownloading) {                LogUtils.e("tital = "+total + "   current = "+current + "  isDownLoading = "+isDownloading);                length = (int) total;                downlenth = (int) current;                downType = 2;                preance.update(new FileInfo(info.getId(),info.getUrl(),info.getFileName(),(int)total,(int)current,downType));            }            @Override            public void onSuccess(File result) {                LogUtils.e("下载成功");//在这里下载成功的数据写进数据库里即可(已下载界面的数据从这里来的,哈哈,那个界面自己写)                downType = 3;            }            @Override            public void onError(Throwable ex, boolean isOnCallback) {                LogUtils.e("下载失败");                downType = 4;            }            @Override            public void onCancelled(CancelledException cex) {                LogUtils.e("下载取消");                downType = 5;            }            @Override            public void onFinished() {                LogUtils.e("下载完成");            }        });

最后在Adapter可以根据downType来显示下载过程中的状态了

这里写图片描述

这里写图片描述

这里写图片描述

已下载的界面没有写代码,偷个懒,请自己写哈

源码下载

0 0