閱讀311 返回首頁    go 技術社區[雲棲]


Android異步下載網絡圖片

項目中有時候需要獲取網絡上的圖片,並下載下來到手機客戶端顯示。怎麼做呢?

實現思路是:

 1:在UI線程中啟動一個線程,讓這個線程去下載圖片。

 2:圖片完成下載後發送一個消息去通知UI線程

 2:UI線程獲取到消息後,更新UI。

 這裏的UI線程就是主線程。

 這兩個步驟涉及到一些知識點,即是:ProgressDialog,Handler,Thread/Runnable,URL,HttpURLConnection等等一係列東東的使用。

 現在讓我們開始來實現這個功能吧!

 第一步:新建項目。

 第二步:設計好UI,如下所示

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:andro
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    > 
    <Button
      android:
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="異步下載方式一"
     >
    </Button>
    
    <Button
      android:
      android:layout_width="fill_parent"
      android:layout_height="wrap_content"
      android:text="異步下載方式二"
     >
    </Button>
    
    <FrameLayout
     android:layout_width="fill_parent"
     android:layout_height="match_parent"
     android:
    >
    
   <ImageView
    android: 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    android:scaleType="centerInside" 
    android:padding="2dp"
    >
   </ImageView> 
    
    <ProgressBar 
     android: 
     android:layout_width="wrap_content" 
     android:layout_height="wrap_content" 
     android:layout_gravity="center">
  </ProgressBar> 
    
  </FrameLayout> 
</LinearLayout>

第三步:獲取UI相應View組件,並添加事件監聽。

public class DownLoaderActivity extends Activity implements OnClickListener{ 
    private static final String params="https://upload.wikimedia.org/wikipedia/commons/thumb/e/ea/Hukou_Waterfall.jpg/800px-Hukou_Waterfall.jpg";
    private Button btnFirst,btnSecond;
    private ProgressBar progress;
    private FrameLayout frameLayout;
    private Bitmap bitmap=null;
    ProgressDialog dialog=null;
    
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        btnFirst=(Button)this.findViewById(R.id.btnFirst);
        btnSecond=(Button)this.findViewById(R.id.btnSecond); 
        progress=(ProgressBar)this.findViewById(R.id.progress); 
        progress.setVisibility(View.GONE);
        frameLayout=(FrameLayout)this.findViewById(R.id.frameLayout);
        
        btnFirst.setOnClickListener(this);
        btnSecond.setOnClickListener(this);  
    }

第四步:在監聽事件中處理我們的邏輯,即是下載服務器端圖片數據。

這裏我們需要講解一下了。

通常的我們把一些耗時的工作用另外一個線程來操作,比如,下載上傳圖片,讀取大批量XML數據,讀取大批量sqlite數據信息。為什麼呢?答案大家都明白,用戶體驗問題。

在這裏,首先我構造一個進度條對話框,用來顯示下載進度,然後開辟一個線程去下載圖片數據,下載數據完畢後,通知主UI線程去更新顯示我們的圖片。

Handler是溝通Activity 與Thread/runnable的橋梁。而Handler是運行在主UI線程中的,它與子線程可以通過Message對象來傳遞數據。具體代碼如下:

/**這裏重寫handleMessage方法,接受到子線程數據後更新UI**/
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg){
            switch(msg.what){
            case 1:
                //關閉
                ImageView view=(ImageView)frameLayout.findViewById(R.id.image);
                view.setImageBitmap(bitmap);
                dialog.dismiss();
                break;
            }
        }
    };

我們在這裏彈出進度對話框,使用HTTP協議來獲取數據。

