1. 程式人生 > >Android基礎 第五天

Android基礎 第五天

GET方式提交資料

HttpUrlConnection提交資料到伺服器

get提交提交資料到伺服器 (資料拼接到到哪裡?)

(資料是寫到哪裡的?用post提交的時候需要設定兩個頭資訊,還要設定開啟輸出流,最後通過conn.getOutputStream拿到一個輸出流,向伺服器寫資料)
//兩個頭資訊
conn.setRequestProperty(“Content-Type”, “application/x-www-form-urlencoded”);
conn.setRequestProperty(“Content-Length”, String.valueOf(data.length()));

conn.setDoOutput(true);//不要忘記這行程式碼
conn.getOutputStream().write(data.getBytes());//向伺服器寫資料
這兩個寫完之後,要總結 兩者的差異!! 比如: post請求要多設定幾個頭資訊 , 寫資料的之前要注意什麼細節! 資料的提交方式區別

HttpClient方式 GET 傳遞資料,(Android6.0之後被廢棄掉了)

httpClient的這種方式更加面向物件,程式碼更好理解

用這種方式要需要設定兩個頭資訊嗎? 不需要,應為HttpClient把這些細節都給封裝起來了,不用需關心其中的細節

String path = “

http://192.168.1.103:8080/web/LoginServlet?qq=“+URLEncoder.encode(qq, “utf-8”)+”&pwd=”+URLEncoder.encode(pwd, “utf-8”);
//1.開啟瀏覽器 simple base default
HttpClient client = new DefaultHttpClient();
//2.輸入地址或者資料
HttpGet httpGet = new HttpGet(path);
//3.敲回車
HttpResponse response = client.execute(httpGet);

//獲取狀態碼
int code = response.getStatusLine().getStatusCode();
if(code == 200){
//拿到伺服器返回的輸入流,
InputStream is = response.getEntity().getContent();
//開始讀取資料,後面和HttpUrlConnection 就一樣了
String result = StreamTools.readStream(is);
……發訊息,更新UI執行緒
HttpClient方式 POST 傳資料

String path = “http://192.168.1.103:8080/web/LoginServlet“;
//1.開啟瀏覽器
HttpClient client = new DefaultHttpClient();

//2.輸入地址或者資料
HttpPost httpPost = new HttpPost(path);
HttpGet httpGet = new HttpGet(path);
//3.準備資料
List<NameValuePair> parameters = new ArrayList<NameValuePair>();
parameters.add(new BasicNameValuePair("qq", qq));//map
parameters.add(new BasicNameValuePair("pwd", pwd));
//4.把資料加入到httpPost中
httpPost.setEntity(new UrlEncodedFormEntity(parameters, "utf-8"));

//3.敲回車
HttpResponse response = client.execute(httpPost);
//獲取狀態碼
int code = response.getStatusLine().getStatusCode();
if(code == 200){
    InputStream is = response.getEntity().getContent();
    String result = StreamTools.readStream(is);
    ....

多執行緒斷點下載

在寫程式碼之前必須先看著圖片 先把思路整理清楚,然後再開始寫程式碼!

1,請求伺服器得到即將下載檔案的大小(傳送一次get請求),然後在本地用RandomAccessFile建立一個大小和伺服器檔案大小一樣的檔案!
int length = conn.getContentLength();
RandomAccessFile raf = new RandomAccessFile(getFileName(path), “rw”);
raf.setLength(length); //關鍵的程式碼,設定檔案的大小

2,計算每個執行緒下載的起始位置和結束位置(有個公式,看今天的資料圖),最後一個執行緒比較特殊,它的結束位置是totalLength -1 !!!
int blocksize = length / threadCount;
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();
}
3,開啟每個執行緒去下載檔案的某一部分(怎麼告訴伺服器只下載一部分?Range),同時寫檔案的時候要記得呼叫RandomAccessFile的seek方法,調整寫入的位置!!
conn.setRequestProperty(“Range”, “bytes=”+startIndex+”-“+endIndex);
raf.seek(xxx)
4,斷點下載的本質是,每次下載一點內容,就將當前的進度儲存到檔案裡面去(RandomAccessFile的模式rwd,”d” 代表著 資料是立刻被儲存到底層硬碟裝置裡面)
while((len = is.read(buffer))!=-1){
//把每個執行緒下載的資料放在自己的空間裡面.
System.out.println(“執行緒:”+threadId+”正在下載:”+new String(buffer));
raf.write(buffer,0, len);
//len 代表著每次寫入檔案資料的大小,也就是代表著進度!
currentPosition+=len;

    File file = new File(threadId+".position");
    RandomAccessFile fos = new RandomAccessFile(file,"rwd");
    //System.out.println("執行緒:"+threadId+"寫到了"+currentPosition);
    fos.write(String.valueOf(currentPosition).getBytes());
    fos.close();//fileoutstream資料是不一定被寫入到底層裝置裡面的,有可能是儲存在快取裡面.
    //raf 的 rwd模式,資料是立刻被儲存到底層硬碟裝置裡面.
}

下次再次開啟的時候,判斷一下檔案內容是否存在,如果有,代表著要從原來的位置開始
將裡面的內容讀取出來,然後——很重要——>重新設定告訴伺服器下載的範圍(Range)和呼叫RandomAccessFile的seek方法,調整寫入的位置!
//檢查當前執行緒是否已經下載過一部分的資料了
File info = new File(threadId+”.position”);
RandomAccessFile raf = new RandomAccessFile(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());

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);//每個執行緒寫檔案的開始位置都是不一樣的.
}

5,當所有的執行緒都下載完畢之後,要刪除臨時儲存的檔案,以免出現bug。
(用一個變數當作計數器,當多執行緒改變一個變數的時候,要記住加上synchronized關鍵字同步起來,以免發生資料的錯亂)
synchronized (MultiDownloader.class) {
runningThreadCount–;
if(runningThreadCount<=0){
for(int i=0;i