面试记录第十一节——(volley框架)

一、问:volley框架的历史

答:volley是在2013年Google I/O大会上推出了一个新的网络通信框架Volley。Volley既可以访问网络取得数据,也可以加载图片,并且在性能方面也进行了大幅度的调整,它的设计目标就是非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。

二、问:五种volley请求方式分别为?

答:StringRequest、 JsonRequest、 ImageRequest、 ImageLoader、 NetworkImageView

三、问:volley如何使用?

答:分三步
  • 第一步:创建一个RequestQueue对象,他用的是Volley中的静态方法Volley.newRequestQueue(),这里拿到的对象是一个请求对象,它可以缓存所有的http请求,按照一定的算法,并发的发送这些请求。RequestQueue的设计,非常适合并发,因此我们不用每一次请求都创建RequestQueue,也就是说,在一个Activity中不管多少请求,只需要创建一个RequestQueue即可。如下:

RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);

  • 第二步:创建一个StringQuest对象。首先查看下StringRequest的源码,有连个构造方法。
  StringRequest sr = new StringRequest("", new Response.Listener<String>() {
       @Override
       public void onResponse(String s) {

       }
   }, new Response.ErrorListener() {
       @Override
       public void onErrorResponse(VolleyError volleyError) {

       }
   });
  • 第三部:就是把我们创建的sr对象,添加到我们第一步创建的mQueue队列中,然后就可以高并发的发送请求了。

mQueue .add(sr);


四、问:volley源码解析(可以忽略不看,重点看下面总结)?

答:
  • 我们来看创建RequestQueue对象是怎么创建对象的?

1、RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);这里用到的是newRequestQueue,我们点击源码进去查看,如下:

//源码:
    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, (HttpStack)null);
    }

不难看出,上面源码:他返回的是newRequestQueue(context, (HttpStack)null),里面有两个参数,我们继续往下看。

//源码:
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
        File cacheDir = new File(context.getCacheDir(), "volley");
        String userAgent = "volley/0";

        try {
            String network = context.getPackageName();
            PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
            userAgent = network + "/" + queue.versionCode;
        } catch (NameNotFoundException var6) {
            ;
        }

       //HttpStack 是对我们网络框架HurlStack和HttpClient的封装。
        if(stack == null) {
            if(VERSION.SDK_INT >= 9) {

               //这里是做一个手机版本的判断,如果手机版本大于9,他就会创建一个HurlStack
                stack = new HurlStack();
            } else {

                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }
         //根据传入的stack来处理网络请求,
        BasicNetwork network1 = new BasicNetwork((HttpStack)stack);

         //创建一个RequestQueue 对象,并且调用.start()启动。
        RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
        queue1.start();

        //返回此queue1;
        return queue1;
    }

讲解:上面的静态newRequestQueue方法,才是真正做操作的的方法,我们创建的RequestQueue 对象,也是从此方法中返回。

  • 问: queue1.start();开启了具体什么东西呢(继续看源码)
    答:点击start()方法,会看到如下源码:
 public void start() {
        this.stop();

        //源码中会发现,这里会创建一个CacheDispatcher,并且调用CacheDispatcher的start()方法.,开启一个子线程的缓存请求队列。

        //而CacheDispatcher它就是一个线程,继承的是Thread(可以点击查看源码)

        this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
        this.mCacheDispatcher.start();


        //这里也创建了一个networkDispatcher 对象,并且调用了networkDispatcher的start()方法。而networkDispatcher 也是一个子线程。
        //这里和CacheDispatcher不通的是,networkDispatcher处理的是网络请求。

        for(int i = 0; i < this.mDispatchers.length; ++i) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery);
            this.mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }

    }

讲解:

1、从上面中不难看出,这里面创面了两个子线程,一个是CacheDispatcher对象,NetworkDispatcher对象。一个是缓存、一个是网络。所以我们就很清晰的知道。

