移动架构-网络访问框架设计

这里通过设计并实现一个网络框架的搭建,下载

设计思路

支持请求JSON文本类型,这样的话需要一个处理JSON的类,这里使用fastjson依赖
发出请求时,调用层不用关心上传参数,这里就需要对请求进行封装,并且将其封装的参数传递给框架,由框架去处理
获取数据后在框架内完成json数据的解析
回调时,调用层只需传入json的对应响应类,回调响应结果给主线程
并且需要支持高并发请求,使用线程池和人物列队

JSON数据访问解析实现步骤

首先定义三个接口,用于获取网络,处理数据和调用层回调
获取网络主要涉及到设置url,获取网络,监听接口,设置请求参数

//获取网络
public interface IHttpService {
    //设置url
    void setUrl(String url);
    //执行获取网络
    void excute();
    //设置处理接口
    void setHttpListener(IHttpListener httpListener);
    //设置请求参数
    void setRequestData(byte[] requestData);
}

处理数据会有成功和失败

//处理结果
public interface IHttpListener {
    //处理结果 回调
    void onSuccess(HttpEntity httpEntity);
    void onFail();
}

回调与数据处理结果相统一

//回调
public interface IDataListener<M> {
    //回调结果给调用层
    void onSuccess(M m);
    void onFail();
}

然后实现以上接口,完成网络的获取和数据的处理
完成网络的获取,在调用的时候完成相应的参数设置

public class JSONHttpService implements IHttpService {

    private IHttpListener httpListener;
    private HttpClient httpClient = new DefaultHttpClient();
    private HttpPost httpPost;
    private String url;
    private byte[] requestData;
    //httpClient获取网络回调
    private HttpResponseHandler httpResponseHandler = new HttpResponseHandler();

    @Override
    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    public void excute() {
        httpPost = new HttpPost(url);
        ByteArrayEntity byteArrayEntity = new ByteArrayEntity(requestData);
        httpPost.setEntity(byteArrayEntity);
        try {
            httpClient.execute(httpPost,httpResponseHandler);
        } catch (IOException e) {
            httpListener.onFail();
            e.printStackTrace();
        }
    }

    @Override
    public void setHttpListener(IHttpListener httpListener) {
        this.httpListener = httpListener;
    }

    @Override
    public void setRequestData(byte[] requestData) {
        this.requestData = requestData;
    }

    private class HttpResponseHandler extends BasicResponseHandler{
        @Override
        public String handleResponse(HttpResponse response) throws IOException {
            //响应码
            int code = response.getStatusLine().getStatusCode();
            if(code == 200){
                httpListener.onSuccess(response.getEntity());
            }else {
                httpListener.onFail();
            }
            return super.handleResponse(response);
        }
    }
}

完成数据的处理,这里需要注意一点的是响应类的传入以及通知主线程的过程

public class JSONDealListener<M> implements IHttpListener {

    private Class<M> response;
    private IDataListener<M> dataListener;

    Handler handler = new Handler(Looper.getMainLooper());

    public JSONDealListener(Class<M> response, IDataListener<M> dataListener) {
        this.response = response;
        this.dataListener = dataListener;
    }

    @Override
    public void onSuccess(HttpEntity httpEntity) {
        InputStream inputStream = null;
        try {
            inputStream = httpEntity.getContent();
            //得到网络返回的数据
            String content = getContent(inputStream);
            final M m = JSON.parseObject(content, response);
            handler.post(new Runnable() {
                @Override
                public void run() {
                    dataListener.onSuccess(m);
                }
            });
        } catch (IOException e) {
            dataListener.onFail();
        }
    }

    private String getContent(InputStream inputStream) {
        BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
        StringBuilder sb = new StringBuilder();
        String line = null;
        try {
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
        } catch (IOException e) {
            dataListener.onFail();
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                dataListener.onFail();
                e.printStackTrace();
            }
        }
        return sb.toString();
    }

    @Override
    public void onFail() {
        dataListener.onFail();
    }
}

封装请求体,使调用更加方便

public class RequestHolder<T> {
    //执行下载类
    private IHttpService httpService;
    //获取数据,回调结果类
    private IHttpListener httpListener;
    //请求参数对应的实体
    private T requestInfo;
    //链接
    private String url;

    public IHttpService getHttpService() {
        return httpService;
    }

    public void setHttpService(IHttpService httpService) {
        this.httpService = httpService;
    }

    public IHttpListener getHttpListener() {
        return httpListener;
    }

    public void setHttpListener(IHttpListener httpListener) {
        this.httpListener = httpListener;
    }

    public T getRequestInfo() {
        return requestInfo;
    }

    public void setRequestInfo(T requestInfo) {
        this.requestInfo = requestInfo;
    }

    public String getUrl() {
        return url;
    }

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

之后就是设计线程池,用来执行任务,需要建立子线程

public class HttpTask<T> implements Runnable{

