版权声明:本文为博主原创文章,未经我的允许不得转载!
转载请标明出处: http://blog.csdn.net/guiying712/article/details/74427258 ,本文出自:【张华洋的博客】


  • 1、URL的简单构成
  • 2、Http中GET和POST的区别
  • 3、Retrofit 概览
    • 3.1、Url配置
    • 3.2、Retrofit注解
    • 3.3、请求参数注解的使用方法
    • 3.4、Converter.Factory 数据转换工厂
  • 4、使用Retrofit进行网络请求
  • 5、Retrofit二次封装–Builder模式
    • 4.1 Builder模式的定义
    • 4.2 使用Builder的目的:
    • 4.3 具体的封装过程
      • 先创建一个HttpClient类,然后创建一个Builder内部类,代码如下:
      • 给HttpClient创建私有的构造器,初始化OkHttpClient和Retrofit,代码如下:
      • 建立POST和GET方法,代码如下:
      • 最后是正真去执行网络请求的方法,代码如下:
      • 建一个接口,把请求网络的结果回调:
      • 当然我们还要有传参的方法,service中通用方法的封装,下面是封装retrofit的service:

1、URL的简单构成

URL(Uniform Resource Locator)定义:统一资源定位符是对可以从互联网上得到的资源的位置和访问方法的一种简洁的表示,是互联网上标准资源的地址。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。URL构成一般是这样的:

