HTTP服务响应和DownloadManager的使用

1、HttpClient使用:

a、HttpClient GET使用

public class HttpGetDemo extends Activity{  
    @Override 
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.activity_main);  
                 
        BufferedReader in = null; 
        try{  
            //【Step 1】创建一个HttpClient的对象(或使用已有的)
            HttpClient client = new DefaultHttpClient(); 
            //【Step 2】实例化一个HTTP GET或者HTTP POST,本例是HTTP GET 
            HttpGet request = new HttpGet("
http://www.baidu.com"); 
            //【Step 3】设置HTTP参数,本例子是最简单地打开某个网址的HTTP GET,无需设置 
           //【Step 4】通过HttpClient来执行HTTP call(发出HTTP请求)  
            HttpResponse response = client.execute(request);
 
            //【Step 5】处理HTTP响应,本例将整个响应的内容(HTTP 200消息的body)都在String中。 
            in = new BufferedReader( 
                    new InputStreamReader( 
                            response.getEntity().getContent())
); 
            StringBuffer buff = new StringBuffer(""); 
            String line = ""; 
            String NL = System.getProperty("line.separator");  //实际上就是“\n”,可自动适配系统的换行符。 
            while((line = in.readLine()) != null ){ 
                buff.append(line + NL); 
            } 
            in.close();  
            Log.d("PRO",buff.toString());  
        }catch(Exception e){ 
            e.printStackTrace(); 
            Log.d("PRO",e.toString()); 
        }finally{  //try{}catch(){}finally{}中finally见得稍微少,但是很有用,无论是否有异常,都会执行finally的代码,常用于xx.close()。
            if(in != null){ 
                try{ 
                    in.close(); //关闭BufferedReader,同时也关闭了底层的HTTP connection
                }catch(Exception e){ 
                    e.printStackTrace();  
                    Log.d("PRO","error in finally:\n" + e.toString());
 
                } 
            } 
        }         
    }  
    
}


b、HttpClient POST使用

BufferedReader in = null; 
try{ 
    //【Step 1】创建一个HttpClient的对象(或使用已有的) 
    HttpClient client = new DefaultHttpClient();
 
    //【Step 2】实例化一个HTTP GET或者HTTP POST,本例是HTTP POST 
    HttpPost request = new HttpPost("http://epub.cnki.net/kns/brief/default_result.aspx"); 
     
    //【Step 3】设置HTTP参数,本例根据抓包的内容填写,这是体力活,在完整HTTP服务的笔记后,会提供小例子下载。对于HTTP Post,需要传递键值对信息,从上面的转包可以看到,这部分不是作为request URI,而是作为HTML Form URL Encoded,为此我们需要用户元素为NameValuePair格式的list来存储这些信息,并封装在UrlEncodedFormEntiry对象中。通过setEntity()加入到请求对象中。
    List postParameters = new ArrayList();
    postParameters.add(new BasicNameValuePair(
"txt_1_sel","TI$%=|"));
    postParameters.add(new BasicNameValuePair("txt_1_value1","Android"));  
    … … 
    postParameters.add(new BasicNameValuePair("ua","1.11"));      
    UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(postParameters);
    request.setEntity(formEntity);
 
    
    //【Step 4】通过HttpClient来执行HTTP call(发出HTTP请求)
    HttpResponse response =client.execute(request); 
    
    //【Step 5】处理HTTP响应,本例将整个响应的内容(HTTP 200消息的body)都在String中。 
    in = new BufferedReader( 
            new InputStreamReader( 
                    response.getEntity().getContent())); 
    
    StringBuffer buff = new StringBuffer(""); 
    String line = ""; 
    String NL = System.getProperty("line.separator"); 
    while((line = in.readLine())!= null){ 
        buff.append(line + NL); 
    } 
    showInfo(buff.toString());
     
}catch(Exception e){ 
    e.printStackTrace(); 
    showInfo(e.toString());
 
}finally{ 
    if(in != null){ 
        try{ 
            showInfo("== process in.colse() =="); 
            in.close(); 
        }catch(Exception e){ 
            e.printStackTrace(); 
            showInfo(e.toString()); 
        } 
    }
    
}