    private IHttpService httpService;
    public HttpTask(RequestHolder<T> requestHolder){
        httpService = requestHolder.getHttpService();
        httpService.setHttpListener(requestHolder.getHttpListener());
        httpService.setUrl(requestHolder.getUrl());
        T request = requestHolder.getRequestInfo();
        String requestInfo = JSON.toJSONString(request);
        try {
            httpService.setRequestData(requestInfo.getBytes("UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        httpService.excute();
    }
}

实现任务调度池和线程池的创建,这里仿照的使AsyncTask里面使用的线程池

public class ThreadPoolManager {
    private static final String TAG = "cj5785";
    private static ThreadPoolManager instance = new ThreadPoolManager();
    //定义线程池
    private ThreadPoolExecutor threadPoolExecutor;
    //线程池参数初始化
    private LinkedBlockingQueue<Future<?>> taskQueue = new LinkedBlockingQueue<>();
    private RejectedExecutionHandler handler = new RejectedExecutionHandler() {
        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            try {
                taskQueue.put(new FutureTask<Object>(r, null));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    };

    //创建线程池
    private ThreadPoolManager() {
        threadPoolExecutor = new ThreadPoolExecutor(4, 10, 10,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(4), handler);
        threadPoolExecutor.execute(runnable);
    }

    //添加执行任务
    public <T> void execute(FutureTask<T> futureTask) throws InterruptedException {
        taskQueue.put(futureTask);
    }

    private Runnable runnable = new Runnable() {
        @Override
        public void run() {
            while (true) {
                FutureTask futureTask = null;
                try {
                    //阻塞式方法
                    futureTask = (FutureTask) taskQueue.take();
                    Log.d(TAG, "任务数量:" + taskQueue.size());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (futureTask != null) {
                    threadPoolExecutor.execute(futureTask);
                    Log.d(TAG, "线程池大小:" + threadPoolExecutor.getPoolSize());
                }
            }
        }
    };

    public static ThreadPoolManager getInstance() {
        return instance;
    }
}

最后建立调用层与框架层的联系关系

public class Volley {
    public static <T, M> void sendRequest(T request, String url, Class<M> response, IDataListener dataListener) {
        RequestHolder<T> requestHolder = new RequestHolder<>();
        requestHolder.setUrl(url);
        IHttpService httpService = new JSONHttpService();
        IHttpListener httpListener = new JSONDealListener<>(response,dataListener);
        requestHolder.setHttpService(httpService);
        requestHolder.setHttpListener(httpListener);
        HttpTask<T> httpTask = new HttpTask<>(requestHolder);
        try {
            ThreadPoolManager.getInstance().execute(new FutureTask<Object>(httpTask,null));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

然后是个Bean类,用作响应类,相应类是根据json数据的格式封装而成
测试网站的数据为:

{"code":0,"name":"jack","time":"2018-01-01","user_id":"10086"}

对应的Bean文件为

class LoginResponse
{
    private int code;
    private String user_id;
    private String time;
    private String name;
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public String getUser_id() {
        return user_id;
    }
    public void setUser_id(String user_id) {
        this.user_id = user_id;
    }
    public String getTime() {
        return time;
    }
    public void setTime(String time) {
        this.time = time;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }


    @Override
    public String toString() {
        return "LoginResponse{" +
                "code=" + code +
                ", user_id='" + user_id + '\'' +
                ", time='" + time + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
}

最后在activity中进行调用

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "cj5785";
    private static final String url = "http://192.168.1.2:8080/test/";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    public void onStart(View view) {
        for (int i = 0; i < 50; i++) {
            Volley.sendRequest(null, url, LoginResponse.class, new IDataListener<LoginResponse>() {
                @Override
                public void onSuccess(LoginResponse loginRespense) {
                    Log.d(TAG, loginRespense.toString());
                }

                @Override
                public void onFail() {
                    Log.d(TAG, "获取失败");
                }
            });
        }
    }
}

注意在清单文件中标明权限
这里导入了两个依赖,和一个库,在gradle中声明

android {
    ···
    useLibrary 'org.apache.http.legacy'
}

dependencies {
    ···
    implementation 'com.alibaba:fastjson:1.+'
    implementation 'httpcomponents-httpcore:httpcore:4.+'
}

这样就完成了json数据的访问和解析,接下来就是逐步完成其功能

完成下载功能

下载回调监听接口

public interface IDownloadServiceCallable {
    void onDownloadStatusChanged(DownloadItemInfo downloadItemInfo);
    void onTotalLengthReceived(DownloadItemInfo downloadItemInfo);
    void onCurrentSizeChanged(DownloadItemInfo downloadItemInfo,double downLength,long speed);
    void onDownloadSuccess(DownloadItemInfo downloadItemInfo);
    void onDownloadPause(DownloadItemInfo downloadItemInfo);
    void onDownloadError(DownloadItemInfo downloadItemInfo,int var2,String var3);
}

下载文件信息

public class DownloadItemInfo extends BaseEntity<DownloadItemInfo>{
    private long currentLength;
    private long totalLength;
    private String url;
    private String filePath;
    private transient HttpTask httpTask;
    private DownloadStatus status;

    public DownloadItemInfo() {}

    public DownloadItemInfo(String url, String filePath) {
        this.url = url;
        this.filePath = filePath;
    }

    public long getCurrentLength() {
        return currentLength;
    }

    public void setCurrentLength(long currentLength) {
        this.currentLength = currentLength;
    }

    public long getTotalLength() {
        return totalLength;
    }

    public void setTotalLength(long totalLength) {
        this.totalLength = totalLength;
    }

    public String getUrl() {
        return url;
    }

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

    public String getFilePath() {
        return filePath;
    }

    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }

    public HttpTask getHttpTask() {
        return httpTask;
    }

    public void setHttpTask(HttpTask httpTask) {
        this.httpTask = httpTask;
    }

    public DownloadStatus getStatus() {
        return status;
    }

    public void setStatus(DownloadStatus status) {
        this.status = status;
    }
}

下载状态

public enum DownloadStatus {
    waitting(0),
    starting(1),
    downloading(2),
    pause(3),
    finish(4),
    failed(5);
    private int value;

    DownloadStatus(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }
}

实现IHttpListener接口

public class DownLoadListener implements IHttpListener {

    private static final String TAG = "cj5785";
    //下载信息
    private DownloadItemInfo downloadItemInfo;
    //文件信息
    private File file;
    //链接url
    protected String url;
    //断点位置
    private long breakPoint;
    //回调方法
    private IDownloadServiceCallable downloadServiceCallable;
    //获取网络
    private IHttpService httpService;
    //获取主线程
    private Handler handler = new Handler(Looper.getMainLooper());

    public DownLoadListener(DownloadItemInfo downloadItemInfo) {
        this.downloadItemInfo = downloadItemInfo;
    }

    public DownLoadListener(DownloadItemInfo downloadItemInfo,
                            IDownloadServiceCallable downloadServiceCallable,
                            IHttpService httpService) {
        this.downloadItemInfo = downloadItemInfo;
        this.downloadServiceCallable = downloadServiceCallable;
        this.httpService = httpService;
        this.file = new File(downloadItemInfo.getFilePath());
        //得到已下载长度
        this.breakPoint = file.length();
    }

    public void addHttpHeader(Map<String, String> headerMap) {
        long length = getFile().length();
        if (length > 0L) {
            headerMap.put("RANGE", "bytes=" + length + "-");
        }
    }

    @Override
    public void onSuccess(HttpEntity httpEntity) {
        Log.d(TAG, "onSuccess");
        InputStream inputStream = null;
        try {
            inputStream = httpEntity.getContent();
        } catch (IOException e) {
            e.printStackTrace();
        }
        //开始时间
        long startTime = System.currentTimeMillis();
        //下载速度
        long speed = 0L;
        //花费时间
        long useTime = 0L;
        //下载长度
        long getLen = 0L;
        //接收的长度
        long receiveLen = 0L;
        //已下载的长度
        long dataLength = httpEntity.getContentLength();
        Log.d(TAG, "onSuccess: dataLength = " + dataLength);
        //单位时间下载字节数
        long calcSpeedLen = 0L;
        //总长度
        long totalLength = this.breakPoint + dataLength;
        Log.d(TAG, "onSuccess: totalLength = " + totalLength);
        //更新数量
        receviceTotalLength(totalLength);
        //更新状态
        downloadStatusChange(DownloadStatus.downloading);
        byte[] buffer = new byte[1024];
        int count = 0;
        long currentTime = System.currentTimeMillis();
        BufferedOutputStream bos = null;
        FileOutputStream fos = null;
        try {
            if (!makeDir(getFile().getParentFile())) {
                downloadServiceCallable.onDownloadError(downloadItemInfo, 0, "创建文件夹失败");
            } else {
                fos = new FileOutputStream(getFile(), true);
                bos = new BufferedOutputStream(fos);
                int length = 1;
                while ((length = inputStream.read(buffer)) != -1) {
                    if (getHttpService().isCancle()) {
                        downloadServiceCallable.onDownloadError(downloadItemInfo, 1, "用户取消了");
                        return;
                    }
                    if (getHttpService().isPause()) {
                        downloadServiceCallable.onDownloadError(downloadItemInfo, 2, "用户暂停了");
                        return;
                    }
                    bos.write(buffer, 0, length);
                    getLen += (long) length;
                    receiveLen += (long) length;
                    calcSpeedLen += (long) length;
                    ++count;
                    if (receiveLen * 10L / totalLength >= 1L || count >= 5000) {
                        currentTime = System.currentTimeMillis();
                        useTime = currentTime - startTime;
                        startTime = currentTime;
                        speed = 1000L * calcSpeedLen / useTime;
                        count = 0;
                        calcSpeedLen = 0L;
                        receiveLen = 0L;
                        downloadLengthChange(breakPoint + getLen, totalLength, speed);
                    }
                }
                bos.close();
                inputStream.close();
                if (dataLength != getLen) {
                    downloadServiceCallable.onDownloadError(downloadItemInfo, 3, "下载长度不相等");
                } else {
                    downloadLengthChange(breakPoint + getLen, totalLength, speed);
                    downloadServiceCallable.onDownloadSuccess(downloadItemInfo.copy());
                }
            }
        } catch (IOException e) {
            if (getHttpService() != null) {
//                getHttpService().abortRequest();
            }
            return;
        } catch (Exception e) {
            if (getHttpService() != null) {
//                getHttpService().abortRequest();
            }
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                if (httpEntity != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    //创建父文件夹
    private boolean makeDir(File parentFile) {
//        Log.d(TAG, "makeDir");
        return parentFile.exists() && !parentFile.isFile()
                ? parentFile.exists() && parentFile.isDirectory()
                : parentFile.mkdirs();
    }

    //下载长度变化
    private void downloadLengthChange(final long downLength, final long totalLength, final long speed) {
//        Log.d(TAG, "downloadLengthChange");
        downloadItemInfo.setCurrentLength(downLength);
        final DownloadItemInfo copyDownloadItemInfo = downloadItemInfo.copy();
        if (downloadServiceCallable != null) {
            synchronized (downloadServiceCallable) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        downloadServiceCallable.onCurrentSizeChanged(copyDownloadItemInfo, (double) downLength / totalLength, speed);
                    }
                });
            }
        }
    }

    //得到文件
    private File getFile() {
        return file;
    }

    //获取HttpService对象
    private IHttpService getHttpService() {
        return httpService;
    }

    //状态回调
    private void downloadStatusChange(DownloadStatus downloading) {
//        Log.d(TAG, "downloadStatusChange");
        downloadItemInfo.setStatus(downloading);
        final DownloadItemInfo copyDownloadItemInfo = downloadItemInfo.copy();
        if (downloadServiceCallable != null) {
            synchronized (downloadServiceCallable) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        downloadServiceCallable.onDownloadStatusChanged(copyDownloadItemInfo);
                    }
                });
            }
        }
    }

    //更新下载状态
    private void receviceTotalLength(long totalLength) {
//        Log.d(TAG, "receviceTotalLength");
        downloadItemInfo.setCurrentLength(totalLength);
        final DownloadItemInfo copyDownloadItemInfo = downloadItemInfo.copy();
        if (downloadServiceCallable != null) {
            synchronized (downloadServiceCallable) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        downloadServiceCallable.onTotalLengthReceived(copyDownloadItemInfo);
                    }
                });
            }
        }
    }

    @Override
    public void onFail() {

    }
}

