Privacy Policy

Hyeongpil built the 실력 측정기: 오버워치 app as an Ad Supported app. This SERVICE is provided by Hyeongpil at no cost and is intended for use as is.

This page is used to inform visitors regarding my policies with the collection, use, and disclosure of Personal Information if anyone decided to use my Service.

If you choose to use my Service, then you agree to the collection and use of information in relation to this policy. The Personal Information that I collect is used for providing and improving the Service. I will not use or share your information with anyone except as described in this Privacy Policy.

The terms used in this Privacy Policy have the same meanings as in our Terms and Conditions, which is accessible at 실력 측정기: 오버워치 unless otherwise defined in this Privacy Policy.

Information Collection and Use

For a better experience, while using our Service, I may require you to provide us with certain personally identifiable information, including but not limited to Android Ad ID. The information that I request will be retained on your device and is not collected by me in any way.

The app does use third party services that may collect information used to identify you.

Link to privacy policy of third party service providers used by the app

Log Data

I want to inform you that whenever you use my Service, in a case of an error in the app I collect data and information (through third party products) on your phone called Log Data. This Log Data may include information such as your device Internet Protocol (“IP”) address, device name, operating system version, the configuration of the app when utilizing my Service, the time and date of your use of the Service, and other statistics.

Cookies

Cookies are files with a small amount of data that are commonly used as anonymous unique identifiers. These are sent to your browser from the websites that you visit and are stored on your device's internal memory.

This Service does not use these “cookies” explicitly. However, the app may use third party code and libraries that use “cookies” to collect information and improve their services. You have the option to either accept or refuse these cookies and know when a cookie is being sent to your device. If you choose to refuse our cookies, you may not be able to use some portions of this Service.

Service Providers

I may employ third-party companies and individuals due to the following reasons:

  • To facilitate our Service;
  • To provide the Service on our behalf;
  • To perform Service-related services; or
  • To assist us in analyzing how our Service is used.

I want to inform users of this Service that these third parties have access to your Personal Information. The reason is to perform the tasks assigned to them on our behalf. However, they are obligated not to disclose or use the information for any other purpose.

Security

I value your trust in providing us your Personal Information, thus we are striving to use commercially acceptable means of protecting it. But remember that no method of transmission over the internet, or method of electronic storage is 100% secure and reliable, and I cannot guarantee its absolute security.

Links to Other Sites

This Service may contain links to other sites. If you click on a third-party link, you will be directed to that site. Note that these external sites are not operated by me. Therefore, I strongly advise you to review the Privacy Policy of these websites. I have no control over and assume no responsibility for the content, privacy policies, or practices of any third-party sites or services.

Children’s Privacy

These Services do not address anyone under the age of 13. I do not knowingly collect personally identifiable information from children under 13. In the case I discover that a child under 13 has provided me with personal information, I immediately delete this from our servers. If you are a parent or guardian and you are aware that your child has provided us with personal information, please contact me so that I will be able to do necessary actions.

Changes to This Privacy Policy

I may update our Privacy Policy from time to time. Thus, you are advised to review this page periodically for any changes. I will notify you of any changes by posting the new Privacy Policy on this page. These changes are effective immediately after they are posted on this page.

Contact Us

If you have any questions or suggestions about my Privacy Policy, do not hesitate to contact me at chlgudvlf@gmail.com.

안녕하세요!


오늘은 제가 삽질했던 오류 해결법을 올려드리려고 합니다


ZipException은 Gradle에서 라이브러리를 추가할 때 가끔 발생하는 오류인데요


라이브러리 안에 같은 모듈이 중복되어 생기는 오류입니다.

보통 서드파티 라이브러리에서 구글 gms 등을 포함하여 충돌나는 경우가 많습니다.


다음과 같이 해결할 수 있습니다!


compile ('패키지 이름'){
exclude group: 'com.google.android.gms'
}


이렇게 하면 해당하는 패키지에 gms를 제외하고 컴파일되게 되어 충돌을 막을 수 있습니다.


하지만 저는 특수한 케이스에 부딪혔습니다!