//前台ui線程在顯示ProgressDialog,
    //後台線程在下載數據,數據下載完畢,關閉進度框
    @Override
    public void onClick(View view) {
        switch(view.getId()){
        case R.id.btnFirst: 
            dialog = ProgressDialog.show(this, "", 
                    "下載數據,請稍等 …", true, true); 
            //啟動一個後台線程
            handler.post(new Runnable(){
                @Override
                public void run() { 
                     //這裏下載數據
                    try{
                        URL  url = new URL(params);
                        HttpURLConnection conn  = (HttpURLConnection)url.openConnection();
                        conn.setDoInput(true);
                        conn.connect(); 
                        InputStream inputStream=conn.getInputStream();
                        bitmap = BitmapFactory.decodeStream(inputStream); 
                        Message msg=new Message();
                        msg.what=1;
                        handler.sendMessage(msg);
                     
                    } catch (MalformedURLException e1) { 
                        e1.printStackTrace();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }  
                }
            });  
            break;

如此以來,你會發現很好的完成了我們的下載目標了,你可以把它應用到其他方麵去,舉一反三。

運行截圖如下:

 

上麵使用Handler、Thread/Runnable 、URL、HttpURLConnection等等來進行異步下載網絡圖片。

但是采用這種方式有一些缺點,如下:

線程的開銷較大,如果每個任務都要創建一個線程,那麼程序的效率要低很多。 線程無法管理,匿名線程創建並啟動後就不受程序的控製了,如果有很多個請求發送,那麼就會啟動非常多的線程,係統將不堪重負。 另外,前麵已經看到,在新線程中更新UI還必須要引入handler,這讓代碼看上去非常臃腫。

 那麼有沒有比較更好好的實現方式呢?這個可以有!它就是AsyncTask

 AsyncTask的特點是任務在主UI線程之外運行,而回調方法是在主UI線程中,這就有效地避免了使用Handler帶來的麻煩。 

 AsyncTask定義了三種泛型類型 Params,Progress和Result。
  • Params 啟動任務執行的輸入參數。
  • Progress 後台任務執行的百分比。
  • Result 後台執行任務返回的結果。

  當然,使用它還必須覆蓋它的一些抽象方法方法

 doInBackground(Params...)         執行任務

 onPostExecute(Result)             返回任務執行的結果,通常更新UI

 onProgressUpdate (Progress... values) 進度更新

 注意:紅色的是必須實現的。

 第一步:設計好UI,與上節一樣

 第二步:也與上節一樣。

 第三步:主要是實例化AsyncTask,並執行execute(Params)

 我們必須繼承AsyncTask,並覆蓋它的一些方法,我們這裏主要是要獲取網絡圖片,並保存為Bitmap,以便UI根據Bitmap來更新的。

 那麼需要為AsyncTask設置返回的類型參數為String,Integer,Bitmap 類定義如下:

View Code

public class MyASyncTask extends AsyncTask<String, Integer, Bitmap> {

 在doInBackground(Params...)  方法中 ,接受String ....params,返回我們需要的Bitmap.當然我們這裏是獲取圖片Bitmap所以要返回Bitmap

  如果你返回的需要是String或者其他複雜類型時候,需要修改類的定義參數類型為你需要返回的類型,當然接受參數也是根據你的請求需要改變。

@Override
    protected Bitmap doInBackground(String... params) {
        Bitmap bitmap=null;
        try {
            
            URL url = new URL(params[0]);
            HttpURLConnection con=(HttpURLConnection) url.openConnection();
            con.setDoInput(true);
            con.connect();
            InputStream inputStream=con.getInputStream();
            
            bitmap=BitmapFactory.decodeStream(inputStream); 
            inputStream.close();
        } 
         catch (MalformedURLException e) {
                e.printStackTrace();
            }catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
        return bitmap;
    }

 onPostExecute(Result) 中是請求獲得結果後更新UI部分。你會看到他的參數就是我們類中的類型參數。代碼如下:

//執行獲得圖片數據後,更新UI:顯示圖片,隱藏進度條
    @Override
    protected void onPostExecute(Bitmap Result){
        ImageView imgView=(ImageView)this.viewGroup.getChildAt(0);
        imgView.setImageBitmap(Result);
        ProgressBar bar=(ProgressBar)this.viewGroup.getChildAt(1);
        bar.setVisibility(View.GONE);
    }

然後怎麼用呢?在UI線程中執行吧:

MyASyncTask yncTask=new MyASyncTask(this,frameLayout);
   yncTask.execute(params);

運行結果與上節大同小異




最後更新:2017-04-03 22:15:26

  上一篇:go 雲安全聯盟發布“2013雲計算9大威脅”報告
  下一篇:go 雲計算讓黑客手槍換大炮