在回调时侯使用克隆对象

//克隆对象,防止修改
public class BaseEntity<T> implements Serializable{
    private static final long serialVersionUID = 1L;
    public BaseEntity(){}
    public T copy(){
        ByteArrayOutputStream byteArrayOutputStream = null;
        ObjectOutputStream objectOutputStream = null;
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(this);
            ByteArrayInputStream inputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
            Object result= objectInputStream.readObject();
            return (T) result;
        } catch (IOException io) {
            io.printStackTrace();
        } catch (ClassNotFoundException classNot) {
            classNot.printStackTrace();
        } finally {
            if(byteArrayOutputStream != null) {
                try {
                    byteArrayOutputStream.close();
                } catch (IOException io) {
                    io.printStackTrace();
                }
            }

            if(objectOutputStream != null) {
                try {
                    objectOutputStream.close();
                } catch (IOException io) {
                    io.printStackTrace();
                }
            }

        }
        return null;
    }
}

完成下载请求

public class FileDownHttpService implements IHttpService {
    private static final String TAG = "cj5785";
    private HttpClient httpClient = new DefaultHttpClient();
    private HttpGet httpGet;
    private byte[] requestData;
    private String url;
    //请求头信息,保证线程安全
    private Map<String, String> headerMap = Collections.synchronizedMap(new HashMap<String, String>());
    //含有请求处理的接口
    private IHttpListener httpListener;
    //httpClient获取网络回调
    private HttpResponseHandler httpResponseHandler = new HttpResponseHandler();

