한 걸음 두 걸음
android 안드로이드 ] 레드로핏 Retrofit2 실습 _ 서버 없이 본문
https://onepinetwopine.tistory.com/262 이론부분 포스팅입니다.
Retrofit 실습
이 실습은
첫 번째로 자신이 가진 서버 없이 사용해보기
두 번째로 자신이 구현한 서버로 사용(https://onepinetwopine.tistory.com/512)해보기로%ED%95%B4%EB%B3%B4%EA%B8%B0%EB%A1%9C) 구성하였습니다.
환경설정
Manifest에
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
를 추가해줍니다.
HTTP통신이니 인터넷에 접속하기 위한 접근 권한을 허락해주는 것입니다.
Gradle(:APP)에
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
를 추가해줍니다.
여기서 Gson, Jackson, Moshi 등은 다른 유형을 지원하기 위한 변환기입니다. 여섯 모듈은 사용자의 편의를 위해 인기 직렬화 라이브러리를 적용시킬 수 있습니다.
- Gson:com.squareup.retrofit2:converter-gson
- Jackson:com.squareup.retrofit2:converter-jackson
- Moshi:com.squareup.retrofit2:converter-moshi
- Protobuf:com.squareup.retrofit2:converter-protobuf
- Wire:com.squareup.retrofit2:converter-wire
- Simple XML:com.squareup.retrofit2:converter-simplexml
Gson은 Java 객체를 JSON 표현으로 변환하는 데 사용할 수있는 Java 라이브러리입니다. 저는 GSON Converter를 추가해주도록 하겠습니다. (레퍼런스 https://github.com/google/gson) 참고로 2019.12월 현재 레트로핏은 2.6.2, GSON은 2.8.6 버전까지 나와있습니다.
이를 사용하는 이유는 직렬화 / 역직렬화 때문입니다. 데이터를 전송할 때 객체를 연속적인 데이터스트림으로 변형하여 보내도록 합니다. 역직렬화는 반대로 스트림으로 온 데이터를 다시 객체로 복원시키는 것입니다.
이는 Serializable인터페이스를 상속받음으로써 객체를 직렬화할 수 있게 됩니다.(transient를 이용하면 직렬화에서 일부 제외합니다.) 단, 클래스 내부에서 타 클래스 객체를 사용하는 경우 타 클래스도 Serializable한 클래스여야합니다. 이는 https://docs.oracle.com/javase/9/docs/api/java/math/package-summary.html 에서 별도로 확인하셔야합니다. 커스텀클래스라면 Serializable시켜주면 되겠죠?)
여기까지 Retrofit을 위한 환경을 설정하고 본격적으로 코딩을 해볼께요.
01. 개인서버없이 Retrofit 실습하기
최대한 간단하게 실습을 해보겠습니다ㅎㅎ 안드로이드스튜디오를 켜기만 해도 만날 수 있는 Hello world를 클릭하면 Retrofit2로 데이터를 GET방식으로 받아오도록 만들겠습니다. 이 때 사용할 서버는 github의 api입니다. 제가 만든 서버를 활용하는 것이 아니므로, 다른 서버를 사용해야겠죠? github api는 호출시 JSON으로 응답합니다.
https://api.github.com/
https://api.github.com/repos/square/retrofit/contributors
를 호출하면 github에서 제공하는 api의 모든 url을 확인할 수 있도록 json을 받을 수 있습니다.(들어가보시면 확인하실 수 있어요~) 이를 이용해볼께요.
MainActivity.java
코드 먼저 전체 적고 설명하겠습니다.
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView t = (TextView)findViewById(R.id.textview_retrofit);
t.setOnClickListener(v->{
GitHubService gitHubService = GitHubService.retrofit.create(GitHubService.class);
final Call<List<Contributor>> call = gitHubService.repoContributors("square", "retrofit");
new NetworkCall().execute(call);
});
}
private class NetworkCall extends AsyncTask<Call, Void, String> {
@Override
protected String doInBackground(Call... params) {
try {
Call<List<Contributor>> call = params[0];
Response<List<Contributor>> response = call.execute();
return response.body().toString();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
@Override
protected void onPostExecute(String result) {
final TextView textView = (TextView) findViewById(R.id.textview_retrofit);
textView.setText(result);
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textview_retrofit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
GitHubService.java
public interface GitHubService {
@GET("repos/{owner}/{repo}/contributors")
Call<List<Contributor>> repoContributors(
@Path("owner") String owner,
@Path("repo") String repo);
public static final Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
}
Contributor.java
public class Contributor {
String login;
String html_url;
int contributions;
@Override
public String toString() {
return login + " (" + contributions + ")";
}
}
설명
TextView t = (TextView)findViewById(R.id.textview_retrofit);
텍스트뷰를 객체화시켜주고,
t.setOnClickListener(v->{
//레트로핏 이벤트 등록예정
});
setOnClickListener를 달아주었습니다.(람다 표현식으로 JAVA8부터 사용할 수 있습니다.) 텍스트 클릭 시
GitHubService gitHubService = GitHubService.retrofit.create(GitHubService.class);
final Call<List<Contributor>> call = gitHubService.repoContributors("square", "retrofit");
new NetworkCall().execute(call);
이 세 줄의 코드가 실행됩니다.
여기서
GitHubService.retrofit.create()함수는
public final class Retrofit
extends Object
구조를 가진 Retrofit클래스 내부에 있는 함수입니다.
<T> T create(Class<T> service)
Create an implementation of the API endpoints defined by the service interface.
반환형은 제네릭이며 서비스 클래스를 매개변수로 받아오는 함수인 create()는
서비스인터페이스의 API앤드포인트의 구현체를 생성합니다.
여기서 엔드 포인트는 통신 채널의 한쪽 끝입니다. API를 통해 두 시스템이 상호작용 할 때 각 시스템을 앤드포인트라 합니다. 이는 서버의 URL이 될 수도 있고, 서비스가 될 수 도 있습니다. 위의 경우엔 서비스에 해당하겠네요.
이렇게 하는 이유는 레트로핏 클래스가 사용자의 API interface를 callable objects로 만드는 기능을 갖기 때문입니다.
GitHubService 인터페이스를 보면
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.addConverterFactory(GsonConverterFactory.create())
.build();
레트로핏 객체가 생성되어 있고 이는 Gson에 의해 역직렬화하는 모습으로 구성되어 있습니다.
GitHubService 인터페이스의 상단에는
@GET("repos/{owner}/{repo}/contributors")
Call<List<Contributor>> repoContributors(
@Path("owner") String owner,
@Path("repo") String repo);
이렇게 되어 있는데, @GET
어노테이션을 이용하면 쿼리 path 를 만들 수 있습니다.
public interface Call<T>
extends Cloneable
Cloneable을 상속받은 인터페이스입니다.(clone을 사용하기 위해 상속합니다. (that method to make a field-for-field copy of instances of that class.) T는 Successful response body type입니다.
Contributor 데이터 클래스를 리스트로 만든 객체를 받는 Call은 웹 서버에 요청을 보내고 응답을 반환하는 Retrofit 메서드를 호출하는 역할입니다. 각 호출은 자체 HTTP 요청 및 응답 쌍을 생성하며, clone()을 사용하여 폴링 혹은 재시도의 일환으로 동일한 웹 서버에 대해 동일한 매개 변수를 사용하여 여러 번 호출할 수 있습니다.
이를 사용하면 다르게 들어오는 owner
와 repo
를 대응시킬 수 있을 것으로 보입니다. 여기서는 square/retrofit으로 쓰였습니다.
이를 처리하기 위해 NetworkCall클래스를 내부 클래스로 활용하며, 이는 AsyncTask를 상속받습니다. 때문에 doInBackground()함수와 onPostExecute()함수를 오버라이딩하여 작성합니다.
호출 시
new NetworkCall().execute(call);
에서 살펴봐야 할 것은 execute(call)입니다. https://developer.android.com/reference/android/os/AsyncTask.html#execute-Params...-
execute()함수는 AsyncTask 클래스에 있는 함수로, UI스레드에서 호출이 가능하게 합니다.
execute
public final AsyncTask<Params, Progress, Result> execute (Params... params)
들어오는 매개 변수(call)를 사용하여 task을 실행합니다.
그리고 호출자가 참조를 유지할 수 있도록 task가 자신(AsyncTask)을 리턴합니다 (this).
이 기능은 플랫폼 버전에 따라 단일 백그라운드 스레드 또는 스레드 풀에 대해 대기열에서 작업을 예약합니다.
AsyncTasks는 여러 작업을 동시에 수행 할 수있는 스레드 풀로, 시작 Build.VERSION_CODES.HONEYCOMB하면 병렬 실행으로 인한 일반적인 응용 프로그램 오류를 피하기 위해 단일 스레드에서 작업이 다시 실행됩니다.
doInBackground 함수는
protected abstract Result doInBackground (Params... params)
execute()함수가 전달한 매개변수를 공유하여 받으며 백그라운드 스레드에서 실행됩니다.
onPostExecute 함수는
protected void onPostExecute (Result result)
doInBackground 함수가 실행된 후 doInBackground의 반환값인 Result를 매개변수로 받습니다.
그러므로 doInBackground 에서
Call<List<Contributor>> call = params[0];
Response<List<Contributor>> response = call.execute();
return response.body().toString();
가져온 call을 실행시키고 받은 response의 body를 toString으로 반환하고 이를 onPostExecute에서 받아 UI에 setText로 반영합니다.
참고로
Response<T> execute() throws IOException
Synchronously send the request and return its response.
위와 같이 Response 반환형을 갖는 동기함수입니다. 리퀘스트를 보내고 리스폰스까지 받아 반환하지만 백그라운드에서 실행되므로 사용자에겐 비동기로 작용하겠죠?
여기까지 레트로핏 서버 없이 실행해봤습니다! ㅎㅎ
두 번째로는 레트로핏 서버가지고 해볼텐데, 포스팅이 생각보다 많이..길어진 것 같아서 다음 포스팅(https://onepinetwopine.tistory.com/512)으로%EC%9C%BC%EB%A1%9C) 작성하겠습니다
레퍼런스
retrofit을 만든 square사의 공식 페이지
https://square.github.io/retrofit/
Retrofit2 Github
https://github.com/square/retrofit
레트로핏2으로 HTTP 호출하기 - Realm 설명
https://academy.realm.io/kr/posts/retrofit2-for-http-requests/
Retrofit Java docs
https://square.github.io/retrofit/2.x/retrofit/
EndPoint에 대한 설명
https://smartbear.com/learn/performance-monitoring/api-endpoints/
Path에 대한 설명
https://judo0179.tistory.com/13
GET 쿼리 스트링이란? 생활코딩
https://opentutorials.org/course/2136/11945
https://api.github.com/repos/square/retrofit/contributors
API 결과
[
{
"login": "JakeWharton",
"id": 66577,
"node_id": "MDQ6VXNlcjY2NTc3",
"avatar_url": "https://avatars0.githubusercontent.com/u/66577?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/JakeWharton",
"html_url": "https://github.com/JakeWharton",
"followers_url": "https://api.github.com/users/JakeWharton/followers",
"following_url": "https://api.github.com/users/JakeWharton/following{/other_user}",
"gists_url": "https://api.github.com/users/JakeWharton/gists{/gist_id}",
"starred_url": "https://api.github.com/users/JakeWharton/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/JakeWharton/subscriptions",
"organizations_url": "https://api.github.com/users/JakeWharton/orgs",
"repos_url": "https://api.github.com/users/JakeWharton/repos",
"events_url": "https://api.github.com/users/JakeWharton/events{/privacy}",
"received_events_url": "https://api.github.com/users/JakeWharton/received_events",
"type": "User",
"site_admin": false,
"contributions": 1004
},
{
"login": "swankjesse",
"id": 133019,
"node_id": "MDQ6VXNlcjEzMzAxOQ==",
"avatar_url": "https://avatars3.githubusercontent.com/u/133019?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/swankjesse",
"html_url": "https://github.com/swankjesse",
"followers_url": "https://api.github.com/users/swankjesse/followers",
"following_url": "https://api.github.com/users/swankjesse/following{/other_user}",
"gists_url": "https://api.github.com/users/swankjesse/gists{/gist_id}",
"starred_url": "https://api.github.com/users/swankjesse/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/swankjesse/subscriptions",
"organizations_url": "https://api.github.com/users/swankjesse/orgs",
"repos_url": "https://api.github.com/users/swankjesse/repos",
"events_url": "https://api.github.com/users/swankjesse/events{/privacy}",
"received_events_url": "https://api.github.com/users/swankjesse/received_events",
"type": "User",
"site_admin": false,
"contributions": 265
},
{
"login": "pforhan",
"id": 331925,
"node_id": "MDQ6VXNlcjMzMTkyNQ==",
"avatar_url": "https://avatars3.githubusercontent.com/u/331925?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/pforhan",
"html_url": "https://github.com/pforhan",
"followers_url": "https://api.github.com/users/pforhan/followers",
"following_url": "https://api.github.com/users/pforhan/following{/other_user}",
"gists_url": "https://api.github.com/users/pforhan/gists{/gist_id}",
"starred_url": "https://api.github.com/users/pforhan/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/pforhan/subscriptions",
"organizations_url": "https://api.github.com/users/pforhan/orgs",
"repos_url": "https://api.github.com/users/pforhan/repos",
"events_url": "https://api.github.com/users/pforhan/events{/privacy}",
"received_events_url": "https://api.github.com/users/pforhan/received_events",
"type": "User",
"site_admin": false,
"contributions": 48
},
{
"login": "eburke",
"id": 63761,
"node_id": "MDQ6VXNlcjYzNzYx",
"avatar_url": "https://avatars0.githubusercontent.com/u/63761?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/eburke",
"html_url": "https://github.com/eburke",
"followers_url": "https://api.github.com/users/eburke/followers",
"following_url": "https://api.github.com/users/eburke/following{/other_user}",
"gists_url": "https://api.github.com/users/eburke/gists{/gist_id}",
"starred_url": "https://api.github.com/users/eburke/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/eburke/subscriptions",
"organizations_url": "https://api.github.com/users/eburke/orgs",
"repos_url": "https://api.github.com/users/eburke/repos",
"events_url": "https://api.github.com/users/eburke/events{/privacy}",
"received_events_url": "https://api.github.com/users/eburke/received_events",
"type": "User",
"site_admin": false,
"contributions": 36
},
{
"login": "NightlyNexus",
"id": 4032667,
"node_id": "MDQ6VXNlcjQwMzI2Njc=",
"avatar_url": "https://avatars2.githubusercontent.com/u/4032667?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/NightlyNexus",
"html_url": "https://github.com/NightlyNexus",
"followers_url": "https://api.github.com/users/NightlyNexus/followers",
"following_url": "https://api.github.com/users/NightlyNexus/following{/other_user}",
"gists_url": "https://api.github.com/users/NightlyNexus/gists{/gist_id}",
"starred_url": "https://api.github.com/users/NightlyNexus/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/NightlyNexus/subscriptions",
"organizations_url": "https://api.github.com/users/NightlyNexus/orgs",
"repos_url": "https://api.github.com/users/NightlyNexus/repos",
"events_url": "https://api.github.com/users/NightlyNexus/events{/privacy}",
"received_events_url": "https://api.github.com/users/NightlyNexus/received_events",
"type": "User",
"site_admin": false,
"contributions": 29
},
{
"login": "dnkoutso",
"id": 310370,
"node_id": "MDQ6VXNlcjMxMDM3MA==",
"avatar_url": "https://avatars2.githubusercontent.com/u/310370?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/dnkoutso",
"html_url": "https://github.com/dnkoutso",
"followers_url": "https://api.github.com/users/dnkoutso/followers",
"following_url": "https://api.github.com/users/dnkoutso/following{/other_user}",
"gists_url": "https://api.github.com/users/dnkoutso/gists{/gist_id}",
"starred_url": "https://api.github.com/users/dnkoutso/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/dnkoutso/subscriptions",
"organizations_url": "https://api.github.com/users/dnkoutso/orgs",
"repos_url": "https://api.github.com/users/dnkoutso/repos",
"events_url": "https://api.github.com/users/dnkoutso/events{/privacy}",
"received_events_url": "https://api.github.com/users/dnkoutso/received_events",
"type": "User",
"site_admin": false,
"contributions": 26
},
{
"login": "edenman",
"id": 1063557,
"node_id": "MDQ6VXNlcjEwNjM1NTc=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1063557?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/edenman",
"html_url": "https://github.com/edenman",
"followers_url": "https://api.github.com/users/edenman/followers",
"following_url": "https://api.github.com/users/edenman/following{/other_user}",
"gists_url": "https://api.github.com/users/edenman/gists{/gist_id}",
"starred_url": "https://api.github.com/users/edenman/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/edenman/subscriptions",
"organizations_url": "https://api.github.com/users/edenman/orgs",
"repos_url": "https://api.github.com/users/edenman/repos",
"events_url": "https://api.github.com/users/edenman/events{/privacy}",
"received_events_url": "https://api.github.com/users/edenman/received_events",
"type": "User",
"site_admin": false,
"contributions": 24
},
{
"login": "loganj",
"id": 18877,
"node_id": "MDQ6VXNlcjE4ODc3",
"avatar_url": "https://avatars3.githubusercontent.com/u/18877?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/loganj",
"html_url": "https://github.com/loganj",
"followers_url": "https://api.github.com/users/loganj/followers",
"following_url": "https://api.github.com/users/loganj/following{/other_user}",
"gists_url": "https://api.github.com/users/loganj/gists{/gist_id}",
"starred_url": "https://api.github.com/users/loganj/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/loganj/subscriptions",
"organizations_url": "https://api.github.com/users/loganj/orgs",
"repos_url": "https://api.github.com/users/loganj/repos",
"events_url": "https://api.github.com/users/loganj/events{/privacy}",
"received_events_url": "https://api.github.com/users/loganj/received_events",
"type": "User",
"site_admin": false,
"contributions": 17
},
{
"login": "Noel-96",
"id": 20171941,
"node_id": "MDQ6VXNlcjIwMTcxOTQx",
"avatar_url": "https://avatars3.githubusercontent.com/u/20171941?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Noel-96",
"html_url": "https://github.com/Noel-96",
"followers_url": "https://api.github.com/users/Noel-96/followers",
"following_url": "https://api.github.com/users/Noel-96/following{/other_user}",
"gists_url": "https://api.github.com/users/Noel-96/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Noel-96/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Noel-96/subscriptions",
"organizations_url": "https://api.github.com/users/Noel-96/orgs",
"repos_url": "https://api.github.com/users/Noel-96/repos",
"events_url": "https://api.github.com/users/Noel-96/events{/privacy}",
"received_events_url": "https://api.github.com/users/Noel-96/received_events",
"type": "User",
"site_admin": false,
"contributions": 16
},
{
"login": "rcdickerson",
"id": 63143,
"node_id": "MDQ6VXNlcjYzMTQz",
"avatar_url": "https://avatars1.githubusercontent.com/u/63143?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/rcdickerson",
"html_url": "https://github.com/rcdickerson",
"followers_url": "https://api.github.com/users/rcdickerson/followers",
"following_url": "https://api.github.com/users/rcdickerson/following{/other_user}",
"gists_url": "https://api.github.com/users/rcdickerson/gists{/gist_id}",
"starred_url": "https://api.github.com/users/rcdickerson/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/rcdickerson/subscriptions",
"organizations_url": "https://api.github.com/users/rcdickerson/orgs",
"repos_url": "https://api.github.com/users/rcdickerson/repos",
"events_url": "https://api.github.com/users/rcdickerson/events{/privacy}",
"received_events_url": "https://api.github.com/users/rcdickerson/received_events",
"type": "User",
"site_admin": false,
"contributions": 14
},
{
"login": "rjrjr",
"id": 1884445,
"node_id": "MDQ6VXNlcjE4ODQ0NDU=",
"avatar_url": "https://avatars0.githubusercontent.com/u/1884445?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/rjrjr",
"html_url": "https://github.com/rjrjr",
"followers_url": "https://api.github.com/users/rjrjr/followers",
"following_url": "https://api.github.com/users/rjrjr/following{/other_user}",
"gists_url": "https://api.github.com/users/rjrjr/gists{/gist_id}",
"starred_url": "https://api.github.com/users/rjrjr/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/rjrjr/subscriptions",
"organizations_url": "https://api.github.com/users/rjrjr/orgs",
"repos_url": "https://api.github.com/users/rjrjr/repos",
"events_url": "https://api.github.com/users/rjrjr/events{/privacy}",
"received_events_url": "https://api.github.com/users/rjrjr/received_events",
"type": "User",
"site_admin": false,
"contributions": 13
},
{
"login": "kryali",
"id": 174889,
"node_id": "MDQ6VXNlcjE3NDg4OQ==",
"avatar_url": "https://avatars1.githubusercontent.com/u/174889?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/kryali",
"html_url": "https://github.com/kryali",
"followers_url": "https://api.github.com/users/kryali/followers",
"following_url": "https://api.github.com/users/kryali/following{/other_user}",
"gists_url": "https://api.github.com/users/kryali/gists{/gist_id}",
"starred_url": "https://api.github.com/users/kryali/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/kryali/subscriptions",
"organizations_url": "https://api.github.com/users/kryali/orgs",
"repos_url": "https://api.github.com/users/kryali/repos",
"events_url": "https://api.github.com/users/kryali/events{/privacy}",
"received_events_url": "https://api.github.com/users/kryali/received_events",
"type": "User",
"site_admin": false,
"contributions": 9
},
{
"login": "adriancole",
"id": 64215,
"node_id": "MDQ6VXNlcjY0MjE1",
"avatar_url": "https://avatars2.githubusercontent.com/u/64215?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/adriancole",
"html_url": "https://github.com/adriancole",
"followers_url": "https://api.github.com/users/adriancole/followers",
"following_url": "https://api.github.com/users/adriancole/following{/other_user}",
"gists_url": "https://api.github.com/users/adriancole/gists{/gist_id}",
"starred_url": "https://api.github.com/users/adriancole/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/adriancole/subscriptions",
"organizations_url": "https://api.github.com/users/adriancole/orgs",
"repos_url": "https://api.github.com/users/adriancole/repos",
"events_url": "https://api.github.com/users/adriancole/events{/privacy}",
"received_events_url": "https://api.github.com/users/adriancole/received_events",
"type": "User",
"site_admin": false,
"contributions": 9
},
{
"login": "Jawnnypoo",
"id": 1459320,
"node_id": "MDQ6VXNlcjE0NTkzMjA=",
"avatar_url": "https://avatars3.githubusercontent.com/u/1459320?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Jawnnypoo",
"html_url": "https://github.com/Jawnnypoo",
"followers_url": "https://api.github.com/users/Jawnnypoo/followers",
"following_url": "https://api.github.com/users/Jawnnypoo/following{/other_user}",
"gists_url": "https://api.github.com/users/Jawnnypoo/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Jawnnypoo/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Jawnnypoo/subscriptions",
"organizations_url": "https://api.github.com/users/Jawnnypoo/orgs",
"repos_url": "https://api.github.com/users/Jawnnypoo/repos",
"events_url": "https://api.github.com/users/Jawnnypoo/events{/privacy}",
"received_events_url": "https://api.github.com/users/Jawnnypoo/received_events",
"type": "User",
"site_admin": false,
"contributions": 8
},
{
"login": "holmes",
"id": 59162,
"node_id": "MDQ6VXNlcjU5MTYy",
"avatar_url": "https://avatars3.githubusercontent.com/u/59162?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/holmes",
"html_url": "https://github.com/holmes",
"followers_url": "https://api.github.com/users/holmes/followers",
"following_url": "https://api.github.com/users/holmes/following{/other_user}",
"gists_url": "https://api.github.com/users/holmes/gists{/gist_id}",
"starred_url": "https://api.github.com/users/holmes/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/holmes/subscriptions",
"organizations_url": "https://api.github.com/users/holmes/orgs",
"repos_url": "https://api.github.com/users/holmes/repos",
"events_url": "https://api.github.com/users/holmes/events{/privacy}",
"received_events_url": "https://api.github.com/users/holmes/received_events",
"type": "User",
"site_admin": false,
"contributions": 7
},
{
"login": "JayNewstrom",
"id": 713288,
"node_id": "MDQ6VXNlcjcxMzI4OA==",
"avatar_url": "https://avatars3.githubusercontent.com/u/713288?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/JayNewstrom",
"html_url": "https://github.com/JayNewstrom",
"followers_url": "https://api.github.com/users/JayNewstrom/followers",
"following_url": "https://api.github.com/users/JayNewstrom/following{/other_user}",
"gists_url": "https://api.github.com/users/JayNewstrom/gists{/gist_id}",
"starred_url": "https://api.github.com/users/JayNewstrom/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/JayNewstrom/subscriptions",
"organizations_url": "https://api.github.com/users/JayNewstrom/orgs",
"repos_url": "https://api.github.com/users/JayNewstrom/repos",
"events_url": "https://api.github.com/users/JayNewstrom/events{/privacy}",
"received_events_url": "https://api.github.com/users/JayNewstrom/received_events",
"type": "User",
"site_admin": false,
"contributions": 7
},
{
"login": "swanson",
"id": 56947,
"node_id": "MDQ6VXNlcjU2OTQ3",
"avatar_url": "https://avatars3.githubusercontent.com/u/56947?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/swanson",
"html_url": "https://github.com/swanson",
"followers_url": "https://api.github.com/users/swanson/followers",
"following_url": "https://api.github.com/users/swanson/following{/other_user}",
"gists_url": "https://api.github.com/users/swanson/gists{/gist_id}",
"starred_url": "https://api.github.com/users/swanson/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/swanson/subscriptions",
"organizations_url": "https://api.github.com/users/swanson/orgs",
"repos_url": "https://api.github.com/users/swanson/repos",
"events_url": "https://api.github.com/users/swanson/events{/privacy}",
"received_events_url": "https://api.github.com/users/swanson/received_events",
"type": "User",
"site_admin": false,
"contributions": 7
},
{
"login": "crazybob",
"id": 170788,
"node_id": "MDQ6VXNlcjE3MDc4OA==",
"avatar_url": "https://avatars2.githubusercontent.com/u/170788?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/crazybob",
"html_url": "https://github.com/crazybob",
"followers_url": "https://api.github.com/users/crazybob/followers",
"following_url": "https://api.github.com/users/crazybob/following{/other_user}",
"gists_url": "https://api.github.com/users/crazybob/gists{/gist_id}",
"starred_url": "https://api.github.com/users/crazybob/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/crazybob/subscriptions",
"organizations_url": "https://api.github.com/users/crazybob/orgs",
"repos_url": "https://api.github.com/users/crazybob/repos",
"events_url": "https://api.github.com/users/crazybob/events{/privacy}",
"received_events_url": "https://api.github.com/users/crazybob/received_events",
"type": "User",
"site_admin": false,
"contributions": 6
},
{
"login": "danrice-square",
"id": 1952897,
"node_id": "MDQ6VXNlcjE5NTI4OTc=",
"avatar_url": "https://avatars0.githubusercontent.com/u/1952897?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/danrice-square",
"html_url": "https://github.com/danrice-square",
"followers_url": "https://api.github.com/users/danrice-square/followers",
"following_url": "https://api.github.com/users/danrice-square/following{/other_user}",
"gists_url": "https://api.github.com/users/danrice-square/gists{/gist_id}",
"starred_url": "https://api.github.com/users/danrice-square/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/danrice-square/subscriptions",
"organizations_url": "https://api.github.com/users/danrice-square/orgs",
"repos_url": "https://api.github.com/users/danrice-square/repos",
"events_url": "https://api.github.com/users/danrice-square/events{/privacy}",
"received_events_url": "https://api.github.com/users/danrice-square/received_events",
"type": "User",
"site_admin": false,
"contributions": 5
},
{
"login": "vanniktech",
"id": 5759366,
"node_id": "MDQ6VXNlcjU3NTkzNjY=",
"avatar_url": "https://avatars0.githubusercontent.com/u/5759366?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/vanniktech",
"html_url": "https://github.com/vanniktech",
"followers_url": "https://api.github.com/users/vanniktech/followers",
"following_url": "https://api.github.com/users/vanniktech/following{/other_user}",
"gists_url": "https://api.github.com/users/vanniktech/gists{/gist_id}",
"starred_url": "https://api.github.com/users/vanniktech/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/vanniktech/subscriptions",
"organizations_url": "https://api.github.com/users/vanniktech/orgs",
"repos_url": "https://api.github.com/users/vanniktech/repos",
"events_url": "https://api.github.com/users/vanniktech/events{/privacy}",
"received_events_url": "https://api.github.com/users/vanniktech/received_events",
"type": "User",
"site_admin": false,
"contributions": 5
},
{
"login": "Turbo87",
"id": 141300,
"node_id": "MDQ6VXNlcjE0MTMwMA==",
"avatar_url": "https://avatars2.githubusercontent.com/u/141300?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/Turbo87",
"html_url": "https://github.com/Turbo87",
"followers_url": "https://api.github.com/users/Turbo87/followers",
"following_url": "https://api.github.com/users/Turbo87/following{/other_user}",
"gists_url": "https://api.github.com/users/Turbo87/gists{/gist_id}",
"starred_url": "https://api.github.com/users/Turbo87/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/Turbo87/subscriptions",
"organizations_url": "https://api.github.com/users/Turbo87/orgs",
"repos_url": "https://api.github.com/users/Turbo87/repos",
"events_url": "https://api.github.com/users/Turbo87/events{/privacy}",
"received_events_url": "https://api.github.com/users/Turbo87/received_events",
"type": "User",
"site_admin": false,
"contributions": 5
},
{
"login": "naturalwarren",
"id": 1113225,
"node_id": "MDQ6VXNlcjExMTMyMjU=",
"avatar_url": "https://avatars3.githubusercontent.com/u/1113225?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/naturalwarren",
"html_url": "https://github.com/naturalwarren",
"followers_url": "https://api.github.com/users/naturalwarren/followers",
"following_url": "https://api.github.com/users/naturalwarren/following{/other_user}",
"gists_url": "https://api.github.com/users/naturalwarren/gists{/gist_id}",
"starred_url": "https://api.github.com/users/naturalwarren/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/naturalwarren/subscriptions",
"organizations_url": "https://api.github.com/users/naturalwarren/orgs",
"repos_url": "https://api.github.com/users/naturalwarren/repos",
"events_url": "https://api.github.com/users/naturalwarren/events{/privacy}",
"received_events_url": "https://api.github.com/users/naturalwarren/received_events",
"type": "User",
"site_admin": false,
"contributions": 5
},
{
"login": "guptasourabh04",
"id": 27951986,
"node_id": "MDQ6VXNlcjI3OTUxOTg2",
"avatar_url": "https://avatars1.githubusercontent.com/u/27951986?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/guptasourabh04",
"html_url": "https://github.com/guptasourabh04",
"followers_url": "https://api.github.com/users/guptasourabh04/followers",
"following_url": "https://api.github.com/users/guptasourabh04/following{/other_user}",
"gists_url": "https://api.github.com/users/guptasourabh04/gists{/gist_id}",
"starred_url": "https://api.github.com/users/guptasourabh04/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/guptasourabh04/subscriptions",
"organizations_url": "https://api.github.com/users/guptasourabh04/orgs",
"repos_url": "https://api.github.com/users/guptasourabh04/repos",
"events_url": "https://api.github.com/users/guptasourabh04/events{/privacy}",
"received_events_url": "https://api.github.com/users/guptasourabh04/received_events",
"type": "User",
"site_admin": false,
"contributions": 4
},
{
"login": "artem-zinnatullin",
"id": 967132,
"node_id": "MDQ6VXNlcjk2NzEzMg==",
"avatar_url": "https://avatars0.githubusercontent.com/u/967132?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/artem-zinnatullin",
"html_url": "https://github.com/artem-zinnatullin",
"followers_url": "https://api.github.com/users/artem-zinnatullin/followers",
"following_url": "https://api.github.com/users/artem-zinnatullin/following{/other_user}",
"gists_url": "https://api.github.com/users/artem-zinnatullin/gists{/gist_id}",
"starred_url": "https://api.github.com/users/artem-zinnatullin/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/artem-zinnatullin/subscriptions",
"organizations_url": "https://api.github.com/users/artem-zinnatullin/orgs",
"repos_url": "https://api.github.com/users/artem-zinnatullin/repos",
"events_url": "https://api.github.com/users/artem-zinnatullin/events{/privacy}",
"received_events_url": "https://api.github.com/users/artem-zinnatullin/received_events",
"type": "User",
"site_admin": false,
"contributions": 3
},
{
"login": "chriscizek",
"id": 17190266,
"node_id": "MDQ6VXNlcjE3MTkwMjY2",
"avatar_url": "https://avatars1.githubusercontent.com/u/17190266?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/chriscizek",
"html_url": "https://github.com/chriscizek",
"followers_url": "https://api.github.com/users/chriscizek/followers",
"following_url": "https://api.github.com/users/chriscizek/following{/other_user}",
"gists_url": "https://api.github.com/users/chriscizek/gists{/gist_id}",
"starred_url": "https://api.github.com/users/chriscizek/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/chriscizek/subscriptions",
"organizations_url": "https://api.github.com/users/chriscizek/orgs",
"repos_url": "https://api.github.com/users/chriscizek/repos",
"events_url": "https://api.github.com/users/chriscizek/events{/privacy}",
"received_events_url": "https://api.github.com/users/chriscizek/received_events",
"type": "User",
"site_admin": false,
"contributions": 3
},
{
"login": "codebutler",
"id": 3827,
"node_id": "MDQ6VXNlcjM4Mjc=",
"avatar_url": "https://avatars2.githubusercontent.com/u/3827?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/codebutler",
"html_url": "https://github.com/codebutler",
"followers_url": "https://api.github.com/users/codebutler/followers",
"following_url": "https://api.github.com/users/codebutler/following{/other_user}",
"gists_url": "https://api.github.com/users/codebutler/gists{/gist_id}",
"starred_url": "https://api.github.com/users/codebutler/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/codebutler/subscriptions",
"organizations_url": "https://api.github.com/users/codebutler/orgs",
"repos_url": "https://api.github.com/users/codebutler/repos",
"events_url": "https://api.github.com/users/codebutler/events{/privacy}",
"received_events_url": "https://api.github.com/users/codebutler/received_events",
"type": "User",
"site_admin": false,
"contributions": 3
},
{
"login": "icastell",
"id": 1678605,
"node_id": "MDQ6VXNlcjE2Nzg2MDU=",
"avatar_url": "https://avatars2.githubusercontent.com/u/1678605?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/icastell",
"html_url": "https://github.com/icastell",
"followers_url": "https://api.github.com/users/icastell/followers",
"following_url": "https://api.github.com/users/icastell/following{/other_user}",
"gists_url": "https://api.github.com/users/icastell/gists{/gist_id}",
"starred_url": "https://api.github.com/users/icastell/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/icastell/subscriptions",
"organizations_url": "https://api.github.com/users/icastell/orgs",
"repos_url": "https://api.github.com/users/icastell/repos",
"events_url": "https://api.github.com/users/icastell/events{/privacy}",
"received_events_url": "https://api.github.com/users/icastell/received_events",
"type": "User",
"site_admin": false,
"contributions": 3
},
{
"login": "jjNford",
"id": 965425,
"node_id": "MDQ6VXNlcjk2NTQyNQ==",
"avatar_url": "https://avatars3.githubusercontent.com/u/965425?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/jjNford",
"html_url": "https://github.com/jjNford",
"followers_url": "https://api.github.com/users/jjNford/followers",
"following_url": "https://api.github.com/users/jjNford/following{/other_user}",
"gists_url": "https://api.github.com/users/jjNford/gists{/gist_id}",
"starred_url": "https://api.github.com/users/jjNford/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/jjNford/subscriptions",
"organizations_url": "https://api.github.com/users/jjNford/orgs",
"repos_url": "https://api.github.com/users/jjNford/repos",
"events_url": "https://api.github.com/users/jjNford/events{/privacy}",
"received_events_url": "https://api.github.com/users/jjNford/received_events",
"type": "User",
"site_admin": false,
"contributions": 3
},
{
"login": "ojh102",
"id": 14901903,
"node_id": "MDQ6VXNlcjE0OTAxOTAz",
"avatar_url": "https://avatars2.githubusercontent.com/u/14901903?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/ojh102",
"html_url": "https://github.com/ojh102",
"followers_url": "https://api.github.com/users/ojh102/followers",
"following_url": "https://api.github.com/users/ojh102/following{/other_user}",
"gists_url": "https://api.github.com/users/ojh102/gists{/gist_id}",
"starred_url": "https://api.github.com/users/ojh102/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/ojh102/subscriptions",
"organizations_url": "https://api.github.com/users/ojh102/orgs",
"repos_url": "https://api.github.com/users/ojh102/repos",
"events_url": "https://api.github.com/users/ojh102/events{/privacy}",
"received_events_url": "https://api.github.com/users/ojh102/received_events",
"type": "User",
"site_admin": false,
"contributions": 3
},
{
"login": "f2prateek",
"id": 843979,
"node_id": "MDQ6VXNlcjg0Mzk3OQ==",
"avatar_url": "https://avatars2.githubusercontent.com/u/843979?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/f2prateek",
"html_url": "https://github.com/f2prateek",
"followers_url": "https://api.github.com/users/f2prateek/followers",
"following_url": "https://api.github.com/users/f2prateek/following{/other_user}",
"gists_url": "https://api.github.com/users/f2prateek/gists{/gist_id}",
"starred_url": "https://api.github.com/users/f2prateek/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/f2prateek/subscriptions",
"organizations_url": "https://api.github.com/users/f2prateek/orgs",
"repos_url": "https://api.github.com/users/f2prateek/repos",
"events_url": "https://api.github.com/users/f2prateek/events{/privacy}",
"received_events_url": "https://api.github.com/users/f2prateek/received_events",
"type": "User",
"site_admin": false,
"contributions": 3
}
]x
'FrontEnd > Android' 카테고리의 다른 글
안드로이드 Android ] 모듈화 / module 만들기 (0) | 2019.12.24 |
---|---|
안드로이드 Android Studio ] Java level 변경하기 (0) | 2019.12.09 |
Android 안드로이드 ] 위젯 버튼 클릭 이벤트 설정하기 (0) | 2019.09.22 |
Android 안드로이드 ] 30분 이내의 업데이트 주기를 가진 위젯 (0) | 2019.09.22 |
Android 안드로이드 ] 위젯 Widget 리스트뷰 로딩중...문제 해결! (0) | 2019.09.21 |