一、Volley工作流程图:

二、Network
    在NetworkDispatcher中须要处理的网络请求。由以下进行处理:
    NetworkResponse networkResponse = mNetwork.performRequest(request);

看一下mNetwork的定义:(定义在NetworkDispatcher中)

    /** The network interface for processing requests. */
private final Network mNetwork;

NetworkDispatcher.mNetwork初始化发生在RequestQueue.start()中:

    NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);

而RequestQueue.mNetwork是在其构造函数中传入的:

    public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}

由前面分析知RequestQueue的构建是在Volley.newRequestQueue中实现的:

    //创建以stack为參数的Network对象
Network network = new BasicNetwork(stack);
//创建RequestQueue对象
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();//继续向下分析的入口
    能够看出mNetwork事实上是BasicNetwork对象。
 
  则NetworkResponse中mNetwork实际上调用的是BasicNetwork.performRequest(),这是一个专门用来处理网络请求的函数,其作用为调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse
看一下其源代码:
   @Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = new HashMap<String, String>();
try {
/** 忽略网络处理的细节*/
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry()); /**运行网络请求
* 这里调用了HttpStack.performRequest。并得到一个HttpResponse返回结果*/
httpResponse = mHttpStack.performRequest(request, headers); StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders()); /**新奇度验证:
* 304 Not Modified:client有缓冲的文件并发出了一个条件性的请求
* (通常是提供If-Modified-Since头表示客户仅仅想比指定日期更新的文档)。
* server告诉客户。原来缓冲的文档还能够继续使用。*/
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
/** 解析成NetworkResponse,返回*/
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
request.getCacheEntry().data, responseHeaders, true);
} // 推断responses是否有实体信息,一些响应如204,并不包括content。所以须要验证
if (httpResponse.getEntity() != null) {
//实体信息转化成byte[]
responseContents = entityToBytes(httpResponse.getEntity());
} else {
// 无实体信息情况
responseContents = new byte[0];
} // 超时情况处理.
long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
logSlowRequests(requestLifetime, request, responseContents, statusLine); if (statusCode < 200 || statusCode > 299) {
throw new IOException();
}
return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
} catch (SocketTimeoutException e) {
attemptRetryOnException("socket", request, new TimeoutError());
} catch (ConnectTimeoutException e) {
attemptRetryOnException("connection", request, new TimeoutError());
} catch (MalformedURLException e) {
throw new RuntimeException("Bad URL " + request.getUrl(), e);
} catch (IOException e) {
...
}
}
}

总结一下Network.performRequest所做的工作:

1、由传入的HttpStack对象运行网络请求:mHttpStack.performRequest()

2、解析响应结果,将HttpResponse解析成NetworkResponse;

3、对返回结果进行新奇度验证(304)

4、将response的实体信息转化为byte数组

5、超时情况处理,假设发生超时,认证失败等错误。进行重试操作(attemptRetryOnException)。直到成功、抛出异常(不满足重试策略等)结束。

attemptRetryOnException()是依据重试策略进行请求重试操作:

    /**
* Attempts to prepare the request for a retry. If there are no more attempts remaining in the
* request's retry policy, a timeout exception is thrown.
*/
private static void attemptRetryOnException(String logPrefix, Request<? > request,
VolleyError exception) throws VolleyError {
RetryPolicy retryPolicy = request.getRetryPolicy();
int oldTimeout = request.getTimeoutMs(); try {
retryPolicy.retry(exception);
} catch (VolleyError e) {
request.addMarker(
String.format("%s-timeout-giveup [timeout=%s]", logPrefix, oldTimeout));
throw e;
}
request.addMarker(String.format("%s-retry [timeout=%s]", logPrefix, oldTimeout));
}
三、HttpClientStack、HurlStack
    据上面源代码知。网络请求处理的逻辑实际上是交由传进来的參数HttpStack进行处理。

前面已经分析过。Android2.3之前使用 HttpClientStack,之后使用HurlStack。