    @Override
    public void setUrl(String url) {
        this.url = url;
    }

    @Override
    public void excute() {
		httpGet = new HttpGet(url);
        //创建请求头,以方便断点下载
        constrcutHeader();
//        ByteArrayEntity byteArrayEntity = new ByteArrayEntity(requestData);
//        httpGet.setEntity(byteArrayEntity);
        try {
            httpClient.execute(httpGet, httpResponseHandler);
        } catch (IOException e) {
            httpListener.onFail();
            e.printStackTrace();
        }
    }


    @Override
    public void setHttpListener(IHttpListener httpListener) {
        this.httpListener = httpListener;
    }

    @Override
    public void setRequestData(byte[] requestData) {
        this.requestData = requestData;
    }

    @Override
    public Map<String, String> getHttpHeadMap() {
        return headerMap;
    }

    @Override
    public void pause() {

    }

    @Override
    public boolean isPause() {
        return false;
    }

    @Override
    public void cancle() {

    }

    @Override
    public boolean isCancle() {
        return false;
    }

    private void constrcutHeader() {
        Iterator iterator = headerMap.keySet().iterator();
        while (iterator.hasNext()) {
            String key = (String) iterator.next();
            String value = headerMap.get(key);
            Log.d(TAG, "constrcutHeader:{key=" + key + ",value=" + value + "}");
            httpGet.addHeader(key, value);
        }
    }

    public Map<String, String> getHeaderMap() {
        return headerMap;
    }

    private class HttpResponseHandler extends BasicResponseHandler {
        @Override
        public String handleResponse(HttpResponse response) throws IOException {
            //响应码
            int code = response.getStatusLine().getStatusCode();
            if (code == 200 || code == 206) {
                httpListener.onSuccess(response.getEntity());
            } else {
                httpListener.onFail();
            }
            return null;
        }
    }
}

编写下载管理,实现下载封装

public class DownFileManager implements IDownloadServiceCallable {

    private static final String TAG = "cj5785";
    //保证线程安全
    private byte[] lock = new byte[0];

