三级缓存加载图片
步骤:
1. 搭建服务器端
2. 界面布局
整体 : ListView+提示视图
Item : LinearLayout
3. 动态显示列表
使用Handler+Thread处理联网请求, 得到json数据, 解析成List<ShopInfo>
使用BaseAdapter显示文本列表
根据url, 异步请求显示图片(使用三级缓存)
4. 动态显示列表中的图片
--->Bitmap--->手机本地的图片文件--->服务器端的图片文件
1). 图片的三级缓存
一级缓存: 内存缓存, 缓存的是bitmap对象, 用Map<String, Bitmap>结构保存, key是url
二级缓存: 本地(sd卡)缓存, 缓存的是图片文件, /storage/sdcard/Android/data/packageName/files/图片文件名(xxx.jpg)
三级缓存: 远程服务器缓存, 缓存的是图片文件, 远程服务器上的应用中
2). 如何使用三级缓存? -----如何根据图片的url动态显示图片?
String iamgePath = http://192.168.80.1:8080
1). 根据url从一级缓存中取对应的bitmap对象
如果有, 显示(结束)
如果没有, 进入2)
2). 从二级缓存中查找: 得到文件名并在sd卡的缓存目录下加载对应的图片得到Bitmap对象
如果有: 显示, 缓存到一级缓存中(结束)
如果没有, 进入3)
3). 显示代表提示正在加载的图片, 启动分线程联网请求得到Bitmap对象
如果没有: 显示提示错误的图片(结束)
如果有:
显示
缓存到一级缓存
缓存到二级缓存
3). 在ListView使用图片三级缓存会存在图片闪动的bug
1). 原因
converView被复用了
2). 解决
a. 每次getView()都将图片的url保存到ImageView上: imageView.setTag(imagePath)
b. 在分线程准备请求服务器加载图片之前, 比较准备加载图片的url与ImageView中保存的最新图片的url是同一个,
如果不是同一个, 当前加载图片的任务不应该再执行
如果相同, 继续执行加载远程图片
c. 在主线程准备显示图片之前, 比较加载到图片的url与ImageView中保存的最新图片的url是同一个
如果不是同一个, 不需要显示此图片
如果相同, 显示图片
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<ListView
android:id="@+id/lv_main"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
<LinearLayout
android:id="@+id/ll_main_loading"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
android:visibility="gone">
<ProgressBar
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="正在加载中..." />
</LinearLayout>
</FrameLayout>
<?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="60dp"
android:orientation="horizontal" >
<ImageView
android:id="@+id/iv_item_icon"
android:layout_width="60dp"
android:layout_height="60dp"
android:src="@drawable/ic_launcher"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_vertical"
android:layout_marginLeft="10dp">
<TextView
android:id="@+id/tv_item_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="名称"
android:textSize="18sp"/>
<TextView
android:id="@+id/tv_item_price"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="内容"
android:textSize="18sp"/>
</LinearLayout>
</LinearLayout>
package com.jane.threeCache;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.List;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.atguigu.app05_handler.R;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class MainActivity extends Activity
{
protected static final int WHAT_REQUEST_SUCCESS = 1;
protected static final int WHAT_REQUEST_ERROR = 2;
private ListView lv_main;
private LinearLayout ll_main_loading;
private List<ShopInfo> data;
private ShopInfoAdapter adapter;
private Handler handler = new Handler()
{
public void handleMessage(android.os.Message msg)
{
switch (msg.what)
{
case WHAT_REQUEST_SUCCESS:
ll_main_loading.setVisibility(View.GONE);
lv_main.setAdapter(adapter);
break;
case WHAT_REQUEST_ERROR:
ll_main_loading.setVisibility(View.GONE);
Toast.makeText(MainActivity.this, "加载数据失败", 1).show();
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
lv_main = (ListView) findViewById(R.id.lv_main);
ll_main_loading = (LinearLayout) findViewById(R.id.ll_main_loading);
adapter = new ShopInfoAdapter();
ll_main_loading.setVisibility(View.VISIBLE);
new Thread()
{
public void run()
{
try
{
String jsonString = requestJson();
data = new Gson().fromJson(jsonString, new TypeToken<List<ShopInfo>>(){}.getType());
handler.sendEmptyMessage(WHAT_REQUEST_SUCCESS);
} catch (Exception e)
{
e.printStackTrace();
handler.sendEmptyMessage(WHAT_REQUEST_ERROR);
}
}
}.start();
}
private String requestJson() throws Exception
{
String result = null;
String path = "http://192.168.80.1:8080/L05_Web/ShopInfoListServlet";
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.connect();
int responseCode = connection.getResponseCode();
if (responseCode == 200)
{
InputStream is = connection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while ((len = is.read(buffer)) != -1)
{
baos.write(buffer, 0, len);
}
baos.close();
is.close();
connection.disconnect();
result = baos.toString();
} else
{
}
return result;
}
class ShopInfoAdapter extends BaseAdapter
{
private ImageLoader imageLoader;
public ShopInfoAdapter()
{
imageLoader = new ImageLoader(MainActivity.this,R.drawable.loading, R.drawable.error);
}
@Override
public int getCount()
{
return data.size();
}
@Override
public Object getItem(int position)
{
return data.get(position);
}
@Override
public long getItemId(int position)
{
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
if (convertView == null)
{
convertView = View.inflate(MainActivity.this,R.layout.item_main, null);
}
ShopInfo shopInfo = data.get(position);
TextView nameTV = (TextView) convertView.findViewById(R.id.tv_item_name);
TextView priceTV = (TextView) convertView.findViewById(R.id.tv_item_price);
ImageView imageView = (ImageView) convertView.findViewById(R.id.iv_item_icon);
nameTV.setText(shopInfo.getName());
priceTV.setText(shopInfo.getPrice() + "元");
String imagePath = shopInfo.getImagePath();
imageLoader.loadImage(imagePath, imageView);
return convertView;
}
}
}
package com.jane.threeCache;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;
public class ImageLoader
{
private Context context;
private int loadingImageRes;
private int errorImageRes;
public ImageLoader(Context context, int loadingImageRes, int errorImageRes)
{
super();
this.context = context;
this.loadingImageRes = loadingImageRes;
this.errorImageRes = errorImageRes;
}
private Map<String, Bitmap> cacheMap = new HashMap<String, Bitmap>();
public void loadImage(String imagePath, ImageView imageView)
{
imageView.setTag(imagePath);
Bitmap bitmap = getFromFirstCache(imagePath);
if(bitmap!=null)
{
imageView.setImageBitmap(bitmap);
return;
}
bitmap = getFromSecondCache(imagePath);
if(bitmap!=null)
{
imageView.setImageBitmap(bitmap);
cacheMap.put(imagePath, bitmap);
return;
}
loadBitmapFromThirdCache(imagePath, imageView);
}
private void loadBitmapFromThirdCache(final String imagePath, final ImageView imageView)
{
new AsyncTask<Void, Void, Bitmap>()
{
protected void onPreExecute()
{
imageView.setImageResource(loadingImageRes);
}
@Override
protected Bitmap doInBackground(Void... params)
{
Bitmap bitmap = null;
try
{
String newImagePath = (String) imageView.getTag();
if(newImagePath!=imagePath) {
return null;
}
URL url = new URL(imagePath);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.connect();
int responseCode = connection.getResponseCode();
if(responseCode==200)
{
InputStream is = connection.getInputStream();
bitmap = BitmapFactory.decodeStream(is);
is.close();
if(bitmap!=null)
{
cacheMap.put(imagePath, bitmap);
String filesPath = context.getExternalFilesDir(null).getAbsolutePath();
String fileName = imagePath.substring(imagePath.lastIndexOf("/")+1);
String filePath = filesPath+"/"+fileName;
bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(filePath));
}
}
connection.disconnect();
} catch (Exception e)
{
e.printStackTrace();
}
return bitmap;
}
protected void onPostExecute(Bitmap bitmap)
{
String newImagePath = (String) imageView.getTag();
if(newImagePath!=imagePath)
{
return;
}
if(bitmap==null)
{
imageView.setImageResource(errorImageRes);
} else
{
imageView.setImageBitmap(bitmap);
}
}
}.execute();
}
private Bitmap getFromSecondCache(String imagePath)
{
String filesPath = context.getExternalFilesDir(null).getAbsolutePath();
String fileName = imagePath.substring(imagePath.lastIndexOf("/")+1);
String filePath = filesPath+"/"+fileName;
return BitmapFactory.decodeFile(filePath);
}
private Bitmap getFromFirstCache(String imagePath)
{
return cacheMap.get(imagePath);
}
}
package com.jane.threeCache;
public class ShopInfo
{
private int id;
private String name;
private double price;
private String imagePath;
public ShopInfo(int id, String name, double price, String imagePath)
{
super();
this.id = id;
this.name = name;
this.price = price;
this.imagePath = imagePath;
}
public ShopInfo()
{
super();
}
public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getName()
{
return name;
}
public void setName(String name)
{
this.name = name;
}
public double getPrice()
{
return price;
}
public void setPrice(double price)
{
this.price = price;
}
public String getImagePath()
{
return imagePath;
}
public void setImagePath(String imagePath)
{
this.imagePath = imagePath;
}
@Override
public String toString()
{
return "ShopInfo [id=" + id + ", name=" + name + ", price=" + price
+ ", imagePath=" + imagePath + "]";
}
}
自动增值练习
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:id="@+id/tv_demo_number"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="10"
android:textColor="#ff0000"
android:textSize="20sp"/>
<Button
android:id="@+id/btn_demo_increase"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="自动增加" />
<Button
android:id="@+id/btn_demo_decrease"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="自动减少" />
<Button
android:id="@+id/btn_demo_pause"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="暂停"
android:enabled="false"/>
</LinearLayout>
package com.example.myhandler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
public class HandlerDemoActivity extends Activity implements OnClickListener
{
private static final int WHAT_INCREASE = 1;
private static final int WHAT_DECREASE = 2;
private TextView tv_demo_number;
private Button btn_demo_increase;
private Button btn_demo_decrease;
private Button btn_demo_pause;
private Handler handler = new Handler()
{
public void handleMessage(android.os.Message msg)
{
int number = Integer.parseInt(tv_demo_number.getText().toString());
switch (msg.what)
{
case WHAT_INCREASE:
if (number == 20)
{
btn_demo_pause.setEnabled(false);
Toast.makeText(HandlerDemoActivity.this, "已经达到最大值", 0)
.show();
return;
}
number++;
tv_demo_number.setText(number + "");
handler.sendEmptyMessageDelayed(WHAT_INCREASE, 1000);
break;
case WHAT_DECREASE:
if (number == 1)
{
btn_demo_pause.setEnabled(false);
Toast.makeText(HandlerDemoActivity.this, "已经达到最小值", 0)
.show();
return;
}
number--;
tv_demo_number.setText(number + "");
handler.sendEmptyMessageDelayed(WHAT_DECREASE, 1000);
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_demo);
init();
}
private void init()
{
tv_demo_number = (TextView) findViewById(R.id.tv_demo_number);
btn_demo_increase = (Button) findViewById(R.id.btn_demo_increase);
btn_demo_decrease = (Button) findViewById(R.id.btn_demo_decrease);
btn_demo_pause = (Button) findViewById(R.id.btn_demo_pause);
btn_demo_increase.setOnClickListener(this);
btn_demo_decrease.setOnClickListener(this);
btn_demo_pause.setOnClickListener(this);
}
@Override
public void onClick(View v)
{
if (v == btn_demo_increase)
{
btn_demo_increase.setEnabled(false);
btn_demo_decrease.setEnabled(true);
btn_demo_pause.setEnabled(true);
handler.removeMessages(WHAT_DECREASE);
handler.sendEmptyMessage(WHAT_INCREASE);
} else if (v == btn_demo_decrease)
{
btn_demo_increase.setEnabled(true);
btn_demo_decrease.setEnabled(false);
btn_demo_pause.setEnabled(true);
handler.removeMessages(WHAT_INCREASE);
handler.sendEmptyMessage(WHAT_DECREASE);
} else if (v == btn_demo_pause)
{
btn_demo_increase.setEnabled(true);
btn_demo_decrease.setEnabled(true);
btn_demo_pause.setEnabled(false);
handler.removeMessages(WHAT_INCREASE);
handler.removeMessages(WHAT_DECREASE);
}
}
}
使用和不使用Handler进行异步任务
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ProgressBar
android:id="@+id/pb_handler1_loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:visibility="invisible"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="getSubmit1"
android:text="GET Submit" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="getSubmit2"
android:text="GET Submit2" />
<EditText
android:id="@+id/et_handler1_result"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="显示结果" >
</EditText>
</LinearLayout>
package com.example.myhandler;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.EditText;
import android.widget.ProgressBar;
public class HandlerTestActivity extends Activity
{
private ProgressBar pb_handler1_loading;
private EditText et_handler1_result;
private Handler handler = new Handler()
{
public void handleMessage(android.os.Message msg)
{
if (msg.what == 1)
{
String result = (String) msg.obj;
et_handler1_result.setText(result);
pb_handler1_loading.setVisibility(View.INVISIBLE);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler_test);
pb_handler1_loading = (ProgressBar) findViewById(R.id.pb_handler1_loading);
et_handler1_result = (EditText) findViewById(R.id.et_handler1_result);
}
public void getSubmit1(View v)
{
pb_handler1_loading.setVisibility(View.VISIBLE);
new Thread()
{
public void run()
{
String path = "http://192.168.80.1:8080/Android/index.jsp?username=jane&age=18";
try
{
final String result = requestToString(path);
runOnUiThread(new Runnable()
{
@Override
public void run()
{
et_handler1_result.setText(result);
pb_handler1_loading.setVisibility(View.INVISIBLE);
}
});
} catch (Exception e)
{
e.printStackTrace();
}
}
}.start();
}
public void getSubmit2(View v)
{
pb_handler1_loading.setVisibility(View.VISIBLE);
new Thread()
{
public void run()
{
String path = "http://192.168.80.1:8080/Android/index.jsp?username=jane&age=18";
try
{
String result = requestToString(path);
Message message = Message.obtain();
message.what = 1;
message.obj = result;
handler.sendMessage(message);
} catch (Exception e)
{
e.printStackTrace();
}
}
}.start();
}
public String requestToString(String path) throws Exception
{
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.connect();
InputStream is = connection.getInputStream();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len = -1;
while ((len = is.read(buffer)) != -1)
{
baos.write(buffer, 0, len);
}
baos.close();
is.close();
String result = baos.toString();
connection.disconnect();
return result;
}
}