• Android 异步加载


    Android的Lazy Load主要体现在网络数据(图片)异步加载、数据库查询、复杂业务逻辑处理以及费时任务操作导致的异步处理等方面。在介绍Android开发过程中,异步处理这个常见的技术问题之前,我们简单回顾下Android开发过程中需要注意的几个地方。

    Android应用开发过程中必须遵循单线程模型(Single Thread Model)的原则。因为Android的UI操作并不是线程安全的,所以涉及UI的操作必须在UI线程中完成。但是并非所有的操作都能在主线程中进行,Google工程师在设计上约定,Android应用在5s内无响应的话会导致ANR(Application Not Response),这就要求开发者必须遵循两条法则:1、不能阻塞UI线程,2、确保只在UI线程中访问Android UI工具包。于是,开启子线程进行异步处理的技术方案应运而生。

    本文以自定义ListView,异步加载网络图片示例,总结了Android开发过程中,常用的三种异步加载的技术方案。

    思路解析:

        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"/>

    1:相关资源:activity_main.xml:

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="wrap_content">
     5     <ListView 
     6         android:id="@+id/lv_main"
     7         android:layout_width="match_parent"
     8         android:layout_height="match_parent"/>
     9    
    10     </LinearLayout>

    2:子布局:

     1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     2     xmlns:tools="http://schemas.android.com/tools"
     3     android:layout_width="match_parent"
     4     android:layout_height="wrap_content"
     5     android:orientation="horizontal" >
     6     
     7     <ImageView 
     8         android:id="@+id/iv_icon"
     9         android:layout_width="64dp"
    10         android:layout_height="64dp"
    11         android:src="@drawable/ic_launcher"
    12         android:padding="5dp"
    13         android:contentDescription="@string/iv_icon_text"/>
    14     
    15     <LinearLayout 
    16         android:layout_width="match_parent"
    17         android:layout_height="match_parent"
    18         android:orientation="vertical"
    19         android:paddingLeft="4dp"
    20         android:gravity="center">
    21         <TextView 
    22             android:id="@+id/tv_title"
    23             android:layout_width="match_parent"
    24             android:layout_height="wrap_content"
    25             android:text="@string/title_text"
    26             android:maxLines="1"
    27             android:textSize="15sp"/>
    28         <TextView 
    29             android:id="@+id/tv_content"
    30             android:layout_width="match_parent"
    31             android:layout_height="wrap_content"
    32             android:text="@string/content_text"
    33             android:maxLines="3"
    34             android:textSize="10sp"/>
    35     </LinearLayout>
    36     </LinearLayout>

    3:我们需要新建一个Bean类,用于声明一些属性和方法。

     1 package cn.edu.bzu.async_listview;
     2 
     3 public class NewsBean {
     4     public String imgIconUrl;   //图片的URL
     5     public String newsTitle;     //课程的标题
     6     public String newsContent;   //课程的内容
     7     
     8     public NewsBean(String imgIconUrl, String newsTitle, String newsContent) {
     9         super();
    10         this.imgIconUrl = imgIconUrl;
    11         this.newsTitle = newsTitle;
    12         this.newsContent = newsContent;
    13     }
    14 
    15     public NewsBean() {
    16         super();
    17     }
    18 
    19     public String getImgIconUrl() {
    20         return imgIconUrl;
    21     }
    22 
    23     public void setImgIconUrl(String imgIconUrl) {
    24         this.imgIconUrl = imgIconUrl;
    25     }
    26 
    27     public String getNewsTitle() {
    28         return newsTitle;
    29     }
    30 
    31     public void setNewsTitle(String newsTitle) {
    32         this.newsTitle = newsTitle;
    33     }
    34 
    35     public String getNewsContent() {
    36         return newsContent;
    37     }
    38 
    39     public void setNewsContent(String newsContent) {
    40         this.newsContent = newsContent;
    41     }
    42     
    43     
    44 }
    View Code

    4:创建适配器,获取数据。

    思路解析:

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

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

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

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

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

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

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

        附录:ListView适配器的代码:

     1 package cn.edu.bzu.async_listview;
     2 
     3 import java.util.List;
     4 
     5 import android.content.Context;
     6 import android.view.LayoutInflater;
     7 import android.view.View;
     8 import android.view.ViewGroup;
     9 import android.widget.BaseAdapter;
    10 import android.widget.ImageView;
    11 import android.widget.TextView;
    12 
    13 public class NewsAdapter extends BaseAdapter {
    14     private List<NewsBean> mList;
    15     private LayoutInflater mInflater;//把layout布作为我们的每一个item
    16     private ImageLoader mImageLoader;
    17     //添加一个构造方法
    18     public NewsAdapter(Context context,List<NewsBean> data){
    19         mList=data;//将数组映射过来
    20         mInflater=LayoutInflater.from(context);
    21         mImageLoader=new ImageLoader();
    22     }
    23     @Override
    24     public int getCount() {
    25         return mList.size();
    26     }
    27 
    28     @Override
    29     public Object getItem(int position) {
    30         return mList.get(position);//直接返回Position
    31     }
    32 
    33     @Override
    34     public long getItemId(int position) {
    35         return position;
    36     }
    37     //最重要的:
    38     public View getView(int positon, View convertView, ViewGroup parent) {
    39         ViewHolder viewHolder=null;
    40         if(convertView==null){
    41             viewHolder=new ViewHolder();
    42             convertView=mInflater.inflate(R.layout.listview_item, null); //布局转化为视图
    43             //对其中元素进行初始化
    44             viewHolder.ivIcon=(ImageView) convertView.findViewById(R.id.iv_icon);
    45             viewHolder.tvTitle=(TextView) convertView.findViewById(R.id.tv_title);
    46             viewHolder.tv_Content=(TextView) convertView.findViewById(R.id.tv_content);
    47             convertView.setTag(viewHolder);//设置标贴
    48         }else{
    49             viewHolder=(ViewHolder) convertView.getTag();
    50         }
    51         viewHolder.ivIcon.setImageResource(R.drawable.ic_launcher);
    52         
    53         String url=mList.get(positon).imgIconUrl;
    54         viewHolder.ivIcon.setTag(url);
    55         //new ImageLoader().showImageByThread(viewHolder.ivIcon,url);  //图片id,图片的链接
    56         mImageLoader.showImageByAsyncTask(viewHolder.ivIcon,url);  //使用继承AsyncTask的方式实现图片的异步加载
    57         viewHolder.tvTitle.setText(mList.get(positon).newsTitle);
    58         viewHolder.tv_Content.setText(mList.get(positon).newsContent);
    59         
    60         return convertView;
    61     }
    62     class ViewHolder{
    63         public TextView tvTitle,tv_Content;
    64         public ImageView ivIcon;
    65     }
    66 }
    View Code

    5:在加载图片的时候,我们需要避免因网罗或者缓存的原因等造成的程序图片缓慢,卡的现象声明一个ImageLoad类,在其中对图片进行处理。

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

    首先我们需要创建一个方法:showImageByAsyncTask,并且传入值为ImageView以及图片的url。其次新建一个类继承AsyncTask,这个类为匿名内部类,

      1 package cn.edu.bzu.async_listview;
      2 
      3 import java.io.BufferedInputStream;
      4 import java.io.IOException;
      5 import java.io.InputStream;
      6 import java.net.HttpURLConnection;
      7 import java.net.URL;
      8 
      9 import android.os.AsyncTask;
     10 import android.os.Message;
     11 
     12 
     13 import android.graphics.Bitmap;
     14 import android.graphics.BitmapFactory;
     15 import android.os.Handler;
     16 import android.util.LruCache;
     17 import android.widget.ImageView;
     18 
     19 /**
     20  * 用于处理图片的加载
     21  *
     22  */
     23 public class ImageLoader {
     24     private ImageView mImageView;
     25     private String mUrl;
     26     private LruCache<String, Bitmap> mCaches ;  //用户图片的缓存
     27     
     28     public ImageLoader(){
     29         int maxMemory=(int) Runtime.getRuntime().maxMemory();  //获取最大可用内存
     30         int cacheSize=maxMemory/4;  //缓存的大小
     31         mCaches=new LruCache<String,Bitmap>(cacheSize){
     32             @Override
     33             protected int sizeOf(String key, Bitmap value) {
     34                 //在每次存入缓存的时候调用
     35                 return value.getByteCount(); //告诉系统,存入的图片的大小
     36             }
     37         };
     38     }
     39     /**
     40      * 把bitmap加入到缓存中
     41      * @param url
     42      * @param bitmap
     43      */
     44     public void addBitmapToCache(String url,Bitmap bitmap){
     45         if(getBitmapFromCache(url)==null){
     46             mCaches.put(url, bitmap);
     47         }
     48     }
     49     
     50     /**
     51      * 把图片从缓存中取出来
     52      * @param url
     53      * @return bitmap
     54      */
     55     public Bitmap getBitmapFromCache(String url){
     56         return mCaches.get(url);
     57     }
     58     /**
     59      * UI主线程
     60      */
     61     private Handler mHandler=new Handler(){
     62         public void handleMessage(Message msg) {
     63             super.handleMessage(msg); 
     64             //通过设置tag属性避免缓存图片对正确图片的影响
     65             if(mImageView.getTag().equals(mUrl)){
     66                 mImageView.setImageBitmap((Bitmap) msg.obj);
     67             }
     68         };
     69     };
     70     /**
     71      * 通过多线程的方式加载图片
     72      * @param imageView
     73      * @param url
     74      */
     75     public void showImageByThread(ImageView imageView,final String url){
     76         mImageView=imageView; //将ImageView保存进成员变量中
     77         mUrl=url;
     78         new Thread(){
     79             @Override
     80             public void run() {
     81                 super.run();
     82                 Bitmap bitmap=getBitmapFromURL(url);
     83                 Message message=Message.obtain();
     84                 message.obj=bitmap;
     85                 mHandler.sendMessage(message); //将内容发送到Handle线程中
     86             }
     87         }.start();
     88     }
     89     /**
     90      * 通过url得到bitmap
     91      * @param urlString
     92      * @return bitmap
     93      */
     94     public Bitmap getBitmapFromURL(String urlString){
     95         Bitmap bitmap;
     96         InputStream is = null;
     97         try {
     98             URL url=new URL(urlString);
     99             HttpURLConnection connection=(HttpURLConnection) url.openConnection(); //打开链接   //注意是:HttpURLConnection而不是HttpsURLConnection
    100             is=new BufferedInputStream(connection.getInputStream());
    101             bitmap=BitmapFactory.decodeStream(is); //将这个流转换为bitmap
    102             connection.disconnect(); //资源释放
    103             return bitmap;
    104         } catch (java.io.IOException e) {
    105             e.printStackTrace();
    106         }finally{
    107             try {
    108                 is.close();
    109             } catch (IOException e) {
    110                 e.printStackTrace();
    111             }
    112         }
    113         return null;
    114     }
    115     /**
    116      * 通过AsyncTask的方式异步加载图片
    117      * @param imageView
    118      * @param url
    119      */
    120     public void showImageByAsyncTask(ImageView imageView,String url){
    121         Bitmap bitmap=getBitmapFromCache(url);  //从缓存中取出图片
    122         if(bitmap==null){
    123             new NewsAsyncTask(imageView,url).execute(url);    
    124         }else{
    125             imageView.setImageBitmap(bitmap);
    126         }
    127         
    128     }
    129     private class NewsAsyncTask extends AsyncTask<String, Void, Bitmap>{
    130         private ImageView mImageView;
    131         private String mUrl;
    132         public NewsAsyncTask(ImageView imageView,String url){
    133             mImageView=imageView;
    134             mUrl=url;
    135         }
    136         /**
    137          * 从网络中获取图片,如果图片已经下载,则加入到缓存
    138          */
    139         @Override
    140         protected Bitmap doInBackground(String... params) {
    141             String url=params[0];
    142             Bitmap bitmap=getBitmapFromURL(url);
    143             if(bitmap!=null){
    144                 addBitmapToCache(url, bitmap);
    145             }
    146             return bitmap ;
    147         }
    148         @Override
    149         protected void onPostExecute(Bitmap bitmap) {
    150             super.onPostExecute(bitmap);
    151             if(mImageView.getTag().equals(mUrl)){
    152                  mImageView.setImageBitmap(bitmap);    
    153             }
    154         }
    155     }
    156 }
    View Code

    6:MainActivity:

      1 package cn.edu.bzu.async_listview;
      2 
      3 import java.io.BufferedReader;
      4 import java.io.IOException;
      5 import java.io.InputStream;
      6 import java.io.InputStreamReader;
      7 import java.io.UnsupportedEncodingException;
      8 import java.util.ArrayList;
      9 import java.util.List;
     10 import java.net.MalformedURLException;
     11 import java.net.URL;
     12 
     13 import org.json.JSONArray;
     14 import org.json.JSONException;
     15 import org.json.JSONObject;
     16 
     17 import android.app.Activity;
     18 import android.os.AsyncTask;
     19 import android.os.Bundle;
     20 import android.util.Log;
     21 import android.widget.ListView;
     22 
     23 public class MainActivity extends Activity {
     24 
     25     private ListView mListView;
     26     private static String URL="http://www.imooc.com/api/teacher?type=4&num=30";  //慕课网提供的api链接
     27     @Override
     28     protected void onCreate(Bundle savedInstanceState) {
     29         super.onCreate(savedInstanceState);
     30         setContentView(R.layout.activity_main);
     31         mListView=(ListView) findViewById(R.id.lv_main);
     32         new NewAsyncTask().execute(URL);
     33     }
     34     
     35     /**
     36      *实现网络的异步访问
     37      */
     38     class NewAsyncTask extends AsyncTask<String, Void, List<NewsBean> >{ //参数:params:传入值    progress :进程   Result:返回值 
     39         
     40         @Override
     41         protected List<NewsBean> doInBackground(String... params) {
     42             return getJsonData(params[0]);  //得到从url读取的JSON数据
     43         } 
     44         @Override
     45             protected void onPostExecute(List<NewsBean> newsBean) {
     46                 // 将生成的newsBean设置给ListView
     47                 super.onPostExecute(newsBean);
     48                 NewsAdapter adapter=new NewsAdapter(MainActivity.this, newsBean);//创建适配器对象
     49                 mListView.setAdapter(adapter);
     50             }
     51     }
     52         /**
     53          * 将url对应的json数据转换为我们所封装的NewsBean对象
     54          * @param url
     55          * @return newsList
     56          */
     57     private List<NewsBean> getJsonData(String url) {
     58         List<NewsBean> newsBeanList=new ArrayList<NewsBean>();
     59         try {
     60             String jsonString=readStream(new URL(url).openStream()); //此句功能与url.openConnection().getInputStream()相同,可根据URL直接联网获取数据,返回值类型 InputStream;
     61             //Log.d("json",jsonString ); // 打印读取的json信息
     62             //解析json数据
     63             JSONObject jsonObject; 
     64             NewsBean newsBean; //用于封装jsonObject
     65             
     66             jsonObject=new JSONObject(jsonString);  //json数据添加到jsonObject中
     67             JSONArray jsonArray=jsonObject.getJSONArray("data"); //取出json中的data数据,data为一个数组类型
     68             for(int i=0;i<jsonArray.length();i++){
     69                 //取出data中的数据
     70                 jsonObject=jsonArray.getJSONObject(i);
     71                 newsBean=new NewsBean();
     72                 //获取图片以及title,content
     73                 newsBean.imgIconUrl=jsonObject.getString("picSmall");
     74                 newsBean.newsTitle=jsonObject.getString("name");
     75                 newsBean.newsContent=jsonObject.getString("description");
     76                 newsBeanList.add(newsBean);//把newsBean对象添加到list集合中。
     77             }
     78             
     79         } catch (IOException e) {
     80             e.printStackTrace();
     81         } catch (JSONException e) {
     82             e.printStackTrace();
     83         }
     84         return newsBeanList;
     85     }
     86     
     87     /**
     88      * 通过inputStream解析网页所返回的数据
     89      * @param is
     90      * @return  result
     91      */
     92     private String readStream(InputStream is){
     93          InputStreamReader isr;  
     94          String result="";
     95          try {
     96              String line=""; //每行的数据
     97             isr=new InputStreamReader(is,"utf-8");  //字节流转换为字符流
     98             BufferedReader br=new BufferedReader(isr);  //将字符流以buffer的形式读取出来
     99             while((line=br.readLine())!=null){
    100                 result+=line;  //拼接到result中
    101             }
    102         } catch (UnsupportedEncodingException e) {
    103             e.printStackTrace();
    104         }catch (IOException e) {
    105             e.printStackTrace();
    106         }
    107          return result;
    108     }
    109                                                                  
    110     }
    View Code

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

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

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

  • 相关阅读:
    io几乎没有,iowait却很高
    rcu使用遇到问题汇总
    Groovy In Action 笔记 (5) -- List 相关
    Groovy In Action 笔记 (4) -- String相关
    Groovy In Action 笔记 (3) -- 基本数据类型
    Groovy In Action 笔记 (2) -- '=='与‘equals’
    Groovy In Action 笔记 (1) -- 概述
    ansible notes
    grafana中如何根据label选取数据
    向node_exporter中添加新监控信息
  • 原文地址:https://www.cnblogs.com/xuyinghui/p/4665477.html
Copyright © 2020-2023  润新知