Volley와 Stetho로 네트워크 디버깅

Volley는 2013년 구글에서 공개한 안드로이드 네트워크 라이브러리다. 구글 플레이 스토어 팀에서 개발/적용 후 공개한 것이 Volley의 시작으로 알려져 있다. 그 명성이 지금은 예전만 못하지만, 공개 당시에는 가장 좋은 네트워크 라이브러리였다. 더 자세한 소개는 공식 트레이닝 사이트(한글)를 참조하자.

Stetho는 2015년 페이스북에서 공개한 안드로이드 디버깅 도구다. 크롬 브라우저에 내장된 개발자 도구인 DevTools를 활용해 안드로이드 앱의 네트워크, 데이터베이스, 프리퍼런스, 레이아웃 등을 디버깅 할 수 있다. 더 자세한 기능 소개는 공식 페이지를 참조하자.

네트워크 라이브러리와 연계 동작하는 라이브러리들은 보통 Volley를 지원한다. 하지만, Stetho의 네트워크 디버깅 기능은 앱이 OkHttp나 안드로이드의 HttpUrlConnection를 써야 쓸 수 있다. 환경이 맞다면 좋겠지만, 이미 개발된 앱의 네트워크 라이브러리를 Stetho 쓰자고 바꾸기는 어렵다. 같은 고민인지, 아래처럼 Volly의 RequestQueue 객체 생성 시에 OkHttp 라이브러리의 특정 클래스 객체만 활용하는 우회법을 제안하는 글이 많다.

OkHttpClient client = new OkHttpClient();
client.networkInterceptors().add(new StethoInterceptor());
mRequestQueue = Volley.newRequestQueue(getApplicationContext(), new OkHttpStack(client));

물론 네트워크 라이브러리를 바꾸는 것에 비해 수정은 적지만, 쓰지 않아도 되는 라이브러리를 추가하는건 여전하다. 이 같은 고민을 한다면 포기하기 전에 여기를 살펴보자. Stetho의 UrlConnectionManager를 활용하는 기능이 포함된 StethoVolleyHurlStack 클래스를 제공하여, OkHttp를 사용하지 않고도 Stetho의 네트워크 디버깅을 사용하도록 도와준다.

RequestQueue queue = Volley.newRequestQueue(this, new StethoVolleyHurlStack());

2020년 1월 테스트로는 잘 되지만, 1년 넘게 commit이 없고 pull-request도 처리 안하는 것으로 보아 유지보수가 중단된 것 같다.

그 밖에

  • 데이터베이스, 프리퍼런스, 레이아웃 디버깅과 같은 다른 기능들은 Stetho 라이브러리 적용만으로 쓸 수 있다.

참고

워드프레스에 저작권 표시하기

블로깅을 하다보면, 저작권을 달고 싶어지기도 한다. ‘복잡한 저작권 문제를 내가 어떻게 정리할까?’ 싶다면 Creative Commons 를 이용한 저작권 표시를 고려해 보자.

얼마 전까지 워드프레스에 Creative Commons를 이용해 저작권 표시를 하려면 푸터(footer) 파일을 커스터마이징해야 했다. 19년 7월 1일부터 공식 Creative Commons 워드프레스 플러그인이 출시되어, 이를 이용하면 저작권 뱃지도 쉽게 표시할 수 있다.

아래를 따라, 플러그인을 설치하고 적용해 보자.

  1. 내 블로그의 ‘관리자 – 플러그인 – 새로 추가’ 에서 ‘Creative Commons’ 키워드로 검색
  2. 플러그인 설치
  3. 활성화
  4. 내 블로그의 ‘관리자 – 설정 – Creative Commons’를 통해 저작권 설정 화면으로 이동
  5. 저작권 옵션 선택
  6. 적용

어떤 뱃지를 선택해야할 지 고민하는 사람을 위해, Creative Commons는 간단한 문답으로 찾아보는 저작권 선택하기도구도 제공하니 이용해보자.

그 밖에

  • Creative Commons 플러그인은 저작권 표시를 글의 끝에 표시하는 것보다 블로그에 위젯으로 다는 것을 권장한다.
  • Creative Commons 뱃지를 단 결과는 이 블로그에서도 볼 수 있다.

참고

The user 12345 does not meet the requirements to access device identifiers

안드로이드는 휴대폰을 구분할 수 있는 변경 불가능한 식별자(이하 편의상 UID, Unique identifier)를 제공해 왔다. 공식 문서에 언급된 API들은 아래와 같다.

