MCPcopy Index your code
hub / github.com/BoBoMEe/Android-Demos

github.com/BoBoMEe/Android-Demos @1.0

Chat with this repo
repository ↗ · DeepWiki ↗ · release 1.0 ↗ · + Follow
122 symbols 271 edges 28 files 28 documented · 23%
What it actually does AI analysis from the code graph — generated when you open this
loading…
README

概述

Retrofit是由Square公司出品的针对于Android和Java的类型安全的Http客户端,网络服务基于OkHttp

实质上就是对okHttp的封装,使用面向接口的方式进行网络请求,利用动态生成的代理类封装了网络接口请求的低层。

变化

如果之前使用过Retrofit1,2.0后的API会有一些变化, 比如创建方式,拦截器,错误处理,转换器等, 参考:更新到Retrofit2的一些技巧

  1. 在Retrofit1中使用的是RestAdapter,而Retrofit2中使用的Retrofit实例,之前的setEndpoint变为了baseUrl。
  2. Retrofit1中使用setRequestInterceptor设置拦截器,对http请求进行相应等处理。
  3. Retrofit2通过OKHttp的拦截器拦截http请求进行监控,重写或重试等,包括日志打印等。
  4. converter,Retrofit1中的setConverter,换以addConverterFactory,用于支持Gson转换。

Retrofit1不能同时操作response返回数据(比如说返回的 Header 部分或者 URL)和序列化后的数据(JAVABEAN), Retrofit1中同步和异步执行同一个方法需要分别定义接口。

更多变化参考:官方CHANGELOG.md

配置

1.9实例:

先来看一下1.9的配置来感受一下变化

//gson converter
       final static Gson gson = new GsonBuilder()
            .setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'")
            .serializeNulls()
            .create();

// client
        OkHttpClient client = new OkHttpClient();
        client.setReadTimeout(12, TimeUnit.SECONDS);

        RestAdapter.Builder builder = new RestAdapter.Builder();
        builder.setClient(new OkClient(client))
               .setLogLevel(RestAdapter.LogLevel.FULL)
               .setEndpoint("https://api.github.com")
               .setConverter(new GsonConverter(gson))
               .setErrorHandler(new ErrorHandler() {
                           @Override
                           public Throwable handleError(RetrofitError cause) {
                               return null;
                           }
                       })
               .setRequestInterceptor(authorizationInterceptor)
        RestAdapter restAdapter = builder.build();
        apiService = restAdapter.create(ApiService.class);

2.0配置

引入依赖

    compile 'com.squareup.retrofit2:retrofit:2.0.2'
    compile 'com.squareup.retrofit2:converter-gson:2.0.2'
    compile 'com.squareup.retrofit2:adapter-rxjava:2.0.2'

    compile 'com.squareup.okhttp3:logging-interceptor:3.2.0'

OkHttp配置

   HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        client = new OkHttpClient.Builder()
                .addInterceptor(interceptor)
                .retryOnConnectionFailure(true)
                .connectTimeout(15, TimeUnit.SECONDS)
                .addNetworkInterceptor(authorizationInterceptor)
                .build();

其中 level 为 BASIC / HEADERS / BODY,BODY等同于1.9中的FULL retryOnConnectionFailure:错误重联 addInterceptor:设置应用拦截器,可用于设置公共参数,头信息,日志拦截等 addNetworkInterceptor:网络拦截器,可以用于重试或重写,对应与1.9中的setRequestInterceptor。 详见:Interceptors 中文翻译:Okhttp-wiki 之 Interceptors 拦截器

Retrofit配置

        Retrofit retrofit = new Retrofit.Builder()
        .baseUrl(BASE_URL)
        .client(client)
        .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build();
        apiService = retrofit.create(ApiService.class);

其中baseUrl相当于1.9中的setEndPoint, addCallAdapterFactory提供RxJava支持,如果没有提供响应的支持(RxJava,Call),则会跑出异常。 addConverterFactory提供Gson支持,可以添加多种序列化Factory,但是GsonConverterFactory必须放在最后。 参考:用 Retrofit 2 简化 HTTP 请求

使用

retrofit2.0后:BaseUrl要以/结尾;@GET 等请求不要以/开头;@Url: 可以定义完整url,不要以 / 开头。

API介绍:

//定以接口
public interface GitHubService {
  @GET("users/{user}/repos")
  Call<List<Repo>> listRepos(@Path("user") String user);
}

//获取实例
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl("https://api.github.com/")
    .build();

GitHubService service = retrofit.create(GitHubService.class);

//同步请求
Call<List<Repo>> call = service.listRepos("octocat");
try {
     Response<List<Repo>> repos  = call.execute();
} catch (IOException e) {
     e.printStackTrace();
}

//call只能调用一次。否则会抛 IllegalStateException
Call<List<Repo>> clone = call.clone();

//异步请求
clone.enqueue(new Callback<List<Repo>>() {
        @Override
        public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
            // Get result bean from response.body()
            String links = response.headers().get("Link");
        }

        @Override
        public void onFailure(Call<List<Repo>> call, Throwable t) {

        }
    });

// 取消
call.cancel();
//rxjava support
public interface GitHubService {
  @GET("users/{user}/repos")
  Observable<List<Repo>> listRepos(@Path("user") String user);
}

// 获取实例
// Http request
Observable<List<Repo>> call = service.listRepos("octocat");

retrofit注解:

方法注解,包含@GET、@POST、@PUT、@DELETE、@PATH、@HEAD、@OPTIONS、@HTTP。 标记注解,包含@FormUrlEncoded、@Multipart、@Streaming。 参数注解,包含@Query,@QueryMap、@Body、@Field,@FieldMap、@Part,@PartMap。

其他注解,@Path、@Header,@Headers、@Url

HTTP可以替代其他方法的任意一种

   /**
     * method 表示请的方法,不区分大小写
     * path表示路径
     * hasBody表示是否有请求体
     */
    @HTTP(method = "get", path = "users/{user}", hasBody = false)
    Call<ResponseBody> getFirstBlog(@Path("user") String user);

@Path:URL占位符,用于替换和动态更新,相应的参数必须使用相同的字符串被@Path进行注释

@GET("group/{id}/users")
Call<List<User>> groupList(@Path("id") int groupId);
//--> http://baseurl/group/groupId/users

//等同于:
@GET
Call<List<User>> groupListUrl(
      @Url String url);

@Query,@QueryMap:查询参数,用于GET查询

@GET("group/users")
Call<List<User>> groupList(@Query("id") int groupId);
//--> http://baseurl/group/users?id=groupId

@Body:用于POST请求体,将实例对象根据转换方式转换为对应的json字符串参数, 这个转化方式是GsonConverterFactory定义的。

 @POST("add")
 Call<List<User>> addUser(@Body User user);

@Field,@FieldMap:Post方式传递简单的键值对, 需要添加@FormUrlEncoded表示表单提交 Content-Type:application/x-www-form-urlencoded

@FormUrlEncoded
@POST("user/edit")
Call<User> updateUser(@Field("first_name") String first, @Field("last_name") String last);

@Part,@PartMap:用于POST文件上传 其中@Part MultipartBody.Part代表文件,@Part("key") RequestBody代表参数 需要添加@Multipart表示支持文件上传的表单,Content-Type: multipart/form-data

 @Multipart
    @POST("register")
    Call<User> registerUser(@Part MultipartBody.Part photo, @Part("username") RequestBody username, @Part("password") RequestBody password);
File file = new File(Environment.getExternalStorageDirectory(), "icon.png");
RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);
MultipartBody.Part photo = MultipartBody.Part.createFormData("photos", "icon.png", photoRequestBody);

Call<User> call = userBiz.registerUser(photo, RequestBody.create(null, "abc"), RequestBody.create(null, "123"));

参考: Retrofit2 完全解析 探索与okhttp之间的关系

@Header:header处理,不能被互相覆盖,用于修饰参数,动态Header值:

@GET("user")
Call<User> getUser(@Header("Authorization") String authorization)

等同于 :

@Headers("Authorization: authorization")//这里authorization就是上面方法里传进来变量的值
@GET("widget/list")
Call<User> getUser()

@Headers 用于修饰方法,固定Header值

多个设置 :

@Headers({
    "Accept: application/vnd.github.v3.full+json",
    "User-Agent: Retrofit-Sample-App"
})

自定义Converter

要自定以Converter,需要先看一下GsonConverterFactory的实现, GsonConverterFactory实现了内部类Converter.Factory。

其中GsonConverterFactory中的主要两个方法,主要用于解析request和response的, 在Factory中还有一个方法stringConverter,用于String的转换。

