zoukankan      html  css  js  c++  java
  • 请求网络图片缓存到本地 ,还有一些现成的图片加载框架的使用 Ace网络篇(一)

    现在去买年货~~~~~~ 占坑,

    现在来填坑 填完睡觉,感谢这俩月的把自己往死里逼得奋斗从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");//还是我们可爱的羊驼
  • 相关阅读:
    VC++SDK编程——字体及位置示例
    VC2008以资源形式实现多语言版本(非Unicode) .转
    跟着编程之美学算法——最长递增子序列(转)
    跟着编程之美学算法——最长公共子序列
    MFC的多国语言界面的实现
    随意输入N个英文字符,找出其中最长连续的排列。
    C++读写EXCEL文件方式比较 .
    操作EXCEL和符号分隔文本的类CSpreadSheet .
    每个程序员都必读的12篇文章
    VC项目配置详解
  • 原文地址:https://www.cnblogs.com/AceIsSunshineRain/p/5173231.html
Copyright © 2011-2022 走看看