Build나 TelephonyManager 클래스를 통해 제공되는 이 기능이 안드로이드 10(API29 혹은 Q OS)부터 개인정보 보호 강화 정책에 따라 추가 제약을 받는다고 한다. targetSdkVersion 기준 28까지는 READ_PHONE_STATE 권한을 가진 앱은 UID를 읽을 수 있으나, 29부터 READ_PRIVILEGED_PHONE_STATE 권한이 있어야 한다. 그러므로, TelephonyManager.getDeviceId() API를 각각 O OS와 Q OS에서 호출한다면 아래와 같이 동작할 것이다.

O OS + READ_PHONE_STATE 권한이 없을 때

Caused by: java.lang.SecurityException: getDeviceId: uid 10237 does not have android.permission.READ_PHONE_STATE.

O OS + READ_PHONE_STATE 권한이 있을 때

2019-12-31 14:59:56.528 32231-32231/me.sunphiz.android.myapplication2 D/sunphiz: deviceId= 230064929080355

Q OS + READ_PHONE_STATE 권한이 없을 때

Caused by: java.lang.SecurityException: getDeviceId: The user 10265 does not meet the requirements to access device identifiers.

Q OS + READ_PHONE_STATE 권한이 있을 때

2019-12-31 14:54:39.741 5812-5812/me.sunphiz.android.myapplication2 D/sunphiz: deviceId= null

Q OS + READ_PRIVILEGED_PHONE_STATE 권한이 있을 때

2019-12-31 14:59:56.528 32231-32231/me.sunphiz.android.myapplication2 D/sunphiz: deviceId= 230064929080355

그럼 READ_PRIVILEGED_PHONE_STATE 권한은 어떻게 얻을 수 있을까? 결론만 이야기하면, 다운로드 앱(구글 플레이 스토어 등에서 다운받아 설치한 앱)은 저 권한을 얻을 방법이 없다. 저 퍼미션이 선언된 시스템의 AndroidManifest.xml을 보면, protectionLevel이 signatureOrSystem이기 때문이다. 이 조건은 단말 제조사만 충족할 수 있으므로, 다운로드 앱은 targetSdkVersion 29부터 UID에 접근할 수 없다는 결론이 된다.

그럼 어떻게하란 말인가? UID를 이용하던 기능이 있다면, 아래 방법 중 하나를 적용해보자.

  1. targetSdkVersion을 28이하로 유지
  2. 광고 및 사용자 분석을 위한 고유 식별자‘로 대체 고려
  3. Android 식별자 사용 권장사항‘을 보고 기능 변경

그 밖에

UID는 변경이 불가능한 값이라 편리하다. 동시에, 내 서비스를 통해 값이 유출된다면 의도치 않은 사용자 피해가 생긴다. 조금 번거롭더라도 더 안전한 방향으로 개선하자.

참조

안드로이드의 다크 모드

다크 모드를 쉽게 설명하면, 어두운 배경에 밝은 글씨로 화면을 구성하는 것을 가리킨다. 오랫동안 다양한 서비스(이클립스, 인텔리제이 등)나 운영체제(윈도우 10, Mac OS 등)가 단순하게는 월페이퍼 변경 수준에서 복잡하게는 색상 구성(theme 혹은 appearance로 주로 표현)변경까지 다양한 방법으로 지원해 왔다. 스마트폰에서는 안드로이드의 리소스 관리에 한정자 값(qualifier value)으로 ‘night/nonight’가 API8에 포함되면서 기능을 사용할 방법이 열렸다. 본격적으로 삼성 갤럭시 One UI의 주요 기능으로써 P OS(API28)부터 지원을 시작한 후, 안드로이드(Android 10, Q OS 혹은 API29)아이폰(iOS 13)도 공식 지원하고 있다.

다크 모드에 설명이나 구현 방법에 대한 자료는 이미 인터넷에 충분하므로 여기서는, 다크 모드 자체에 대한 설명보다 ‘내 앱에도 적용해야 할까?’, 적용한다면 ‘내 앱에는 적용을 어떻게 할까?’라는 질문에 함께 고민해 보자.

해야 할까?

내 앱에 적용을 할지 고민하기 위해, 다크 모드의 장점을 먼저 살펴 보자. 보통 아래처럼 3가지를 꼽는다. 자세한 설명이 궁금하다면 이 글을 참고하자.

  1. 눈의 피로 개선
  2. 화면의 수면 연장
  3. 배터리 절약

