Android 網路學習之擷取伺服器的圖片
首先需要搭建一個Tomcat伺服器,然後測試伺服器上的圖片使用PC上的瀏覽器是否可以正常下載下來
可以看到伺服器上的圖片資料是可以正常訪問的。圖片的地址:http://localhost:8080/meinv.jpg
那如何在我們Android上從網路下載圖片呢?
直接上擷取網狀圖片的代碼:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View v) { //1: 確定網址 String path = http://localhost:8080/meinv.jpg; try { //2:把網址封裝為一個URL對象 URL url = new URL(path); //3:擷取用戶端和伺服器的連線物件,此時還沒有建立串連 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //4:初始化連線物件 conn.setRequestMethod(GET); //設定連線逾時 conn.setConnectTimeout(5000); //設定讀取逾時 conn.setReadTimeout(5000); //5:發生請求,與伺服器建立串連 conn.connect(); //如果響應碼為200,說明請求成功 if(conn.getResponseCode() == 200) { //擷取伺服器回應標頭中的流 InputStream is = conn.getInputStream(); //讀取流裡的資料,構建成bitmap位元影像 Bitmap bm = BitmapFactory.decodeStream(is); //顯示在介面上 ImageView imageView = (ImageView) findViewById(R.id.lv); imageView.setImageBitmap(bm); }} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();} }}
運行看效果:
從控制台的列印可以是警告: 網路工作在主線程中異常。
上面的警告就是從4.0以後引入的,如果網路任務在主線程中,就會警示告。所以我們需要開啟一個線程來執行網路任務。
修改後的代碼為:
public void click(View v) { //開啟一個線程 Thread thread = new Thread() { @Override public void run() { // TODO Auto-generated method stub //1: 確定網址 String path = http://localhost:8080/meinv.jpg; try { //2:把網址封裝為一個URL對象 URL url = new URL(path); //3:擷取用戶端和伺服器的連線物件,此時還沒有建立串連 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //4:初始化連線物件 conn.setRequestMethod(GET); //設定連線逾時 conn.setConnectTimeout(5000); //設定讀取逾時 conn.setReadTimeout(5000); //5:發生請求,與伺服器建立串連 conn.connect(); //如果響應碼為200,說明請求成功 if(conn.getResponseCode() == 200) { //擷取伺服器回應標頭中的流 InputStream is = conn.getInputStream(); //讀取流裡的資料,構建成bitmap位元影像 Bitmap bm = BitmapFactory.decodeStream(is); //顯示在介面上 ImageView imageView = (ImageView) findViewById(R.id.lv); imageView.setImageBitmap(bm); } } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; //啟動線程任務 thread.start(); }
再次運行看效果:
又報出一個警告: 調用錯誤線程異常,也就是說只有建立它的view,才能調用該view。 直白點就是只有主線程(UI線程)才能更新UI,別的線程是不能隨便更新UI的。
如果需要更新UI,那隻能主線程來更新UI,那別的線程如何告訴主線程需要更新UI呢? 這就需要引入另一個知識點:訊息
如果別的線程需要更新UI,就發生訊息給主線程,主線程收到後會自動的更新UI
代碼修改為:
if(conn.getResponseCode() == 200) { //擷取伺服器回應標頭中的流 InputStream is = conn.getInputStream(); //讀取流裡的資料,構建成bitmap位元影像 Bitmap bm = BitmapFactory.decodeStream(is); //發生更新UI的訊息 Message msg = handler.obtainMessage(); msg.obj = bm; handler.sendMessage(msg); //顯示在介面上 //ImageView imageView = (ImageView) findViewById(R.id.lv); //imageView.setImageBitmap(bm); }
加入Handler,也就是處理訊息的handle
Handler handler = new Handler(){public void handleMessage(android.os.Message msg) {//更新UIImageView imageView = (ImageView) findViewById(R.id.lv);imageView.setImageBitmap((Bitmap) msg.obj);};};
再次運行看效果:
可以看到圖片正常顯示出來了。
我們再次修改代碼增加擷取失敗的處理邏輯
if(conn.getResponseCode() == 200) { //擷取伺服器回應標頭中的流 InputStream is = conn.getInputStream(); //讀取流裡的資料,構建成bitmap位元影像 Bitmap bm = BitmapFactory.decodeStream(is); //發生更新UI的訊息 Message msg = handler.obtainMessage(); msg.obj = bm; msg.what = GET_OK; handler.sendMessage(msg); //顯示在介面上 //ImageView imageView = (ImageView) findViewById(R.id.lv); //imageView.setImageBitmap(bm); } else { //發送擷取失敗的訊息Message msg = handler.obtainMessage();msg.what = GET_ERROR;handler.sendMessage(msg);}
訊息處理過程:
static final int GET_ERROR = 0;static final int GET_OK = 1;Handler handler = new Handler(){public void handleMessage(android.os.Message msg) {//更新UIswitch (msg.what) {case GET_OK:ImageView imageView = (ImageView) findViewById(R.id.lv);imageView.setImageBitmap((Bitmap) msg.obj);break;case GET_ERROR:Toast.makeText(MainActivity.this, 訪問失敗!, Toast.LENGTH_SHORT).show();break;default:break;}};};
我們可以將地址改錯,顯示效果
關於訊息機制簡單說明一下:
1:發生訊息系統會使用訊息佇列(MessageQueue)和訊息輪詢對象(Looper)
2:訊息輪詢對象的作用就是不停的檢測訊息佇列中是否有小心,如果一旦有訊息,訊息輪詢者就會將訊息對象交給訊息處理器(Handler),處理器會調用handleMessage方法來處理這條訊息。handleMessage方法運行在主線程中,所以可以重新整理ui
但是平常應用中,比如朋友圈的大量圖片,第一次瀏覽時都是先緩衝到本地,第二次瀏覽時直接從本地讀取即可,那我們來實現一下:
public void click(View v) { //指定檔案的路徑final File file = new File(getCacheDir(), info.jpg); //如果檔案存在,直接從本地開啟if(file.exists()) {System.out.println(從緩衝讀取的);Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());ImageView imageView = (ImageView) findViewById(R.id.lv);imageView.setImageBitmap(bm); }else {System.out.println(從網上下載的); //開啟一個線程 Thread thread = new Thread() { @Override public void run() { // TODO Auto-generated method stub //1: 確定網址 String path = http://192.168.1.109:8080/meinv.jpg; try { //2:把網址封裝為一個URL對象 URL url = new URL(path); //3:擷取用戶端和伺服器的連線物件,此時還沒有建立串連 HttpURLConnection conn = (HttpURLConnection) url.openConnection(); //4:初始化連線物件 conn.setRequestMethod(GET); //設定連線逾時 conn.setConnectTimeout(5000); //設定讀取逾時 conn.setReadTimeout(5000); //5:發生請求,與伺服器建立串連 conn.connect(); //如果響應碼為200,說明請求成功 if(conn.getResponseCode() == 200) { //擷取伺服器回應標頭中的流 InputStream is = conn.getInputStream(); //讀取伺服器返迴流裡的資料,把資料寫入到本地,緩衝起來 FileOutputStream fos = new FileOutputStream(file); byte[] b = new byte[1024]; int len = 0; while((len = is.read(b)) != -1) { fos.write(b, 0, len); } fos.close(); is.close(); //從本地載入圖片 Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath()); //讀取流裡的資料,構建成bitmap位元影像 //Bitmap bm = BitmapFactory.decodeStream(is); //發生更新UI的訊息 Message msg = handler.obtainMessage(); msg.obj = bm; msg.what = GET_OK; handler.sendMessage(msg); //顯示在介面上 //ImageView imageView = (ImageView) findViewById(R.id.lv); //imageView.setImageBitmap(bm); } else { //發送擷取失敗的訊息Message msg = handler.obtainMessage();msg.what = GET_ERROR;handler.sendMessage(msg);} } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; //啟動線程任務 thread.start(); } }
上面是增加從本地緩衝中擷取圖片檔案。
第一次運行時:包檔案名稱下的cache下就會存在info.jpg檔案
緩衝檔案
當退出再次進來,就會直接從緩衝去擷取
關於從網路上擷取檔案,就簡單的說到這裡