网络编程(一)

本文详细介绍了Android中实现断点续传的技术原理,并通过代码示例解析了多线程下载的具体实现过程。此外,还深入探讨了Volley网络库的使用方法,包括StringRequest、JsonRequest及ImageRequest的实践应用。

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

网络编程

  • 在Android中开发中,处处都需要去请求网络,处理网络请求的方式有很多种,有Android中自带的HTTPURLConnection,还有一些开源框架,如Volley,OkHttp,Xutils等
  • 请求网络,就要想到断点续传和断点下载,这其中的原理又是什么呢?看下代码:
//联网获取文件的长度 算出每个线程下载的位置  开子线程  
        new Thread() {
            public void run() {
                //[一]获取服务器文件的大小   因为一会要开多个线程  方便计算下载的位置 
                try {
                    //[1.1]创建URL对象指定我们要访问的 网络(路径)
                    URL url = new URL(path);
                    //[1.2]拿到httpurlconnection对象  用于发送或者接收数据 
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    //[1.3]设置请求方式
                    conn.setRequestMethod("GET");//get要求大写  默认就是get请求
                    //[1.4]设置请求超时时间
                    conn.setConnectTimeout(5000);
                    //[1.5]获取服务器返回的状态码 
                    int code = conn.getResponseCode();
                    //[1.6]如果code == 200 说明请求成功
                    if (code == 200) {
                        runningThred = threadCount;
                        //[1.7]获取服务器文件的大小  
                        int length = conn.getContentLength();
                        System.out.println("length:"+length);

                        //[二]在客户端创建一个大小和服务器一模一样的文件 
                        RandomAccessFile raf =new RandomAccessFile(getFileName(path), "rw");
                        raf.setLength(length); //文件创建出来 但是没有内容

                        //[三开启多个线程去下载 计算出每个线程下载的开始位置和结束位置]
                        int blockSize = length/threadCount; //每个线程下载的大小 
                        for (int i = 0; i < threadCount; i++) {
                            int startIndex = i*blockSize;
                            int endIndex = (i+1)*blockSize-1;
                            //有特殊情况 就是最后一个线程  的结束位置 是文件长度-1 
                            if (i==threadCount-1) {
                                endIndex = length-1;
                            }
                            System.out.println("当前线程下载的id:"+i+"理论开始位置和结束位置:"+startIndex+"~~~"+endIndex);

        //四开启多个线程去下载我们指定路径的文件 
        DownLoadThread downLoadThread = new DownLoadThread(startIndex, endIndex, i);
                            downLoadThread.start();

                        }

                    }

                } catch (Exception e) {

                }               
            };
        }.start();
  • 通过查看代码,我们从网络下载数据的时候,创建一个线程,首先获取路径,创建URL,HTTPURLConnection对象,设置网络请求,获取网络中文件的大小,在本地创建一个和网络中大小相同的文件,记录每个线程下载的位置,开启线程去下载文件
private  class DownLoadThread extends Thread{
            private int startIndex;
            private int endIndex;
            private int threadId;
            private int pbMax; //当前线程进度条的最大值
            private int pbLastPositin; //代表进度条上次显示的位置 
            public DownLoadThread(int startIndex,int endIndex,int threadId){
                this.startIndex= startIndex;
                this.endIndex = endIndex;
                this.threadId= threadId;
            }

            @Override
            public void run() {

                try {

                    //[1.0]算出当前线程进度条的最大值 
                    pbMax = endIndex - startIndex;

                    //[1.1]创建URL对象指定我们要访问的 网络(路径)
                    URL url = new URL(path);
                    //[1.2]拿到httpurlconnection对象  用于发送或者接收数据 
                    HttpURLConnection conn = (HttpURLConnection) url
                            .openConnection();
                    //[1.3]设置请求方式
                    conn.setRequestMethod("GET");//get要求大写  默认就是get请求
                    //[1.4]设置请求超时时间
                    conn.setConnectTimeout(5000);

                    File file = new File(getFileName(path)+threadId+".txt");
                    if (file.exists()&&file.length()>0) {
                        //说明下载中断过  继续上次的那个位置继续下   读取上次下载的位置 
                        FileInputStream fis = new FileInputStream(file);
                        BufferedReader bufr = new BufferedReader(new InputStreamReader(fis));
                        String lastDownLoadPositionn = bufr.readLine(); //就是上次下载的位置
                        //改变一下当前下载的位置  
                        startIndex = Integer.parseInt(lastDownLoadPositionn);
                        //把startIndex 赋值给pbLastPositin
                        pbLastPositin = startIndex;

                        fis.close();
                        System.out.println("当前线程下载的id:"+threadId+"真实下载的开始位置和结束位置:"+startIndex+"~~~"+endIndex);
                    }

                    //[1.4.1]设置Range 头 告诉服务器每个线程下载的开始位置和结束位置 
                    conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);

                    //[1.5]获取服务器返回的状态码 
                    int code = conn.getResponseCode();
                    //[1.6]如果code == 200 说明请求成功  206请求部分资源成功 
                    if (code == 206) {
                        //[1.7]获取服务器返回的数据   是以流的形式返回的
                        InputStream in = conn.getInputStream(); //这流里面的数据就是网络中的数据  
                        RandomAccessFile raf =new RandomAccessFile(getFileName(path), "rw");
                        raf.seek(startIndex);//设置每个线程写入的位置 
                        int len = 0;
                        int total=0; //代表当前线程下载的大小

                        byte[] buffer = new byte[1024*1024];//1Mb
                        while((len=in.read(buffer))!=-1){
                            total+=len;
                            //[1.8]实现断点续传的逻辑 就是把当前线程下载的位置给存起来  存到一个txt文本文件中  
                            int currentThreadPosition = startIndex+total;

                            //[1.9]把currentThreadPostition位置给存起来  
                            RandomAccessFile randomAccessFile =new RandomAccessFile(getFileName(path)+threadId+".txt", "rwd");
                            randomAccessFile.write(String.valueOf(currentThreadPosition).getBytes());
                            randomAccessFile.close();

                            raf.write(buffer, 0, len);

                            //[2.0]更新进度条的进度  
                            pbs.get(threadId).setMax(pbMax); //设置进度条的最大值
                            pbs.get(threadId).setProgress(pbLastPositin+total);

                        }
                        raf.close();//关闭流

                        synchronized (DownLoadThread.class) {
                            //当所有的线程都下载完毕了  把.txt文件删除
                            runningThred--;
                            if (runningThred==0) {

                                for (int i = 0; i < threadCount; i++) {
                                    File deleteFile = new File(getFileName(path)+i+".txt");
                                    deleteFile.delete();
                                }

                            }
                        }
                        System.out.println("线程id:"+threadId+"下载完毕了");



                    }

                } catch (Exception e) {

                }

            }

        }
  • 在这个方法里面进行网络下载,每次下载的时候判断之前是否下载过,文件是否存在,如果之前下载过,那么就从之前下载的位置下载,这就是断点下载的原理。

    Volley

  • 在Volley内部封装了请求网络的细节,我们只需简单的调用它的方法就可以去实现网络请求数据,Volley可是说是把AsyncHttpClient和Universal-Image-Loader的优点集于了一身,既可以像AsyncHttpClient一样非常简单地进行HTTP通信,也可以像Universal-Image-Loader一样轻松加载网络上的图片。对于大数据量的操作,Volley就表现的不如人意了。

    Volley里面有非常方便的方法
  • StringRequest 的使用
    • 先创建一个请求队列