1、先看两者的父类HttpStack:
    public interface HttpStack {
/**
* Performs an HTTP request with the given parameters.
* <p>A GET request is sent if request.getPostBody() == null. A POST request is sent otherwise,
* and the Content-Type header is set to request.getPostBodyContentType().</p>
* @param request the request to perform
* @param 发起请求之前,加入额外的请求 Headers {@link Request#getHeaders()}
*/
public HttpResponse performRequest(Request<? > request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError;
}

2、HttpClientStack(使用HttpClient来实现)

    @Override
public HttpResponse performRequest(Request<? > request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);//见附一
addHeaders(httpRequest, additionalHeaders);
addHeaders(httpRequest, request.getHeaders());
onPrepareRequest(httpRequest);// Nothing.空函数,用于重写;该函数在request被excute之前被调用
//一些网络设置
HttpParams httpParams = httpRequest.getParams();
int timeoutMs = request.getTimeoutMs();
// TODO: Reevaluate this connection timeout based on more wide-scale
// data collection and possibly different for wifi vs. 3G.
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
return mClient.execute(httpRequest);
}

附一:createHttpRequest函数:

    /**
* 依据传进来的request来构造合适的HttpUriRequest
*/
static HttpUriRequest createHttpRequest(Request<?> request,
Map<String, String> additionalHeaders) throws AuthFailureError {
switch (request.getMethod()) {
case Method.DEPRECATED_GET_OR_POST: {
// This is the deprecated way that needs to be handled for backwards compatibility.
// If the request's post body is null, then the assumption is that the request is
// GET. Otherwise, it is assumed that the request is a POST.
byte[] postBody = request.getPostBody();
if (postBody != null) {
HttpPost postRequest = new HttpPost(request.getUrl());
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
HttpEntity entity;
entity = new ByteArrayEntity(postBody);
postRequest.setEntity(entity);
return postRequest;
} else {
return new HttpGet(request.getUrl());
}
}
/***********一般较多使用的是POST与GET。其等同于HttpClient的一般使用流程***************/
case Method.GET:
return new HttpGet(request.getUrl());
case Method.DELETE:
return new HttpDelete(request.getUrl());
case Method.POST: {
HttpPost postRequest = new HttpPost(request.getUrl());
//这里就看到了前面实现Request时,重写getBodyContentType()函数的意义
postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(postRequest, request);
return postRequest;
}
case Method.PUT: {
HttpPut putRequest = new HttpPut(request.getUrl());
putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
setEntityIfNonEmptyBody(putRequest, request);
return putRequest;
}
default:
throw new IllegalStateException("Unknown request method.");
}
}

3、HurlStack(由HttpURLConnection来实现)

    @Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
throws IOException, AuthFailureError {
String url = request.getUrl();
HashMap<String, String> map = new HashMap<String, String>();
map.putAll(request.getHeaders());
map.putAll(additionalHeaders);
//UrlRewriter见附一
if (mUrlRewriter != null) {
String rewritten = mUrlRewriter.rewriteUrl(url);
if (rewritten == null) {
thrownew IOException("URL blocked by rewriter: " + url);
}
url = rewritten;
}
/**************HttpURLConnection的一般使用流程*******************/
URL parsedUrl = new URL(url);
HttpURLConnection connection = openConnection(parsedUrl, request);
for (String headerName : map.keySet()) {
connection.addRequestProperty(headerName, map.get(headerName));
}
setConnectionParametersForRequest(connection, request);
// Initialize HttpResponse with data from the HttpURLConnection.
ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);
int responseCode = connection.getResponseCode();
if (responseCode == -1) {
// -1 is returned by getResponseCode() if the response code could not be retrieved.
// Signal to the caller that something was wrong with the connection.
thrownew IOException("Could not retrieve response code from HttpUrlConnection.");
}
StatusLine responseStatus = new BasicStatusLine(protocolVersion,
connection.getResponseCode(), connection.getResponseMessage());
BasicHttpResponse response = new BasicHttpResponse(responseStatus);
response.setEntity(entityFromConnection(connection));
for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
if (header.getKey() != null) {
Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
response.addHeader(h);
}
}
return response;
}

附一:UrlRewriter

    /** 对URLs在使用前进行重写转换*/
public interface UrlRewriter {
/**
* Returns a URL to use instead of the provided one, or null to indicate
* this URL should not be used at all.
*/
public String rewriteUrl(String originalUrl);
}

參数mUrlRewriter通过HttpStack的构造函数传入进来,故能够自行进行定义:

    public HurlStack(UrlRewriter urlRewriter, SSLSocketFactory sslSocketFactory) {
mUrlRewriter = urlRewriter;
mSslSocketFactory = sslSocketFactory;
}
四、NetworkResponse
    回到起点NetworkDispatcher(Thread)中的run()函数,当中:

NetworkResponse networkResponse =
mNetwork.performRequest(request);

以下继续看NetworkResponse的源代码:

NetworkResponse类非常easy,仅是用以在多个类中传递数据,其成员变量:
1)成员变量
int statusCode Http
响应状态码

