网络技术之——网络连接URLConnection、单/多线程下载

“网络连接”:

android中的HTTP的网络连接中HttpURLConnection方法和java中的HttpURLConnection用法是一致的,参考:
day0812-doGet和doPost
但在6.0之后,舍弃了HttpClient方法。

1.概述:


网络连接不允许在主线程中进行(3.0之前是允许的),所以通常情况我们都利用异步来操作,方式,利用Handler和AsyncTask.

这里我们介绍三种方式来访问网络:
普通URLConnection和HTTP协议访问(http访问包括HttpURLConnection和HttpClient两种)

2.用法:

URLConnection:

**<一>使用步骤:
注:在子线程中连接服务器。

1)获取URLConnection实例:

URL url = new URL("http://192.168.0.74:8080/myServer/Myserverlet");
URLConnection connection = url.openConnection();

2)获得服务器返回的输入流

InputStream is = connection.getInputStream();

3)接下来,就是对数据流进行读取。

4)最后别忘了,联网需要权限的设置:

这里写图片描述
**<二>范例:
功能:利用URLConnection连接服务器,并将从服务器返回的的数据显示到textview中。
思路:先建立子线程,连接服务器,由于线程中获得的数据需要传给活动,绘制Textview,所以采用异步中的Handler方法主活动接收消息。

1.连接网络:
网络技术之——网络连接URLConnection、单/多线程下载_第1张图片

这里将获得的数据存到buffer缓存中,获得主线程的Handler,并发送。最后别忘了关闭流。
注:这里连接的是自己创建的服务器,但由于用手机模拟器模拟,其本身也是有地址的,所以不能用localhost,要用电脑主机地址。

2.开启线程:
网络技术之——网络连接URLConnection、单/多线程下载_第2张图片

一定不要忘了start线程。

3.创建主活动中的Handler子类:
网络技术之——网络连接URLConnection、单/多线程下载_第3张图片
效果图:
网络技术之——网络连接URLConnection、单/多线程下载_第4张图片

HttpURLConnection:

**<一>使用步骤:
HttpURLConnection与URLConnection用法基本相同:

1)获得实例:

URL url = new URL("http://192.168.0.74:8080/myServer/Myserverlet");
HttpURLConnection connection = (HttpURLConnection)url.openConnection();

2)设置HTTP请求的方式(GET和POST)以及其他自由定制:

connection.setRequestMethod("GET");
connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);

3)获得输入流并进行读取。

InputStream is = connection.getInputStream();

4)最后关闭HTTP连接:

connection.disconnect();

5)权限不要忘了。

**<二>更改上面范例:
doGet:

URL url = new URL("http://360.com");
            URLConnection connect = (URLConnection) url.openConnection();

            // 3.强制造型成httpUrlConnection
            HttpURLConnection httpConnection = (HttpURLConnection) connect;
            // 4.设置请求方式
            httpConnection.setRequestMethod("GET");
            httpConnection.setConnectTimeout(3000);// 连接chaoshi
            httpConnection.setReadTimeout(3000); // 等待返回时间超时
            InputStream is = httpConnection.getInputStream();

“下载文件”:

单线程下载:

1)方法:

下载一般放在后台,即开启服务去进行,且需要开启一个新的线程。

2)范例:单线程下载服务器文件

功能:
服务器中有一音乐文件,地址为:http://192.168.0.74:8080/myServer/music/apple.mp3
这里采用单线程去下载,此例使用AsyncTask异步线程的方法,当然也可以使用Handler去开启一个异步线程。

下载文件的线程:

    //创建一个线程下载文件,利用AsyncTask
    class MyAsyncTask extends AsyncTask<String, Integer, String>{
        @Override
        protected void onPostExecute(String result) {
            // TODO Auto-generated method stub
            super.onPostExecute(result);
        }
        @Override
        protected void onProgressUpdate(Integer... values) {
            // TODO Auto-generated method stub
            super.onProgressUpdate(values);
            int progress = (int)((values[0]*(100.0))/values[1]);
            Log.d("message", values[1]+""+values[0]);
            //proBar.setMax(values[1]);//利用比例来调整bar的值,并不用设置setmax。
            proBar.setProgress(progress);
        }
        @Override
        protected String doInBackground(String... arg0) {
            // TODO Auto-generated method stub
            try {
                URL uri = new URL("http://192.168.0.74:8080/myServer/music/apple.mp3");
                URLConnection connection = uri.openConnection();// 建立连接
                int length = connection.getContentLength();//获得文件总长度
                InputStream is = connection.getInputStream();//获得流

                //输出流,写到自己文件中
                File file = new File(Environment.getExternalStorageDirectory(),"apple3.mp3");
                //判断文件是否存在了
                if(!file.exists()){
                    file.createNewFile();
                }
                FileOutputStream os = new FileOutputStream(file);
                byte[] array = new byte[1024];//写到数组中
                int sum = 0;//写入的总长度
                int index = is.read(array);
                while(index!=-1){
                    os.write(array, 0, index);
                    sum+=index;//长度变化
                    index=is.read(array);
                    publishProgress(sum,length);//将下载进度和总长度传到onProgressUpdate方法中改吧seekbar
                }
                os.flush();
                os.close();
                is.close();

            } catch (MalformedURLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }


            return null;
        }

    };