//主要用于响应体的处理,Factory中默认实现为返回null,表示不处理
 @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonResponseBodyConverter<>(gson, adapter);
  }

/**
  *主要用于请求体的处理,Factory中默认实现为返回null,不能处理返回null
  *作用对象Part、PartMap、Body
  */
  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonRequestBodyConverter<>(gson, adapter);
  }
//Converter.Factory$stringConverter
/**
  *作用对象Field、FieldMap、Header、Path、Query、QueryMap
  *默认处理是toString
  */
  public Converter<?, String> stringConverter(Type type, Annotation[] annotations,
          Retrofit retrofit) {
        return null;
      }

GsonRequestBodyConverter实现了Converter接口, 主要实现了转化的方法

T convert(F value) throws IOException;

自定义StringConverterFactory

缓存策略

Retrofit的低层依赖的是OkHttp,因此设置缓存就需要用到OkHttp的interceptors, 参考:Retrofit2.0+okhttp3缓存机制以及遇到的问题 How Retrofit with OKHttp use cache data when offline 使用Retrofit和Okhttp实现网络缓存。无网读缓存,有网根据过期时间重新请求 如果想要弄清楚缓存机制,则需要了解一下HTTP语义,缓存的设置需要靠请求和响应头。有如下需求

  • 没有网或者网络较差的时候要使用缓存
  • 有网络的时候,为了保证不同的需求,实时性数据,不用缓存。

OkHttp3中有一个Cache类是用来定义缓存的,此类详细介绍了几种缓存策略,具体可看此类源码。

noCache :不使用缓存,全部走网络 noStore : 不使用缓存,也不存储缓存 onlyIfCached : 只使用缓存 maxAge :设置最大失效时间,失效则不使用 maxStale :设置最大失效时间,失效则不使用 minFresh :设置最小有效时间,失效则不使用 FORCE_NETWORK : 强制走网络 FORCE_CACHE :强制走缓存

配置目录

 private static final int HTTP_RESPONSE_DISK_CACHE_MAX_SIZE = 10 * 1024 * 1024;

  private Cache cache() {
         //设置缓存路径
         final File baseDir = AppUtil.getAvailableCacheDir(sContext);
         final File cacheDir = new File(baseDir, "HttpResponseCache");
         //设置缓存 10M
         return new Cache(cacheDir, HTTP_RESPONSE_DISK_CACHE_MAX_SIZE);
     }

其中获取cacahe目录的代码

  public static File getAvailableCacheDir(Context context) {
          //
          File cacheDir = context.getCacheDir();

          //判断SD卡是否可用
          if (hasSDCardMounted()) {

              // 获取SD卡可用空间
              File externalCacheDir = getExternalCacheDir(context);

              long externalUsableSpace = getUsableSpace(externalCacheDir);
              long cacheDirUsableSpace = getUsableSpace(cacheDir);
               //Sd可用空间小于data可用空间时,使用data
              if (externalUsableSpace < cacheDirUsableSpace) {
                  return cacheDir;
              } else {
                  return externalCacheDir;
              }

          } else {
              return cacheDir;
          }
      }

//getExternalCacheDir(context) 

    @TargetApi(Build.VERSION_CODES.FROYO)
    public static File getExternalCacheDir(Context context) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) {
            File path = context.getExternalCacheDir();

            // In some case, even the sd card is mounted,
            // getExternalCacheDir will return null
            // may be it is nearly full.
            if (path != null) {
                return path;
            }
        }

        // Before Froyo or the path is null,
        // we need to construct the external cache folder ourselves
        final String cacheDir = "/Android/data/" + context.getPackageName() + "/cache/";
        return new File(Environment.getExternalStorageDirectory().getPath() + cacheDir);
    }

最后来一张图看懂Android内存结构,参考:Android文件存储使用参考 - liaohuqiu

```java /** * | ($rootDir) * +- /data -> Environment.getDataDirectory() * | | * | | ($appDataDir) * | +- data/$packageName * | | * | | ($filesDir) * | +- files -> Context.getFilesDir() / Context.getFileStreamPath("") * | | | * | | +- file1 -> Context.getFileStreamPath("file1") * | | * | | ($cacheDir) * | +- cache -> Context.getCacheDir() * | | * | +- app_$name ->(Context.getDir(String name, int mode) * | * | ($rootDir) * +- /storage/sdcard0 -> Environment.getExternalStorageDirectory()/ Environment.getExternalStoragePublicDirectory("") * | | * | +- dir1 -> Environment.getExternalStoragePublicDirectory("dir1") * | | * | | ($appDataDir) * | +- Andorid/data/$packageName * | | * | | ($filesDir) * | +- files -> Context.getExternalFilesDir("") * | | | * | | +- file1 -> Context.getExternalFilesDir("file1") * | | +- Music -> Context.getExternalFilesDir(Environment.Music); * | | +- Picture -> Context.getExternalFilesDir(Environment.Picture); * | | +- ... -> Context.getExternalFilesDir(String type) * | | * | | ($cacheDir) * | +- cache -> Context.getExternalCacheDir() * | | * | +- ??? *

 *


 * 1.  其中$appDataDir中的数据,在app卸载之后,会被系统删除。
 *


 * 2.  $appDataDir下的$cacheDir:
 * Context.getCacheDir():机身内存不足时,文件会被删除
 * Context.getExternalCacheDir():空间不足时,文件不会实时被删除,可能返回空对象,Context.getExternalFilesDir("")亦同
 *


 * 3. 内部存储中的$appDataDir是安全的,只有本应用可访问
 * 外部存储中的$appDataDir其他应用也可访问,但是$filesDir中的媒体文件,不会被

Extension points exported contracts — how you extend this code

GithubApi (Interface)
(no doc)
app/src/main/java/com/bobomee/android/retrofit2demo/github/GithubApi.java
Networkreceiver (Interface)
(no doc)
htttp/src/main/java/com/bobomee/android/htttp/util/HttpNetUtil.java

Core symbols most depended-on inside this repo

clear
called by 6
app/src/main/java/com/bobomee/android/retrofit2demo/util/PreferencesHelper.java
create
called by 4
htttp/src/main/java/com/bobomee/android/htttp/retrofit2/converfactory/StringConverterFactory.java
addSubscription
called by 3
app/src/main/java/com/bobomee/android/retrofit2demo/ui/fragment/BaseFragment.java
isConnected
called by 3
htttp/src/main/java/com/bobomee/android/htttp/util/HttpNetUtil.java
contributors
called by 2
app/src/main/java/com/bobomee/android/retrofit2demo/github/GithubApi.java
getGithubApi
called by 2
app/src/main/java/com/bobomee/android/retrofit2demo/github/GithubService.java
attachFragment
called by 2
app/src/main/java/com/bobomee/android/retrofit2demo/ui/MainActivity.java
getAccessToken
called by 2
app/src/main/java/com/bobomee/android/retrofit2demo/util/PreferencesHelper.java

Shape

Method 92
Class 23
Enum 5
Interface 2

Languages

Java100%

Modules by API surface

app/src/main/java/com/bobomee/android/retrofit2demo/ui/fragment/SimplePostFragmeent.java18 symbols
app/src/main/java/com/bobomee/android/retrofit2demo/ui/fragment/SimpleGetFragment.java15 symbols
htttp/src/main/java/com/bobomee/android/htttp/util/HttpNetUtil.java8 symbols
app/src/main/java/com/bobomee/android/retrofit2demo/ui/fragment/BaseFragment.java7 symbols
app/src/main/java/com/bobomee/android/retrofit2demo/github/model/AccessTokenResp.java7 symbols
htttp/src/main/java/com/bobomee/android/htttp/okhttp/interceptor/UserAgentInterceptor.java6 symbols
app/src/main/java/com/bobomee/android/retrofit2demo/util/PreferencesHelper.java6 symbols
app/src/main/java/com/bobomee/android/retrofit2demo/ui/BaseActivity.java6 symbols
app/src/main/java/com/bobomee/android/retrofit2demo/ui/MainActivity.java5 symbols
app/src/main/java/com/bobomee/android/retrofit2demo/github/GithubApi.java5 symbols
htttp/src/main/java/com/bobomee/android/htttp/util/CacheUtil.java3 symbols
htttp/src/main/java/com/bobomee/android/htttp/retrofit2/converfactory/StringConverterFactory.java3 symbols

For agents

$ claude mcp add Android-Demos \
  -- python -m otcore.mcp_server <graph>

⬇ download graph artifact