您的位置:首页 > 理论基础 > 计算机网络

请求网络图片缓存到本地 ,还有一些现成的图片加载框架的使用 Ace网络篇(一)

2016-01-31 14:15 701 查看
现在去买年货~~~~~~ 占坑,

现在来填坑 填完睡觉,感谢这俩月的把自己往死里逼得奋斗从JAVA什么都不懂到现在,做这些也是给在自学路上的新人(我也是菜鸟)一点点我力所能及的帮助,等我水平更高了还会继续分享,分享,开源是种快乐.

这篇讲你怎样获取一张网络图片,很基础,但我还是那句话 万变不理其中,很多集成好的框架也是这个原理,框架虽易但希望你能懂得其中原理,代码再复杂,也是这种"一招一式"

现在来看请求一张照片分几步走:

获取网络图片

这里我并没有说异步线程,我直接在主线程执行这些

1 确定图片的网址
2 发送http请求

URL url = new URL(address);
//获取连接对象,并没有建立连接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置连接和读取超时
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
//设置请求方法,注意必须大写
conn.setRequestMethod("GET");
//建立连接,发送get请求
//conn.connect();
//建立连接,然后获取响应吗,200说明请求成功
conn.getResponseCode();
* 服务器的图片是以流的形式返回给浏览器的

//拿到服务器返回的输入流
InputStream is = conn.getInputStream();
//把流里的数据读取出来,并构造成图片
Bitmap bm = BitmapFactory.decodeStream(is);
* 把图片设置为ImageView的显示内容

ImageView iv = (ImageView) findViewById(R.id.iv);
iv.setImageBitmap(bm);


3 别忘了添加权限

代码:布局就一个imageview和button 不写了 记得添加网络请求权限

public class MainActivity extends Activity {

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

public void click(View v){
String path = "http://pic.cnblogs.com/avatar/859887/20160120170921.png";
//我酷炫吊炸天可爱卖萌的羊驼
//发送http请求
try {
//1.使用网址构造一个URL对象
URL url = new URL(path);
//2.获取连接对象
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//3.设置一些属性
//设置请求方式,注意大写
conn.setRequestMethod("GET");
//设置请求超时
conn.setConnectTimeout(8000);
//设置读取超时
conn.setReadTimeout(8000);
//4.发送请求,建立连接
conn.connect();
//5.判断请求是否成功
if(conn.getResponseCode() == 200){
//获取服务器返回的流,流里就是客户端请求的数据
InputStream is = conn.getInputStream();

//读取流里的数据,构造出一张图片
Bitmap bm = BitmapFactory.decodeStream(is);

//把下载的图片显示至imageview
ImageView iv = (ImageView) findViewById(R.id.iv);
iv.setImageBitmap(bm);
}
else{
Toast.makeText(this, "请求失败了亲~~", 0).show();
}
} catch (Exception e) {
e.printStackTrace();
}
}

}


其实我不开子线程是个坑下面给我自己写个坑:

来 让主线程sleep 10秒......系统会抛出Application Not Response

因为主线程不能被阻塞:
1 在Android中,主线程被阻塞会导致应用不能刷新ui界面,不能响应用户操作,用户体验将非常差
2 主线程阻塞时间过长,系统会抛出ANR异常
3 ANR:Application Not Response;应用无响应
4 任何耗时操作都不可以写在主线程
5 因为网络交互属于耗时操作,如果网速很慢,代码会阻塞,所以网络交互的代码不能运行在主线程

那么我们需要开启子线程,开启子线程的两种方法:请看我的这个教程会再讲三个知识点:消息队列,主线程(UI线程),缓存到本地文件

一 只有主线程能刷新ui:

1 刷新ui的代码只能运行在主线程,运行在子线程是没有任何效果的
2 如果需要在子线程中刷新ui,使用消息队列机制
3 主线程也叫ui线程

二 消息队列
1 主线程创建时,系统会同时创建消息队列对象(MessageQueue)和消息轮询器对象(Looper)
2 轮询器的作用,就是不停的检测消息队列中是否有消息(Message)
3 Looper一旦发现Message Queue中有消息,就会把消息取出,然后把消息扔给Handler对象,Handler会调用自己的handleMessage方法来处理这条消息
4 handleMessage方法运行在主线程
5 主线程创建时,消息队列和轮询器对象就会被创建,但是消息处理器对象,需要使用时,自行创建

主线程创建:

//消息队列
Handler handler = new Handler(){
//主线程中有一个消息轮询器looper,不断检测消息队列中是否有新消息,如果发现有新消息,自动调用此方法,注意此方法是在主线程中运行的
public void handleMessage(android.os.Message msg) {

}
};


在子线程中使用Handler对象往消息队列里发消息

