4.下载图片文件

4.1 问题

应用程序需要从Web或其他远程服务器下载一张图片并显示。

4.2 解决方案

(API Level 4)
使用AsyncTask在后台线程中下载数据。AsyncTask是封装类,它可以很方便地让需要长时间允许操作的线程在后台允许;同样,它通过一个内部线程池管理线程的并发。除了管理后台线程外,在操作执行前、中、后都会提供回调方法,让你可以做任何需要在主UI线程中进行的更新。

4.3 实现机制

在下载图片的环境中,我们会创建ImageView的一个子类,叫做WebImageView,它会从远程来源中延迟加载一张图片并且在该图片可用时就显示它。下载过程会在一个AsyncTask操作中执行(参见以下代码清单)。
WebImageView

public class WebImageView extends ImageView {

    private Drawable mPlaceholder,mImage; 

    public WebImageView(Context context) {
        super(context,null);
    }

    public WebImageView(Context context, AttributeSet attrs) {
        super(context, attrs,0);
    }

    public WebImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public void setPlaceholder(Drawable drawable) {
        this.mPlaceholder = drawable;
        if (mImage == null){
            setImageDrawable(mPlaceholder);
        }
    }

    public void setPlaceholder(int resid) {
        mPlaceholder = getResources().getDrawable(resid);
        if (mImage == null){
            setImageDrawable(mPlaceholder);
        }
    }
    
    public void setImageURl(String url) {
        DownloadTask task = new DownloadTask();
        task.execute(url);
    }


    private class DownloadTask extends AsyncTask<String,Void,Bitmap> {
        @Override
        protected Bitmap doInBackground(String... params) {
            String url = params[0];
            try {
                URLConnection connection = (new URL(url)).openConnection();
                InputStream is = connection.getInputStream();
                BufferedInputStream bis = new BufferedInputStream(is);

                ByteArrayBuffer baf = new ByteArrayBuffer(50);
                int current = 0;
                while ((current = bis.read()) != -1) {
                    baf.append((byte) current);
                }
                byte[] imageData = baf.toByteArray();
                return BitmapFactory.decodeByteArray(imageData, 0, imageData.length);
            } catch (Exception exc) {
                return null;
            }
        }

        @Override
        protected void onPostExecute(Bitmap result) {
            mImage = new BitmapDrawable(getContext().getResources(),result);
            if (mImage != null){
                setImageDrawable(mImage);
            }
        }
    }
}

正如你所看到的那样,WebImageView是Android的mageView小部件的一个简单扩展。在远程内容下载完成之前,setPlaceholderImage()会为要显示的图片设置一个本地Drawable。大多数有趣的工作都是从使用setImageUrl()向视图传入一个给定远程URL开始的,该方法表示自定义的AsyncTask开始工作了。
请注意,AsyncTask是强类型化的,它需要三个值:输入参数、进度值、结果值。在这种情况下,会传递给任务的execute()方法一个字符串,而后台操作应该返回一个Bitmap。对于中间的进度值,我们在本例中并不会使用,因此它被设置为Void。继承AsyncTask后,唯一需要实现的方法就是doInBackground(),它定义了后台线程中需要大量运行的操作。在前面的示例中,这里是与提供的远程URL进行连接以及下载图片的地方。完成后,我们会试图用下载的数据创建一个Bitmap。发生任何错误时,操作将中止并返回null。

要点:
Android UI类是线程不安全的。确保在更新UI时使用运行在主线程上的回调方法。不要在doInBackground()中更新视图。

以下两段代码清单展示了在一个Activity中使用这个类的示例。因为这个类不是android.widget或android.view包的一部分,所以在XML中使用它时必须先指定它的完全限定包名。

res/layout/main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.jennyni.webimageviewdemo.MainActivity">
    
    <com.jennyni.webimageviewdemo.WebImageView
        android:id="@+id/webImage"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    </com.jennyni.webimageviewdemo.WebImageView>

</LinearLayout>

示例Activity

public class WebImageActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        WebImageView  imageView = (WebImageView)findViewById(R.id.webImage);
        imageView.setPlaceholderImage(R.drawable.ic_launcher);
        imageView.setImageUrl("http://lorempixel.com/400/200");

    }
}

本例中,首先会设置一张本地图片(应用程序图标)作为WebImageView的占位图片,这张图片会立刻显示给用户。然后我们会告诉视图从Web上获取Apress的徽标图片。如前所述,这里会在后台下载图片,下载完成后会替换掉视图中的占位图片。正是因为创建后台操作的简单性,Android团队才会把AsyncTask叫作“无痛苦使用线程”。

猜你喜欢

转载自blog.csdn.net/qq_41121204/article/details/83857314