Retrofit是什么?
一个类型安全的 HTTP 客户端,适用于 Android 和 Java
Retrofit是基于Rsetful风格用于构建网络服务的架构,Rsetful特点如下
- 无状态性:每个请求都包含所有必要信息,服务器不保存客户端状态
- 资源导向:通过 URI 识别和操作资源
- 标准 HTTP 方法:使用 GET、POST、PUT、DELETE 等方法进行操作
- 数据格式:通常使用 JSON 或 XML 进行数据传输
- 可缓存性:响应可以被缓存以提高性能
Retrofit使用
依赖
添加retrofit
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
添加GsonConverterFactory,用于将获取的Json数据转为Java对象
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
权限
<uses-permission android:name="android.permission.INTERNET" />
创建访问接口
如下BASE_URL必须以 / 结尾
public interface GitHubService {
String BASE_URL = "https://api.github.com/";
@GET("users/{user}/repos")
Call<List<Repo>> listRepos(@Path("user") String user);
}
创建单例
用单例管理Retrofit并设置url和转换器
public class RetrofitManager {
private final static RetrofitManager instance = new RetrofitManager();
private final Retrofit mRetrofit;
private RetrofitManager() {
mRetrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
public static RetrofitManager getInstance() {
return instance;
}
public Retrofit getRetrofit() {
return mRetrofit;
}
}
创建返回数据对应的对象
我们将要返回的数据是Json,格式如下
{
"id": 132935648,
"node_id": "MDEwOlJlcG9zaXRvcnkxMzI5MzU2NDg=",
"name": "boysenberry-repo-1",
"full_name": "octocat/boysenberry-repo-1",
"private": false,
"owner": {
"login": "octocat",
"id": 583231,
"node_id": "MDQ6VXNlcjU4MzIzMQ==",
"avatar_url": "https://avatars.githubusercontent.com/u/583231?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/octocat",
"html_url": "https://github.com/octocat",
"followers_url": "https://api.github.com/users/octocat/followers",
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
"organizations_url": "https://api.github.com/users/octocat/orgs",
"repos_url": "https://api.github.com/users/octocat/repos",
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
"received_events_url": "https://api.github.com/users/octocat/received_events",
"type": "User",
"user_view_type": "public",
"site_admin": false
},
"html_url": "https://github.com/octocat/boysenberry-repo-1",
"description": "Testing",
"fork": true,
"url": "https://api.github.com/repos/octocat/boysenberry-repo-1",
"forks_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/forks",
"keys_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/keys{/key_id}",
"collaborators_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/collaborators{/collaborator}",
"teams_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/teams",
"hooks_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/hooks",
"issue_events_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/issues/events{/number}",
"events_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/events",
"assignees_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/assignees{/user}",
"branches_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/branches{/branch}",
"tags_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/tags",
"blobs_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/git/blobs{/sha}",
"git_tags_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/git/tags{/sha}",
"git_refs_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/git/refs{/sha}",
"trees_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/git/trees{/sha}",
"statuses_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/statuses/{sha}",
"languages_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/languages",
"stargazers_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/stargazers",
"contributors_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/contributors",
"subscribers_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/subscribers",
"subscription_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/subscription",
"commits_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/commits{/sha}",
"git_commits_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/git/commits{/sha}",
"comments_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/comments{/number}",
"issue_comment_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/issues/comments{/number}",
"contents_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/contents/{+path}",
"compare_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/compare/{base}...{head}",
"merges_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/merges",
"archive_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/{archive_format}{/ref}",
"downloads_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/downloads",
"issues_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/issues{/number}",
"pulls_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/pulls{/number}",
"milestones_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/milestones{/number}",
"notifications_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/notifications{?since,all,participating}",
"labels_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/labels{/name}",
"releases_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/releases{/id}",
"deployments_url": "https://api.github.com/repos/octocat/boysenberry-repo-1/deployments",
"created_at": "2018-05-10T17:51:29Z",
"updated_at": "2025-03-05T12:57:28Z",
"pushed_at": "2024-05-26T07:02:05Z",
"git_url": "git://github.com/octocat/boysenberry-repo-1.git",
"ssh_url": "git@github.com:octocat/boysenberry-repo-1.git",
"clone_url": "https://github.com/octocat/boysenberry-repo-1.git",
"svn_url": "https://github.com/octocat/boysenberry-repo-1",
"homepage": "",
"size": 4,
"stargazers_count": 314,
"watchers_count": 314,
"language": null,
"has_issues": false,
"has_projects": true,
"has_downloads": true,
"has_wiki": true,
"has_pages": false,
"has_discussions": false,
"forks_count": 18,
"mirror_url": null,
"archived": false,
"disabled": false,
"open_issues_count": 1,
"license": null,
"allow_forking": true,
"is_template": false,
"web_commit_signoff_required": false,
"topics": [],
"visibility": "public",
"forks": 18,
"open_issues": 1,
"watchers": 314,
"default_branch": "master"
}
对应的类如下,手动为其生成toString
public class Repo {
/**
*
*/
private String id;
/**
*
*/
private String nodeId;
/**
*
*/
private String name;
/**
*
*/
private String fullName;
/**
*
*/
private Boolean isPrivate;
/**
*
*/
private Owner owner;
/**
*
*/
private String htmlUrl;
/**
*
*/
private String description;
/**
*
*/
private Boolean fork;
/**
*
*/
private String url;
/**
*
*/
private String forksUrl;
/**
*
*/
private String keysUrl;
/**
*
*/
private String collaboratorsUrl;
/**
*
*/
private String teamsUrl;
/**
*
*/
private String hooksUrl;
/**
*
*/
private String issueEventsUrl;
/**
*
*/
private String eventsUrl;
/**
*
*/
private String assigneesUrl;
/**
*
*/
private String branchesUrl;
/**
*
*/
private String tagsUrl;
/**
*
*/
private String blobsUrl;
/**
*
*/
private String gitTagsUrl;
/**
*
*/
private String gitRefsUrl;
/**
*
*/
private String treesUrl;
/**
*
*/
private String statusesUrl;
/**
*
*/
private String languagesUrl;
/**
*
*/
private String stargazersUrl;
/**
*
*/
private String contributorsUrl;
/**
*
*/
private String subscribersUrl;
/**
*
*/
private String subscriptionUrl;
/**
*
*/
private String commitsUrl;
/**
*
*/
private String gitCommitsUrl;
/**
*
*/
private String commentsUrl;
/**
*
*/
private String issueCommentUrl;
/**
*
*/
private String contentsUrl;
/**
*
*/
private String compareUrl;
/**
*
*/
private String mergesUrl;
/**
*
*/
private String archiveUrl;
/**
*
*/
private String downloadsUrl;
/**
*
*/
private String issuesUrl;
/**
*
*/
private String pullsUrl;
/**
*
*/
private String milestonesUrl;
/**
*
*/
private String notificationsUrl;
/**
*
*/
private String labelsUrl;
/**
*
*/
private String releasesUrl;
/**
*
*/
private String deploymentsUrl;
/**
*
*/
private String createdAt;
/**
*
*/
private String updatedAt;
/**
*
*/
private String pushedAt;
/**
*
*/
private String gitUrl;
/**
*
*/
private String sshUrl;
/**
*
*/
private String cloneUrl;
/**
*
*/
private String svnUrl;
/**
*
*/
private String homepage;
/**
*
*/
private Integer size;
/**
*
*/
private Integer stargazersCount;
/**
*
*/
private Integer watchersCount;
/**
*
*/
private Boolean hasIssues;
/**
*
*/
private Boolean hasProjects;
/**
*
*/
private Boolean hasDownloads;
/**
*
*/
private Boolean hasWiki;
/**
*
*/
private Boolean hasPages;
/**
*
*/
private Boolean hasDiscussions;
/**
*
*/
private Integer forksCount;
/**
*
*/
private Boolean archived;
/**
*
*/
private Boolean disabled;
/**
*
*/
private Integer openIssuesCount;
/**
*
*/
private Boolean allowForking;
/**
*
*/
private Boolean isTemplate;
/**
*
*/
private Boolean webCommitSignoffRequired;
/**
*
*/
private String visibility;
/**
*
*/
private Integer forks;
/**
*
*/
private Integer openIssues;
/**
*
*/
private Integer watchers;
/**
*
*/
private String defaultBranch;
public void setId(String id) {
this.id = id;
}
public String getId() {
return this.id;
}
public void setNodeId(String nodeId) {
this.nodeId = nodeId;
}
public String getNodeId() {
return this.nodeId;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public void setFullName(String fullName) {
this.fullName = fullName;
}
public String getFullName() {
return this.fullName;
}
public void setPrivate(Boolean isPrivate) {
this.isPrivate = isPrivate;
}
public Boolean getPrivate() {
return this.isPrivate;
}
public void setOwner(Owner owner) {
this.owner = owner;
}
public Owner getOwner() {
return this.owner;
}
public void setHtmlUrl(String htmlUrl) {
this.htmlUrl = htmlUrl;
}
public String getHtmlUrl() {
return this.htmlUrl;
}
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return this.description;
}
public void setFork(Boolean fork) {
this.fork = fork;
}
public Boolean getFork() {
return this.fork;
}
public void setUrl(String url) {
this.url = url;
}
public String getUrl() {
return this.url;
}
public void setForksUrl(String forksUrl) {
this.forksUrl = forksUrl;
}
public String getForksUrl() {
return this.forksUrl;
}
public void setKeysUrl(String keysUrl) {
this.keysUrl = keysUrl;
}
public String getKeysUrl() {
return this.keysUrl;
}
public void setCollaboratorsUrl(String collaboratorsUrl) {
this.collaboratorsUrl = collaboratorsUrl;
}
public String getCollaboratorsUrl() {
return this.collaboratorsUrl;
}
public void setTeamsUrl(String teamsUrl) {
this.teamsUrl = teamsUrl;
}
public String getTeamsUrl() {
return this.teamsUrl;
}
public void setHooksUrl(String hooksUrl) {
this.hooksUrl = hooksUrl;
}
public String getHooksUrl() {
return this.hooksUrl;
}
public void setIssueEventsUrl(String issueEventsUrl) {
this.issueEventsUrl = issueEventsUrl;
}
public String getIssueEventsUrl() {
return this.issueEventsUrl;
}
public void setEventsUrl(String eventsUrl) {
this.eventsUrl = eventsUrl;
}
public String getEventsUrl() {
return this.eventsUrl;
}
public void setAssigneesUrl(String assigneesUrl) {
this.assigneesUrl = assigneesUrl;
}
public String getAssigneesUrl() {
return this.assigneesUrl;
}
public void setBranchesUrl(String branchesUrl) {
this.branchesUrl = branchesUrl;
}
public String getBranchesUrl() {
return this.branchesUrl;
}
public void setTagsUrl(String tagsUrl) {
this.tagsUrl = tagsUrl;
}
public String getTagsUrl() {
return this.tagsUrl;
}
public void setBlobsUrl(String blobsUrl) {
this.blobsUrl = blobsUrl;
}
public String getBlobsUrl() {
return this.blobsUrl;
}
public void setGitTagsUrl(String gitTagsUrl) {
this.gitTagsUrl = gitTagsUrl;
}
public String getGitTagsUrl() {
return this.gitTagsUrl;
}
public void setGitRefsUrl(String gitRefsUrl) {
this.gitRefsUrl = gitRefsUrl;
}
public String getGitRefsUrl() {
return this.gitRefsUrl;
}
public void setTreesUrl(String treesUrl) {
this.treesUrl = treesUrl;
}
public String getTreesUrl() {
return this.treesUrl;
}
public void setStatusesUrl(String statusesUrl) {
this.statusesUrl = statusesUrl;
}
public String getStatusesUrl() {
return this.statusesUrl;
}
public void setLanguagesUrl(String languagesUrl) {
this.languagesUrl = languagesUrl;
}
public String getLanguagesUrl() {
return this.languagesUrl;
}
public void setStargazersUrl(String stargazersUrl) {
this.stargazersUrl = stargazersUrl;
}
public String getStargazersUrl() {
return this.stargazersUrl;
}
public void setContributorsUrl(String contributorsUrl) {
this.contributorsUrl = contributorsUrl;
}
public String getContributorsUrl() {
return this.contributorsUrl;
}
public void setSubscribersUrl(String subscribersUrl) {
this.subscribersUrl = subscribersUrl;
}
public String getSubscribersUrl() {
return this.subscribersUrl;
}
public void setSubscriptionUrl(String subscriptionUrl) {
this.subscriptionUrl = subscriptionUrl;
}
public String getSubscriptionUrl() {
return this.subscriptionUrl;
}
public void setCommitsUrl(String commitsUrl) {
this.commitsUrl = commitsUrl;
}
public String getCommitsUrl() {
return this.commitsUrl;
}
public void setGitCommitsUrl(String gitCommitsUrl) {
this.gitCommitsUrl = gitCommitsUrl;
}
public String getGitCommitsUrl() {
return this.gitCommitsUrl;
}
public void setCommentsUrl(String commentsUrl) {
this.commentsUrl = commentsUrl;
}
public String getCommentsUrl() {
return this.commentsUrl;
}
public void setIssueCommentUrl(String issueCommentUrl) {
this.issueCommentUrl = issueCommentUrl;
}
public String getIssueCommentUrl() {
return this.issueCommentUrl;
}
public void setContentsUrl(String contentsUrl) {
this.contentsUrl = contentsUrl;
}
public String getContentsUrl() {
return this.contentsUrl;
}
public void setCompareUrl(String compareUrl) {
this.compareUrl = compareUrl;
}
public String getCompareUrl() {
return this.compareUrl;
}
public void setMergesUrl(String mergesUrl) {
this.mergesUrl = mergesUrl;
}
public String getMergesUrl() {
return this.mergesUrl;
}
public void setArchiveUrl(String archiveUrl) {
this.archiveUrl = archiveUrl;
}
public String getArchiveUrl() {
return this.archiveUrl;
}
public void setDownloadsUrl(String downloadsUrl) {
this.downloadsUrl = downloadsUrl;
}
public String getDownloadsUrl() {
return this.downloadsUrl;
}
public void setIssuesUrl(String issuesUrl) {
this.issuesUrl = issuesUrl;
}
public String getIssuesUrl() {
return this.issuesUrl;
}
public void setPullsUrl(String pullsUrl) {
this.pullsUrl = pullsUrl;
}
public String getPullsUrl() {
return this.pullsUrl;
}
public void setMilestonesUrl(String milestonesUrl) {
this.milestonesUrl = milestonesUrl;
}
public String getMilestonesUrl() {
return this.milestonesUrl;
}
public void setNotificationsUrl(String notificationsUrl) {
this.notificationsUrl = notificationsUrl;
}
public String getNotificationsUrl() {
return this.notificationsUrl;
}
public void setLabelsUrl(String labelsUrl) {
this.labelsUrl = labelsUrl;
}
public String getLabelsUrl() {
return this.labelsUrl;
}
public void setReleasesUrl(String releasesUrl) {
this.releasesUrl = releasesUrl;
}
public String getReleasesUrl() {
return this.releasesUrl;
}
public void setDeploymentsUrl(String deploymentsUrl) {
this.deploymentsUrl = deploymentsUrl;
}
public String getDeploymentsUrl() {
return this.deploymentsUrl;
}
public void setCreatedAt(String createdAt) {
this.createdAt = createdAt;
}
public String getCreatedAt() {
return this.createdAt;
}
public void setUpdatedAt(String updatedAt) {
this.updatedAt = updatedAt;
}
public String getUpdatedAt() {
return this.updatedAt;
}
public void setPushedAt(String pushedAt) {
this.pushedAt = pushedAt;
}
public String getPushedAt() {
return this.pushedAt;
}
public void setGitUrl(String gitUrl) {
this.gitUrl = gitUrl;
}
public String getGitUrl() {
return this.gitUrl;
}
public void setSshUrl(String sshUrl) {
this.sshUrl = sshUrl;
}
public String getSshUrl() {
return this.sshUrl;
}
public void setCloneUrl(String cloneUrl) {
this.cloneUrl = cloneUrl;
}
public String getCloneUrl() {
return this.cloneUrl;
}
public void setSvnUrl(String svnUrl) {
this.svnUrl = svnUrl;
}
public String getSvnUrl() {
return this.svnUrl;
}
public void setHomepage(String homepage) {
this.homepage = homepage;
}
public String getHomepage() {
return this.homepage;
}
public void setSize(Integer size) {
this.size = size;
}
public Integer getSize() {
return this.size;
}
public void setStargazersCount(Integer stargazersCount) {
this.stargazersCount = stargazersCount;
}
public Integer getStargazersCount() {
return this.stargazersCount;
}
public void setWatchersCount(Integer watchersCount) {
this.watchersCount = watchersCount;
}
public Integer getWatchersCount() {
return this.watchersCount;
}
public void setHasIssues(Boolean hasIssues) {
this.hasIssues = hasIssues;
}
public Boolean getHasIssues() {
return this.hasIssues;
}
public void setHasProjects(Boolean hasProjects) {
this.hasProjects = hasProjects;
}
public Boolean getHasProjects() {
return this.hasProjects;
}
public void setHasDownloads(Boolean hasDownloads) {
this.hasDownloads = hasDownloads;
}
public Boolean getHasDownloads() {
return this.hasDownloads;
}
public void setHasWiki(Boolean hasWiki) {
this.hasWiki = hasWiki;
}
public Boolean getHasWiki() {
return this.hasWiki;
}
public void setHasPages(Boolean hasPages) {
this.hasPages = hasPages;
}
public Boolean getHasPages() {
return this.hasPages;
}
public void setHasDiscussions(Boolean hasDiscussions) {
this.hasDiscussions = hasDiscussions;
}
public Boolean getHasDiscussions() {
return this.hasDiscussions;
}
public void setForksCount(Integer forksCount) {
this.forksCount = forksCount;
}
public Integer getForksCount() {
return this.forksCount;
}
public void setArchived(Boolean archived) {
this.archived = archived;
}
public Boolean getArchived() {
return this.archived;
}
public void setDisabled(Boolean disabled) {
this.disabled = disabled;
}
public Boolean getDisabled() {
return this.disabled;
}
public void setOpenIssuesCount(Integer openIssuesCount) {
this.openIssuesCount = openIssuesCount;
}
public Integer getOpenIssuesCount() {
return this.openIssuesCount;
}
public void setAllowForking(Boolean allowForking) {
this.allowForking = allowForking;
}
public Boolean getAllowForking() {
return this.allowForking;
}
public void setIsTemplate(Boolean isTemplate) {
this.isTemplate = isTemplate;
}
public Boolean getIsTemplate() {
return this.isTemplate;
}
public void setWebCommitSignoffRequired(Boolean webCommitSignoffRequired) {
this.webCommitSignoffRequired = webCommitSignoffRequired;
}
public Boolean getWebCommitSignoffRequired() {
return this.webCommitSignoffRequired;
}
public void setVisibility(String visibility) {
this.visibility = visibility;
}
public String getVisibility() {
return this.visibility;
}
public void setForks(Integer forks) {
this.forks = forks;
}
public Integer getForks() {
return this.forks;
}
public void setOpenIssues(Integer openIssues) {
this.openIssues = openIssues;
}
public Integer getOpenIssues() {
return this.openIssues;
}
public void setWatchers(Integer watchers) {
this.watchers = watchers;
}
public Integer getWatchers() {
return this.watchers;
}
public void setDefaultBranch(String defaultBranch) {
this.defaultBranch = defaultBranch;
}
public String getDefaultBranch() {
return this.defaultBranch;
}
@Override
public String toString() {
return "Repo{" +
"id='" + id + '\'' +
'}';
}
}
public class Owner implements Serializable {
/**
*
*/
private String login;
/**
*
*/
private Integer id;
/**
*
*/
private String nodeId;
/**
*
*/
private String avatarUrl;
/**
*
*/
private String gravatarId;
/**
*
*/
private String url;
/**
*
*/
private String htmlUrl;
/**
*
*/
private String followersUrl;
/**
*
*/
private String followingUrl;
/**
*
*/
private String gistsUrl;
/**
*
*/
private String starredUrl;
/**
*
*/
private String subscriptionsUrl;
/**
*
*/
private String organizationsUrl;
/**
*
*/
private String reposUrl;
/**
*
*/
private String eventsUrl;
/**
*
*/
private String receivedEventsUrl;
/**
*
*/
private String type;
/**
*
*/
private String userViewType;
/**
*
*/
private Boolean siteAdmin;
public void setLogin(String login) {
this.login = login;
}
public String getLogin() {
return this.login;
}
public void setId(Integer id) {
this.id = id;
}
public Integer getId() {
return this.id;
}
public void setNodeId(String nodeId) {
this.nodeId = nodeId;
}
public String getNodeId() {
return this.nodeId;
}
public void setAvatarUrl(String avatarUrl) {
this.avatarUrl = avatarUrl;
}
public String getAvatarUrl() {
return this.avatarUrl;
}
public void setGravatarId(String gravatarId) {
this.gravatarId = gravatarId;
}
public String getGravatarId() {
return this.gravatarId;
}
public void setUrl(String url) {
this.url = url;
}
public String getUrl() {
return this.url;
}
public void setHtmlUrl(String htmlUrl) {
this.htmlUrl = htmlUrl;
}
public String getHtmlUrl() {
return this.htmlUrl;
}
public void setFollowersUrl(String followersUrl) {
this.followersUrl = followersUrl;
}
public String getFollowersUrl() {
return this.followersUrl;
}
public void setFollowingUrl(String followingUrl) {
this.followingUrl = followingUrl;
}
public String getFollowingUrl() {
return this.followingUrl;
}
public void setGistsUrl(String gistsUrl) {
this.gistsUrl = gistsUrl;
}
public String getGistsUrl() {
return this.gistsUrl;
}
public void setStarredUrl(String starredUrl) {
this.starredUrl = starredUrl;
}
public String getStarredUrl() {
return this.starredUrl;
}
public void setSubscriptionsUrl(String subscriptionsUrl) {
this.subscriptionsUrl = subscriptionsUrl;
}
public String getSubscriptionsUrl() {
return this.subscriptionsUrl;
}
public void setOrganizationsUrl(String organizationsUrl) {
this.organizationsUrl = organizationsUrl;
}
public String getOrganizationsUrl() {
return this.organizationsUrl;
}
public void setReposUrl(String reposUrl) {
this.reposUrl = reposUrl;
}
public String getReposUrl() {
return this.reposUrl;
}
public void setEventsUrl(String eventsUrl) {
this.eventsUrl = eventsUrl;
}
public String getEventsUrl() {
return this.eventsUrl;
}
public void setReceivedEventsUrl(String receivedEventsUrl) {
this.receivedEventsUrl = receivedEventsUrl;
}
public String getReceivedEventsUrl() {
return this.receivedEventsUrl;
}
public void setType(String type) {
this.type = type;
}
public String getType() {
return this.type;
}
public void setUserViewType(String userViewType) {
this.userViewType = userViewType;
}
public String getUserViewType() {
return this.userViewType;
}
public void setSiteAdmin(Boolean siteAdmin) {
this.siteAdmin = siteAdmin;
}
public Boolean getSiteAdmin() {
return this.siteAdmin;
}
}
使用过程
- 获取Retrofit调用create()创建接口实例
- 调用接口的方法获取Call实例
- 调用Call中异步的enqueue()注册回调处理成功和失败情景
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
List<Repo> mRepoList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Retrofit retrofit = RetrofitManager.getInstance().getRetrofit();
GitHubService gitHubService = retrofit.create(GitHubService.class);
Call<List<Repo>> repos = gitHubService.listRepos("octocat");
repos.enqueue(new Callback<List<Repo>>() {
@Override
public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
int code = response.code();
if (code == HttpURLConnection.HTTP_OK) {
mRepoList = response.body();
if (mRepoList == null || mRepoList.isEmpty()) {
Log.d(TAG, "onResponse: null or empty");
} else {
for (Repo repo : mRepoList) {
Log.d(TAG, "onResponse: repo = " + repo);
}
}
} else {
Log.d(TAG, "onResponse: code = " + code);
}
}
@Override
public void onFailure(Call<List<Repo>> call, Throwable t) {
Log.d(TAG, "onFailure: " + t);
}
});
}
}
Retrofit源码解析
Retrofit.Bulider
- 无参Build设置Platform为子类Android
- 通过baseUrl()设置url
- callFactory未设置时,默认为OkHttpClient
- callbackExecutor未设置时,默认为platform.defaultCallbackExecutor()
- callAdapterFactories无论是否设置,都需要加上platform.defaultCallAdapterFactories()
- converterFactories默认添加BuiltInConverters(),还有设置的converterFactories、platform.defaultConverterFactories()
public final class Retrofit {
final okhttp3.Call.Factory callFactory;
final HttpUrl baseUrl;
final List<Converter.Factory> converterFactories;
final List<CallAdapter.Factory> callAdapterFactories;
final @Nullable Executor callbackExecutor;
final boolean validateEagerly;
Retrofit(
okhttp3.Call.Factory callFactory,
HttpUrl baseUrl,
List<Converter.Factory> converterFactories,
List<CallAdapter.Factory> callAdapterFactories,
@Nullable Executor callbackExecutor,
boolean validateEagerly) {
this.callFactory = callFactory;
this.baseUrl = baseUrl;
this.converterFactories = converterFactories; // Copy+unmodifiable at call site.
this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.
this.callbackExecutor = callbackExecutor;
this.validateEagerly = validateEagerly;
}
......
public static final class Builder {
private final Platform platform;
private @Nullable okhttp3.Call.Factory callFactory;
private @Nullable HttpUrl baseUrl;
private final List<Converter.Factory> converterFactories = new ArrayList<>();
private final List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>();
private @Nullable Executor callbackExecutor;
private boolean validateEagerly;
......
public Builder() {
this(Platform.get());
}
public Builder baseUrl(URL baseUrl) {
Objects.requireNonNull(baseUrl, "baseUrl == null");
return baseUrl(HttpUrl.get(baseUrl.toString()));
}
public Builder baseUrl(String baseUrl) {
Objects.requireNonNull(baseUrl, "baseUrl == null");
return baseUrl(HttpUrl.get(baseUrl));
}
public Builder baseUrl(HttpUrl baseUrl) {
Objects.requireNonNull(baseUrl, "baseUrl == null");
List<String> pathSegments = baseUrl.pathSegments();
if (!"".equals(pathSegments.get(pathSegments.size() - 1))) {
throw new IllegalArgumentException("baseUrl must end in /: " + baseUrl);
}
this.baseUrl = baseUrl;
return this;
}
public Builder addConverterFactory(Converter.Factory factory) {
converterFactories.add(Objects.requireNonNull(factory, "factory == null"));
return this;
}
......
public Retrofit build() {
if (baseUrl == null) {
throw new IllegalStateException("Base URL required.");
}
okhttp3.Call.Factory callFactory = this.callFactory;
if (callFactory == null) {
callFactory = new OkHttpClient();
}
Executor callbackExecutor = this.callbackExecutor;
if (callbackExecutor == null) {
callbackExecutor = platform.defaultCallbackExecutor();
}
List<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);
callAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));
List<Converter.Factory> converterFactories =
new ArrayList<>(
1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());
converterFactories.add(new BuiltInConverters());
converterFactories.addAll(this.converterFactories);
converterFactories.addAll(platform.defaultConverterFactories());
return new Retrofit(
callFactory,
baseUrl,
unmodifiableList(converterFactories),
unmodifiableList(callAdapterFactories),
callbackExecutor,
validateEagerly);
}
}
}
Platform
根据虚拟机判断当前平台
class Platform {
private static final Platform PLATFORM = findPlatform();
static Platform get() {
return PLATFORM;
}
private static Platform findPlatform() {
return "Dalvik".equals(System.getProperty("java.vm.name"))
? new Android() //
: new Platform(true);
}
......
List<? extends CallAdapter.Factory> defaultCallAdapterFactories(
@Nullable Executor callbackExecutor) {
DefaultCallAdapterFactory executorFactory = new DefaultCallAdapterFactory(callbackExecutor);
return hasJava8Types
? asList(CompletableFutureCallAdapterFactory.INSTANCE, executorFactory)
: singletonList(executorFactory);
}
}
Android
defaultCallbackExecutor()返回MainThreadExecutor(),其通过主线程Looper创建Handler将Runnable提交回主线程运行
static final class Android extends Platform {
Android() {
super(Build.VERSION.SDK_INT >= 24);
}
@Override
public Executor defaultCallbackExecutor() {
return new MainThreadExecutor();
}
static final class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable r) {
handler.post(r);
}
}
}
Retrofit
create()通过反射创建接口的代理对象
- 若是Java8且是默认方法则调用platform.invokeDefaultMethod()
- 否则调用loadServiceMethod()处理注解生成ServiceMethod,再调用ServiceMethod的invoke()
public final class Retrofit {
private final Map<Method, ServiceMethod<?>> serviceMethodCache = new ConcurrentHashMap<>();
public <T> T create(final Class<T> service) {
validateServiceInterface(service);
return (T)
Proxy.newProxyInstance(
service.getClassLoader(),
new Class<?>[] {service},
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override
public @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)
throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
args = args != null ? args : emptyArgs;
return platform.isDefaultMethod(method)
? platform.invokeDefaultMethod(method, service, proxy, args)
: loadServiceMethod(method).invoke(args);
}
});
}
ServiceMethod<?> loadServiceMethod(Method method) {
ServiceMethod<?> result = serviceMethodCache.get(method);
if (result != null) return result;
synchronized (serviceMethodCache) {
result = serviceMethodCache.get(method);
if (result == null) {
result = ServiceMethod.parseAnnotations(this, method);
serviceMethodCache.put(method, result);
}
}
return result;
}
}
loadServiceMethod()先从缓存中获取,若没有则加锁,调用ServiceMethod.parseAnnotations()处理注解,并放入缓存
ServiceMethod
调用RequestFactory.parseAnnotations处理注解,返回HttpServiceMethod.parseAnnotations()
abstract class ServiceMethod<T> {
static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {
RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);
......
return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);
}
abstract @Nullable T invoke(Object[] args);
}
RequestFactory
RequestFactory中Builder的build()调用parseMethodAnnotation()处理注解
- @DELETE:从服务器中删除资源
- @GET:从服务器中获取资源
- @PATCH:
- @POST:在服务器中新建资源
- @PUT:在服务器中更新资源
- @OPTIONS
final class RequestFactory {
static RequestFactory parseAnnotations(Retrofit retrofit, Method method) {
return new Builder(retrofit, method).build();
}
......
static final class Builder {
......
Builder(Retrofit retrofit, Method method) {
this.retrofit = retrofit;
this.method = method;
this.methodAnnotations = method.getAnnotations();
this.parameterTypes = method.getGenericParameterTypes();
this.parameterAnnotationsArray = method.getParameterAnnotations();
}
......
RequestFactory build() {
for (Annotation annotation : methodAnnotations) {
parseMethodAnnotation(annotation);
}
.......
int parameterCount = parameterAnnotationsArray.length;
parameterHandlers = new ParameterHandler<?>[parameterCount];
for (int p = 0, lastParameter = parameterCount - 1; p < parameterCount; p++) {
parameterHandlers[p] =
parseParameter(p, parameterTypes[p], parameterAnnotationsArray[p], p == lastParameter);
}
......
return new RequestFactory(this);
}
private void parseMethodAnnotation(Annotation annotation) {
if (annotation instanceof DELETE) {
parseHttpMethodAndPath("DELETE", ((DELETE) annotation).value(), false);
} else if (annotation instanceof GET) {
parseHttpMethodAndPath("GET", ((GET) annotation).value(), false);
} else if (annotation instanceof HEAD) {
parseHttpMethodAndPath("HEAD", ((HEAD) annotation).value(), false);
} else if (annotation instanceof PATCH) {
parseHttpMethodAndPath("PATCH", ((PATCH) annotation).value(), true);
} else if (annotation instanceof POST) {
parseHttpMethodAndPath("POST", ((POST) annotation).value(), true);
} else if (annotation instanceof PUT) {
parseHttpMethodAndPath("PUT", ((PUT) annotation).value(), true);
} else if (annotation instanceof OPTIONS) {
parseHttpMethodAndPath("OPTIONS", ((OPTIONS) annotation).value(), false);
} else if (annotation instanceof HTTP) {
HTTP http = (HTTP) annotation;
parseHttpMethodAndPath(http.method(), http.path(), http.hasBody());
} else if (annotation instanceof retrofit2.http.Headers) {
String[] headersToParse = ((retrofit2.http.Headers) annotation).value();
if (headersToParse.length == 0) {
throw methodError(method, "@Headers annotation is empty.");
}
headers = parseHeaders(headersToParse);
} else if (annotation instanceof Multipart) {
if (isFormEncoded) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isMultipart = true;
} else if (annotation instanceof FormUrlEncoded) {
if (isMultipart) {
throw methodError(method, "Only one encoding annotation is allowed.");
}
isFormEncoded = true;
}
}
private void parseHttpMethodAndPath(String httpMethod, String value, boolean hasBody) {
......
this.httpMethod = httpMethod;
this.hasBody = hasBody;
if (value.isEmpty()) {
return;
}
int question = value.indexOf('?');
if (question != -1 && question < value.length() - 1) {
String queryParams = value.substring(question + 1);
Matcher queryParamMatcher = PARAM_URL_REGEX.matcher(queryParams);
......
}
this.relativeUrl = value;
this.relativeUrlParamNames = parsePathParameters(value);
}
}
}
HttpServiceMethod
parseAnnotations()生成ServiceMethod对象,创建responseConverter,判断isKotlinSuspendFunction
- 为false返回CallAdapted
- 为true且continuationWantsResponse为true返回SuspendForResponse,否则返回SuspendForBody,暂不分析
abstract class HttpServiceMethod<ResponseT, ReturnT> extends ServiceMethod<ReturnT> {
static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(
Retrofit retrofit, Method method, RequestFactory requestFactory) {
boolean isKotlinSuspendFunction = requestFactory.isKotlinSuspendFunction;
boolean continuationWantsResponse = false;
boolean continuationBodyNullable = false;
Annotation[] annotations = method.getAnnotations();
Type adapterType;
if (isKotlinSuspendFunction) {
Type[] parameterTypes = method.getGenericParameterTypes();
Type responseType =
Utils.getParameterLowerBound(
0, (ParameterizedType) parameterTypes[parameterTypes.length - 1]);
if (getRawType(responseType) == Response.class && responseType instanceof ParameterizedType) {
responseType = Utils.getParameterUpperBound(0, (ParameterizedType) responseType);
continuationWantsResponse = true;
} else {
}
adapterType = new Utils.ParameterizedTypeImpl(null, Call.class, responseType);
annotations = SkipCallbackExecutorImpl.ensurePresent(annotations);
} else {
adapterType = method.getGenericReturnType();
}
CallAdapter<ResponseT, ReturnT> callAdapter =
createCallAdapter(retrofit, method, adapterType, annotations);
Type responseType = callAdapter.responseType();
......
Converter<ResponseBody, ResponseT> responseConverter =
createResponseConverter(retrofit, method, responseType);
okhttp3.Call.Factory callFactory = retrofit.callFactory;
if (!isKotlinSuspendFunction) {
return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);
} else if (continuationWantsResponse) {
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForResponse<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter);
} else {
return (HttpServiceMethod<ResponseT, ReturnT>)
new SuspendForBody<>(
requestFactory,
callFactory,
responseConverter,
(CallAdapter<ResponseT, Call<ResponseT>>) callAdapter,
continuationBodyNullable);
}
}
......
@Override
final @Nullable ReturnT invoke(Object[] args) {
Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
return adapt(call, args);
}
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);
}
HttpServiceMethod的invoke()生成OkHttpCall作为参数,并调用子类的adapt()
CallAdapted
将CallAdapter适配为HttpServiceMethod
static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
private final CallAdapter<ResponseT, ReturnT> callAdapter;
CallAdapted(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, ReturnT> callAdapter) {
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
return callAdapter.adapt(call);
}
}
SuspendForResponse(暂不分析)
static final class SuspendForResponse<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
SuspendForResponse(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, Call<ResponseT>> callAdapter) {
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
}
@Override
protected Object adapt(Call<ResponseT> call, Object[] args) {
call = callAdapter.adapt(call);
Continuation<Response<ResponseT>> continuation =
(Continuation<Response<ResponseT>>) args[args.length - 1];
try {
return KotlinExtensions.awaitResponse(call, continuation);
} catch (Exception e) {
return KotlinExtensions.suspendAndThrow(e, continuation);
}
}
}
SuspendForBody(暂不分析)
static final class SuspendForBody<ResponseT> extends HttpServiceMethod<ResponseT, Object> {
private final CallAdapter<ResponseT, Call<ResponseT>> callAdapter;
private final boolean isNullable;
SuspendForBody(
RequestFactory requestFactory,
okhttp3.Call.Factory callFactory,
Converter<ResponseBody, ResponseT> responseConverter,
CallAdapter<ResponseT, Call<ResponseT>> callAdapter,
boolean isNullable) {
super(requestFactory, callFactory, responseConverter);
this.callAdapter = callAdapter;
this.isNullable = isNullable;
}
@Override
protected Object adapt(Call<ResponseT> call, Object[] args) {
call = callAdapter.adapt(call);
Continuation<ResponseT> continuation = (Continuation<ResponseT>) args[args.length - 1];
try {
return isNullable
? KotlinExtensions.awaitNullable(call, continuation)
: KotlinExtensions.await(call, continuation);
} catch (Exception e) {
return KotlinExtensions.suspendAndThrow(e, continuation);
}
}
}
CallAdapter
具体的CallAdapter由Factory的get()返回
public interface CallAdapter<R, T> {
Type responseType();
T adapt(Call<R> call);
abstract class Factory {
public abstract @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit);
protected static Type getParameterUpperBound(int index, ParameterizedType type) {
return Utils.getParameterUpperBound(index, type);
}
protected static Class<?> getRawType(Type type) {
return Utils.getRawType(type);
}
}
}
DefaultCallAdapterFactory
- 在Retrofit.Build的build()中,若未设置callAdapterFactories,则默认为platform.defaultCallAdapterFactories(),其返回DefaultCallAdapterFactory
- 参数callbackExecutor为Platform的子类Android的内部类MainThreadExecutor
final class DefaultCallAdapterFactory extends CallAdapter.Factory {
private final @Nullable Executor callbackExecutor;
DefaultCallAdapterFactory(@Nullable Executor callbackExecutor) {
this.callbackExecutor = callbackExecutor;
}
@Override
public @Nullable CallAdapter<?, ?> get(
Type returnType, Annotation[] annotations, Retrofit retrofit) {
if (getRawType(returnType) != Call.class) {
return null;
}
.....
final Type responseType = Utils.getParameterUpperBound(0, (ParameterizedType) returnType);
final Executor executor =
Utils.isAnnotationPresent(annotations, SkipCallbackExecutor.class)
? null
: callbackExecutor;
return new CallAdapter<Object, Call<?>>() {
@Override
public Type responseType() {
return responseType;
}
@Override
public Call<Object> adapt(Call<Object> call) {
return executor == null ? call : new ExecutorCallbackCall<>(executor, call);
}
};
}
static final class ExecutorCallbackCall<T> implements Call<T> {
final Executor callbackExecutor;
final Call<T> delegate;
ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
this.callbackExecutor = callbackExecutor;
this.delegate = delegate;
}
@Override
public void enqueue(final Callback<T> callback) {
Objects.requireNonNull(callback, "callback == null");
delegate.enqueue(
new Callback<T>() {
@Override
public void onResponse(Call<T> call, final Response<T> response) {
callbackExecutor.execute(
() -> {
if (delegate.isCanceled()) {
// Emulate OkHttp's behavior of throwing/delivering an IOException on
// cancellation.
callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
} else {
callback.onResponse(ExecutorCallbackCall.this, response);
}
});
}
@Override
public void onFailure(Call<T> call, final Throwable t) {
callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
}
});
}
@Override
public boolean isExecuted() {
return delegate.isExecuted();
}
@Override
public Response<T> execute() throws IOException {
return delegate.execute();
}
@Override
public void cancel() {
delegate.cancel();
}
@Override
public boolean isCanceled() {
return delegate.isCanceled();
}
......
}
}
当客户端调用Calle的enqueue()时
- 将委托给DefaultCallAdapterFactory中匿名内部类ExecutorCallbackCall中的delegate,其是在HttpServiceMethod的invoke()中创建并传入的OkHttpCall
- 当OkHttpCall的enqueue()结束后通过Callback回调callbackExecutor.execute()创建Runnable回调onFailure()和onResponse(),而callbackExecutor.execute()委托给了主线程的handler.post()
3382

被折叠的 条评论
为什么被折叠?