分析:将下载的进程传到onProgressUpdate方法中进行更新,传过来的参数还有文件的总长度:
网络技术之——网络连接URLConnection、单/多线程下载_第5张图片

进度条进度的设置,设置成百分比:
进度*100.0/总长度

这里写图片描述
点击事件中开启线程:
这里写图片描述
读写权限:
这里写图片描述

综合上面实例的效果演示:
网络技术之——网络连接URLConnection、单/多线程下载_第6张图片


注:下载后的文件所存放的位置在输出流的地址文件中,即:

File file = new File(Environment.getExternalStorageDirectory(),"apple3.mp3");

设置了下载到文件在SD卡下:apple.mp3文件中

多线程下载:

网络技术之——网络连接URLConnection、单/多线程下载_第7张图片

思路:建立一个线程,专门用于打开连接,并开启其他多个线程进行下载,每个线程分管一段,但往往并不能正好分完,所以最后一个线程可以将剩余的全部下载完。

1)建立线程,打开连接,并开启其他线程进行下载。这里进行的其他操作是,需要将用于下载的线程存储到数组中,用于开启后,每秒得到所有线程的进度总和

new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        String urlPath="http://192.168.0.30:8080/MyWebTest/music/aa.mp3";
                        URL  url = new URL(urlPath);
                        URLConnection connection=url.openConnection();//建立连接
                         length=connection.getContentLength();//获得文件长度
                        File file=new File(Environment.getExternalStorageDirectory(),"cc.mp3");//创建写入的路径
                        if(!file.exists()){
                            file.createNewFile();
                        }
                        MultiThread[] threads=new MultiThread[5];//建立一个数组用于存储5个线程
                        for (int i=0;i<5;i++){
                            MultiThread thread=null;//建立5个线程 ,MultiThread类是自定义的线程类****
                            if(i==4){
                                thread = new MultiThread(length / 5*4, length , urlPath, file.getAbsolutePath());
                            }else {
                                 thread = new MultiThread(length / 5 * i, length / 5 * (i + 1)-1, urlPath, file.getAbsolutePath());
                            }
                            thread.start();
                            threads[i]=thread;
                        }
/*获取各个线程下载的进程的总和,显示到进度条,用于上面存到数组中的线程*/
                        boolean isFinish=true;

                        while(isFinish){
                            int sum=0;
                            for (MultiThread thread:threads){//获得每个线程中的进程,getSum方法
                                sum+= thread.getSum();
                            }
                              Message msg=  handler.obtainMessage();//用handler将获得的进度和传给活动,并显示
                            msg.what=0x23;
                            msg.arg1=sum;
                            handler.sendMessage(msg);
                            if(sum+10>=length){//为避免死循环,当下载到文件中长度时停止下载
                                isFinish=false;
                            }
                            Thread.sleep(1000);//每休眠1s一获得每个线程的下载进度
                        }
                    } catch (MalformedURLException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            }).start();

注:thread = new MultiThread(length / 5 * i, length / 5 * (i + 1)-1, urlPath, file.getAbsolutePath());
由于setRequestProperty是包前包后的,所以这里在传入start和end时,end应该-1.
2)用于下载的线程:这里创建 了5个

public class MultiThread extends Thread{
    /*创建构造器,需要传入下载的开始位置,结束位置,要下载的url地址,以及下载到的地址 */
        public MultiThread(long start, long end, String url, String filePath) {
            this.start = start;
            this.end = end;
            this.urlPath = url;
            this.filePath = filePath;
        }
        private int sum=0;//用于记录每个线程下载的进度
        private long start;
        private long end;
        private String urlPath;
        private String filePath;

        public int getSum() {//用于获得该线程的下载进度
            return sum;
        }

        @Override
        public void run() {
            try {
                URL url=new URL(urlPath);
                URLConnection connection=url.openConnection();
            /*下载指定位置文件*/
                connection.setAllowUserInteraction(true);
                connection.setRequestProperty("Range", "bytes=" + start + "-"
                        + end);

                 InputStream is= connection.getInputStream();
                byte [] array=new byte[1024];
                is.read(array);
                File file=new File(filePath);
            /*下载到指定的位置,即从文件的哪里开始写入*/
                RandomAccessFile randomAccessFileile=new RandomAccessFile(file,"rw");
                randomAccessFileile.seek(start);

                int index=is.read(array);
                while (index!=-1){
                    randomAccessFileile.write(array,0,index);
                    sum+=index;
                    index=is.read(array);
                }
                randomAccessFileile.close();
                is.close();
            } catch (MalformedURLException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
}


特殊的代码:从指定的开始位置和结束位置进行下载setRequestProperty方法

这里写图片描述

从指定位置开始写入:RandomAccessFile类

这里写图片描述
3)活动接收进度值,并显示到进度条中:
网络技术之——网络连接URLConnection、单/多线程下载_第8张图片
效果演示:

注:
1>先打开服务器,否则无法下载其路径下的文件。
2>且路径“http://192.168.0.74:8080/myServer/music/apple.mp3”
myServer:是服务器名称,
music/apple.mp3:music文件夹下的apple.mp3

网络技术之——网络连接URLConnection、单/多线程下载_第9张图片

你可能感兴趣的:(多线程,android,网络)