기존 앱에 '파이어베이스'를 연동한 상태에서 '구글 플레이 게임' 을 연동하기 위해

BaseGameUtils를 모듈로 추가했었는데요


이때 zip exception com/google/android/gms/internal/z~~~.class 오류가 발생되어 고통을 받았었습니다.

이 해결법을 알려드리겠습니다!!


build.gradle(Module: BaseGameUtils) 에는 다음과 같이 선언되어있습니다.

if (!project.hasProperty('gms_library_version')) {
ext.gms_library_version = '8.4.0'
}
compile "com.google.android.gms:play-services-games:${gms_library_version}"
compile "com.google.android.gms:play-services-plus:${gms_library_version}"


제 build.gradle(Module: app) 에는 파이어베이스가 다음과 같이 선언되어 있었습니다.

compile 'com.google.firebase:firebase-core:10.0.1'
compile 'com.google.firebase:firebase-ads:10.0.1'


여기서 app의 파이어베이스에도 gms가 포함되어있었고, BaseGameUtils에도 gms가 포함되어 충돌이 일어났었는데요!

모듈이 달라 디버깅하기 쉽지 않았었습니다...

따라서 다음과 같이

compile "com.google.android.gms:play-services-games:10.0.1"
compile "com.google.android.gms:play-services-plus:10.0.1"

build.gradle(Module: BaseGameUtils) 의 버전을 맞춰주시면 아름답게 컴파일 되는 것을 확인할 수 있습니다!


저와 같은 고통을 받지 않으셨으면 좋겠습니다.


감사합니다!



안녕하세요!


안드로이드 앱을 개발하다보면 서버와의 통신 작업이 많이 필요하게 되는데요

이때 정말 편하게 사용할 수 있는 라이브러리인 Retrofit을 소개해보려고 합니다.


이 글을 작성할 때 

생각자유의 안드로이드 이야기 - Retrofit 기본 기능에 대해서 알아보자 

http://goo.gl/fOumEV

이 글을 많이 참조하였습니다!


http://square.github.io/retrofit/


저는 Retrofit을 이용하여 날씨 정보를 가져오는 API 통신을 해보겠습니다.

https://developers.skplanetx.com/apidoc/kor/weather/

SKplanet 에서 제공하는 Weather Planet에서 정보를 받아오겠습니다.


먼저 build.gradle(Module : app) 에 다음과 같이 추가해줍니다.

dependencies {
compile 'com.squareup.retrofit2:retrofit:2.1.0' // Retrofit
compile 'com.squareup.retrofit2:converter-gson:2.0.0-beta3' // Gson 변환을 위한 converter
}


인터넷 통신을 하기 위해 퍼미션을 추가 해 줍니다.

<uses-permission android:name="android.permission.INTERNET"/>


이로써 Retrofit 을 사용하기 위한 준비가 끝났습니다.

이제 본격적으로 날씨 정보 얻어오기를 달려보겠습니다!


저는 현재날씨(시간별) 데이터를 받아보겠습니다.

Weather Planet 의 현재 날씨 API는 다음과 같은 양식으로 되어있습니다.

Resource URI

http://apis.skplanetx.com/weather/current/hourly?version={version}&lat={lat}&lon={lon}&city={city}&county={county}&village={village}

Protocol - REST

HTTP Method - GET


여기서 

http://apis.skplanetx.com/ 은 baseURL

weather/current/hourly 는 현재날씨(시간별)에 해당하는 URL

? 뒤쪽 부분은 변수 명이 됩니다.


이를 이용하여 Interface Class를 선언해 보겠습니다

저는 WeatherRepo 클래스 안에 인터페이스를 선언하여 Call<WeatherRepo> 로 나오게 되었습니다.

WeatherRepo 클래스는 API 통신을 하며 데이터를 받는 객체 클래스로 아래에서 설명드리겠습니다.

버전 정보와 위도, 경도만 필요하기 때문에 country 와 village는 생략하였습니다.

public interface WeatherApiInterface {
@Headers({"Accept: application/json"})
@GET("weather/current/hourly")
Call<WeatherRepo> get_Weather_retrofit(@Query("version") int version, @Query("lat") String lat, @Query("lon") String lon);
}

이와 같이

@Headers 에는 필요한 헤더 부분을 ({ ~~ . ~~ }) 형식으로 넣어줄 수 있습니다.

@GET 부분에는 현재날씨(시간별)에 해당하는 URL 을 넣어줍니다.

다음으로 Call입니다.

Call <'주고받을 객체'> '함수명' (@Query ('변수 이름') '자료형' '변수 이름')  형태로 나타나 있는데요

@Query 를 통해 위치가 바뀌어도 동적으로 값을 받아올 수 있습니다!


다음으로는 응답 샘플 코드를 보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
{
    "result":{
        "message":"성공",
        "code":9200,
        "requestUrl":"/weather/current/hourly?lon=&village=도곡동&county=강남구&lat=&city=서울&version=1"
    },
    "common":{
        "alertYn":"Y",
        "stormYn":"N"
    },
    "weather":{
        "hourly":[
            {
                "grid":{
                    "latitude":"37.4870600000",
                    "longitude":"127.0460400000",
                    "city":"서울",
                    "county":"강남구",
                    "village":"도곡동"
                },
                "wind":{
                    "wdir":"266.00",
                    "wspd":"3.20"
                },
                "precipitation":{
                    "type":"0",
                    "sinceOntime":"0.00"
                },
                "sky":{
                    "name":"맑음",
                    "code":"SKY_O01"
                },
                "temperature":{
                    "tc":"6.80",
                    "tmax":"8.10",
                    "tmin":"-0.90"
                },
                "humidity":"31.00",
                "lightning":"0",
                "timeRelease":"2013-11-11 14:00:00"
            }
        ]
    }
}
cs


여기서 저희는 필요한 것만 골라서 받아올 수 있습니다!

갓 구글께서는 JSON을 쉽게 파싱할 수 있도록 GSON 라이브러리를 만들어 주셨습니다.

이를 야무지게 사용해 보겠습니다.


public class WeatherRepo {

@SerializedName("result")
Result result;
@SerializedName("weather")
weather weather;

public class Result {
@SerializedName("message") String message;
@SerializedName("code") String code;

public String getMessage() {return message;}
public String getCode() {return code;}
}

public class weather {

public List<hourly> hourly = new ArrayList<>();
public List<hourly> getHourly() {return hourly;}

public class hourly {
@SerializedName("sky") Sky sky;
@SerializedName("precipitation") precipitation precipitation;
@SerializedName("temperature") temperature temperature;
@SerializedName("wind") wind wind;

public class Sky{
@SerializedName("name") String name;
@SerializedName("code") String code;

public String getName() {return name;}
public String getCode() {return code;}
}

public class precipitation{ // 강수 정보
@SerializedName("sinceOntime") String sinceOntime; // 강우
@SerializedName("type") String type; //0 :없음 1:비 2: 비/눈 3: 눈

public String getSinceOntime() {return sinceOntime;}
public String getType() {return type;}
}
public class temperature{
@SerializedName("tc") String tc; // 현재 기온

public String getTc() {return tc;}
}
public class wind{ // 바람
@SerializedName("wdir") String wdir;
@SerializedName("wspd") String wspd;

public String getWdir() {return wdir;}
public String getWspd() {return wspd;}
}
public Sky getSky() {return sky;}
public hourly.precipitation getPrecipitation() {return precipitation;}
public hourly.temperature getTemperature() {return temperature;}
public hourly.wind getWind() {return wind;}
}
}
public Result getResult() {return result;}
public weather getWeather() {return weather;}

public interface WeatherApiInterface {
@Headers({"Accept: application/json"})
@GET("weather/current/hourly")
Call<WeatherRepo> get_Weather_retrofit(@Query("version") int version, @Query("lat") String lat, @Query("lon") String lon);
}
}

위의 응답 데이터 양식과 아래의 코드를 보면 

GSON이 쉽고 편하고 아름답게 파싱해 주는 것을 볼 수 있습니다!

여기서 @SerializedName 어노테이션이 포인트인데요

이 어노테이션을 이용하면 wspd(풍속) 을 

@SerializedName("wspd") String windSpeed; 처럼 변수 이름을 바꿔 넣을 수도 있습니다.


응답코드에 hourly 뒤를 보시면 JSONArray 형태로 되어 있는데요

이를 위와 같이 클래스로 만들어 ArrayList<hourly> 형태로 만들어준다면 쉽게 파싱이 가능합니다!

이부분을 잘 몰라서 삽질 했던 기억이 나네요...


다음으로는 위에서 만든 Retrofit interface를 구현해보도록 하겠습니다!

저는 날씨 정보를 가져오는 스레드인

WeatherThread 에서 날씨 정보를 받아오겠습니다.


public class WeatherThread extends Thread {
final static String TAG = "WeatherThread";
Context mContext;
WeatherRepo weatherRepo;
Handler handler;

int version = 1;
String lat;
String lon;

public WeatherThread(Handler handler, Context mContext, double lat, double lon) {
this.mContext = mContext;
this.lat = String. valueOf(lat);
this.lon = String .valueOf(lon);
this.handler = handler;
}

@Override
public void run() {
super.run();
Retrofit client = new Retrofit.Builder().baseUrl("http://apis.skplanetx.com/").addConverterFactory(GsonConverterFactory.create()).build();
WeatherRepo.WeatherApiInterface service = client.create(WeatherRepo.WeatherApiInterface.class);
Call<WeatherRepo> call = service.get_Weather_retrofit(version, lat, lon);
call.enqueue(new Callback<WeatherRepo>() {
@Override
public void onResponse(Call<WeatherRepo> call, Response<WeatherRepo> response) {
if(response.isSuccessful()){
weatherRepo = response.body();
Log.d(TAG,"response.raw :"+response.raw());
if(weatherRepo.getResult().getCode().equals("9200")){ // 9200 = 성공
Weather.getInstance().setTemperature(weatherRepo.getWeather().getHourly().get(0).getTemperature().getTc());
Weather.getInstance().setCloud(weatherRepo.getWeather().getHourly().get(0).getSky().getName());
Weather.getInstance().setWind_direction(weatherRepo.getWeather().getHourly().get(0).getWind().getWdir());
Weather.getInstance().setWind_speed(weatherRepo.getWeather().getHourly().get(0).getWind().getWspd());
Weather.getInstance().setIcon(weatherRepo.getWeather().getHourly().get(0).getSky().getCode());

Message msg = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("weather","weather");
msg.setData(bundle);
handler.sendMessage(msg);
}else{
Log.e(TAG,"요청 실패 :"+weatherRepo.getResult().getCode());
Log.e(TAG,"메시지 :"+weatherRepo.getResult().getMessage());
}
}
}

@Override
public void onFailure(Call<WeatherRepo> call, Throwable t) {
Log.e(TAG,"날씨정보 불러오기 실패 :" + t.getMessage() );
Log.e(TAG,"요청 메시지 :"+call.request());
}
});
}
}

생성자를 통해 위도와 경도를 받아와 

service.get_Weather_retrofit(version, lat, lon) 부분에 값을 넣어줍니다.

요청이 성공적으로 수행되면 onResponse로 진입을 하게 되는데 이곳에서 response.isSuccessful() 로 요청이 성공적으로 이루어 지면

WeatherRepo 에 response.body를 입혀줍니다. response.body 에는 위의 응답 샘플 코드와 같은 정보가 들어있어 이를 

GSON으로 파싱 할 수 있을 것입니다.

response.raw 함수로 응답으로 온 http raw 데이터를 볼 수 있습니다.

응답코드가 정상인지 확인한 후 

weatherRepo.get~~~ 함수들로 값을 받아와 객체에 저장할 수 있습니다!


Retrofit은 제가 사용한 GET 방법 외에도 POST, PUT 등 다양한 Request 방식을 지원합니다.

여러가지 사용법은 추후 공부를 더 하게되면 추가 해 나가도록 하겠습니다!


이상 Retrofit 사용법 글을 마치겠습니다 감사합니다!




+ Recent posts