    public void down(String url) {

        synchronized (lock) {
            String[] preFixs = url.split("/");
            String afterFix = preFixs[preFixs.length - 1];
            File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath(), afterFix);
            DownloadItemInfo downloadItemInfo = new DownloadItemInfo(url, file.getAbsolutePath());
            RequestHolder requestHolder = new RequestHolder();
            //设置下载策略
            IHttpService httpService = new FileDownHttpService();
            //得到请求头参数
            Map<String, String> map = httpService.getHttpHeadMap();
            //处理结果
            IHttpListener httpListener = new DownLoadListener(downloadItemInfo, this, httpService);
            //组装request
            requestHolder.setHttpListener(httpListener);
            requestHolder.setHttpService(httpService);
            requestHolder.setUrl(url);
            //实例化HttpTask
            HttpTask httpTask = new HttpTask(requestHolder);
            try {
                ThreadPoolManager.getInstance().execute(new FutureTask<Object>(httpTask, null));
            } catch (InterruptedException e) {
            }
        }
    }

    @Override
    public void onDownloadStatusChanged(DownloadItemInfo downloadItemInfo) {
        Log.d(TAG, "onDownloadStatusChanged");
    }

    @Override
    public void onTotalLengthReceived(DownloadItemInfo downloadItemInfo) {
        Log.d(TAG, "onTotalLengthReceived");
    }

    @Override
    public void onCurrentSizeChanged(DownloadItemInfo downloadItemInfo, double downLength, long speed) {
        Log.i(TAG, "下载速度: " + speed / 1000 + "k/s");
        Log.i(TAG, "路径: " + downloadItemInfo.getFilePath() + ",下载进度: " + downLength + ",速度: " + speed);
    }

    @Override
    public void onDownloadSuccess(DownloadItemInfo downloadItemInfo) {
        Log.i(TAG, "下载成功! 路径: " + downloadItemInfo.getFilePath() + ",url: " + downloadItemInfo.getUrl());
    }

    @Override
    public void onDownloadPause(DownloadItemInfo downloadItemInfo) {
        Log.d(TAG, "onDownloadPause");
    }

    @Override
    public void onDownloadError(DownloadItemInfo downloadItemInfo, int var2, String var3) {
        Log.d(TAG, "onDownloadError " + var3);
    }
}

修改HttpTask,使其可以处理文件下载响应

public class HttpTask<T> implements Runnable {

    private static final String TAG = "cj5785";
    private IHttpService httpService;