세 꼭지 모두 좋은 말이지만, 앱 본연의 기능과 직접적 연관이 없다. 다크 모드가 적용된 기능이 불완전한 앱을 사용자가 좋아할까? 그럴리 없을 것이다. 그러니, 다크모드 보다는 기능을 우선하자. 다크 모드를 적용하기로 결정했다면 일반 모드(다크 모드를 사용하지 않을 때) 만큼 자연스럽게 제대로 만들자.

어떻게 할까?

다크 모드를 적용 하기로 결심했는가? 그럼, 다크 모드 적용 방법과 적용 범위에 따라 아래 같이 5가지로 분류해보자.

  • 강제 설정 기능 사용하기
    안드로이드 10은 어두운 테마 강제 설정 기능을 제공한다. 그러므로, 사용자의 핸드폰에 안드로이드 10이 설치되어 있다면 쉽게 다크 모드를 적용할 수 있다. 다만, 무엇이든 그렇듯이 알아서 되는 것이 내 입맛에 꼭 맞을리 없다. 안드로이드의 기능이니 꾸준히 나아지겠지만, 완벽을 기대하긴 어려울 것이다. 더 나은 다크 모드를 원한다면 테마와 스타일을 곁들이거나, 강제 설정 기능을 아예 활성화 하지 않고 다크 모드를 적용하길 권장한다.
    방법은 공식 가이드를 참고하자.
  • 적용하지 않기
    색상 구성이 서비스의 아이덴티티와 직결되는 경우라면, 전략적으로 적용하지 않을 수 있다.
    페이스북 앱이 검은색 바탕에 파란색 따봉을 띄울 때, 사용자가 어떻게 느낄지 상상이 잘 안된다. 또 백신처럼 기능에 치중하는 것이 미덕인 서비스라면, 사용자 요구가 커질 때 적용하는 것도 좋겠다.
  • 느낌만 살리기
    핸드폰이 다크 모드인 상태에서 실행 시, ‘로딩 중’ 같은 초기 화면에만 적용할 수 있다.
    슬랙(Slack)과 같은 앱이 한동안 이 형태로 다크 모드를 지원했다.
  • 중요 페이지만 적용하기
    메인 화면이나, 사용자가 주로 머무는 화면에 다크 모드를 적용하고 나머지는 그대로 둘 수 있다.
    구글 뉴스는 언론사와 같은 뉴스 제공자로부터 컨텐츠를 받아 큐레이션만 하고, 뉴스 선택 시 컨텐츠를 연결하는 방식으로 동작했다. 서비스 구조 상 컨텐츠 제어가 어렵다면 뉴스 목록 페이지만 다크 모드를 적용할 수 있다.
  • 완벽하게 적용하기
    장기적으로 대부분의 앱이 이렇게 할 것이다. 앞서 예로 든 슬랙이나 구글 뉴스도 과도기를 거쳐 현재는 완벽한 다크 모드를 지원한다. 다만, 이상적인 만큼 생각보다 품이 많이 든다.
    제조사 앱은 완벽하게 적용되어 있으며, 메이저 앱들 중 다수도 이미 완벽하게 지원하고 있다.
    적용 방법은 공식 문서를 참고하자.

그 밖에

  • 다크 모드를 지원하지 않는 낮은 OS가 설치된 휴대폰이라면, 다크 모드는 쓸 수 없다. 혹시, 삼성 휴대폰 사용자라면 갤럭시테마의 고대비테마를 통해 비슷한 경험과 장점을 누릴 수 있다.
  • 현재 Q OS의 안드로이드 OS 점유율은 2019년 5월 기준, 통계에 표시하지 않을 정도로 낮다. 강제 설정 기능만으로 다크 모드를 지원하면, 대부분의 사용자는 다크 모드를 경험할 수 없다.

참조

char와int 비교/치환

public class MyString {
  public static void main(String[] args) {
    char[] data = {'a', 'b', 'c'};
    String str = new String(data);
    char c = str.charAt(0);
    int i = c;
		
    println("c= " + c);
    println("i= " + i);
    println("(0+c)= " + (0 + c));
    println("(int)c= " + (int) c);
    println("(char)i= " + (char) i);

    if (i == c) println("i==c? true");		
    if (c == i) println("c==i? true");
    if (c == 97) println("c==97? true");
    if (i == 97) println("i==97? true");
  }

  static void println(String _msg) {
    System.out.println(_msg);
  }
}

output?
char와int 비교/치환 더보기