zoukankan      html  css  js  c++  java
  • Android异步加载

    一、为什么要使用异步加载?

           1.Android是单线程模型

        2.耗时操作阻碍UI线程

    二、异步加载最常用的两种方式

         1.多线程、线程池

      2.AsyncTask

    三、实现ListView图文混排

    3-1 实现读取网页中的json数据到ListView中 (图片首先为默认图片)

          3.1.1:主布局只有一个ListView和一个listView_item的布局

          3.1.2:网页json数据的链接(http://www.imooc.com/api/teacher?type=4&num=30),打开后为json数据,开发者可以通过使用Json格式化工具进行清楚的查看json数据的信息

          3.1.3:书写解析网页JSON数据的代码

    package cn.edu.bzu.async_listview;
    
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.io.UnsupportedEncodingException;
    import java.util.ArrayList;
    import java.util.List;
    import java.net.MalformedURLException;
    import java.net.URL;
    
    import org.json.JSONArray;
    import org.json.JSONException;
    import org.json.JSONObject;
    
    import android.app.Activity;
    import android.os.AsyncTask;
    import android.os.Bundle;
    import android.util.Log;
    import android.widget.ListView;
    
    public class MainActivity extends Activity {
    
        private ListView mListView;
        private static String URL="http://www.imooc.com/api/teacher?type=4&num=30";  //慕课网提供的api链接
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mListView=(ListView) findViewById(R.id.lv_main);
            new NewAsyncTask().execute(URL);
        }
        
        /**
         *实现网络的异步访问
         */
        class NewAsyncTask extends AsyncTask<String, Void, List<NewsBean> >{ //参数:params:传入值    progress :进程   Result:返回值 
            
            @Override
            protected List<NewsBean> doInBackground(String... params) {
                return getJsonData(params[0]);  //得到从url读取的JSON数据
            } 
            @Override
                protected void onPostExecute(List<NewsBean> newsBean) {
                    // 将生成的newsBean设置给ListView
                    super.onPostExecute(newsBean);
                    NewsAdapter adapter=new NewsAdapter(MainActivity.this, newsBean);//创建适配器对象
                    mListView.setAdapter(adapter);
                }
        }
            /**
             * 将url对应的json数据转换为我们所封装的NewsBean对象
             * @param url
             * @return newsList
             */
        private List<NewsBean> getJsonData(String url) {
            List<NewsBean> newsBeanList=new ArrayList<NewsBean>();
            try {
                String jsonString=readStream(new URL(url).openStream()); //此句功能与url.openConnection().getInputStream()相同,可根据URL直接联网获取数据,返回值类型 InputStream;
                //Log.d("json",jsonString );  打印读取的json信息
                //解析json数据
                JSONObject jsonObject; 
                NewsBean newsBean; //用于封装jsonObject
                
                jsonObject=new JSONObject(jsonString);  //json数据添加到jsonObject中
                JSONArray jsonArray=jsonObject.getJSONArray("data"); //取出json中的data数据,data为一个数组类型
                for(int i=0;i<jsonArray.length();i++){
                    //取出data中的数据
                    jsonObject=jsonArray.getJSONObject(i);
                    newsBean=new NewsBean();
                    newsBean.imgIconUrl=jsonObject.getString("picSmall");
                    newsBean.newsTitle=jsonObject.getString("name");
                    newsBean.newsContent=jsonObject.getString("description");
                    newsBeanList.add(newsBean);
                }
                
            } catch (IOException e) {
                e.printStackTrace();
            } catch (JSONException e) {
                e.printStackTrace();
            }
            return newsBeanList;
        }
        
        /**
         * 通过inputStream解析网页所返回的数据
         * @param is
         * @return  result
         */
        private String readStream(InputStream is){
             InputStreamReader isr;  
             String result="";
             try {
                 String line=""; //每行的数据
                isr=new InputStreamReader(is,"utf-8");  //字节流转换为字符流
                BufferedReader br=new BufferedReader(isr);  //将字符流以buffer的形式读取出来
                while((line=br.readLine())!=null){
                    result+=line;  //拼接到result中
                }
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }catch (IOException e) {
                e.printStackTrace();
            }
             return result;
        }
                                                                     
        }
    

    思路解析:

        1) 一个内部类继承AsyncTask,书写未实现的方法,其中在方法中有一个doBackground()方法,在其方法中书写得到Json数据的方法

       2) 书写通过inputStream解析网页所返回的数据,只有拿到网页中的json数据才能实现解析的操作

       3)将url对应的json数据转换为我们所封装的NewsBean对象,在这个方法中,我们通过第二步拿到了json数据,然后进行json数据的解析,并且封装到实体类对象中,这样你的实体类中就有解析的json数据了

      4)创建ListView的适配器

      5)在继承AsyncTask的类中书写onPostExecute()方法,在这个方法中,实现绑定适配器,加载数据源的操作

      6)在onCreate方法中执行这个异步操作:new NewAsyncTask().execute(URL);并且传入url地址

      7)在清单文件中添加联网权限 <use-permission  android:name="android.permission.INTERNET"/>

    3-2  实现在ListView中添加网络中图片

      &由于加载图片是一种耗时操作,所以我们可以通过新建Thread的方法或者继承AsyncTask来实现

      3.2.1:我们通过新线程的方式来实现加载网络图片,新建ImageLoader.java

    package cn.edu.bzu.async_listview;
    
    import java.io.BufferedInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    import android.os.Message;
    
    
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.Handler;
    import android.widget.ImageView;
    
    /**
     * 用于处理图片的加载
     * @author monster
     *
     */
    public class ImageLoader {
        private ImageView mImageView;
        private String mUrl;
        /**
         * UI主线程
         */
        private Handler mHandler=new Handler(){
            public void handleMessage(Message msg) {
                super.handleMessage(msg); 
                //通过设置tag属性避免缓存图片对正确图片的影响
                if(mImageView.getTag().equals(mUrl)){
                    mImageView.setImageBitmap((Bitmap) msg.obj);
                }
            };
        };
        /**
         * 通过多线程的方式加载图片
         * @param imageView
         * @param url
         */
        public void showImageByThread(ImageView imageView,final String url){
            mImageView=imageView; //将ImageView保存进成员变量中
            mUrl=url;
            new Thread(){
                @Override
                public void run() {
                    super.run();
                    Bitmap bitmap=getBitmapFromURL(url);
                    Message message=Message.obtain();
                    message.obj=bitmap;
                    mHandler.sendMessage(message); //将内容发送到Handle线程中
                }
            }.start();
        }
        /**
         * 通过url得到bitmap
         * @param urlString
         * @return bitmap
         */
        public Bitmap getBitmapFromURL(String urlString){
            Bitmap bitmap;
            InputStream is = null;
            try {
                URL url=new URL(urlString);
                HttpURLConnection connection=(HttpURLConnection) url.openConnection(); //打开链接   //注意是:HttpURLConnection而不是HttpsURLConnection
                is=new BufferedInputStream(connection.getInputStream());
                bitmap=BitmapFactory.decodeStream(is); //将这个流转换为bitmap
                connection.disconnect(); //资源释放
                return bitmap;
            } catch (java.io.IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return null;
            
        }
    }
    

      思路解析:

        1)新建一个线程来实现加载图片

        2)创建加载图片的方法,方法的参数为图片的url,这个url可以通过解析刚才的json数据得到

    HttpURLConnection connection=(HttpURLConnection) url.openConnection(); //打开链接   //注意是:HttpURLConnection而不是HttpsURLConnection

         通过这条语句,将连接转化成流,然后得到流,最后将流转换为bitmap对象

        3)得到bitmap对象后,我们新建Handler线程,在这个线程中进行图片的更换,由于bitmap在我们的新线程中,所以我们通过handler的消息传递进行将bitmap对象传入到主线程中去

       4)由于ListView的缓存机制,所以我们通过在适配器为图片设置tag的方法从而实现图片的正确加载,避免导致图片的来回替换

        在Handler中,我们通过通过设置判断tag属性,来判断图片的url是否相等

        附录:ListView适配器的代码:

    package cn.edu.bzu.async_listview;
    
    import java.util.List;
    
    import android.content.Context;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.BaseAdapter;
    import android.widget.ImageView;
    import android.widget.TextView;
    
    public class NewsAdapter extends BaseAdapter {
        private List<NewsBean> mList;
        private LayoutInflater mInflater;
        
        public NewsAdapter(Context context,List<NewsBean> data){
            mList=data;
            mInflater=LayoutInflater.from(context);
        }
        @Override
        public int getCount() {
            return mList.size();
        }
    
        @Override
        public Object getItem(int position) {
            return mList.get(position);
        }
    
        @Override
        public long getItemId(int position) {
            return position;
        }
    
        @Override
        public View getView(int positon, View convertView, ViewGroup parent) {
            ViewHolder viewHolder=null;
            if(convertView==null){
                viewHolder=new ViewHolder();
                convertView=mInflater.inflate(R.layout.listview_item, null); //布局转化为视图
                viewHolder.ivIcon=(ImageView) convertView.findViewById(R.id.iv_icon);
                viewHolder.tvTitle=(TextView) convertView.findViewById(R.id.tv_title);
                viewHolder.tv_Content=(TextView) convertView.findViewById(R.id.tv_content);
                convertView.setTag(viewHolder);
            }else{
                viewHolder=(ViewHolder) convertView.getTag();
            }
            viewHolder.ivIcon.setImageResource(R.drawable.ic_launcher);
            
            String url=mList.get(positon).imgIconUrl;
            viewHolder.ivIcon.setTag(url);
            new ImageLoader().showImageByThread(viewHolder.ivIcon,url);  //图片id,图片的链接
            viewHolder.tvTitle.setText(mList.get(positon).newsTitle);
            viewHolder.tv_Content.setText(mList.get(positon).newsContent);
            
            return convertView;
        }
        class ViewHolder{
            public TextView tvTitle,tv_Content;
            public ImageView ivIcon;
        }
    }
    

        3.2.2 :我们通过使用继承AsyncTask的方法来实现图片的异步加载

    首先我们需要创建一个方法:showImageByAsyncTask,并且传入值为ImageView以及图片的url

       其次新建一个类继承AsyncTask,这个类为匿名内部类,为ImageLoder中的类:

    private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap>{
    		private ImageView mImageView;
    		private String mUrl;
    		public NewsAsyncTask(ImageView imageView,String url){
    			mImageView=imageView;
    			mUrl=url;
    		}
    		@Override
    		protected Bitmap doInBackground(String... params) {
    			return getBitmapFromURL(params[0]);
    		}
    		@Override
    		protected void onPostExecute(Bitmap bitmap) {
    			super.onPostExecute(bitmap);
    			if(mImageView.getTag().equals(mUrl)){
    				 mImageView.setImageBitmap(bitmap);	
    			}
    		}
    	}

        然后仅仅需要修改NewsAdapter中为图片控件赋值的代码即可:

    		//new ImageLoader().showImageByThread(viewHolder.ivIcon,url);  //图片id,图片的链接  --->>使用多线程的方法
    		new ImageLoader().showImageByAsyncTask(viewHolder.ivIcon,url);  //使用继承AsyncTask的方式实现图片的异步加载

      至此,我们为控件赋值以及异步加载数据的功能已经实现,我们来看下效果:

      

    四、LruCache缓存机制(使用内存空间换取效率)

           问题分析:上述的程序中存在一个问题,问题是当用户每次刷新的时候,都需要从网络中读取数据并且进行加载,所以这样增加了用户的流量费用,不利于用户的使用

          那么如何提高用户的体验??---->>>>使用缓存

          Lru算法:

            Lru:Least Recently Used 近期最少使用算法

           Android提供了LruCache类来实现这个缓存算法

      思路:1.声明LruCache对象

                2.在构造方法中实现创建LruCache对象,在这里需要实现它的一个内部方法,用于告诉系统图片的大小,在此之前,你需要得到运行的最大内存,然后用最大内存的一部分

              3.添加两个方法:一个将图片加入到缓存,一个从缓存中读取图片

                          ---->>将图片加入到缓存,从网络中读取数据,如果图片不为空,则加入到缓存

                          ----->>从缓存中读取图片,通过键值对的形式读取图片

             4.在异步加载图片的使用,首先需要从缓存中读取图片,如果图片为空的话,加载图片,加载出来的图片添加到缓存中

             5.在adapter中创建ImageLoader对象,实现添加适配器的时候自动加载缓存机制

        附:代码

    package cn.edu.bzu.async_listview;
    
    import java.io.BufferedInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.net.HttpURLConnection;
    import java.net.URL;
    
    import android.os.AsyncTask;
    import android.os.Message;
    
    
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.os.Handler;
    import android.util.LruCache;
    import android.widget.ImageView;
    
    /**
     * 用于处理图片的加载
     * @author monster
     *
     */
    public class ImageLoader {
        private ImageView mImageView;
        private String mUrl;
        private LruCache<String, Bitmap> mCaches ;  //用户图片的缓存
        
        public ImageLoader(){
            int maxMemory=(int) Runtime.getRuntime().maxMemory();  //获取最大可用内存
            int cacheSize=maxMemory/4;  //缓存的大小
            mCaches=new LruCache<String,Bitmap>(cacheSize){
                @Override
                protected int sizeOf(String key, Bitmap value) {
                    //在每次存入缓存的时候调用
                    return value.getByteCount(); //告诉系统,存入的图片的大小
                }
            };
        }
        /**
         * 把bitmap加入到缓存中
         * @param url
         * @param bitmap
         */
        public void addBitmapToCache(String url,Bitmap bitmap){
            if(getBitmapFromCache(url)==null){
                mCaches.put(url, bitmap);
            }
        }
        
        /**
         * 把图片从缓存中取出来
         * @param url
         * @return bitmap
         */
        public Bitmap getBitmapFromCache(String url){
            return mCaches.get(url);
        }
        /**
         * UI主线程
         */
        private Handler mHandler=new Handler(){
            public void handleMessage(Message msg) {
                super.handleMessage(msg); 
                //通过设置tag属性避免缓存图片对正确图片的影响
                if(mImageView.getTag().equals(mUrl)){
                    mImageView.setImageBitmap((Bitmap) msg.obj);
                }
            };
        };
        /**
         * 通过多线程的方式加载图片
         * @param imageView
         * @param url
         */
        public void showImageByThread(ImageView imageView,final String url){
            mImageView=imageView; //将ImageView保存进成员变量中
            mUrl=url;
            new Thread(){
                @Override
                public void run() {
                    super.run();
                    Bitmap bitmap=getBitmapFromURL(url);
                    Message message=Message.obtain();
                    message.obj=bitmap;
                    mHandler.sendMessage(message); //将内容发送到Handle线程中
                }
            }.start();
        }
        /**
         * 通过url得到bitmap
         * @param urlString
         * @return bitmap
         */
        public Bitmap getBitmapFromURL(String urlString){
            Bitmap bitmap;
            InputStream is = null;
            try {
                URL url=new URL(urlString);
                HttpURLConnection connection=(HttpURLConnection) url.openConnection(); //打开链接   //注意是:HttpURLConnection而不是HttpsURLConnection
                is=new BufferedInputStream(connection.getInputStream());
                bitmap=BitmapFactory.decodeStream(is); //将这个流转换为bitmap
                connection.disconnect(); //资源释放
                return bitmap;
            } catch (java.io.IOException e) {
                e.printStackTrace();
            }finally{
                try {
                    is.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return null;
        }
        /**
         * 通过AsyncTask的方式异步加载图片
         * @param imageView
         * @param url
         */
        public void showImageByAsyncTask(ImageView imageView,String url){
            Bitmap bitmap=getBitmapFromCache(url);  //从缓存中取出图片
            if(bitmap==null){
                new NewsAsyncTask(imageView,url).execute(url);    
            }else{
                imageView.setImageBitmap(bitmap);
            }
            
        }
        private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap>{
            private ImageView mImageView;
            private String mUrl;
            public NewsAsyncTask(ImageView imageView,String url){
                mImageView=imageView;
                mUrl=url;
            }
            /**
             * 从网络中获取图片,如果图片已经下载,则加入到缓存
             */
            @Override
            protected Bitmap doInBackground(String... params) {
                String url=params[0];
                Bitmap bitmap=getBitmapFromURL(url);
                if(bitmap!=null){
                    addBitmapToCache(url, bitmap);
                }
                return bitmap ;
            }
            @Override
            protected void onPostExecute(Bitmap bitmap) {
                super.onPostExecute(bitmap);
                if(mImageView.getTag().equals(mUrl)){
                     mImageView.setImageBitmap(bitmap);    
                }
            }
        }
    }
    

    五:源代码下载:

          https://github.com/monsterLin/Async_ListView

       

  • 相关阅读:
    java中split函数参数特殊字符的处理(转义),如:"." 、"\"、"|"
    Javascript编程风格
    HTTP 错误 500.0的解决方法。
    在WCF中进行大数据量传输
    “您已使用临时配置文件登陆”的解决方法
    关于命令行执行Migrations
    转一篇关于部署的文章
    MSDeploy使用备忘
    不可征服
    git初始化项目
  • 原文地址:https://www.cnblogs.com/boy1025/p/4543801.html
Copyright © 2011-2022 走看看