    public HttpTask(RequestHolder<T> requestHolder) {
        httpService = requestHolder.getHttpService();
        httpService.setHttpListener(requestHolder.getHttpListener());
        httpService.setUrl(requestHolder.getUrl());
        IHttpListener httpListener = requestHolder.getHttpListener();
        httpListener.addHttpHeader(httpService.getHttpHeadMap());
        try {
            T request = requestHolder.getRequestInfo();
            if (request != null) {
                String requestInfo = JSON.toJSONString(request);
                httpService.setRequestData(requestInfo.getBytes("UTF-8"));
            }

        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run() {
        httpService.excute();
    }
}

调用下载,完成下载测试

public void onDown(View view) {
	DownFileManager downFileService = new DownFileManager();
	downFileService.down("http://192.168.1.2:8080/test/test.apk");
}

以上,就完成了文件的下载,同时实现了断点续传

结合数据库实现下载管理

下载需要保存的记录
  • 下载id
  • 下载url
  • 下载存储的文件路径filePath
  • 下载文件显示名displayName
  • 下载文件总大小totalLen
  • 下载文件当前大小currentLen
  • 下载开始时间startTime
  • 下载结束时间finishTime
  • 用户userId
  • 下载优先级priority
  • 下载停止模式stopMode
  • 下载的状态status
下载流程图

文件下载流程图

下载Dao应具备的功能

内部应该含有DownItemInfo list集合:此集合包含着自动暂停,没有完成的下载
比较器Comparator:根据Id排序
查找自动取消的下载集合,方便外层做恢复下载:findAllAutoCancelRecords()
更新文件长度
根据id查找下载记录对象:findRecordById(int recordId)
根据下载地址和下载文件路径查找下载记录:findRecord(String url, String filePath)
根据 下载文件路径查找下载记录
根据id更新文件当前下载大小:updateCurrentLen(int id, Long currentLen)
根据下载id删除下载记录:removeRecord(int id)
根据id从内存中移除下载记录:removeRecordFromMemery(int id)

实现

将之前的数据库框架使用在项目中
DbField.java DbTable.java
BaseDao.java BaseDaoFactory.java IBaseDao.java
添加相应的下载操作数据库类

public class DownloadDao extends BaseDao<DownloadItemInfo> {

    //保存应该下载的集合
    private List<DownloadItemInfo> downloadItemInfoList =
            Collections.synchronizedList(new ArrayList<DownloadItemInfo>());

    //比较器
    private DownloadInfoComparator downloadInfoComparator = new DownloadInfoComparator();

    //生成下载ID
    private Integer generateRecordId() {
        int maxId = 0;
        String sql = "select max(id) from " + getTableName();
        synchronized (DownloadDao.class) {
            Cursor cursor = database.rawQuery(sql, null);
            if (cursor.moveToNext()) {
                String[] colmName = cursor.getColumnNames();
                int index = cursor.getColumnIndex("max(id)");
                if (index != -1) {
                    Object value = cursor.getInt(index);
                    if (value != null) {
                        maxId = Integer.parseInt(String.valueOf(value));
                    }
                }
            }
        }
        return maxId + 1;
    }

    //根据下载地址和下载文件路径查找下载记录
    public DownloadItemInfo findRecord(String url, String filePath) {
        synchronized (DownloadDao.class) {
            for (DownloadItemInfo record : downloadItemInfoList) {
                if (record.getUrl().equals(url) && record.getFilePath().equals(filePath)) {
                    return record;
                }
            }
            //内存集合找不到,就从数据库中查找
            DownloadItemInfo where = new DownloadItemInfo();
            where.setUrl(url);
            where.setFilePath(filePath);
            List<DownloadItemInfo> resultList = super.query(where);
            if (resultList.size() > 0) {
                return resultList.get(0);
            }
            return null;
        }
    }

    @Override
    protected String createTable() {
        return "create table if not exists tb_downloadInfo(id Integer primary key,url TEXT not null," +
                "filePath TEXT not null,displayName TEXT,status Integer,totalLen Long,currentLen Long," +
                "startTime TEXT,finishTime TEXT,userId TEXT,httpTaskType TEXT,priority Integer," +
                "stopMode Integer,downloadMaxSizeKey TEXT,unique(filePath))";
    }

    //查找下载记录
    public List<DownloadItemInfo> findRecord(String filePath) {
        synchronized (DownloadDao.class) {
            DownloadItemInfo where = new DownloadItemInfo();
            where.setFilePath(filePath);
            List<DownloadItemInfo> resultList = super.query(where);
            return resultList;
        }
    }

    //添加下载记录
    public int addRecrod(String url, String filePath, String displayName, int priority) {
        synchronized (DownloadDao.class) {
            DownloadItemInfo existDownloadInfo = findRecord(url, filePath);
            if (existDownloadInfo == null) {
                DownloadItemInfo record = new DownloadItemInfo();
                record.setId(generateRecordId());
                record.setUrl(url);
                record.setFilePath(filePath);
                record.setDisplayName(displayName);
                record.setStatus(DownloadStatus.waitting.getValue());
                record.setTotalLen(0L);
                record.setCurrentLen(0L);
                SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
                record.setStartTime(dateFormat.format(new Date()));
                record.setFinishTime("0");
                record.setPriority(priority);
                super.insert(record);
                downloadItemInfoList.add(record);
                return record.getId();
            }
            return -1;
        }
    }

    //更新下载记录
    public int updateRecord(DownloadItemInfo record) {
        DownloadItemInfo where = new DownloadItemInfo();
        where.setId(record.getId());
        int result = 0;
        synchronized (DownloadDao.class) {
            try {
                result = super.update(record, where);
            } catch (Throwable e) {
            }
            if (result > 0) {
                for (int i = 0; i < downloadItemInfoList.size(); i++) {
                    if (downloadItemInfoList.get(i).getId().intValue() == record.getId()) {
                        downloadItemInfoList.set(i, record);
                        break;
                    }
                }
            }
        }
        return result;
    }

    //根据下载地址和下载文件路径查找下载记录
    public DownloadItemInfo findSigleRecord(String filePath) {
        List<DownloadItemInfo> downloadInfoList = findRecord(filePath);
        if (downloadInfoList.isEmpty()) {
            return null;
        }
        return downloadInfoList.get(0);
    }

    //根据id查找下载记录对象
    public DownloadItemInfo findRecordById(int recordId) {
        synchronized (DownloadDao.class) {
            for (DownloadItemInfo record : downloadItemInfoList) {
                if (record.getId() == recordId) {
                    return record;
                }
            }
            DownloadItemInfo where = new DownloadItemInfo();
            where.setId(recordId);
            List<DownloadItemInfo> resultList = super.query(where);
            if (resultList.size() > 0) {
                return resultList.get(0);
            }
            return null;
        }
    }

    //根据id从内存中移除下载记录
    public boolean removeRecordFromMemery(int id) {
        synchronized (DownloadItemInfo.class) {
            for (int i = 0; i < downloadItemInfoList.size(); i++) {
                if (downloadItemInfoList.get(i).getId() == id) {
                    downloadItemInfoList.remove(i);
                    break;
                }
            }
            return true;
        }
    }

    class DownloadInfoComparator implements Comparator<DownloadItemInfo> {

        @Override
        public int compare(DownloadItemInfo o1, DownloadItemInfo o2) {
            return o2.getId() - o1.getId();
        }
    }
}

策略枚举类

public enum Priority {
    low(0),
    middle(1),
    high(2);
    private int value;

    Priority(int value) {
        this.value = value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    public static Priority getInstance(int value) {
        for (Priority priority : Priority.values()) {
            if (priority.getValue() == value) {
                return priority;
            }
        }
        return Priority.middle;
    }
}

停止模式枚举类

public enum DownloadStopMode {
    //后台根据下载优先级调度自动停止下载任务
    auto(0),
    //手动停止下载任务/
    hand(1);
    //值
    private Integer value;

    public void setValue(Integer value) {
        this.value = value;
    }

    public Integer getValue() {
        return value;
    }

    DownloadStopMode(Integer value) {
        this.value = value;
    }

    public static DownloadStopMode getInstance(int value) {
        for (DownloadStopMode mode : DownloadStopMode.values()) {
            if (mode.getValue() == value) {
                return mode;
            }
        }
        return DownloadStopMode.auto;
    }
}

修改DownFileManager

public class DownFileManager implements IDownloadServiceCallable {

    private static final String TAG = "cj5785";
    //保证线程安全
    private byte[] lock = new byte[0];
    DownloadDao downloadDao = BaseDaoFactory.getInstance().getDataHelper(
            DownloadDao.class, DownloadItemInfo.class);
    SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
    //观察者模式
    private final List<IDownloadCallable> applisteners = new CopyOnWriteArrayList<>();

    private static List<DownloadItemInfo> downloadFileTaskList = new CopyOnWriteArrayList();
    Handler handler = new Handler(Looper.getMainLooper());

    public int download(String url) {
        String[] preFixs = url.split("/");
        return download(url, Environment.getExternalStorageDirectory().getAbsolutePath()
                        + File.separator + preFixs[preFixs.length - 1],
                preFixs[preFixs.length - 1], Priority.middle);
    }

    public int download(String url, String filePath) {
        String[] preFixs = url.split("/");
        return download(url, filePath, preFixs[preFixs.length - 1], Priority.middle);
    }

    public int download(String url, String filePath, String displayName) {
        return download(url, filePath, displayName, Priority.middle);
    }

    public int download(String url, String filePath, String displayName, Priority priority) {
        if (priority == null) {
            priority = Priority.low;
        }
        File file = new File(filePath);
        DownloadItemInfo downloadItemInfo = null;
        downloadItemInfo = downloadDao.findRecord(url, filePath);
        //尚未下载
        if (downloadItemInfo == null) {
            //根据文件路径查找
            List<DownloadItemInfo> infoList = downloadDao.findRecord(filePath);
            //大于0  表示下载
            if (infoList.size() > 0) {
                DownloadItemInfo sameDown = infoList.get(0);
                if (sameDown.getCurrentLen() == sameDown.getTotalLen()) {
                    synchronized (applisteners) {
                        for (IDownloadCallable downloadCallable : applisteners) {
                            downloadCallable.onDownloadError(sameDown.getId(), 2, "文件已经下载了");
                        }
                    }
                }
            }
            //插入数据库
            int recrodId = downloadDao.addRecrod(url, filePath, displayName, priority.getValue());
            if (recrodId != -1) {
                synchronized (applisteners) {
                    for (IDownloadCallable downloadCallable : applisteners) {
                        //通知应用层  数据库被添加了
                        downloadCallable.onDownloadInfoAdd(downloadItemInfo.getId());
                    }
                }
            }
            //插入失败时,再次进行查找,确保能查得到
            else {
                //插入
                downloadItemInfo = downloadDao.findRecord(url, filePath);
            }
        }
        //是否正在下载
        if (isDowning(file.getAbsolutePath())) {
            synchronized (applisteners) {
                for (IDownloadCallable downloadCallable : applisteners) {
                    downloadCallable.onDownloadError(downloadItemInfo.getId(), 4, "正在下载,请不要重复添加");
                }
            }
            return downloadItemInfo.getId();
        }

        if (downloadItemInfo != null) {
            downloadItemInfo.setPriority(priority.getValue());
            downloadItemInfo.setStopMode(DownloadStopMode.auto.getValue());
            //判断数据库存的 状态是否是完成
            if (downloadItemInfo.getStatus() != DownloadStatus.finish.getValue()) {
                if (downloadItemInfo.getTotalLen() == 0L || file.length() == 0L) {
                    Log.i(TAG, "还未开始下载");
                    downloadItemInfo.setStatus(DownloadStatus.failed.getValue());
                }
                //判断数据库中 总长度是否等于文件长度
                if (downloadItemInfo.getTotalLen() == file.length() && downloadItemInfo.getTotalLen() != 0) {
                    downloadItemInfo.setStatus(DownloadStatus.finish.getValue());
                    synchronized (applisteners) {
                        for (IDownloadCallable downloadCallable : applisteners) {
                            try {
                                downloadCallable.onDownloadError(downloadItemInfo.getId(), 4, "已经下载了");
                            } catch (Exception e) {
                            }
                        }
                    }
                }
            } else {
                if (!file.exists() || (downloadItemInfo.getTotalLen() != downloadItemInfo.getCurrentLen())) {
                    downloadItemInfo.setStatus(DownloadStatus.failed.getValue());
                }
            }
            //更新
            downloadDao.updateRecord(downloadItemInfo);

            //判断是否已经下载完成
            if (downloadItemInfo.getStatus() == DownloadStatus.finish.getValue()) {
                Log.i(TAG, "已经下载完成  回调应用层");
                final int downId = downloadItemInfo.getId();
                synchronized (applisteners) {
                    handler.post(new Runnable() {
                        @Override
                        public void run() {
                            for (IDownloadCallable downloadCallable : applisteners) {
                                downloadCallable.onDownloadStatusChanged(downId, DownloadStatus.finish);
                            }
                        }
                    });
                }
                downloadDao.removeRecordFromMemery(downId);
                return downloadItemInfo.getId();
            }//之前的下载 状态为暂停状态
            List<DownloadItemInfo> allDowning = downloadFileTaskList;
            //当前下载不是最高级  则先退出下载
            if (priority != Priority.high) {
                for (DownloadItemInfo downling : allDowning) {
                    //从下载表中,获取到全部正在下载的任务
                    downling = downloadDao.findSigleRecord(downling.getFilePath());
                    if (downling != null && downling.getPriority() == Priority.high.getValue()) {
                        if (downling.getFilePath().equals(downloadItemInfo.getFilePath())) {
                            break;
                        } else {
                            return downloadItemInfo.getId();
                        }
                    }
                }
            }
            //
            reallyDown(downloadItemInfo);
            if (priority == Priority.high || priority == Priority.middle) {
                synchronized (allDowning) {
                    for (DownloadItemInfo downloadItemInfo1 : allDowning) {
                        if (!downloadItemInfo.getFilePath().equals(downloadItemInfo1.getFilePath())) {
                            DownloadItemInfo downingInfo = downloadDao.findSigleRecord(downloadItemInfo1.getFilePath());
                            if (downingInfo != null) {
                                pause(downloadItemInfo.getId(), DownloadStopMode.auto);
                            }
                        }
                    }
                }
                return downloadItemInfo.getId();
            }
        }
        return -1;
    }

    //停止
    public void pause(int downloadId, DownloadStopMode mode) {
        if (mode == null) {
            mode = DownloadStopMode.auto;
        }
        final DownloadItemInfo downloadInfo = downloadDao.findRecordById(downloadId);
        if (downloadInfo != null) {
            // 更新停止状态
            if (downloadInfo != null) {
                downloadInfo.setStopMode(mode.getValue());
                downloadInfo.setStatus(DownloadStatus.pause.getValue());
                downloadDao.updateRecord(downloadInfo);
            }
            for (DownloadItemInfo downing : downloadFileTaskList) {
                if (downloadId == downing.getId()) {
                    downing.getHttpTask().pause();
                }
            }
        }
    }

    //判断当前是否正在下载
    private boolean isDowning(String absolutePath) {
        for (DownloadItemInfo downloadItemInfo : downloadFileTaskList) {
            if (downloadItemInfo.getFilePath().equals(absolutePath)) {
                return true;
            }
        }
        return false;
    }

    //添加观察者
    public void setDownCallable(IDownloadCallable downloadCallable) {
        synchronized (applisteners) {
            applisteners.add(downloadCallable);
        }
    }

    //下载
    public DownloadItemInfo reallyDown(DownloadItemInfo downloadItemInfo) {
        synchronized (lock) {
            //实例化DownloadItem
            RequestHolder requestHolder = new RequestHolder();
            //设置请求下载的策略
            IHttpService httpService = new FileDownHttpService();
            //得到请求头的参数 map
            Map<String, String> map = httpService.getHttpHeadMap();
            //处理结果的策略
            IHttpListener httpListener = new DownLoadListener(downloadItemInfo, this, httpService);
            requestHolder.setHttpListener(httpListener);
            requestHolder.setHttpService(httpService);
            requestHolder.setUrl(downloadItemInfo.getUrl());
            HttpTask httpTask = new HttpTask(requestHolder);
            downloadItemInfo.setHttpTask(httpTask);
            //添加
            downloadFileTaskList.add(downloadItemInfo);
            httpTask.start();
        }
        return downloadItemInfo;
    }

    @Override
    public void onDownloadStatusChanged(DownloadItemInfo downloadItemInfo) {
        Log.d(TAG, "onDownloadStatusChanged");
    }

    @Override
    public void onTotalLengthReceived(DownloadItemInfo downloadItemInfo) {
        Log.d(TAG, "onTotalLengthReceived");
    }

    @Override
    public void onCurrentSizeChanged(DownloadItemInfo downloadItemInfo, double downLength, long speed) {
        Log.i(TAG, "下载速度: " + speed / 1000 + "k/s");
        Log.i(TAG, "路径: " + downloadItemInfo.getFilePath() + ",下载进度: " + downLength + ",速度: " + speed);
    }

    @Override
    public void onDownloadSuccess(DownloadItemInfo downloadItemInfo) {
        Log.i(TAG, "下载成功! 路径: " + downloadItemInfo.getFilePath() + ",url: " + downloadItemInfo.getUrl());
    }

    @Override
    public void onDownloadPause(DownloadItemInfo downloadItemInfo) {
        Log.d(TAG, "onDownloadPause");
    }

    @Override
    public void onDownloadError(DownloadItemInfo downloadItemInfo, int var2, String var3) {
        Log.d(TAG, "onDownloadError " + var3);
    }
}

完善DownloadItemInfo类

@DbTable("tb_downloadInfo")
public class DownloadItemInfo extends BaseEntity<DownloadItemInfo> {
    public long currentLength;
    public long totalLength;
    public Integer id;
    public Integer priority;
    public Integer stopMode;
    public Integer status;
    public Long totalLen;
    public Long currentLen;
    public String displayName;
    public String startTime;
    public String finishTime;
    public String userId;
    public String httpTaskType;
    public String url;
    public String filePath;
    private transient HttpTask httpTask;
//    private DownloadStatus status;

    public DownloadItemInfo() {
    }

    public DownloadItemInfo(String url, String filePath) {
        this.url = url;
        this.filePath = filePath;
    }

    public long getCurrentLength() {
        return currentLength;
    }

    public void setCurrentLength(long currentLength) {
        this.currentLength = currentLength;
    }

    public long getTotalLength() {
        return totalLength;
    }

    public void setTotalLength(long totalLength) {
        this.totalLength = totalLength;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Integer getPriority() {
        return priority;
    }

    public void setPriority(Integer priority) {
        this.priority = priority;
    }

    public Integer getStopMode() {
        return stopMode;
    }

    public void setStopMode(Integer stopMode) {
        this.stopMode = stopMode;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public Long getTotalLen() {
        return totalLen;
    }

    public void setTotalLen(Long totalLen) {
        this.totalLen = totalLen;
    }

    public Long getCurrentLen() {
        return currentLen;
    }

    public void setCurrentLen(Long currentLen) {
        this.currentLen = currentLen;
    }

    public String getDisplayName() {
        return displayName;
    }

    public void setDisplayName(String displayName) {
        this.displayName = displayName;
    }

    public String getStartTime() {
        return startTime;
    }

    public void setStartTime(String startTime) {
        this.startTime = startTime;
    }

    public String getFinishTime() {
        return finishTime;
    }

    public void setFinishTime(String finishTime) {
        this.finishTime = finishTime;
    }

    public String getUserId() {
        return userId;
    }

    public void setUserId(String userId) {
        this.userId = userId;
    }

    public String getHttpTaskType() {
        return httpTaskType;
    }

    public void setHttpTaskType(String httpTaskType) {
        this.httpTaskType = httpTaskType;
    }

    public String getUrl() {
        return url;
    }

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

    public String getFilePath() {
        return filePath;
    }

    public void setFilePath(String filePath) {
        this.filePath = filePath;
    }

    public HttpTask getHttpTask() {
        return httpTask;
    }

    public void setHttpTask(HttpTask httpTask) {
        this.httpTask = httpTask;
    }
}

转载于:https://www.cnblogs.com/cj5785/p/10664612.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值