[ scheme : ][ //authority ][ path ][ ? query ]

名称 功能
schema 访问服务器以获取资源时要使用哪种协议,比如,http,https和FTP等
host HTTP服务器的IP地址或域名
port HTTP服务器的默认端口是80,这种情况下端口号可以省略,如果使用了别的端口,必须指明,例如http://www.cnblogs.com:8080
authority 将host和Port用冒号连接组成authority ,authority = host:port
path 访问资源的路径
query 发给 http 服务器的数据

URL的例子:

http://www.cnblogs.com:8080/yourpath/mdeditor?stove=10&path=1&id=6

  • scheme : http
  • authority : www.cnblogs.com:8080
  • path : /yourpath/mdeditor
  • query : 在 后的部分为 stove=10&path=1&id=6

因为 authority又进一步可以划分为 host:port形式,其中 host:port 用冒号分割,冒号前的是host,冒号后是port,所以:
- host : www.cnblogs.com
- port : 8080

上面讲的都是URL中的参数,除了URL中的参数还有两个HTTP协议中常用的参数是:header (请求头)和body(常用于post请求中的请求提,有多种封装方法,不暴露在URL中)这两个参数。

综上:可以看出整个网络请求中的参数主要可以分为:scheme、authority、path、query、header、body 这6块。


2、Http中GET和POST的区别

HTTP定义了与服务器交互的不同方法,最基本的方法有四种,分别是GET、POST、PUT、DELETE。一个URL地址,它用于描述一个网络上的资源,而HTTP中的GET,POST,PUT,DELETE就对应着对这个资源的查,改,增,删4个操作。
1、GET请求: GET是向服务器发起索取数据的一种请求,用于获取信息而非修改信息;GET请求的数据会附在URL之后(就是把数据放置在HTTP协议头中),以 ? 分割 URL 和 传输数据,参数之间以 & 相连,例如:

login?name=hyddd&password=idontknow&verify=%E4%BD%A0%E5%A5;

%XX中的XX为该符号以16进制表示的ASCII,如果数据是英文字母/数字,原样发送,如果是空格,转换为+,如果是中文/其他字符,则直接把字符串用BASE64加密

2、POST请求: POST是向服务器提交数据的一种请求,POST请求可能会导致新的资源的建立和/或已有资源的修改; POST把提交的数据则放置在是HTTP包的包体中。


3、Retrofit 概览

Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。这里没有说它是网络请求框架,主要原因在于网络请求的工作并不是 Retrofit 来完成的,因为Retrofit 2.0 开始内置 OkHttp,前者专注于接口的封装,后者专注于网络请求的高效,二者分工协作。

应用程序通过 Retrofit 请求网络,实际上是使用 Retrofit 接口层封装请求参数、Header、Url 等信息,之后由 OkHttp 完成后续的请求操作,在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,后者根据用户的需求对结果进行解析的过程。

3.1、Url配置

Retrofit 支持的协议包括 GET/POST/PUT/DELETE/HEAD/PATCH,这些协议均以注解的形式进行配置,例如GET的用法:

网络请求地址: http://gank.io/api/data/福利/10/1

public interface GirlsService {@GET("api/data/{type}/{count}/{page}")Call<ResponseBody> getGirls(@Path("type") String type, @Path("count") int count, @Path("page") int page);}

这些注解都有一个参数value,用来配置其路径,比如示例中的”api/data/{type}/{count}/{page}”,@GET中所填写的value和baseUrl组成一个完整 的路径,baseUrl会在构造Retrofit对象时给出,@GET注解中使用{type}、{count}、{page}声明了访问路径,可以把{type}、{count}、{page}当做占位符,实际运行中会通过@PATH(“type”)所标注的参数进行替换。

3.2、Retrofit注解

Retrofit通过使用注解来简化请求,大体分为以下几类:
1. 用于标注请求方式的注解
2. 用于标记请求头的注解
3. 用于标记请求参数的注解
4. 用于标记请求和响应格式的注解

1、请求方法注解

注解 说明
@GET get请求
@POST post请求
@PUT put请求
@DELETE delete请求
@PATCH patch请求,该请求是对put请求的补充,用于更新局部资源
@HEAD head请求
@OPTIONS option请求
@HTTP 通用注解,可以替换以上所有的注解,其拥有三个属性:method,path,hasBody

2、请求头注解

注解 说明
@Headers 用于添加固定请求头,可以同时添加多个。通过该注解添加的请求头不会相互覆盖,而是共同存在
@Header 作为方法的参数传入,用于添加不固定值的Header,该注解会更新已有的请求头

3、请求参数注解

注解 说明
@Body 多用于post请求发送非表单数据,比如想要以post方式传递json格式数据
@Filed 多用于post请求中表单字段,Filed和FieldMap需要FormUrlEncoded结合使用
@FiledMap 和@Filed作用一致,用于不确定表单参数
@Part 用于表单字段,Part和PartMap与Multipart注解结合使用,适合文件上传的情况
@PartMap 用于表单字段,默认接受的类型是 Map,可用于实现多文件上传
@Path 用于url中的占位符
@Query 用于Get中指定参数
@QueryMap 和Query使用类似
@Url 指定请求路径

4、请求和响应格式注解

注解 说明
@FormUrlEncoded 表示请求发送编码表单数据,每个键值对需要使用@Field注解
@Multipart 表示请求发送multipart数据,需要配合使用@Part
@Streaming 表示响应用字节流的形式返回,如果没使用该注解,默认会把数据全部载入到内存中,该注解在在下载大文件的特别有用

3.3、请求参数注解的使用方法

1、@Path:动态的url访问(这里只列举GET请求,其他请求一样处理)

网络请求地址: http://gank.io/api/data/Android/10/1

Path 分为带参数和不带参数两种情况:

不带参数的情况:

public interface GirlsService {@GET("api/data/Android/10/1")Call<ResponseBody> getGirls();
}

带参数的情况:

public interface GirlsService {@GET("api/data/{type}/{count}/{page}")Call<ResponseBody> getGirls(@Path("type") String type, @Path("count") int count, @Path("page") int page);
}

带参数就是在path路径中的type、count以及page是可以改变的,例如:http://gank.io/api/data/福利/10/1
1. 其中“福利”就是type,而且这个值还有其他选择,如Android、视频等;
2. 其中“10”就是count,表示每次请求的个数,可以随意替换;
3. 其中“1”就是page,代表页码,根据请求次数依次叠加;

2、@Query 和@QueryMap:查询参数的设置

网络请求地址 :https://api.github.com/users/whatever?client_id=xxxx&client_secret=yyyy

@Query的使用方法:

public interface UserService {@GET("users/whatever")Call<ResponseBody> getUser(@Query("client_id") String id, @Query("client_secret") String secret)
}

访问代码:

Call<ResponseBody> call = retrofit.create(UserService.class).getUser("zhangsan", "12345@");

这样我们就完成了参数的指定,当然相同的方式也适用于POST,只需要把注解修改为@POST即可。

但是在实际的应用场景中,我们每次网络请求很少会只有一个查询参数,这时候就需要能够携带多个Query的@QueryMap:

@QueryMap的使用方法:

public interface UserService {@GET("users/whatever")Call<ResponseBody> getUser(@QueryMap Map<String, String> info)
}

访问代码:

    Map<String, String> params = new HashMap<>()params.put("client_id", "zhangsan");params.put("client_secret", "12345@");Call<ResponseBody> call = retrofit.create(UserService.class).getUser(params);

3、@Body:以POST请求体的方式向服务器上传 json 字符串

网络请求地址 :https://api.github.com/login

应用程序跟服务器通信,很多时候会选择直接使用POST方式将 json 字符串作为请求体发送到服务器,使用方法如下:

先定义个实体类User:

public class User{public String name;public String password;public User(String name, String password) {this.name= name;this.password= password;}}

@Body的使用方法:

public interface LoginService{@POST("login")Call<ResponseBody> login(@Body User user);
}

访问代码:

     Call<ResponseBody> call =  retrofit.create(LoginService.class).login(new User("zhangsan", "12345@"))

其实就是使用@Body这个注解标识我们的参数对象即可。

但是我们平时并不会这样写,因为每次请求都要创建一个实体类,例如访问代码中的User,这样就会很麻烦,因为我们POST的是 json 字符串,所以我们可以这样写:

我们将@Body原来声明的参数User 改成了OKhttp中的RequestBody,并且使用 @Headers声明了请求头的参数类型:

public interface LoginService{@Headers({"Content-Type:application/json", "Accept:application/json"})@POST("login")Call<ResponseBody> login(@Body RequestBody body);
}

访问代码:

        JSONObject requestObject = new JSONObject();try {requestObject .put("client_id", "zhangsan");requestObject .put("client_secret", "12345@");} catch (Exception e) {e.printStackTrace();}RequestBody requestBody = RequestBody.create(okhttp3.MediaType.parse("application/json;charset=utf-8"), requestObject.toString());Call<ResponseBody> call =  retrofit.create(LoginService.class).login(requestBody);

首先创建一个JSONObject对象,然后将参数put到JSONObject对象中,再将这个JSONObject对象转换成我们需要的参数RequestBody ,然后就可以访问了。

4、@Field 和 @FieldMap:以表单的方式传递键值对 @FormUrlEncoded

网络请求地址 :https://api.github.com/login

其实我们用 POST 的场景相对较多,绝大多数的服务端接口都需要做加密、鉴权和校验,GET 显然不能很好的满足这个需求,使用 POST 提交表单的场景就更是刚需了;

定义一个方法:

public interface LoginService{@POST("login")@FormUrlEncodedCall<ResponseBody> login(@Field("username") String username, @Field("password") String password);
}

访问的代码:

    Call<ResponseBody> call = retrofit.create(LoginService.class).login("zhangsan", "12345@");

首先通过@POST指明Url,并且添加@FormUrlEncoded,然后通过@Field 声明了表单的项。

如果你不确定表单项的个数,还有能够携带多个Field的FieldMap:

定义一个方法:

    public interface LoginService{@FormUrlEncoded@POST("login")Call<ResponseBody> login(@FieldMap Map<String, String> map);}

访问的代码:

    Map<String, String> params = new ArrayMap<>()params.put("username", "zhangsan");params.put("password", "12345@");Call<ResponseBody> call = retrofit.create(LoginService.class).login(params);

5、@Part & 和 PartMap:文件上传 @Multipart

单文件上传方法:

    public interface FileUploadService {  @Multipart@POST("upload")Call<ResponseBody> upload(@Part("description") RequestBody description,@Part MultipartBody.Part file);@Multipart@POST("register")Call<ResponseBody> register(@PartMap Map<String, RequestBody> params,  @Part("password") RequestBody password);}

单文件上传访问代码:

     //先创建 serviceFileUploadService service = retrofit.create(FileUploadService.class);//构建要上传的文件File file = new File(filename);RequestBody requestFile = RequestBody.create(MediaType.parse("application/otcet-stream"), file);MultipartBody.Part body =MultipartBody.Part.createFormData("aFile", file.getName(), requestFile);String descriptionString = "This is a description";RequestBody description = RequestBody.create(MediaType.parse("multipart/form-data"), descriptionString);Call<ResponseBody> call = service.upload(description, body);call.enqueue(new Callback<ResponseBody>() {@Overridepublic void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {System.out.println("success");}@Overridepublic void onFailure(Call<ResponseBody> call, Throwable t) {t.printStackTrace();}});

多文件上传访问代码:

     //多文件上传File file = new File(Environment.getExternalStorageDirectory(), "messenger_01.png");RequestBody photo = RequestBody.create(MediaType.parse("image/png", file);Map<String, RequestBody> photos = new HashMap<>();photos.put("photos", photo);photos.put("username",  RequestBody.create(null, "abc"));Call<User> call = service.register(photos, RequestBody.create(null, "12345@"));

6、@Url :指定请求路径

网络请求地址: http://gank.io/api/data/Android/10/1

由于使用@Path 会存在 url 被转义的情况,但是使用 @Url 就不存在这个问题;

定义一个方法:

public interface ApiService {@GETCall<ResponseBody> getGirls(@Url String url);
}

访问的代码:

    Call<ResponseBody> call = retrofit.create(ApiService .class).getGirls("api/data/Android/10/1");

7、@Header 和@HeaderMap以及@Headers :添加请求头

动态添加单个请求头:

public interface UserService {@GET("users/whatever")Call<ResponseBody> getUser(@Header("Token") String Token, @Url String url)
}

动态添加多个请求头:

public interface UserService {@POSTCall<ResponseBody> executePostHeaders(@HeaderMap Map<String, String> headers, @Url String url, @Body RequestBody body);
}

静态添加请求头:

public interface UserService {@Headers({"Content-Type:application/json", "Accept:application/json"})@POSTCall<ResponseBody> executePostJson(@Header("Token") String Token, @Url String url, @Body RequestBody body);
}

3.4、Converter.Factory 数据转换工厂

给Retrofit增加数据解析功能:

         Retrofit retrofit = new Retrofit.Builder().baseUrl("http://gank.io/").addConverterFactory(GsonConverterFactory.create()).build();

这里添加了gson转化工厂:addConverterFactory(GsonConverterFactory.create() ),Retrofit内部会根据这个转换工厂及返回数据所指定的泛型实现直接转换;Retrofit也支持自带如下格式:

  1. Gson: com.squareup.retrofit2:converter-gson
  2. Jackson: com.squareup.retrofit2:converter-jackson
  3. Moshi: com.squareup.retrofit2:converter-moshi
  4. Protobuf: com.squareup.retrofit2:converter-protobuf
  5. Wire: com.squareup.retrofit2:converter-wire
  6. Simple XML: com.squareup.retrofit2:converter-simplexml
  7. Scalars (primitives, boxed, and String): com.squareup.retrofit2:converter-scalars

4、使用Retrofit进行网络请求

我们以GET请求为例,实现一个完整的网络请求;

1、首先创建具体网络请求方法

public interface ApiService {@GET("api/data/{type}/{count}/{page}")Call<ResponseBody> getGirls(@Path("type") String type, @Path("count") int count, @Path("page") int page);}

注意:如果 Call 的泛型指定的类不是ResponseBody,Retrofit会将返回的 string 用 json 转换器自动转换该类的一个对象,转换不成功就报错;如果不需要Gson转换,那么就指定泛型为ResponseBody,而且只能是ResponseBody,子类都不行。

2、然后创建一个Retrofit实例

  OkHttpClient client = new OkHttpClient.Builder().connectTimeout(3, TimeUnit.SECONDS).writeTimeout(3, TimeUnit.SECONDS).readTimeout(3, TimeUnit.SECONDS).build();Retrofit retrofit = new Retrofit.Builder().baseUrl("http://gank.io/").addConverterFactory(GsonConverterFactory.create()).client(client ).build();

这里的baseUrl就是网络请求URL相对固定的地址,一般包括请求协议(如Http)、域名或IP地址、端口号等;还有addConverterFactory方法表示需要用什么转换器来解析返回值,GsonConverterFactory.create()表示调用Gson库来解析json返回值;client用于传入具体的OkHttpClient;

3、获取定义的接口实例

通过 Retrofit.create 就可以拿到我们定义的 ApiService 的实例,

  ApiService girlsService = retrofit.create(ApiService.class);

4、调用请求方法,并得到 Call 实例

  Call<ResponseBody> girlsCall = girlsService.getGirls("福利", size, page);

Call 其实在Retrofit中就是行使网络请求并处理返回值的类,调用的时候把需要拼接的参数传递进去,最后得到的完整Url地址为:

http://gank.io/api/data/福利/10/1

5、使用Call实例完成同步或异步请求

同步请求

Response<ResponseBody> response = girlsCall.execute().body();

需要注意的是同步网络请求一定要在子线程中完成,不能直接在UI线程执行,不然会crash;

异步请求

    girlsCall.enqueue(new Callback<ResponseBody>() {@Overridepublic void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {}@Overridepublic void onFailure(Call<GirlsBean> call, Throwable t) {}});

6、取消这个请求

Call 提供了cancel 方法可以取消请求,前提是该请求还没有执行;

girlsCall.cancel();  

5、Retrofit二次封装–Builder模式

4.1 Builder模式的定义

将一个复杂对象的构建和表示分离,使得同样的构建过程可以创建不同的表示。

4.2 使用Builder的目的:

当我们请求网络的时候,需要一系列的参数,包括路径和请求参数,请求头,设置超时时间等等,考虑到以后可能会改,比如添加SSL判断,或者要使用gzip等,为了提高他的可拓展性,使用建造者模式可以让外部调用post方法以后,当内部逻辑改变时,不用去修改。直接在Builder类进行添加新的变量,并在post方法内部进行逻辑更改就好,外部使用者不会受到影响。

具体实现后的使用方法:

  private void getData() {HttpClient client = new HttpClient.Builder().baseUrl("https://10.33.31.200:8890/").url("msp/mobile/login").params("username", "admin").params("password", "B213CEAC2F1022023EF2699AA62599CF").params("passwordLevel", "0").params("captcha", "").params("captchaKey", "").params("loginAddr", "10.33.31.200").params("mac", "6c:5c:14:8a:72:12").build();client.get(new HttpClient.OnResultListener() {@Overridepublic void onSuccess(String result) {Log.d("HttpClient", result);}@Overridepublic void onFailure(String message) {Log.d("HttpClient", message);}});

4.3 具体的封装过程

先创建一个HttpClient类,然后创建一个Builder内部类,代码如下:
   public static final class Builder {private String baseUrl = BASE_URL;private String url;private Map<String, String> params = new ArrayMap<>();public Builder() {}public Builder baseUrl(String baseUrl) {this.baseUrl = baseUrl;return this;}public Builder url(String url) {this.url = url;return this;}public Builder params(String key, String value) {this.params.put(key, value);return this;}public HttpClient build() {BASE_URL = baseUrl;HttpClient client = HttpClient.getIns();client.setBuilder(this);return client;}
}
给HttpClient创建私有的构造器,初始化OkHttpClient和Retrofit,代码如下:
    private HttpClient() {HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(MyApplication.getIntstance(), new int[0], R.raw.ivms8700, STORE_PASS);OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(7000, TimeUnit.MILLISECONDS).sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager).hostnameVerifier(HttpsUtils.getHostnameVerifier()).build();retrofit = new Retrofit.Builder().baseUrl(BASE_URL).client(okHttpClient).build();}
建立POST和GET方法,代码如下:
public synchronized void get(final OnResultListener onResultListener) {Builder builder = mBuilder;if (!builder.params.isEmpty()) {String value = "";for (Map.Entry<String, String> entry : builder.params.entrySet()) {String mapKey = entry.getKey();String mapValue = entry.getValue();String span = value.equals("") ? "" : "&";String part = StringUtil.buffer(span, mapKey, "=", mapValue);value = StringUtil.buffer(value, part);}builder.url(StringUtil.buffer(builder.url, "?", value));}mCall = retrofit.create(HttpParams.class).params(builder.url);request(builder, onResultListener);
}public synchronized void post(final OnResultListener onResultListener) {Builder builder = mBuilder;mCall = retrofit.create(HttpParams.class).params(builder.url, builder.params);request(builder, onResultListener);
}
最后是正真去执行网络请求的方法,代码如下:
 private void request(final Builder builder, final OnResultListener onResultListener) {mCall.enqueue(new Callback<ResponseBody>() {@Overridepublic void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {Log.d("HttpClient", "Call<ResponseBody> Request onSuccess=====>" + call.request());int code = response.code();if (code == 200) {String result = null;try {result = response.body().string();Log.d("HttpClient", "Http Response=====>" + result);} catch (IOException e) {e.printStackTrace();}onResultListener.onSuccess(result);return;}onResultListener.onFailure(response.toString());}@Overridepublic void onFailure(Call<ResponseBody> call, Throwable t) {Log.d("HttpClient", "Call<ResponseBody> Request onFailure=====>" + call.request());t.printStackTrace();onResultListener.onFailure(t.getMessage());}});
}
建一个接口,把请求网络的结果回调:
public interface OnResultListener {void onSuccess(String result);void onFailure(String message);
}
当然我们还要有传参的方法,service中通用方法的封装,下面是封装retrofit的service:

既然要封装,肯定就不能用retrofit的常规用法:ApiService接口里每个接口文档上的接口都写一个方法,而是应该用QueryMap/FieldMap注解,接受一个以Map形式封装好的键值对。

注意:如果方法的泛型指定的类不是ResonseBody,retrofit会将返回的string成用json转换器自动转换该类的一个对象,转换不成功就报错.

public interface HttpParams {/*** POST方式将json字符串作为请求体发送到服务器* 其中@FormUrlEncoded 以表单的方式传递键值对* 其中 @Path:所有在网址中的参数(URL的问号前面)* 另外@FieldMap 用于POST请求,提交多个表单数据,@Field:用于POST请求,提交单个数据* 其中@Path(value = "filePath", encoded = true) String filePath是为了防止URL被转义为https://10.33.31.200:8890/msp%2Fmobile%2Flogin%3FloginAddr=10.33.31.200*/
@FormUrlEncoded
@POST("{dataPath}")
Call<ResponseBody> params(@Path(value = "dataPath", encoded = true) String dataPath,@FieldMap Map<String, String> param);@GET
Call<ResponseBody> params(@Url String url);@Multipart
@POST("upload")
Call<ResponseBody> upload(@Part("description") RequestBody description,@Part MultipartBody.Part file);
}

这样一个retrofit的二次封装库算是做好了。

点击源码查看详细使用方法:HttpClient

使用Retrofit2封装适用于组件化项目的网络库相关推荐

  1. Android组件化实践2——经典组件化项目架构

    Android组件化实践2--经典组件化项目架构 京东的采用aab( Android App bundles)之后的架构图 安居客项目架构演化 参考:https://zhuanlan.zhihu.co ...

  2. 组件化开发 ——— 制作私有库

    组件化开发 --- 制作私有库 上篇文章大致说了组件化优缺点及内容,现在就针对公共基础组件制作私有库的问题继续搞起来. 我们的原则是:将一个项目组件化拆分掉,一般会拆分一些基础组件.一些功能组件和业务 ...

  3. 模块化封装和组件化封装

    1.概念 组件化是把重复代码进行封装,例如util类的封装. 模块化是指把一组功能进行隔离,可以独立运行,独立管理,例如login(登录)和home(首页). 组件化开发的原则 专一 可配置性 标准性 ...

  4. android两个项目依赖关系图,关于android:Android组件化项目搭建遇到的问题记录

    1. ARouter 的依赖问题 What went wrong: Execution failed for task ':app:kaptDebugKotlin'. A failure occurr ...

  5. android 组件导出,阳光沙滩-android组件化项目,build生成的文件重复的情况

    1.最近弄一个组件化的项目练手.项目第一次run可以跑起来,等我杀死stop项目后,过一段时间在再次run想跑项目,就会报如下错误 这个component_base_back 3.xml文件是第二次r ...

  6. automake生成静态库文件_基于CocoaPods的组件化原理及私有库实践

    轮子为什么会存在 智人能在残酷的进化大战中存活下来,原因之一就是智人懂得将知识沉淀成外物,辅助彼此之间的合作,从而使得整个群体产生了规模效应,即1+1>2的效果. 从一个角度上说,石器时代是基于 ...

  7. 京东金融Vue组件化项目实战(目前最新)

    下载地址:百度网盘

  8. 京东金融Vue组件化项目实战(完整)

    下载地址:百度网盘

  9. 拥抱组件化开发,手淘项目内部架构分享

    前两天技术群里一个小伙伴突然问: 不知道你有没有遇到过,每次过年回家 或 相亲都会遇到这样的段子 亲戚 (或未来丈母娘) : 你现在干啥呢? 我:我是做Android的,换种方式说 ,是做app.做手 ...

最新文章

  1. GMM-HMM语音识别模型 原理篇
  2. 【小白学习keras教程】三、Kears中常见模型层Padding、Conv2D、MaxPooling2D、Flatten和Dense
  3. Net4.0的网站在IE10、IE11出现“__doPostBack未定义”的解决办法。
  4. oracle中文字段名怎么查询_sql注入联合查询总结
  5. 约瑟夫环问题 poj 1012 poj 2244
  6. 读取excel数据,根据word模板生成word文件。【python】【word vba】两种方法
  7. 在xp3下,Apache , PHP, Zend Studio怎样配置环境?
  8. RedHat7安装yum并下载gcc
  9. PyQt5多个GUI界面设计
  10. 阿里云服务器实现内网互通
  11. 小米手机WIFI显示已连接,但无法访问互联网,新路由器其他设备都能连
  12. 高效记忆/形象记忆(07)英语单词记忆-熟词拆分
  13. 跨品种套利 - 期货
  14. 语音转换成文字要怎么做呢?
  15. 笛卡尔坐标系中八个卦限对应的位置
  16. H5飞翔的小鸟游戏微信小程序源码
  17. day7.总结 列表、字典
  18. 中国人,怎样毁了 祖传中医
  19. buu [AFCTF2018]One Secret, Two encryption
  20. objc_msgSend流程分析之缓存查找

热门文章

  1. matlab中refcurve,利用matlab中自带的的perfcurve函数评价多分类
  2. 闪电网络简介Lightning Network
  3. Linux计划任务介绍
  4. 10-1枚举类的使用
  5. #打卡day1 ROS talker/listener
  6. 如何在一台计算机上使用两个网络,电脑一拖二显示器怎么用?一台主机两个显示器的连接设置方法...
  7. 如何卸载Oracle 10g
  8. Android 检测输入键盘是否弹起
  9. 明确不站队BAT,帆软——这家只有几百人的BI公司靠什么赢得生存?
  10. 设计模式之禅——模板方法模式(Template Method Pattern)