RequestQueue mQueue = Volley.newRequestQueue(context);
  • 创建一个StringRequest对象,发送HTTP请求
StringRequest stringRequest = new StringRequest("http://www.baidu.com", 
//传入一个响应成功回调的监听 和一个响应失败的监听
                        new Response.Listener<String>() {  
                            @Override  
                            public void onResponse(String response) {  
                                Log.d("TAG", response);  
                            }  
                        }, new Response.ErrorListener() {  
                            @Override  
                            public void onErrorResponse(VolleyError error) {  
                                Log.e("TAG", error.getMessage(), error);  
                            }  
                        });  
  • 最后一步是将StringRequest对象添加到请求队列中
mQueue.add(stringRequest); 
  • 以上这是GET 请求,怎么发送post请求呢?其实跟GET请求类似,只不过在StringQuest的构造方法中添加请求的方式,默认是get
StringRequest stringRequest = new StringRequest(Method.POST, url,  listener, errorListener) {  
    @Override  
    protected Map<String, String> getParams() throws AuthFailureError {  
        Map<String, String> map = new HashMap<String, String>();  
        map.put("params1", "value1");  
        map.put("params2", "value2");  
        return map;  
    }  
};  
  • JsonRequest的使用
    • JsonRequest也是继承Request类,不过JsonRequest是一个抽象类,它的子类有JsonObjectRequest和JsonArrayRequest,这里可以看出来,一个是用于请求一段JSON数据的,一个是用于请求一段JSON数组的。它们的使用方式和StringRequest方式是一样的,按照三步走。
  • ImageRequest的使用
    • 其实请求的图片的方式也是跟上面一样,就是new对象的时候参数多了一些
ImageRequest imageRequest = new ImageRequest(  
        "http://developer.android.com/images/home/aw_dac.png",  
        new Response.Listener<Bitmap>() {  
            @Override  
            public void onResponse(Bitmap response) {  
                imageView.setImageBitmap(response);  
            }  
        }, 0, 0, Config.RGB_565, new Response.ErrorListener() {  
            @Override  
            public void onErrorResponse(VolleyError error) {  
                imageView.setImageResource(R.drawable.default_image);  
            }  
        });  
  • 第二个和第三个参数是图片的宽高,如果设置为0,不会对图片进行压缩
  • Volley中还有ImageLoader,它要比ImageQuest更加高效,因为它不仅可以帮我们队图片进行缓存,还可以过滤掉重复的连接,避免重复的发送请求,它内部也是使用ImageQuest来实现的
    • 创建一个RequestQueue对象。
RequestQueue mQueue = Volley.newRequestQueue(context);
  • 创建一个ImageLoader对象。
//第一个参数是请求队列,第二个是缓存对象
ImageLoader imageLoader = new ImageLoader(mQueue, new ImageCache() {  
    @Override  
    public void putBitmap(String url, Bitmap bitmap) {  
    }  

    @Override  
    public Bitmap getBitmap(String url) {  
        return null;  
    }  
});  
  • 获取一个ImageListener对象。
/*第一个参数是ImageView,第二个参数是加载过程中需要显示的图片,第三个参数是加载失败的图片*/
ImageListener listener = ImageLoader.getImageListener(imageView,  
        R.drawable.default_image, R.drawable.failed_image);  
  • 调用ImageLoader的get()方法加载网络上的图片。
imageLoader.get(url, listener); 
/*如果想要对图片的大小进行限制,可以使用这个方法*/
imageLoader.get(url,  
                listener, 宽, 高);  
  • 当创建请求队列对象的时候,它的内部默认会有五个线程在后台运行,不断等待网络请求的到来,默认情况下,每条请求都是可以缓存的,也可以调用Request的setShouldCache(false)方法来改变这一默认行为。
  • 接下来开篇Okhttp
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值