2、volley第一次请求网络的时候,,会同时开启两个请求队列,他会判断这个请求在我们本地是否有一份,如果有就直接在缓存中读取(也就是mCacheDispatcher.start();),这样就加快了我们读取的速度。

3、如果没有我们才会走NetworkDispatcher,从网络中获取数据,当从网络中获取数据的时候,我们会把数据保存在CacheDispatcher开启的子线程里面,这样我们第二次再次获取数据的时候,就直接从缓存中读取数据。大大提高获取数据的性能和速度。这就是Volley的缓存机制。

问:他又是怎么添加到缓存中的呢,也就是缓存CacheDispatcher又是怎么运行的。

答:

1、我们刚才说过,CacheDispatcher它内部是一个线程,继承的是Thread。既然是线程,那就来看看它的run()方法。

2、chcheDispatcher会从缓存中取出响应结果,如果entre也就是相应为空的话(entry == null),我们就把这个请求加入到网络请求中。

3、如果不为空的话,在判断这个缓存是否过期(entry.isExpired()),如果过期了,也会从网络请求中获取。

4、最后我们会调用e.parseNetworkResponse来进行数据的解析,然后吧解析后的数据进行回调。


 public void run() {
        if(DEBUG) {
            VolleyLog.v("start new dispatcher", new Object[0]);
        }

        Process.setThreadPriority(10);
        this.mCache.initialize();

        //开启了一个死循环,也就是说,这个缓存的线程始终是运行的。
        while(true) {
            while(true) {
                while(true) {
                    while(true) {
                        try {
                            while(true) {
                                final Request e = (Request)this.mCacheQueue.take();
                                e.addMarker("cache-queue-take");
                                if(e.isCanceled()) {
                                    e.finish("cache-discard-canceled");
                                } else {
                                    //它是一个内部类,同一个缓存队列中来获取缓存数据,来判断。
                                    Entry entry = this.mCache.get(e.getCacheKey());
                                    //如果这个缓存是一个空的话,请求就会加到mNetworkQueue中。
                                    if(entry == null) {
                                        e.addMarker("cache-miss");
                                        this.mNetworkQueue.put(e);
                                    } else if(entry.isExpired()) {//判断这个缓存是否过期
                                        e.addMarker("cache-hit-expired");
                                        e.setCacheEntry(entry);
                                        //如果过期,也会把这个请求加入到网络请求中。
                                        this.mNetworkQueue.put(e);
                                    } else {//从
                                        e.addMarker("cache-hit");
                                        Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
                                        e.addMarker("cache-hit-parsed");
                                        if(entry.refreshNeeded()) {
                                            e.addMarker("cache-hit-refresh-needed");
                                            e.setCacheEntry(entry);
                                            response.intermediate = true;
                                            this.mDelivery.postResponse(e, response, new Runnable() {
                                                public void run() {
                                                    try {
                                                        CacheDispatcher.this.mNetworkQueue.put(e);
                                                    } catch (InterruptedException var2) {
                                                        ;
                                                    }

                                                }
                                            });
                                        } else {
                                            this.mDelivery.postResponse(e, response);
                                        }
                                    }
                                }
                            }
                        } catch (InterruptedException var4) {
                            if(this.mQuit) {
                                return;
                            }
                        }
                    }
                }
            }
        }
    }
问:网络请求Networkdispatcher是如何实现的呢?

答:

1、networkdispatcher也是继承的thread,而它的run方法中也是开启了一个 while(true) 循环,说明这个网络请求也是不停的运行。

2、在 NetworkResponse e = this.mNetwork.performRequest(request);这句中,执行了performRequest方法,而这个方法就是用来发送网络请求的。而Network是一个接口。

3、当NetworkResponse对象收到返回值,通过parseNetworkResponse解析,而解析后的的resposne对象,就会保存在缓存Cache中