byte[]
data
 Body 数据

Map<String, String> headers 响应 Headers

boolean notModified 表示是否为 304 响应

long networkTimeMs 请求耗时

2)其主体仅仅为几个构造函数:

    public NetworkResponse(int statusCode, byte[] data, Map<String, String> headers,
boolean notModified) {
this.statusCode = statusCode;
this.data = data;
this.headers = headers;
this.notModified = notModified;
} public NetworkResponse(byte[] data) {
this(HttpStatus.SC_OK, data, Collections.<String, String>emptyMap(), false);
} public NetworkResponse(byte[] data, Map<String, String> headers) {
this(HttpStatus.SC_OK, data, headers, false);
}

3)回想一下前面分析的设计NetworkResponse的类之间数据的传递关系:

这里的主体是依据NetworkDispatcher.run()函数进行分析的
0、函数中调用Network.performRequest();
     NetworkResponse networkResponse = mNetwork.performRequest(request);
     而Network.performRequest()是基于HttpStack实现的;
1、HttpClientStack与HurlStack(分别基于HttpClient与HttpURLConnection实现)中的public HttpResponse performRequest()函数返回HttpResponse ;
2、Network(实际为BasicNetwork)中performRequest()方法。使用1中的两个HttpStack类。获取到其返回值HttpResponse,然后将其解析成为NetworkResponse;
3、Request中 abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
    将NetworkResponse解析成Response;
    而该函数的调用是在NetworkDispatcher中的run()函数中调用的。                
4、在NetworkDispatcher.run()的最后一步:
    mDelivery.postResponse(request, response);
    将response传递给了ResponseDelivery
后面继续看Delivery的逻辑;
ResponseDelivery mDelivery的实际类型是ExecutorDelivery:
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
}; }

能够看到非常easy,就是使用主线程的Looper构建一个Handler。以下全部的post操作都是调用这个Handler来运行Runnable;

比方:

@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}

将传递来的Response转化为ResponseDeliveryRunnable ,显然这是一个Runnable;

private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable; public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
mRequest = request;
mResponse = response;
mRunnable = runnable;
} @SuppressWarnings("unchecked")
@Override
public void run() {
// If this request has canceled, finish it and don't deliver.
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
} // Deliver a normal response or error, depending.
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
} // If this is an intermediate response, add a marker, otherwise we're done
// and the request can be finished.
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
} // If we have been provided a post-delivery runnable, run it.
if (mRunnable != null) {
mRunnable.run();
}
} }

在这个子线程中,转而调用 Request来deliverResponse:

以StringRequest为例,来看这个函数:

@Override
protected void deliverResponse(String response) {
if (mListener != null) {
mListener.onResponse(response);
}
}

这个Listener就是自己在定义Request的时候声明的ResponseListener,能够看到这个Listener工作在子线程中,所以假设要更新界面,注意使用Handler把消息传递主线程进行处理。


***************************************************** Volley图片载入的实现 *******************************************************
Volley的图片载入主要还是基于上面的原理来实现的。详细例如以下:
ImageLoader的使用:
//创建ImageLoader
imageLoader = new ImageLoader(httpUtils.getRequestQueue(), imageCache);
public ImageLoader(RequestQueue queue, ImageCache imageCache) {
mRequestQueue = queue;
mCache = imageCache; }
这里面的ImageCache是自己定义的:
// 获取最大内存缓存大小
int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
MAX_CACHE_SIZE = maxMemory / 8; // 定义为应用最大缓存的1/8 mImageLruCache = new LruCache<String, Bitmap>(MAX_CACHE_SIZE){
@Override
protected int sizeOf(String url, Bitmap bitmap){
return bitmap.getRowBytes() * bitmap.getHeight() / 1024;
} }; // 创建ImageCache
imageCache = new ImageLoader.ImageCache() {
@Override
public void putBitmap(String url, Bitmap bitmap) {
mImageLruCache.put(url, bitmap);
} @Override
public Bitmap getBitmap(String url) {
return mImageLruCache.get(url);
}
};

使用LruCache来实现ImageCache接口,实现图片的内存缓存:

public interface ImageCache {
public Bitmap getBitmap(String url);
public void putBitmap(String url, Bitmap bitmap);
}

载入图片时的使用方法:

imageListener = ImageLoader.getImageListener(myImageView, default_pg, failed_pg);
imageLoader.get(imageUrl, imageListener);

来到ImageLoader#get:

