Android 网络学习之获取服务器的图片

首先需要搭建一个Tomcat服务器,然后测试服务器上的图片使用PC上的浏览器是否可以正常下载下来

可以看到服务器上的图片数据是可以正常访问的。图片的地址:http://localhost:8080/meinv.jpg

那如何在我们Android上从网络下载图片呢?

直接上获取网络图片的代码:

public class MainActivity extends Activity {

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

    public void click(View v)
    {
    	//1: 确定网址
    	String path = "http://localhost:8080/meinv.jpg";

    	try {
    		//2:把网址封装为一个URL对象
    		URL url = new URL(path);

    		//3:获取客户端和服务器的连接对象,此时还没有建立连接
    		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    		//4:初始化连接对象
    		conn.setRequestMethod("GET");
    		//设置连接超时
    		conn.setConnectTimeout(5000);
    		//设置读取超时
    		conn.setReadTimeout(5000);
    		//5:发生请求,与服务器建立连接
    		conn.connect();
    		//如果响应码为200,说明请求成功
    		if(conn.getResponseCode() == 200)
    		{
    			//获取服务器响应头中的流
    			InputStream is = conn.getInputStream();

    			//读取流里的数据,构建成bitmap位图
    			Bitmap bm = BitmapFactory.decodeStream(is);

    			//显示在界面上
    			ImageView imageView = (ImageView) findViewById(R.id.lv);
    			imageView.setImageBitmap(bm);
    		}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
    }
}

运行看效果:

从控制台的打印可以是警告: 网络工作在主线程中异常。

上面的警告就是从4.0以后引入的,如果网络任务在主线程中,就会报警告。所以我们需要开启一个线程来执行网络任务。

修改后的代码为:

public void click(View v)
    {
    	//开启一个线程
    	Thread thread = new Thread()
    	{
    		@Override
    		public void run() {
    			// TODO Auto-generated method stub
    	    	//1: 确定网址
    	    	String path = "http://localhost:8080/meinv.jpg";
    	    	try {
    	    		//2:把网址封装为一个URL对象
    	    		URL url = new URL(path);

    	    		//3:获取客户端和服务器的连接对象,此时还没有建立连接
    	    		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    	    		//4:初始化连接对象
    	    		conn.setRequestMethod("GET");
    	    		//设置连接超时
    	    		conn.setConnectTimeout(5000);
    	    		//设置读取超时
    	    		conn.setReadTimeout(5000);
    	    		//5:发生请求,与服务器建立连接
    	    		conn.connect();
    	    		//如果响应码为200,说明请求成功
    	    		if(conn.getResponseCode() == 200)
    	    		{
    	    			//获取服务器响应头中的流
    	    			InputStream is = conn.getInputStream();

    	    			//读取流里的数据,构建成bitmap位图
    	    			Bitmap bm = BitmapFactory.decodeStream(is);

    	    			//显示在界面上
    	    			ImageView imageView = (ImageView) findViewById(R.id.lv);
    	    			imageView.setImageBitmap(bm);
    	    		}
    			} catch (Exception e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	};

    	//启动线程任务
    	thread.start();
    }

再次运行看效果:

又报出一个警告: 调用错误线程异常,也就是说只有创建它的view,才能调用该view。 直白点就是只有主线程(UI线程)才能更新UI,别的线程是不能随便更新UI的。

如果需要更新UI,那只能主线程来更新UI,那别的线程如何告诉主线程需要更新UI呢? 这就需要引入另一个知识点:消息

如果别的线程需要更新UI,就发生消息给主线程,主线程收到后会自动的更新UI

代码修改为:

if(conn.getResponseCode() == 200)
    	    		{
    	    			//获取服务器响应头中的流
    	    			InputStream is = conn.getInputStream();

    	    			//读取流里的数据,构建成bitmap位图
    	    			Bitmap bm = BitmapFactory.decodeStream(is);

    	    			//发生更新UI的消息
    	    			Message msg = handler.obtainMessage();
    	    			msg.obj = bm;
    	    			handler.sendMessage(msg);
    	    			//显示在界面上
    	    			//ImageView imageView = (ImageView) findViewById(R.id.lv);
    	    			//imageView.setImageBitmap(bm);
    	    		}

加入Handler,也就是处理消息的handle

	Handler handler = new Handler()
	{
		public void handleMessage(android.os.Message msg)
		{
			//更新UI
			ImageView imageView = (ImageView) findViewById(R.id.lv);
			imageView.setImageBitmap((Bitmap) msg.obj);
		};
	};

再次运行看效果:

可以看到图片正常显示出来了。

我们再次修改代码增加获取失败的处理逻辑

if(conn.getResponseCode() == 200)
    	    		{
    	    			//获取服务器响应头中的流
    	    			InputStream is = conn.getInputStream();

    	    			//读取流里的数据,构建成bitmap位图
    	    			Bitmap bm = BitmapFactory.decodeStream(is);

    	    			//发生更新UI的消息
    	    			Message msg = handler.obtainMessage();
    	    			msg.obj = bm;
    	    			msg.what = GET_OK;
    	    			handler.sendMessage(msg);
    	    			//显示在界面上
    	    			//ImageView imageView = (ImageView) findViewById(R.id.lv);
    	    			//imageView.setImageBitmap(bm);
    	    		}
    	    		else {
    	    			//发送获取失败的消息
						Message msg = handler.obtainMessage();
						msg.what = GET_ERROR;
						handler.sendMessage(msg);
					}

消息处理过程:

static final int GET_ERROR = 0;
	static final int GET_OK  = 1;
	Handler handler = new Handler()
	{
		public void handleMessage(android.os.Message msg)
		{
			//更新UI
			switch (msg.what) {
			case GET_OK:
				ImageView imageView = (ImageView) findViewById(R.id.lv);
				imageView.setImageBitmap((Bitmap) msg.obj);
				break;
			case GET_ERROR:
				Toast.makeText(MainActivity.this, "访问失败!", Toast.LENGTH_SHORT).show();
				break;
			default:
				break;
			}

		};
	};

我们可以将地址改错,显示效果

关于消息机制简单说明一下:

1:发生消息系统会使用消息队列(MessageQueue)和消息轮询对象(Looper)

2:消息轮询对象的作用就是不停的检测消息队列中是否有小心,如果一旦有消息,消息轮询器就会将消息对象交给消息处理器(Handler),处理器会调用handleMessage方法来处理这条消息。handleMessage方法运行在主线程中,所以可以刷新ui

但是平常应用中,比如微信朋友圈的大量图片,第一次浏览时都是先缓冲到本地,第二次浏览时直接从本地读取即可,那我们来实现一下:

    public void click(View v)
    {
    	//指定文件的路径
		final File file = new File(getCacheDir(), "info.jpg");
    	//如果文件存在,直接从本地打开
		if(file.exists())
    	{
			System.out.println("从缓存读取的");
			Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());
			ImageView imageView = (ImageView) findViewById(R.id.lv);
			imageView.setImageBitmap(bm);
    	}
		else {

			System.out.println("从网上下载的");
    	//开启一个线程
    	Thread thread = new Thread()
    	{
    		@Override
    		public void run() {
    			// TODO Auto-generated method stub
    	    	//1: 确定网址
    	    	String path = "http://192.168.1.109:8080/meinv.jpg";
    	    	try {
    	    		//2:把网址封装为一个URL对象
    	    		URL url = new URL(path);

    	    		//3:获取客户端和服务器的连接对象,此时还没有建立连接
    	    		HttpURLConnection conn = (HttpURLConnection) url.openConnection();
    	    		//4:初始化连接对象
    	    		conn.setRequestMethod("GET");
    	    		//设置连接超时
    	    		conn.setConnectTimeout(5000);
    	    		//设置读取超时
    	    		conn.setReadTimeout(5000);
    	    		//5:发生请求,与服务器建立连接
    	    		conn.connect();
    	    		//如果响应码为200,说明请求成功
    	    		if(conn.getResponseCode() == 200)
    	    		{
    	    			//获取服务器响应头中的流
    	    			InputStream is = conn.getInputStream();

    	    			//读取服务器返回流里的数据,把数据写入到本地,缓冲起来
    	    			FileOutputStream fos = new FileOutputStream(file);
    	    			byte[] b = new byte[1024];
    	    			int len = 0;
    	    			while((len = is.read(b)) != -1)
    	    			{
    	    				fos.write(b, 0, len);
    	    			}
    	    			fos.close();
    	    			is.close();

    	    			//从本地加载图片
    	    			Bitmap bm = BitmapFactory.decodeFile(file.getAbsolutePath());

    	    			//读取流里的数据,构建成bitmap位图
    	    			//Bitmap bm = BitmapFactory.decodeStream(is);

    	    			//发生更新UI的消息
    	    			Message msg = handler.obtainMessage();
    	    			msg.obj = bm;
    	    			msg.what = GET_OK;
    	    			handler.sendMessage(msg);
    	    			//显示在界面上
    	    			//ImageView imageView = (ImageView) findViewById(R.id.lv);
    	    			//imageView.setImageBitmap(bm);
    	    		}
    	    		else {
    	    			//发送获取失败的消息
						Message msg = handler.obtainMessage();
						msg.what = GET_ERROR;
						handler.sendMessage(msg);
					}
    			} catch (Exception e) {
    				// TODO Auto-generated catch block
    				e.printStackTrace();
    			}
    		}
    	};

    	//启动线程任务
    	thread.start();
    }
    }

上面是增加从本地缓冲中获取图片文件。

第一次运行时:包文件名下的cache下就会存在info.jpg文件

缓冲文件

当退出再次进来,就会直接从缓冲去获取

关于从网络上获取文件,就简单的说到这里

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-04 16:26:10

Android 网络学习之获取服务器的图片的相关文章

Android网络编程之获取网络上的XML

Android网络编程之获取网络上的XML 请尊重他人的劳动成果,转载请注明出处:Android网络编程之获取网络上的XML 为要获取网络上的XML所以需要服务器端提供的支持. 一.创建服务器端: 服务器端项目结构: 服务器端运行效果图: 第一步:创建业务所需的JavaBean package com.jph.server.model; import java.util.Date; /** * 新闻实体类 * @author jph * Date:2014.09.26 */ public cla

Android网络编程之获取网络上的Json

Android网络编程之获取网络上的Json 请尊重他人的劳动成果,转载请注明出处:Android网络编程之获取网络上的Json 为要获取网络上的Json所以需要服务器端提供的支持. 一.创建服务器端: 服务器端项目结构: 服务器端运行效果图: 第一步:创建业务所需的JavaBean package com.jph.sj.model; import java.util.Date; /** * 新闻实体类 * @author jph * Date:2014.09.26 */ public clas

Ajax 学习之获取服务器的值

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><h

Android 网络学习之使用多线程下载,支持断点续传

既然本节是学习如何使用多线程下载,那我们先要明白什么是多线程下载,在搞明白什么是多线程下载之前,需要先知道什么是单线程下载. 上图就是说明了单线程下载的原来,因此单线程下载速度很慢,因为只有一个任务在干活. 这样的话,3个线程下载一个文件,总比1个线程一个文件的速度要快.所以多线程下载数据的速度就快. 既然知道了多线程的下载原理,那我们就分析多个线程是如何下载数据,以及如何保存数据的. 知道多线程下载的原理,以及每个线程如何存放数据后,那就开始写代码. 1: 当然先要获取该数据的大小了,这样才知

Android 网络请求json数据,解析json数据,生成对应的java bean类一步到位,快速开发

Android 网络请求一般都涉及到图片和JSON数据,怎样快速的请求网络JSON数据,解析JSON数据,并且一步生成自己想要的Java bean实体类?这个涉及到Android 开发效率的问题.由于接触Android 网络这方面比较多,自然就找到一些好的方法来快速开发Android 网络模块的相关内容,接下来就为大家揭晓 一步快速请求,解析JSON 数据生成对应的Java bean实体类的方法. 注:我们先把思路讲解下吧: 1.网络请求JSON数据代码可以自己写,当然我还是推荐使用网络上开源的

Android 学习笔记之Volley(六)实现获取服务器的字符串响应...

学习内容: 1.使用StringRequest实现获取服务器的字符串响应...   前几篇一直都在对服务——响应过程的源码进行分析,解析了整个过程,那么Volley中到底实现了哪些请求才是我们在开发中需要进行使用的...Volley实现的东西其实并不是很多,它的主要功能是实现异步进行网络请求和图片加载,其实就是异步加载解析Json数据,异步获取服务器的字符串数据,异步实现网络图片的动态加载,还有一个请求就是清空缓存的请求,不过使用的地方不是很多,主要还是前面三个方面,因此Volley相对于And

Android 学习之获取网络文本文件

上次我们学习如何从网络上获取一张图片,今天我们学习如何从网络上获取文本文件,以XML文件为例子.因为XML文件在实际开发中最为常见. 我们以下面图片为例子学习如何从网络上获取XML文件 我们的xml文件为: <?xml version="1.0" encoding="UTF-8" ?> <newslist> <news> <title>9月起办理手机卡需实名认证</title> <detail>

android网络获取图片并保存在本地

获取网络上的图片有三步: 一.设置连接网络的权限和写入读取SD卡的权限.二.网络访问获得数据流. 三.在SD卡中创建文件夹将数据流转成图片格式存储. 注意:response.getEntity().getContent()方法,而此方法只能调用一次.否则会报错:java.lang.IllegalStateException: Content has been consumed. manifest.xml 赋予权限,注意:在<application...>application>前添加 &

Android开发学习之路--网络编程之xml、json

一般网络数据通过http来get,post,那么其中的数据不可能杂乱无章,比如我要post一段数据,肯定是要有一定的格式,协议的.常用的就是xml和json了.在此先要搭建个简单的服务器吧,首先呢下载xampp,然后安装之类的就不再多讲了,参考http://cnbin.github.io/blog/2015/06/05/mac-an-zhuang-he-shi-yong-xampp/.安装好后,启动xampp,之后在浏览器输入localhost或者127.0.0.1就可以看到如下所示了: 这个就