Android:ListView数据异步加载、Handler、AsyncTask

时间:2022-08-27 14:46:08
Android:ListView数据异步加载、Handler、AsyncTask

Android:ListView数据异步加载、Handler、AsyncTask

实现数据的网络异步加载及文件缓存

用到的权限

<!-- 访问internet权限 -->
<uses-permission android:name="android.permission.INTERNET"/>
 <!-- 在SDCard中创建与删除文件权限 -->
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<!-- 往SDCard写入数据权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>


listview_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <ImageView
        android:layout_width="120dp"
        android:layout_height="120dp"
        android:id="@+id/imageView"
        />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:textColor="#FFFFFF"
        android:id="@+id/textView"        
        />
</LinearLayout>





public class MainActivity extends Activity {
    ListView listView;
    File cache;
    
    Handler handler = new Handler(){
        public void handleMessage(Message msg) {
             listView.setAdapter(new ContactAdapter(MainActivity.this, (List<Contact>)msg.obj,
                     R.layout.listview_item, cache));
        }        

    };
    
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        listView = (ListView) this.findViewById(R.id.listView);
        
        cache = new File(Environment.getExternalStorageDirectory(), "cache");
        if(!cache.exists()) cache.mkdirs();
        
        new Thread(new Runnable() {            
            public void run() {
                try {
                    List<Contact> data = ContactService.getContacts();//这个方法可能需要很长的时间,所以放在新的线程中,线程完成后通过Handler更新界面
                    handler.sendMessage(handler.obtainMessage(22, data));

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();       
    }

    @Override
    protected void onDestroy() {
        for(File file : cache.listFiles()){
            file.delete();
        }
        cache.delete();

        super.onDestroy();
    }
    
}



Service中

public class ContactService {

    /**
     * 获取联系人
     * @return
     */
    public static List<Contact> getContacts() throws Exception{
        String path = "http://192.168.1.100:8080/web/list.xml";
        HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
        conn.setConnectTimeout(5000);
        conn.setRequestMethod("GET");
        if(conn.getResponseCode() == 200){
            return parseXML(conn.getInputStream());
        }
        return null;
    }

    private static List<Contact> parseXML(InputStream xml) throws Exception{
        List<Contact> contacts = new ArrayList<Contact>();
        Contact contact = null;
        XmlPullParser pullParser = Xml.newPullParser();
        pullParser.setInput(xml, "UTF-8");
        int event = pullParser.getEventType();
        while(event != XmlPullParser.END_DOCUMENT){
            switch (event) {
            case XmlPullParser.START_TAG:
                if("contact".equals(pullParser.getName())){
                    contact = new Contact();
                    contact.id = new Integer(pullParser.getAttributeValue(0));
                }else if("name".equals(pullParser.getName())){
                    contact.name = pullParser.nextText();
                }else if("image".equals(pullParser.getName())){
                    contact.image = pullParser.getAttributeValue(0);
                }
                break;

            case XmlPullParser.END_TAG:
                if("contact".equals(pullParser.getName())){
                    contacts.add(contact);
                    contact = null;
                }
                break;
            }
            event = pullParser.next();
        }
        return contacts;
    }
    /**
     * 获取网络图片,如果图片存在于缓存中,就返回该图片,否则从网络中加载该图片并缓存起来
     * @param path 图片路径
     * @return
     */
    public static Uri getImage(String path, File cacheDir) throws Exception{// path -> MD5 ->32字符串.jpg
        File localFile = new File(cacheDir, MD5.getMD5(path)+ path.substring(path.lastIndexOf(".")));
        if(localFile.exists()){
            return Uri.fromFile(localFile);

        }else{
            HttpURLConnection conn = (HttpURLConnection) new URL(path).openConnection();
            conn.setConnectTimeout(5000);
            conn.setRequestMethod("GET");
            if(conn.getResponseCode() == 200){
                FileOutputStream outStream = new FileOutputStream(localFile);
                InputStream inputStream = conn.getInputStream();
                byte[] buffer = new byte[1024];
                int len = 0;
                while( (len = inputStream.read(buffer)) != -1){
                    outStream.write(buffer, 0, len);
                }
                inputStream.close();
                outStream.close();
                return Uri.fromFile(localFile);
            }
        }
        return null;
    }

}


实体
public class Contact {
    public int id;
    public String name;
    public String image;
    public Contact(int id, String name, String image) {
        this.id = id;
        this.name = name;
        this.image = image;
    }
    public Contact(){}
}


网络上准备的XML数据

<?xml version="1.0" encoding="UTF-8"?>
<images>
    <image src="http://192.168.1.100:8080/web/images/1.gif"/>
    <image src="http://192.168.1.100:8080/web/images/2.gif"/>
    <image src="http://192.168.1.100:8080/web/images/3.gif"/>
    <image src="http://192.168.1.100:8080/web/images/4.gif"/>
    <image src="http://192.168.1.100:8080/web/images/5.gif"/>
    <image src="http://192.168.1.100:8080/web/images/6.gif"/>
    <image src="http://192.168.1.100:8080/web/images/7.gif"/>
    <image src="http://192.168.1.100:8080/web/images/8.gif"/>
    <image src="http://192.168.1.100:8080/web/images/9.gif"/>
    <image src="http://192.168.1.100:8080/web/images/10.gif"/>
    <image src="http://192.168.1.100:8080/web/images/11.gif"/>
    <image src="http://192.168.1.100:8080/web/images/12.gif"/>
    <image src="http://192.168.1.100:8080/web/images/13.gif"/>
    <image src="http://192.168.1.100:8080/web/images/14.gif"/>
    <image src="http://192.168.1.100:8080/web/images/15.jpg"/>
    <image src="http://192.168.1.100:8080/web/images/16.jpg"/>
    <image src="http://192.168.1.100:8080/web/images/17.jpg"/>
    <image src="http://192.168.1.100:8080/web/images/18.jpg"/>

</images>



ContactAdapter【ListView的适配器,实现异步加载】

public class ContactAdapter extends BaseAdapter {
    private List<Contact> data;
    private int listviewItem;
    private File cache;
    LayoutInflater layoutInflater;
    
    public ContactAdapter(Context context, List<Contact> data, int listviewItem, File cache) {
        this.data = data;
        this.listviewItem = listviewItem;
        this.cache = cache;
        layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    }
    /**
     * 得到数据的总数
     */
    public int getCount() {
        return data.size();
    }
    /**
     * 根据数据索引得到集合所对应的数据
     */
    public Object getItem(int position) {
        return data.get(position);
    }
    
    public long getItemId(int position) {
        return position;
    }

    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView imageView = null;
        TextView textView = null;
        
        if(convertView == null){//防止多次生成新的view,重用原来的view
            convertView = layoutInflater.inflate(listviewItem, null);
            imageView = (ImageView) convertView.findViewById(R.id.imageView);
            textView = (TextView) convertView.findViewById(R.id.textView);
            convertView.setTag(new DataWrapper(imageView, textView));//保存起来
        }else{
            DataWrapper dataWrapper = (DataWrapper) convertView.getTag();//获取以前的重用
            imageView = dataWrapper.imageView;
            textView = dataWrapper.textView;    
        }

        Contact contact = data.get(position);
        textView.setText(contact.name);
        asyncImageLoad(imageView, contact.image);
        return convertView;
    }


//以下是使用AsyncTask实现的异步加载

    private void asyncImageLoad(ImageView imageView, String path) {
        AsyncImageTask asyncImageTask = new AsyncImageTask(imageView);
        asyncImageTask.execute(path);
        
    }
    
    private final class AsyncImageTask extends AsyncTask<String, Integer, Uri>{
        private ImageView imageView;
        public AsyncImageTask(ImageView imageView) {
            this.imageView = imageView;
        }
        protected Uri doInBackground(String... params) {//子线程中执行的
            try {
                return ContactService.getImage(params[0], cache);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }
        protected void onPostExecute(Uri result) {//运行在主线程
            if(result!=null && imageView!= null)
                imageView.setImageURI(result);

        }    
    }

//以下是使用Handler实现的异步加载,因为每次都生成一个新线程,如果条目太多的话就会有很多很多的线程,造成效率下降

    /*
    private void asyncImageLoad(final ImageView imageView, final String path) {
        final Handler handler = new Handler(){
            public void handleMessage(Message msg) {//运行在主线程中
                Uri uri = (Uri)msg.obj;
                if(uri!=null && imageView!= null)
                    imageView.setImageURI(uri);
            }
        };
        
        Runnable runnable = new Runnable() {            
            public void run() {
                try {
                    Uri uri = ContactService.getImage(path, cache);//这个方法可能很耗时间
                    handler.sendMessage(handler.obtainMessage(10, uri));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        };
        new Thread(runnable).start();
    }
*/
    private final class DataWrapper{//DataHolder模式,使用public装数据,避免调用函数的开销
        public ImageView imageView;
        public TextView textView;
        public DataWrapper(ImageView imageView, TextView textView) {
            this.imageView = imageView;
            this.textView = textView;
        }

    }
}

工具类

public class MD5 {

    public static String getMD5(String content) {
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            digest.update(content.getBytes());
            return getHashString(digest);
            
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return null;
    }
    
    private static String getHashString(MessageDigest digest) {
        StringBuilder builder = new StringBuilder();
        for (byte b : digest.digest()) {
            builder.append(Integer.toHexString((b >> 4) & 0xf));
            builder.append(Integer.toHexString(b & 0xf));
        }
        return builder.toString();
    }
}