public ImageContainer get(String requestUrl, final ImageListener listener) {
return get(requestUrl, listener, 0, 0); } public ImageContainer get(String requestUrl, ImageListener imageListener,
int maxWidth, int maxHeight) {
return get(requestUrl, imageListener, maxWidth, maxHeight, ImageView.ScaleType.CENTER_INSIDE); } public ImageContainer get(String requestUrl, ImageListener imageListener,
int maxWidth, int maxHeight, ImageView.ScaleType scaleType) { // 假设操作不是在主线程,则直接抛出异常
throwIfNotOnMainThread(); // 为图片的URL创建一个特定的cacheKey,注意这个cache还和图片的大小及scaleType相关
final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType); // 这里会使用自己定义的LruCache去获取一个Bitmap实例
Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
// 假设缓存中已经存在。则直接返回
if (cachedBitmap != null) {
// Return the cached bitmap.
ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);
imageListener.onResponse(container, true);
return container;
} // 假设缓存中不存在,则进行获取
ImageContainer imageContainer =
new ImageContainer(null, requestUrl, cacheKey, imageListener); // 通知Observer这时能够使用默认的图片
imageListener.onResponse(imageContainer, true); // 推断是否已经有了一个同样的请求在等待
BatchedImageRequest request = mInFlightRequests.get(cacheKey);
if (request != null) {
// If it is, add this request to the list of listeners.
request.addContainer(imageContainer);
return imageContainer;
} // 创建一个Request,反复之前的流程
Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType,
cacheKey); mRequestQueue.add(newRequest);
mInFlightRequests.put(cacheKey,
new BatchedImageRequest(newRequest, imageContainer));
return imageContainer;
}

处理逻辑大致和前面的addRequest同样。首先推断缓存中是否已经存在该url相应的bitmap。假设存在直接返回;假设不存在,先推断是否已经有了一个同样的请求在等待。假设是,把这个请求加入到监听者链表中。假设不存在,则创建一个Request<Bitmap>,加入到RequestQueue中,从网络中去获取;从网络中获取的流程和前面分析的同样。

先来看Request<Bitmap>:

protected Request<Bitmap> makeImageRequest(String requestUrl, int maxWidth, int maxHeight,
ScaleType scaleType, final String cacheKey) {
return new ImageRequest(requestUrl, new Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
onGetImageSuccess(cacheKey, response);
}
}, maxWidth, maxHeight, scaleType, Config.RGB_565, new ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
onGetImageError(cacheKey, error);
}
});
}

实际上返回一个ImageRequest类型。来看其请求成功的响应:即把获得的图片存储到缓存中。

protected void onGetImageSuccess(String cacheKey, Bitmap response) {
// 把获取到的图片存储到缓存中
mCache.putBitmap(cacheKey, response);
// 能够看到假设是多个同样请求在等待,则能够同一时候进行更新处理
BatchedImageRequest request = mInFlightRequests.remove(cacheKey); if (request != null) {
// Update the response bitmap.
request.mResponseBitmap = response;
// Send the batched response
batchResponse(cacheKey, request);
}
}

最后NetWork运行的结果会封装成NetWorkResponse。通过ResponseDelivery进行转发,这个类最后会调用Request中deliverResponse方法:

@Override
protected void deliverResponse(Bitmap response) {
mListener.onResponse(response);
}

这个Listener就是最初定义的ImageListener:

public static ImageListener getImageListener(final ImageView view,
final int defaultImageResId, final int errorImageResId) {
return new ImageListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (errorImageResId != 0) {
view.setImageResource(errorImageResId);
}
} @Override
public void onResponse(ImageContainer response, boolean isImmediate) {
if (response.getBitmap() != null) {
view.setImageBitmap(response.getBitmap());
} else if (defaultImageResId != 0) {
view.setImageResource(defaultImageResId);
}
}
};
}

能够看到这里终于给View空间设置了图片,以上就是Volley实现图片载入的流程。

