首先需要搭建一个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 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文件
缓冲文件
当退出再次进来,就会直接从缓冲去获取
关于从网络上获取文件,就简单的说到这里
版权声明:本文为博主原创文章,未经博主允许不得转载。
原文地址:http://blog.csdn.net/longwang155069/article/details/47336735