C、HttpClient POST MultiPart使用

HTTP POST不仅可以通过键值对传递参数,还可以携带更为复杂的参数,例如文件。HTTP Post支持携带body,content-type为multipart。

目前Andriod的HttpClient并不直接支持multipart,我们需要三个额外的jar来支持:Apache Commons IO,Mime4j,以及HttpMime。可以通过下面地址来下载,也会附到我们所提供的小例子源代码中。http://commons.apache.org/proper/commons-io/http://james.apache.org/mime4j/,以及http://hc.apache.org/index.html。这些jar要最终打包在apk中,应用才能在设备中运行。

private HttpResponse multiPartTest() throws Exception{  
    try{              
        HttpClient client = new DefaultHttpClient(); 
        HttpPost request = new HttpPost("http://upload.cloud.189.cn/v5/v5webUploadSmallFileActionl");
 
             
        //设置HTTP参数,本例设置Multipart参数 
        //(1)上传文件readme.txt已经放在asset/下,获取该文件的ContentBody。如果是在存贮中某个目录下,可以用FileBody fb = new FileBody(File f)来得到该ContentBody。
        InputStream is = this.getAssets().open("readme.txt");  
        byte[] data = IOUtils.toByteArray(is);                
        InputStreamBody isb = new InputStreamBody(new ByteArrayInputStream(data), "myUpload");               
        //(2)除了文件外,我们再附加两个参数,生成这两个参数的ContentBody
        StringBody sb1 = new StringBody("some text goes here");  
        StringBody sb2 = new StringBody("some text goes here, too");  
       //(3)作为multipart参数,加入到request中 
        MultipartEntity multiEntity = new MultipartEntity();  
        multiEntity.addPart("myUpload", isb);     
        multiEntity.addPart("one",sb1);  
        multiEntity.addPart("two",sb2);               
        request.setEntity(multiEntity);  
//在上个例子中setEntity()里的参数是UrlEncodedFormEntity本次是MultiPartEntity
               
        return client.execute(request);               
    }catch(Exception e){  
        throw e;  
    }  
}


d、多线程调用HttpClient

(1)创建共享对象

public class CustomHttpClient { 
    private static HttpClient client = null;  //应用共享的对象
     
    /* 采用private的构造器,禁止了其他类通过CustomHttpClient xx = new CustomHttpClient();这种方式创建对象,确保对象的唯一性 */
   private CustomHttpClient(){ 
    } 
    /* 通过静态调用获取对象,第一次调用为空时进行创建 */ 
    public static synchronized HttpClient getCustomHttpClient(){
 
        if(client == null){ 
           /*如果对象为空,创建之*/ 
           //【2.1】设置Http参数 
HttpParams params = new BasicHttpParams();
 
/* 设置HttpParam是的基本参数,其实都是对应http请求的消息头。其中三个都很好理解,重点介绍一些setUserExpectContinue。 一般都设置为flase,设置为true通常是传递request消息很大(例携带大文件),而服务器可能需要认证,我们不希望传完这个大文件,才收到服务器的拒绝。HTTP是TCP流方式,当server收到请求的头字段是Except:100-continue, 不在等待整个请求,返回100 continue应答继续读取,或者给出拒绝请求(final Status code,如4xx)。 具体可以参考:http://www.w3.org/Protocols/rfc2616/rfc2616-sec8.html#sec8.2.3 */
HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1); 
HttpProtocolParams.setContentCharset(params, HTTP.DEFAULT_CONTENT_CHARSET);
HttpProtocolParams.setUseExpectContinue(params, true); 
HttpProtocolParams.setUserAgent(params, "Mozilla/5.0 (Linux; U; Android 2.2.1; en-us; Nexus One Build/FRG83)" +
     " AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1"); 
/* 设置超时时间。超时的异常均属于IOException,此外ClientProtocolException也是与IOException*/
// 从ClientConnectionManager获取连接的时间,这是从连接池中获取连接的超时设置,只有在连接池所有连接都在使用的情况下才可能出现超时。超时会扔出ConnectionPoolTimeoutException。一个HttpClient对应管理器,有连接池,里面有多个连接(socket),这是我对其架构的猜测。
ConnManagerParams.setTimeout(params, 1000); 
// 这是连接到远端web server的超时设置,超时会扔出ConnectTimeoutException
HttpConnectionParams.setConnectionTimeout(params, 5000);//连接超时
// 这是发送请求消息后,最多等待多长时间得到响应的设置,超时会扔出SocketTimeoutException
HttpConnectionParams.setSoTimeout(params, 10000);//socket超时
//【2.2】设置Sheme,注册了http和https 
SchemeRegistry schReg = new SchemeRegistry(); 
schReg.register(new Scheme("http",PlainSocketFactory.getSocketFactory(), 80)); 
schReg.register(new Scheme("https",PlainSocketFactory.getSocketFactory(), 443)); 

//【2】ClientConnectionManager用于管理HTTP连接,我们使用同一个client来处理请求,要确保多线程的使用安全,采用ThreadSafeClientConnManager,是线程安全的连接池。如果多个线程同时请求,或有延迟情况。
ClientConnectionManager conMgr = new ThreadSafeClientConnManager(params,schReg);
//【1】以ThreadSafeClientConnManager为管理器参数,创建可进行多线程调用的同步保护的HttpClient对象
client = new DefaultHttpClient(conMgr,params);
 

        } 
        return client; 
    } 
    /*禁止clone,同样也是保证对象的唯一性*/ 
    public Object clone() throws CloneNotSupportedException{ 
        throw new CloneNotSupportedException(); 
    } 
    
}

(2)使用共享代码

public class HttpActivity extends Activity{ 
    private HttpClient client = null; 
    
    protected void onCreate(Bundle savedInstanceState) {  
         …… //UI处理等 
        client = CustomHttpClient.getCustomHttpClient();        
        getHttpContent(); 
    } 
    
    private void getHttpContent(){ 
        try{  
            HttpGet request = new HttpGet("http://www.google.com");            
            /* 在处理response时,利用Android提供的BasicResponseHandler:handleResponse(HttpResponse response),Returns the response body as a String. if the response was successful (a 2xx status code).  */       
            String page = client.execute(request,new BasicResponseHandler()); 
            Log.d("PRO-HTTP",page); 
        }catch(IOException e){ 
            e.printStackTrace();  
        } 
    } 


(3)修改Http参数

// 我们设置了内部网的一个空地址,通过LogCat中连接超时出现的时间,来判断参数修改是否成功
HttpGet request = new HttpGet("http://192.168.0.199");              

// 读取httpClient的参数设置 
HttpParams clientParams=client.getParams(); 
Log.d("PRO-HTTP",Log.d(String.valueOf(HttpConnectionParams.getConnectionTimeout(clientParams)));//显示为5000
Log.d("PRO-HTTP",String.valueOf(HttpConnectionParams.getSoTimeout(clientParams)));//显示为10000  
             
// 原来设置的连接超时是5秒,下面将重新设置该参数,设为20秒,我们将新的参数设置在request中,将不影响其他的请求
HttpParams params = request.getParams(); 
HttpConnectionParams.setConnectionTimeout(params, 20000);//20s 
request.setParams(params);  
Log.d("PRO-HTTP",String.valueOf(HttpConnectionParams.getConnectionTimeout(params)));//显示20000

Log.d("PRO-HTTP",String.valueOf(HttpConnectionParams.getSoTimeout(params))); //显示0

2、HttpURLConnection使用
a、GET使用

private void httpUrlConnGetTest(){ 
    HttpURLConnection urlConn = null; 
    try{  
        URL url = new URL("http://www.android.com/");  
         
        /* 【1】 获取HttpURLConnection的对象。通过调用URL.openConnection(),并将类型适配为HttpURLConnection类型。 如果是处理https,则使用HttpsURLConnecction,相关的代码参考: http://developer.android.com/reference/javax/net/ssl/HttpsURLConnection.html */
        urlConn = (HttpURLConnection) url.openConnection(); 
        
        /* 【2】 处理request的header,设置超时属性 。 */
        urlConn.setRequestProperty("private-Hello", "Hello world!");//加入属性测试 
        urlConn.setConnectTimeout(3000);  //对应connection timeout
        urlConn.setReadTimeout(5000); //对应Socket timeout
        
        /* 【3】 处理request的body。HTTP Get 没有body,相关的在HTTP POST中演示 */
         
        /* 【4】读取response。*/ 
        // 【4.1】获取response code测试  
        int responseCode = urlConn.getResponseCode();  
        Log.d("PRO","Response code = " + responseCode);
        if(responseCode == HttpURLConnection.HTTP_OK){  
            Log.d("PRO","测试获取头信息Content-Type:" + urlConn.getContentType()); //【4.2】获取header信息测试 
            // 读取body 
            BufferedReader in = new BufferedReader( 
                    new InputStreamReader( 
                            urlConn.getInputStream()));             
            String line = null; 
            while((line = in.readLine()) != null ){  
                Log.d("PRO",line); 
            } 
            in.close(); 
        } 
    }catch(Exception e){  
        e.printStackTrace();      
    }finally{ 
        /* 【5】 断开连接。*/  
        if(urlConn != null) 
            urlConn.disconnect(); 
    } 


b、POST使用

private void httpUrlConnPostTest(){ 
    HttpURLConnection urlConn = null; 
    try{  
        URL url = new URL("http://blog.csdn.net/flowingflying1");//这是个无效的地址,预计回复403 
        urlConn = (HttpURLConnection) url.openConnection(); 
                   
        /* 【3】 处理request的body */   
        urlConn.setDoOutput(true);
  // 设置允许output,即可以带有request body 
        // 为了性能更好,应该设置setFixedLengthStreamingMod(int)或者setChunkedStramingMode(int)。如果不设置,request将的带buffer已经完成body的写,再发送,这对body数据量大的情况下显然效率较低。
       urlConn.setChunkedStreamingMode(0);    
       
// 通过outstream,写入body  
        OutputStream out = new BufferedOutputStream(urlConn.getOutputStream());
        String content = "user=myfriend&action=TEST"; 
        out.write(content.getBytes()); 
        out.close(); 
        
        /* 4、读取response。*/  
        if(urlConn.getResponseCode()  = HttpURLConnection.HTTP_OK){ 
            BufferedReader in = new BufferedReader( 
                    new InputStreamReader( 
                            urlConn.getInputStream()));              
            String line = null; 
            while((line = in.readLine()) != null ){  
                Log.d("PRO",line); 
            } 
            in.close(); 
        }
 
    }catch(Exception e){  
        e.printStackTrace();         
    }finally{  
        if(urlConn != null) 
            urlConn.disconnect(); 
    }
 


3、DownloadManager使用

public class DownloadMrgActivity extends Activity{ 
    private TextView tv = null; 
    private DownloadManager manager = null; 
    private long downloadId = -1;  
     
    protected void onCreate(Bundle savedInstanceState) { 
        super.onCreate(savedInstanceState); 
        setContentView(R.layout.download_mrg); 
        tv = (TextView)findViewById(R.id.tv); 
    }
 

    //【1】通过获取Download_Service的reference来获得DownloadManager实例 
    protected void onResume() {  
        super.onResume(); 
        manager = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        showInfo("get DownloadManager instance : " + manager);

    } 
    //【2】向DownloadManager请求下载某图片 
    public void doDownload(View v){  //按Button触发 android:onClick="doDoownload"
        showInfo("doDownload() is called"); 
       /*【2.1】设置请求 */ 
       DownloadManager.Request dmReq = new DownloadManager.Request

                Uri.parse("http://ww1.sinaimg.cn/large/5cf79a90jw1ecy18vfwlrj20xc18gtgd.jpg")); 
        dmReq.setTitle(getResources().getString(R.string.download_manager));//下来通知栏,显示信息的title
        dmReq.setDescription("食指大动");  //下来通知栏,显示信息的描述 
        dmReq.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI);//缺省不作限制,本例在此设置只允许WIFI连接时下载,也可以设置为NETWORK_MOBILE
        /* 【2.2】DownloadManager在后台下载完成后,会进行广播通知,设置通知接收器:先设置过滤条件,再register接收器*/
        IntentFilter filter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
        registerReceiver(myReceiver, filter); 
        /* 【2.3】 向DownloadManager提交请求,并获得id,该id用于对应该请求的处理 */
        downloadId = manager.enqueue(dmReq);
 
        showInfo("downloadId = " + downloadId);
    } 
    /*【2.2.1】设置DownloadManager的广播接收器*/ 
    public BroadcastReceiver myReceiver = new BroadcastReceiver(){ 
        // 获取id号,可据此判断是哪个请求的完成。intent在之前已经学习过,可以用来唤起component,并传递信息,component包括activity、service、broadcast receiver以及content provider。此处正式broadcast receiver。
        public void onReceive(Context context, Intent intent) { 
            Bundle extras = intent.getExtras(); 
            long doneDownloadId = extras.getLong(DownloadManager.EXTRA_DOWNLOAD_ID);
           
            if(doneDownloadId != downloadId) 
                return; 
            showInfo("Download with id " + doneDownloadId + " is finished.");
            showInfo("Download file uri is " + manager.getUriForDownloadedFile(doneDownloadId));

        } 
        
    };  
     
    //【3】与onResume()对应,进行一些清空处理,如unregister接收器,不再监听DownloadManager的广播
    protected void onPause() {  
        super.onPause(); 
        unregisterReceiver(myReceiver); 
        manager = null;
 
        downloadId = -1;
    } 

    private void showInfo(String s){
        Log.d("PRO-wei",s); 
        tv.setText(s + "\n" + tv.getText()); 
    }
 

}

a、查看和设置保存位置

在Android 4.2开始,manager.getUriForDownloadedFile(id)将返回的scheme是content,小例子返回uri是content://downloads/my_downloads/,没有给出路径,我找了很久都没有找到缺省存放在哪里。当然,有了uri,就可以进行读取,例如:

InputStream input = getContentResolver().openInputStream(uri);

但是,我们还是想知道具体存放在那里,可以通过下面的方式查看下载文件信息,还可以查询很多信息,具体可翻阅API的reference。

Cursor c = manager.query(new DownloadManager.Query().setFilterById(doneDownloadId));
if(c != null){ 
    c.moveToFirst(); 
    showInfo(c.getString(c.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME)));
    c.close(); 

应该查询,文件放在一个隐秘的地方,难怪没能翻出来,LogCat截图如下:

如果希望文件能否放在一个用户容易查找的地方,可以在请求中指定路径,如下:

dmReq.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, "fish.jpg");

在指定路径后,uri给出的是文件的路径file:///mnt/sdcard/Download/fish.jpg。当然要正确运行,我们必须申请WRITE_EXTERNAL_STORAGE权限。

实际上,我们并不需要知道文件路径或者content uri,也可读取文件,我们在layout中增加一个imageview控件,用于显示下载的图片,相关代码如下:

try{  
    ParcelFileDescriptor pfd = manager.openDownloadedFile(doneDownloadId); 
    FileDescriptor fileDescriptor = pfd.getFileDescriptor();  
    Bitmap bitmap = BitmapFactory.decodeFileDescriptor(fileDescriptor);  
    pfd.close();  
    ImageView image = (ImageView)findViewById(R.id.download_image);  
    image.setImageBitmap(bitmap);  
}catch(Exception e){  
    e.printStackTrace();  


b、查看或取消下载

public void cancelDownload(View v){ 
    if(downloadId > 0){ 
        Cursor c = manager.query(new DownloadManager.Query().setFilterById(downloadId));
        if(c == null) 
            return; 
        c.moveToFirst();
 
        int state = c.getInt(c.getColumnIndex(DownloadManager.COLUMN_STATUS));
        if(state != DownloadManager.STATUS_FAILED && state != DownloadManager.STATUS_SUCCESSFUL){ 
            showInfo("Download is not finished, CANCEL it.");
            manager.remove(downloadId);  //如果文件已经下载完成,remove命令并不会删除文件 
            downloadId = -1; 
        }            
        c.close();            
    } 


你可能感兴趣的:(Android)