//创建消息对象
Message msg = new Message();
//消息的obj属性可以赋值任何对象,通过这个属性可以携带数据
msg.obj = bm;
//what属性相当于一个标签,用于区分出不同的消息,从而运行不能的代码
msg.what = 1;
//发送消息
handler.sendMessage(msg);


通过switch语句区分不同的消息


public void handleMessage(android.os.Message msg) {
switch (msg.what) {
//如果是1,说明属于请求成功的消息
case 1:
ImageView iv = (ImageView) findViewById(R.id.iv);
Bitmap bm = (Bitmap) msg.obj;
iv.setImageBitmap(bm);
break;
case 2:
Toast.makeText(MainActivity.this, "请求失败", 0).show();
break;
}
}


缓存图片到本地的功能:

把服务器返回的流里的数据读取出来,然后通过文件输入流写至本地文件

//1.拿到服务器返回的输入流
InputStream is = conn.getInputStream();
//2.把流里的数据读取出来,并构造成图片

FileOutputStream fos = new FileOutputStream(file);
byte[] b = new byte[1024];
int len = 0;
while((len = is.read(b)) != -1){
fos.write(b, 0, len);
}


创建bitmap对象的代码改成

Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
每次发送请求前检测一下在缓存中是否存在同名图片,如果存在,则读取缓存

看代码吧

public class MainActivity extends Activity {

Handler handler = new Handler(){
//只要消息队列有消息,此方法就会在主线程执行
public void handleMessage(android.os.Message msg) {
//在这里刷新ui
switch (msg.what) {
case 1:
ImageView iv = (ImageView) findViewById(R.id.iv);
iv.setImageBitmap((Bitmap)msg.obj);
break;
case 2:
Toast.makeText(MainActivity.this, "请求失败o(╯□╰)o", 0).show();
break;
}

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

public void click(View v){
final String path = "http://pic.cnblogs.com/avatar/859887/20160120170921.png";
final File file = new File(getCacheDir(), getNameFromPath(path));
if(file.exists()){
System.out.println("从缓存获取");
Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());

Message msg = new Message();
msg.obj = bm;
msg.what = 1;
handler.sendMessage(msg);
//            ImageView iv = (ImageView) findViewById(R.id.iv);
//            iv.setImageBitmap((Bitmap)msg.obj);
}
else{
Thread t = new Thread(){
@Override
public void run() {
System.out.println("从网络获取");
//发送http请求
try {
//1.使用网址构造一个URL对象
URL url = new URL(path);
//2.获取连接对象
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//3.设置一些属性
//设置请求方式,注意大写
conn.setRequestMethod("GET");
//设置请求超时
conn.setConnectTimeout(8000);
//设置读取超时
conn.setReadTimeout(8000);
//4.发送请求,建立连接
//                        conn.connect();
//5.判断请求是否成功
if(conn.getResponseCode() == 200){
//获取服务器返回的流,流里就是客户端请求的数据
InputStream is = conn.getInputStream();

//我们自己读取流里的数据,读取1k,就把1k写到本地文件缓存起来
byte[] b = new byte[1024];
int len;

FileOutputStream fos = new FileOutputStream(file);
while((len = is.read(b)) != -1){
fos.write(b, 0, len);
}
fos.close();

//                        因为缓存时已经把流里数据读取完了,此时流里没有任何数据
//                        Bitmap bm = BitmapFactory.decodeStream(is);

Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());

//当子线程需要刷新ui时,只需发送一条消息至消息队列
Message msg = new Message();
//消息对象本身是可以携带数据的
msg.obj = bm;
//使用what标注消息是什么类型的
msg.what = 1;

handler.sendMessage(msg);

}
else{
//如果消息池有消息,取出消息池第一条消息,返回,如果没有,就new一个消息返回
//                        Message msg = handler.obtainMessage();
//                        msg.what = 2;
//                        handler.sendMessage(msg);

handler.sendEmptyMessage(2);
}
} catch (Exception e) {
e.printStackTrace();
}
}
};
t.start();
}

}

public String getNameFromPath(String path){
int index = path.lastIndexOf("/");
return path.substring(index + 1);
}

}


更多开启线程加载图片的教程可以看我的这个教程

阿冰教你一步一步做Android新闻客户端(二)两种异步线程加载图片的方法

眼皮子打架了 我要睡了 明天讲框架 框架就太简单了 教会你用框架只要一秒 但是不懂原理你永远不会进步.

爬起来补坑 下面你自己动手:

1 github.com
2 在github搜索smart-image-view
3 下载开源项目smart-image-view
4 使用自定义组件时,标签名字要写包名(再XML文件里替换刚才我们创建的ImageView)<com.loopj.android.image.SmartImageView/>

SmartImageView的使用

SmartImageView siv = (SmartImageView) findViewById(R.id.siv);
siv.setImageUrl("http://pic.cnblogs.com/avatar/859887/20160120170921.png");//还是我们可爱的羊驼
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: