Android中的网络编程

一、访问网络资源
         1、 使用统一资源定位符 获取网络资源的路径url
URL url = new URL(path);

 
  
         2、通过url打开网络连接(连接方式有http连接,ftp连接,https连接,rtsp连接)
                    此处获取网络连接用到的是http连接,所以使打开一个HttpURLConnection  
   HttpURLConnection openConnection = (HttpConnection)url.openConnection();
   openConnection.setRequestMethod("GET");//设置请求方式
   openConnection.setConnectTimeout(5000);//设置请求超时时间  

 
  
         3、打开连接后就可以获取网络返回的数据(例)
 String contentType = openConnection.getContentType();//获取返回的数据类型
 Object content = openConnection.getContent();//获取放回的数据
 int code = openConnection.getResponseCode();//获取响应码,200--返回正确,404--资源没找到,503--服务器内部错误
 ......

    4、获取输入流,保存返回的数据
InputStream inputStream = openConnection.getInputStream();

    5、根据资源类型将网络资源转换成相应的资源,如是图片资源,则如下转换
Bitmap decodeStream = BitmapFactory.decodeStream(inputStream);

    6、访问网络需要添加网络权限
    7、一个常用的参数:User-Agent,我们可以通过这个参数来获取这个资源的来源信息,如某个网站,iphone6 plus等
         例:
openConnection.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64;Trident/4.0;SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; InfoPath.2");

    8、网络路径中不能包含中文,如果在路径中需要涉及到中文,需要使用将中文使用URLEncoder对中文进行编码
URLEncoder.encode("中文",utf-8);

二、使用smartImageView加载网络图片
    smartImageView继承自ImageView,具有ImageView的所有功能,同时扩展了ImageVIew的功能,如常用的方法setImageUrl(URL url),这个方法可以通过直接传递图片的URL地址来给smartImageView控件设置图片。
    smartIamgeView使用异步加载的方式从网络获取网络图片,加载过程在子线程中执行,在使用时只需要传递图片的路径即可,不需要在子线程访问网络和 加载的问题。
    
public class MainActivity extends Activity {
    String path = "http://f.hiphotos.baidu.com/image/pic/item/a8014c086e061d9507500dd67ff40ad163d9cacd.jpg";
    private SmartImageView smiv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button bt = (Button) findViewById(R.id.bt);
        smiv = (SmartImageView) findViewById(R.id.smiv);
    }

    public void click(View v){
        smiv.setImageUrl(path);
    }
}


三、使用Get请求方式请求网络
    模拟QQ登录
    1、服务器端代码
   
 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //01010101
    String qq = request.getParameter("qq");//采用的编码是iso-8859-1
    String pwd = request.getParameter("pwd");
    System.out.println("qq:"+new String(qq.getBytes("iso-8859-1"),"utf-8"));
    System.out.println("pwd:"+new String(pwd.getBytes("iso-8859-1"),"utf-8"));
    //查询数据库 看qq和密码是否存在
    if("10000".equals(qq)&&"abcde".equals(pwd)){
        //tomcat容器如果发现字符串不识别就默认采用本地码表
        response.getOutputStream().write("登陆成功".getBytes("utf-8"));
    }else{
        response.getOutputStream().write("登陆失败".getBytes("utf-8"));
    }
}


服务器端会通过 HttpServletRequest  request  getParameter()方法 获取到用户请求网络的参数,如用户名和密码。用户发送请求时,输入的请求参数都会展示在地址栏,即地址栏的所有的参数就是服务器端要获取到的所有参数。在发送请求时,只需要在请求地址后面加上相应的参数就能发送get请求,如www.qq.com/web/LogingServlet?qq=10000&pwd=abcde

2、客户端在发送get请求时,需要将请求的参数传递到URL中,然后在发送请求。 如:
//获取请求的参数
final String qq = et_qq.getText().toString().trim();
final String pwd = et_pwd.getText().toString().trim();
//将参数添加到请求地址中
String path = "http://192.168.1.103:8080/web/LoginServlet?qq="+URLEncoder.encode(qq, "utf-8")+"&pwd="+URLEncoder.encode(pwd, "utf-8");

3、get请求的优缺点
    优点:使用方便,只需要的地址后面组拼请求参数即可发送请求
    缺点:(1)请求参数在地址栏都能展示出来,不安全;
           (2)数据的长度有限制

四、使用post请求方式发送网络请求
    1、post请求方式的优缺点
         优点:安全,数据不是在url后面组拼,而是通过流的形式写给服务器;数据的长度没有限制
         缺点:编写麻烦
    2、post请求和get请求的区别
          (1)get请求需要在URL后面组拼提交的数据,post请求不需要组拼任何数据
         (2)post请求必须要指定请求提交的数据长度(如下图,post请求比get请求多了Content-Type请求头Android中的网络编程_第1张图片
         (3)post请求是以流的方式吧数据写给服务器,所有的http请求头必须要告诉服务器写多长的数据(如下图,post请求比get请求多了Content-Length请求头
         3、服务端代码示例            
 @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
        throws ServletException, IOException {
    System.out.println("post过来的请求");
    doGet(req, resp);
}

           4、示例代码
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//1.设置请求方式为POST
conn.setRequestMethod("POST"); //注意单词必须大写.
conn.setConnectTimeout(5000);
//2.设置http请求数据的类型为表单类型
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
//3.设置给服务器写的数据的长度
//qq=10000&pwd=abcde
String data = "qq="+URLEncoder.encode(qq, "utf-8")+"&pwd="+URLEncoder.encode(pwd, "utf-8");
conn.setRequestProperty("Content-Length", String.valueOf(data.length()));
//4.记得指定要给服务器写数据
conn.setDoOutput(true);
//5.开始向服务器写数据
conn.getOutputStream().write(data.getBytes());

五、使用HTTPClient发送网络请求
    1、发送get请求
String path = "http://192.168.1.103:8080/web/LoginServlet?qq="+URLEncoder.encode(qq, "utf-8")+"&pwd="+URLEncoder.encode(pwd, "utf-8");
HttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(path);
HttpResponse response = client.execute(httpGet);
//获取状态码
int code = response.getStatusLine().getStatusCode();  

if(code == 200){
    InputStream is = response.getEntity().getContent();
    String result = StreamTools.readStream(is);
    Message msg = Message.obtain();
    msg.what = SUCCESS;
    msg.obj = result;
    handler.sendMessage(msg);
}else{
    Message msg = Message.obtain();
    msg.what = ERROR;
    handler.sendMessage(msg);
}  

2、发送post请求
          
 String path = "http://192.168.1.103:8080/web/LoginServlet";
HttpClient client = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(path);
List parameters = new ArrayList();
parameters.add(new BasicNameValuePair("qq", qq));
parameters.add(new BasicNameValuePair("pwd", pwd));
httpPost.setEntity(new UrlEncodedFormEntity(parameters, "utf-8"));
HttpResponse response = client.execute(httpPost);
//获取状态码
int code = response.getStatusLine().getStatusCode();

if(code == 200){
    InputStream is = response.getEntity().getContent();
    String result = StreamTools.readStream(is);
    Message msg = Message.obtain();
    msg.what = SUCCESS;
    msg.obj = result;
    handler.sendMessage(msg);
}else{
    Message msg = Message.obtain();
    msg.what = ERROR;
    handler.sendMessage(msg);
} 


StreamTools.readStream()
public class StreamTools {
    /**
     * 工具方法
     * @param is 输入流
     * @return 文本字符串
     * @throws Exception
     */
    public static String readStream(InputStream is) throws Exception{
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = -1;
        while((len = is.read(buffer))!=-1){
            baos.write(buffer, 0, len);
        }
        is.close();
        String temp =  baos.toString();
        return temp;
    }
}

六、使用过异步网络加载框架AsyncHttpClient请求网络
    1、 AsyncHttpClient进一步对 HttpClient进行了封装,这个框架考虑到了子线程问题,我们在使用时就不用再创建子线程,直接使用估计可,同时框架中还使用了线程池,使加载效率更高。

    2、发送get请求
String path = "http://192.168.1.103:8080/web/LoginServlet?qq="+URLEncoder.encode(qq)+"&pwd="+URLEncoder.encode(pwd);
AsyncHttpClient client = new AsyncHttpClient();
client.get(path, new AsyncHttpResponseHandler() {
    @Override
    public void onSuccess(int statusCode, Header[] headers,
            byte[] responseBody) {
        tv_status.setText(new String(responseBody));
    }
    @Override
    public void onFailure(int statusCode, Header[] headers,
            byte[] responseBody, Throwable error) {
        tv_status.setText("http请求失败"+new String(responseBody));
    }
 });

3、发送post请求
String path = "http://192.168.1.103:8080/web/LoginServlet";
AsyncHttpClient client = new AsyncHttpClient();
RequestParams params = new RequestParams();
params.put("qq", qq);
params.put("pwd", pwd);
client.post(path, params, new AsyncHttpResponseHandler(){
    @Override
    public void onSuccess(int statusCode, Header[] headers,
            byte[] responseBody) {
        tv_status.setText("登陆结果:"+new String(responseBody));
    }
    @Override
    public void onFailure(int statusCode, Header[] headers,
            byte[] responseBody, Throwable error) {
        tv_status.setText("请求失败请检查网络");
    }
});

AsyncHttpClient特性:
(1)采用异步http请求,并通过匿名内部类处理回调结果
(2)http请求独立在UI主线程之外
(3)采用线程池来处理并发请求
(4)采用RequestParams类创建GET/POST参数
(5)不需要第三方包即可支持Multipart file文件上传
(6)大小只有25kb
(7)自动为各种移动电话处理连接断开时请求重连
(8)超快的自动gzip响应解码支持
(9)使用BinaryHttpResponseHandler类下载二进制文件(如图片)
(10) 使用JsonHttpResponseHandler类可以自动将响应结果解析为json格式
(11)持久化cookie存储,可以将cookie保存到你的应用程序的SharedPreferences中

使用方法
(1)到官网http://loopj.com/android-async-http/下载最新的android-async-http-1.4.4.jar,然后将此jar包添加进Android应用程序 libs文件夹
(2)通过import com.loopj.android.http.*;引入相关类
(3)创建异步请求

[java] view plaincopy
  1. AsyncHttpClient client = new AsyncHttpClient();  
  2. client.get("http://www.google.com"new AsyncHttpResponseHandler() {  
  3.     @Override  
  4.     public void onSuccess(String response) {  
  5.         System.out.println(response);  
  6.     }  
  7. });  

建议使用静态的Http Client对象
在下面这个例子,我们创建了静态的http client对象,使其很容易连接到Twitter的API
[java] view plaincopy
  1. import com.loopj.android.http.*;  
  2.   
  3. public class TwitterRestClient {  
  4.   private static final String BASE_URL = "http://api.twitter.com/1/";  
  5.   
  6.   private static AsyncHttpClient client = new AsyncHttpClient();  
  7.   
  8.   public static void get(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {  
  9.       client.get(getAbsoluteUrl(url), params, responseHandler);  
  10.   }  
  11.   
  12.   public static void post(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {  
  13.       client.post(getAbsoluteUrl(url), params, responseHandler);  
  14.   }  
  15.   
  16.   private static String getAbsoluteUrl(String relativeUrl) {  
  17.       return BASE_URL + relativeUrl;  
  18.   }  
  19. }  
然后我们可以很容易的在代码中操作Twitter的API
[java] view plaincopy
  1. import org.json.*;  
  2. import com.loopj.android.http.*;  
  3.   
  4. class TwitterRestClientUsage {  
  5.     public void getPublicTimeline() throws JSONException {  
  6.         TwitterRestClient.get("statuses/public_timeline.json"nullnew JsonHttpResponseHandler() {  
  7.             @Override  
  8.             public void onSuccess(JSONArray timeline) {  
  9.                 // Pull out the first event on the public timeline  
  10.                 JSONObject firstEvent = timeline.get(0);  
  11.                 String tweetText = firstEvent.getString("text");  
  12.   
  13.                 // Do something with the response  
  14.                 System.out.println(tweetText);  
  15.             }  
  16.         });  
  17.     }  
  18. }  

AsyncHttpClient, RequestParams ,AsyncHttpResponseHandler三个类使用方法

(1)AsyncHttpClient

public class AsyncHttpClient extends java.lang.Object
 该类通常用在android应用程序中创建异步GET, POST, PUT和DELETE HTTP请求,请求参数通过RequestParams实例创建,响应通过重写匿名内部类 ResponseHandlerInterface的方法处理。
例子:
[java] view plaincopy
  1. AsyncHttpClient client = new AsyncHttpClient();  
  2.  client.get("http://www.google.com"new ResponseHandlerInterface() {  
  3.      @Override  
  4.      public void onSuccess(String response) {  
  5.          System.out.println(response);  
  6.      }  
  7.  });  
(2)RequestParams
public class RequestParams extends java.lang.Object 
用于创建AsyncHttpClient实例中的请求参数(包括字符串或者文件)的集合
例子:
[java] view plaincopy
  1. RequestParams params = new RequestParams();  
  2.  params.put("username""james");  
  3.  params.put("password""123456");  
  4.  params.put("email""[email protected]");  
  5.  params.put("profile_picture"new File("pic.jpg")); // Upload a File  
  6.  params.put("profile_picture2", someInputStream); // Upload an InputStream  
  7.  params.put("profile_picture3"new ByteArrayInputStream(someBytes)); // Upload some bytes  
  8.   
  9.  Map map = new HashMap();  
  10.  map.put("first_name""James");  
  11.  map.put("last_name""Smith");  
  12.  params.put("user", map); // url params: "user[first_name]=James&user[last_name]=Smith"  
  13.   
  14.  Set set = new HashSet(); // unordered collection  
  15.  set.add("music");  
  16.  set.add("art");  
  17.  params.put("like", set); // url params: "like=music&like=art"  
  18.   
  19.  List list = new ArrayList(); // Ordered collection  
  20.  list.add("Java");  
  21.  list.add("C");  
  22.  params.put("languages", list); // url params: "languages[]=Java&languages[]=C"  
  23.   
  24.  String[] colors = { "blue""yellow" }; // Ordered collection  
  25.  params.put("colors", colors); // url params: "colors[]=blue&colors[]=yellow"  
  26.   
  27.  List> listOfMaps = new ArrayList>();  
  28.  Map user1 = new HashMap();  
  29.  user1.put("age""30");  
  30.  user1.put("gender""male");  
  31.  Map user2 = new HashMap();  
  32.  user2.put("age""25");  
  33.  user2.put("gender""female");  
  34.  listOfMaps.add(user1);  
  35.  listOfMaps.add(user2);  
  36.  params.put("users", listOfMaps); // url params: "users[][age]=30&users[][gender]=male&users[][age]=25&users[][gender]=female"  
  37.   
  38.  AsyncHttpClient client = new AsyncHttpClient();  
  39.  client.post("http://myendpoint.com", params, responseHandler);  
(3)public class AsyncHttpResponseHandler extends java.lang.Object implements ResponseHandlerInterface
用于拦截和处理由AsyncHttpClient创建的请求。在匿名类AsyncHttpResponseHandler中的重写 onSuccess(int, org.apache.http.Header[], byte[])方法用于处理响应成功的请求。此外,你也可以重写 onFailure(int, org.apache.http.Header[], byte[], Throwable), onStart(), onFinish(), onRetry() 和onProgress(int, int)方法
例子:
[java] view plaincopy
  1. AsyncHttpClient client = new AsyncHttpClient();  
  2.  client.get("http://www.google.com"new AsyncHttpResponseHandler() {  
  3.      @Override  
  4.      public void onStart() {  
  5.          // Initiated the request  
  6.      }  
  7.   
  8.      @Override  
  9.      public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {  
  10.          // Successfully got a response  
  11.      }  
  12.   
  13.      @Override  
  14.      public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error)  
  15.  {  
  16.          // Response failed :(  
  17.      }  
  18.   
  19.      @Override  
  20.      public void onRetry() {  
  21.          // Request was retried  
  22.      }  
  23.   
  24.      @Override  
  25.      public void onProgress(int bytesWritten, int totalSize) {  
  26.          // Progress notification  
  27.      }  
  28.   
  29.      @Override  
  30.      public void onFinish() {  
  31.          // Completed the request (either success or failure)  
  32.      }  
  33.  });  

利用PersistentCookieStore持久化存储cookie
PersistentCookieStore类用于实现Apache HttpClient的CookieStore接口,可以自动的将cookie保存到Android设备的SharedPreferences中,如果你打算使用cookie来管理验证会话,这个非常有用,因为用户可以保持登录状态,不管关闭还是重新打开你的app
(1)首先创建 AsyncHttpClient实例对象
[java] view plaincopy
  1. AsyncHttpClient myClient = new AsyncHttpClient();  
(2)将客户端的cookie保存到PersistentCookieStore实例对象,带有activity或者应用程序context的构造方法
[java] view plaincopy
  1. PersistentCookieStore myCookieStore = new PersistentCookieStore(this);  
  2. myClient.setCookieStore(myCookieStore);  
(3)任何从服务器端获取的cookie都会持久化存储到myCookieStore中,添加一个cookie到存储中,只需要构造一个新的cookie对象,并且调用addCookie方法
[java] view plaincopy
  1. BasicClientCookie newCookie = new BasicClientCookie("cookiesare""awesome");  
  2. newCookie.setVersion(1);  
  3. newCookie.setDomain("mydomain.com");  
  4. newCookie.setPath("/");  
  5. myCookieStore.addCookie(newCookie);  

7.利用RequestParams上传文件
类RequestParams支持multipart file 文件上传
(1)在RequestParams 对象中添加InputStream用于上传
[java] view plaincopy
  1. InputStream myInputStream = blah;  
  2. RequestParams params = new RequestParams();  
  3. params.put("secret_passwords", myInputStream, "passwords.txt");  
(2)添加文件对象用于上传
[java] view plaincopy
  1. File myFile = new File("/path/to/file.png");  
  2. RequestParams params = new RequestParams();  
  3. try {  
  4.     params.put("profile_picture", myFile);  
  5. catch(FileNotFoundException e) {}  
(3)添加字节数组用于上传
[java] view plaincopy
  1. byte[] myByteArray = blah;  
  2. RequestParams params = new RequestParams();  
  3. params.put("soundtrack"new ByteArrayInputStream(myByteArray), "she-wolf.mp3");  

用BinaryHttpResponseHandler下载二进制数据
BinaryHttpResponseHandler用于获取二进制数据如图片和其他文件
AsyncHttpClient client = new AsyncHttpClient();  
String[] allowedContentTypes = new String[] { "image/png", "image/jpeg" };  
client.get("http://example.com/file.png", new BinaryHttpResponseHandler(allowedContentTypes) {  
    @Override  
    public void onSuccess(byte[] fileData) {  
        // Do something with the file  
    }  
});  

android-async-http 开源框架可以使我们轻松地获取网络数据或者向服务器发送数据,最关键的是,它是异步框架,在底层使用线程池处理并发请求,效率很高,使用又特别简单。

    以往我们在安卓上做项目,比如要下载很多图片、网页或者其他的资源,多数开发者会选择一个线程一个下载任务这种模型,因为安卓自带的 AndroidHttpClient 或者 java 带的 java.net.URL ,默认都是阻塞式操作。这种模型效率不高,对并发要求高的 APP 来讲,并不适用。有的人会选择使用 nio 自己实现,代码复杂度又很高。

    AsyncHttpClient 作为 android-async-http 框架的一个核心应用类,使用简单,可以处理文本、二进制等各种格式的 web 资源。下面提供一些代码来看如何使用:

public class Downloader {  
    public static AsyncHttpClient mHttpc = new AsyncHttpClient();  
    public static String TAG = "Downloader";  
      
    public void downloadText(String uri){  
        mHttpc.get(uri, null, new AsyncHttpResponseHandler(){  
            @Override  
            public void onSuccess(String data){  
                Log.i(TAG, "downloaded, thread id " + Thread.currentThread().getId());  
                // TODO: do something on  
            }  
            @Override  
            public void onFailure(Throwable e, String data){  
                Log.i(TAG, "download failed.");  
                // TODO: error proceed  
            }  
        });  
    }  
      
    public void downloadImage(String uri, String savePath){  
        mHttpc.get(uri, new ImageResponseHandler(savePath));  
    }  
      
    public class ImageResponseHandler extends BinaryHttpResponseHandler{  
        private String mSavePath;  
          
        public ImageResponseHandler(String savePath){  
            super();  
            mSavePath = savePath;  
        }  
        @Override  
        public void onSuccess(byte[] data){  
            Log.i(TAG, "download image, file length " + data.length);  
            // TODO: save image , do something on image  
        }  
        @Override  
        public void onFailure(Throwable e, String data){  
            Log.i(TAG, "download failed");  
            // TODO : error proceed  
        }  
    }  
};  


七、使用post请求上传文件
         
         使用异步网络请求框架上传文件时,只需要将请求的参数设置为要上传的文件即可
public void upload(View view){
    String path = et_path.getText().toString().trim();
    File file = new File(path);
    if(file.exists()&&file.length()>0){
        //上传
        AsyncHttpClient client = new AsyncHttpClient();
        RequestParams params = new RequestParams();
        try {
            params.put("file", file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        client.post("http://192.168.1.103:8080/web/UploadServlet", params, new AsyncHttpResponseHandler() {
            @Override
            public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
                Toast.makeText(MainActivity.this, "上传成功", 0).show();
            }
            
            @Override
            public void onFailure(int statusCode, Header[] headers,
                    byte[] responseBody, Throwable error) {
                Toast.makeText(MainActivity.this, "上传失败", 0).show();
            }
        });
        
    }else{
        Toast.makeText(this, "请检查文件是否存在", 0).show();
    }
}  



八、使用HttpUtils加载网络
    1、发送get请求
         
HttpUtils http = new HttpUtils();
        http.send(HttpRequest.HttpMethod.GET,
            "http://www.lidroid.com",
            new RequestCallBack(){
                @Override
                public void onLoading(long total, long current, boolean isUploading) {
                    testTextView.setText(current + "/" + total);
                }
                @Override
                public void onSuccess(ResponseInfo responseInfo) {
                    textView.setText(responseInfo.result);
                }
                @Override
                public void onStart() {
                }
                @Override
                public void onFailure(HttpException error, String msg) {
                }
        });


    2、下载
            支持断点续传,随时停止下载任务,开始任务
   
 HttpUtils http = new HttpUtils();
    HttpHandler handler = http.download("http://apache.dataguru.cn/httpcomponents/httpclient/source/httpcomponents-client-4.2.5-src.zip",
        "/sdcard/httpcomponents-client-4.2.5-src.zip",
        true, // 如果目标文件存在,接着未完成的部分继续下载。服务器不支持RANGE时将从新下载。
        true, // 如果从请求返回信息中获取到文件名,下载完成后自动重命名。
        new RequestCallBack() {
            @Override
            public void onStart() {
                testTextView.setText("conn...");
            }
            @Override
            public void onLoading(long total, long current, boolean isUploading) {
                testTextView.setText(current + "/" + total);
            }
            @Override
            public void onSuccess(ResponseInfo responseInfo) {
                testTextView.setText("downloaded:" + responseInfo.result.getPath());
            }
            @Override
            public void onFailure(HttpException error, String msg) {
                testTextView.setText(msg);
            }
    });

         

九、多线程下载
    1、多线程下载的步骤
         (1)在磁盘上创建一个与网络文件相同大小的空白文件
         (2)将文件划分为若干块,开启多线程,下载文件,每个线程分别下载不同的块
         (3)当所有的线程都将自己的块下载完成后,文件下载完成

    2、具体实现步骤
         (1) 连接网络,使用getContentLength()方法获取网络文件的大小
         (2) 使用RandomAccessFile在本地创建一个与网络文件相同大小的空文件
         (3) 设置开启多少个线程来下载
         (4) 为每个线程设置需要下载的文件大小(每个线程下载的大小=总文件大小/线程数),设置每个线程下载的区域由于不不能被整除,所以最后一个线程多下载一点,特殊处理
         (5) 创建线程下载文件
         (6) 线程连接网络
         (7) 高告诉服务器只需要下载一部分内容  conn.setRequestProperty("Range", "bytes="+currentPosition+"-"+endIndex);
   Range表示范围,后面的字符串为范围值
   (8) 使用随机文件访问流的seek( int  index)方法设置每个线程下载文件后在本地文件中写入的起始位置
       (9) 将获取到的数据写入到本地文件中

       
public void download(View view) {
    path = et_path.getText().toString().trim();
    if (TextUtils.isEmpty(path) || (!path.startsWith("http://"))) {
        Toast.makeText(this, "对不起路径不合法", 0).show();
        return;
    }
    new Thread(){
        public void run() {
            try {
                //1、连接网络
                URL url = new URL(path);
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(5000);
                conn.setRequestMethod("GET");
                int code = conn.getResponseCode();
                if (code == 200) {
                    //2、使用getContentLength()方法获取网络文件的大小
                    int length = conn.getContentLength();
                    System.out.println("服务器文件的长度为:" + length);
                    //3、使用RandomAccessFile在本地创建一个空文件
                    RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+getFileName(path), "rw");
                    //4、将空文件的大小设置为与网络文件大小相同
                    raf.setLength(length);
                    raf.close();
                    //5、设置将文件划分的块数,threadCount--线程数
                    int blocksize = length / threadCount;
                    runningThreadCount = threadCount;
                    //6、设置每个线程下载的文件区域
                    for (int threadId = 0; threadId < threadCount; threadId++) {
                        int startIndex = threadId * blocksize;
                        int endIndex = (threadId + 1) * blocksize - 1;
                        if (threadId == (threadCount - 1)) {
                            endIndex = length - 1;
                        }
                        new DownloadThread(threadId, startIndex, endIndex).start();
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
    }.start();
}

//创建线程下载文件
private class DownloadThread extends Thread {
    /**
     * 线程id
     */
    private int threadId;
    /**
     * 线程下载的理论开始位置
     */
    private int startIndex;
    /**
     * 线程下载的结束位置
     */
    private int endIndex;
    /**
     * 当前线程下载到文件的那一个位置了.
     */
    private int currentPosition;
    public DownloadThread(int threadId, int startIndex, int endIndex) {
        this.threadId = threadId;
        this.startIndex = startIndex;
        this.endIndex = endIndex;
        System.out.println(threadId + "号线程下载的范围为:" + startIndex
                + "   ~~   " + endIndex);
        currentPosition = startIndex;
    }
    @Override
    public void run() {
        try {
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            //检查当前线程是否已经下载过一部分的数据了 
            File info = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position");
            RandomAccessFile raf = new RandomAccessFile(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+getFileName(path), "rw");
            if(info.exists()&&info.length()>0){
                FileInputStream fis = new FileInputStream(info);
                BufferedReader br = new BufferedReader(new InputStreamReader(fis));
                currentPosition = Integer.valueOf(br.readLine());
                conn.setRequestProperty("Range", "bytes="+currentPosition+"-"+endIndex);
                System.out.println("原来有下载进度,从上一次终止的位置继续下载"+"bytes="+currentPosition+"-"+endIndex);
                fis.close();
                raf.seek(currentPosition);//每个线程写文件的开始位置都是不一样的.
            }else{
            //告诉服务器 只想下载资源的一部分
                conn.setRequestProperty("Range", "bytes="+startIndex+"-"+endIndex);
                System.out.println("原来没有有下载进度,新的下载"+ "bytes="+startIndex+"-"+endIndex);
                raf.seek(startIndex);//每个线程写文件的开始位置都是不一样的.
            }
            InputStream is = conn.getInputStream();
            byte[] buffer = new byte[1024];
            int len = -1;
            while((len = is.read(buffer))!=-1){
                //把每个线程下载的数据放在自己的空间里面.
//                System.out.println("线程:"+threadId+"正在下载:"+new String(buffer));
                raf.write(buffer,0, len);
                currentPosition+=len;
                File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position");
                RandomAccessFile fos = new RandomAccessFile(file,"rwd");
                //System.out.println("线程:"+threadId+"写到了"+currentPosition);
                fos.write(String.valueOf(currentPosition).getBytes());
                fos.close();//fileoutstream数据是不一定被写入到底层设备里面的,有可能是存储在缓存里面.
                //raf 的 rwd模式,数据是立刻被存储到底层硬盘设备里面.
                int max = endIndex - startIndex;
                int progress = currentPosition - startIndex;
                if(threadId==0){
                    pb0.setMax(max);
                    pb0.setProgress(progress);
                }else if(threadId==1){
                    pb1.setMax(max);
                    pb1.setProgress(progress);
                }else if(threadId==2){
                    pb2.setMax(max);
                    pb2.setProgress(progress);
                }
            }
            raf.close();
            is.close();
            System.out.println("线程:"+threadId+"下载完毕了...");
            File f = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position");
            f.renameTo(new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+threadId+".position.finish"));
            synchronized (MainActivity.class) {
                runningThreadCount--;
                if(runningThreadCount<=0){
                    for(int i=0;i

/**
 * 获取一个文件名称
 * @param path
 * @return
 */
public String getFileName(String path){
    int start = path.lastIndexOf("/")+1;
    return path.substring(start);
}  


你可能感兴趣的:(网络通信)