Volley简单学习使用五—— 源代码分析三的更多相关文章

  1. Nouveau源代码分析(三):NVIDIA设备初始化之nouveau_drm_probe

    Nouveau源代码分析(三) 向DRM注冊了Nouveau驱动之后,内核中的PCI模块就会扫描全部没有相应驱动的设备,然后和nouveau_drm_pci_table对比. 对于匹配的设备,PCI模 ...

  2. Android 中View的绘制机制源代码分析 三

    到眼下为止,measure过程已经解说完了,今天開始我们就来学习layout过程.只是在学习layout过程之前.大家有没有发现我换了编辑器,哈哈.最终下定决心从Html编辑器切换为markdown编 ...

  3. 【原创】Kakfa utils源代码分析(三)

    Kafka utils包最后一篇~~~ 十五.ShutdownableThread.scala 可关闭的线程抽象类! 继承自Thread同时还接收一个boolean变量isInterruptible表 ...

  4. [Android]Fragment源代码分析(三) 事务

    Fragment管理中,不得不谈到的就是它的事务管理,它的事务管理写的很的出彩.我们先引入一个简单经常使用的Fragment事务管理代码片段: FragmentTransaction ft = thi ...

  5. Java IO 学习(五)跟踪三个文件IO方法的调用链

    假设我们想要用Java读取一个二进制文件,有好几种方式,本文会选取其中比较典型的三种方式进行详细分析 0. 准备工作 安装openjdk-1.8.0.141(普通的jdk中涉及IO的很多代码是闭源的, ...

  6. [转].net reactor 学习系列(五)---源代码加密程序

    .NET Reactor使用教程(加密源代码示例) 1.打开 Eziriz .NET Reactor,主界面如图1所示: 图1 2.单击 Main Assembly 右边的 Open,选择要加密的软件 ...

  7. Elasticsearch学习之深入聚合分析三---案例实战

    1. 统计指定品牌下每个颜色的销量 任何的聚合,都必须在搜索出来的结果数据中进行,搜索结果,就是聚合分析操作的scope GET /tvs/sales/_search { , "query& ...

  8. IBM openblockchain学习(四)--crypto源代码分析

    crypto是blockchain中加密技术功能的实现,当中包含了椭圆曲线加密和SHA256等加密算法等.接下来将对其核心部分进行解析. elliptic 返回加密层中使用的默认椭圆曲线 func G ...

  9. Hadoop源代码分析

    http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...

随机推荐

  1. Hadoop入门进阶步步高(二)-文件夹介绍

    二.Hadoop文件夹结构 这里重点介绍几个文件夹bin.conf及lib文件夹. 1.$HADOOP_HOME/bin文件夹 文件名 说明 hadoop 用于运行hadoop脚本命令,被hadoop ...

  2. CG 内置函数 和 HLSL 内置函数

    CG 内置函数  英伟达官网链接: http://http.developer.nvidia.com/Cg/index_stdlib.html absacosallanyasinatan2atanbi ...

  3. poj--1789--Truck History(prim)

    Truck History Time Limit: 2000MS   Memory Limit: 65536KB   64bit IO Format: %I64d & %I64u Submit ...

  4. 搭建Hadoop的全分布模式

    此教程仅供参考 注意:此文档目的是为了本人方便以后复习,不适合当教程,以免误导萌新... 1.安装三台Linux2.在每台机器上安装JDK3.配置每台机器的免密码登录 (*) 生成每台机器的公钥和私钥 ...

  5. kotlin官方文档-1.0入门

    什么是Kotlin?   图片发自简书App Kotlin是JetBrains开发的基于JVM的语言,JetBrains想必大家应该很熟悉了,他们创造了很多强大的IDE,android studio谷 ...

  6. NodeJS学习笔记 进阶 (12)Nodejs进阶:crypto模块之理论篇

    个人总结:读完这篇文章需要30分钟,这篇文章讲解了使用Node处理加密算法的基础. 摘选自网络 Nodejs进阶:crypto模块之理论篇 一. 文章概述 互联网时代,网络上的数据量每天都在以惊人的速 ...

  7. MySQL 大数据量文本插入

    导入几万条数据需要等好几分钟的朋友来围观一下! 百万条数据插入,只在一瞬间.呵呵夸张,夸张!! 不到半分钟是真的! 插入指令: load data infile 'c:/wamp/tmp/Data_O ...

  8. Linux 基础入门二

    1.远程连接  ssh协议:secure shell  ~]# ss -tnl 查看系统是否监听在tcp协议的22号接口:  ~]# ip addr list 或者 ifconfig 查看ip地址 确 ...

  9. 关于memset赋最值

    出处[辗转山河弋流歌 by 空灰冰魂] blog.csdn.net/vmurder/article/details/46537613 memset(a, 0x3f, sizeof(a)) //int, ...

  10. Unity 编辑器学习(二)之 全局光照(GI)

    光影流年,花影阡陌.光与影交织的岁月教育我们,不会使用光照的程序员不是个好美术. 一.概述 点击 Window > Lighting > Settings 会弹出Lighting窗口,这个 ...