public void run() {
        Process.setThreadPriority(10);

        while(true) {
            Request request;
            while(true) {
                try {
                    request = (Request)this.mQueue.take();
                    break;
                } catch (InterruptedException var4) {
                    if(this.mQuit) {
                        return;
                    }
                }
            }

            try {
                request.addMarker("network-queue-take");
                if(request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                } else {
                    if(VERSION.SDK_INT >= 14) {
                        TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
                    }
                   // 发送请求,获取返回值
                    NetworkResponse e = this.mNetwork.performRequest(request);
                    request.addMarker("network-http-complete");
                    if(e.notModified && request.hasHadResponseDelivered()) {
                        request.finish("not-modified");
                    } else {
                        //当NetworkResponse对象收到返回值,通过parseNetworkResponse解析
                        Response response = request.parseNetworkResponse(e);
                        request.addMarker("network-parse-complete");
                        if(request.shouldCache() && response.cacheEntry != null) {
                            //而解析后的的resposne对象,就会保存在缓存Cache中
                            this.mCache.put(request.getCacheKey(), response.cacheEntry);
                            request.addMarker("network-cache-written");
                        }


                        request.markDelivered();
                        //当所有数据处理完后,会交给mDelivery.postResponse()方法,进行解析出来的数据做最后处理。
                        this.mDelivery.postResponse(request, response);
                    }
                }
            } catch (VolleyError var5) {
                this.parseAndDeliverNetworkError(request, var5);
            } catch (Exception var6) {
                VolleyLog.e(var6, "Unhandled exception %s", new Object[]{var6.toString()});
                this.mDelivery.postError(request, new VolleyError(var6));
            }
        }
    }


问:最后一步中我们执行的是mQueue .add(sr)方法作用

答:这个方法很简单,也就是判断请求request添加到那个队列中;


1、点击add()方法,进入源码


 public Request add(Request request) {
        request.setRequestQueue(this);
        Set var2 = this.mCurrentRequests;
        synchronized(this.mCurrentRequests) {
            this.mCurrentRequests.add(request);
        }

        request.setSequence(this.getSequenceNumber());
        request.addMarker("add-to-queue");

        //判断当前的请求是否可以缓存
        if(!request.shouldCache()) {
            //如果不能缓存的话,就只能把request请求添加到加载网络请求的队列里面,上面我们已经讲解过volley的缓存机制。
            this.mNetworkQueue.add(request);
            return request;
        } else {

            Map var7 = this.mWaitingRequests;
            synchronized(this.mWaitingRequests) {
                String cacheKey = request.getCacheKey();
                if(this.mWaitingRequests.containsKey(cacheKey)) {
                    Object stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
                    if(stagedRequests == null) {
                        stagedRequests = new LinkedList();
                    }

                    ((Queue)stagedRequests).add(request);
                    this.mWaitingRequests.put(cacheKey, stagedRequests);
                    if(VolleyLog.DEBUG) {
                        VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[]{cacheKey});
                    }
                } else {
                    this.mWaitingRequests.put(cacheKey, (Object)null);

                    //如果可以缓存,就会把这个request请求,添加到CacheQueue队列里面。
                    //注意,在默认情况下,任何请求都是可以缓存的。
                    this.mCacheQueue.add(request);
                }

                return request;
            }
        }
    }

最后总结:如图01

1、当volley开启网络请求的时候,它内部会开启两个请求队列。一个而是Cachedispatcher,一个是NetworkDispatcher。

2、每一次网络请求的时候,他都会去缓存中判断是否拥有一个这样的请求,如果缓存中存在这个请求,并且没有过期,就可以从缓存中读取这个请求,交给主线程进行网络相应。

3、如果缓存中没有这样一个请求或者请求已经过期,就会从网络请求中获取这个请求,交给networdispatcher来进行异步操作,最后还是会交给主线程进行相应。


五、问:volley图片加载?

答:
  • 1、ImageRequest
  1. 创建一个RequestQueue对象。

  2. 创建一个Request对象。

  3. 将Request对象添加到RequestQueue里面。

  4. 注意:ImageRequest的构造函数接收六个参数,第一个参数就是图片的URL地址,这个没什么需要解释的。第二个参数是图片请求成功的回调,这里我们把返回的Bitmap参数设置到ImageView中。第三第四个参数分别用于指定允许图片最大的宽度和高度,如果指定的网络图片的宽度或高度大于这里的最大值,则会对图片进行压缩,指定成0的话就表示不管图片有多大,都不会进行压缩。第五个参数用于指定图片的颜色属性,Bitmap.Config下的几个常量都可以在这里使用,其中ARGB_8888可以展示最好的颜色属性,每个图片像素占据4个字节的大小,而RGB_565则表示每个图片像素占据2个字节大小。第六个参数是图片请求失败的回调,这里我们当请求失败时在ImageView中显示一张默认图片。


RequestQueue mQueue = Volley.newRequestQueue(context); 


ImageRequest imageRequest = new ImageRequest(  
        "http://developer.android.com/images/home/aw_dac.png",  
        new Response.Listener<Bitmap>() {  
            @Override  
            public void onResponse(Bitmap response) {  
                imageView.setImageBitmap(response);  
            }  
        }, 0, 0, Config.RGB_565, new Response.ErrorListener() {  
            @Override  
            public void onErrorResponse(VolleyError error) {  
                imageView.setImageResource(R.drawable.default_image);  
            }  
        });  
  • 2、ImageLoader用法

1. 创建一个RequestQueue对象。
RequestQueue  mQueue = Volley.newRequestQueue(MainActivity.this);


2. 创建一个ImageLoader对象。

ImageLoader imageLoader = new ImageLoader(mQueue, new ImageCache() {  
    @Override  
    public void putBitmap(String url, Bitmap bitmap) {  
    }  

    @Override  
    public Bitmap getBitmap(String url) {  
        return null;  
    }  
});  

3. 获取一个ImageListener对象。

mageListener listener = ImageLoader.getImageListener(imageView,  
        R.drawable.default_image, R.drawable.failed_image);  


注意:通过调用ImageLoader的getImageListener()方法能够获取到一个ImageListener对象,getImageListener()方法接收三个参数,第一个参数指定用于显示图片的ImageView控件,第二个参数指定加载图片的过程中显示的图片,第三个参数指定加载图片失败的情况下显示的图片。

4. 调用ImageLoader的get()方法加载网络上的图片

第一种:imageLoader.get("https://img-my.csdn.net/uploads/201404/13/1397393290_5765.jpeg", listener);  

第二种:imageLoader.get("https://img-my.csdn.net/uploads/201404/13/1397393290_5765.jpeg",  
                listener, 200, 200);  



  • 3、NetworkImageView的用法

NetworkImageView是一个自定义控制,它是继承自ImageView的,具备ImageView控件的所有功能,并且在原生的基础之上加入了加载网络图片的功能。


流程:

1. 创建一个RequestQueue对象。
2. 创建一个ImageLoader对象。
3. 在布局文件中添加一个NetworkImageView控件。
4. 在代码中获取该控件的实例。
5. 设置要加载的图片地址。


//布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent"  
    android:orientation="vertical" >  

    <Button  
        android:id="@+id/button"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:text="Send Request" />  

    <com.android.volley.toolbox.NetworkImageView   
        android:id="@+id/network_image_view"  
        android:layout_width="200dp"  
        android:layout_height="200dp"  
        android:layout_gravity="center_horizontal"  
        />  

</LinearLayout>



//代码
NetworkImageView networkImageView = (NetworkImageView) findViewById(R.id.network_image_view);  

networkImageView.setDefaultImageResId(R.drawable.default_image);  
networkImageView.setErrorImageResId(R.drawable.failed_image);  
networkImageView.setImageUrl("https://img-my.csdn.net/uploads/201404/13/1397393290_5765.jpeg",  
                imageLoader);  

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值