메소드 개수가 65536개 이상일 때 빌드가 안되는 현상

|

안드로이드 어플리케이션을 개발할 때 메소드 개수가 65536개 이상이 되어 빌드가 되지 않는 현상이 있습니다. 빌드시 발생하는 오류 메세지는 다음과 같습니다.


오류메세지

Execution failed for task ':app:transformClassesWithDexForDebug'.
>; com.android.build.api.transform.TransformException:
com.android.ide.common.process.ProcessException:
java.util.concurrent.ExecutionException:
com.android.dex.DexIndexOverflowException:
method ID not in [0, 0xffff]: 65536

DexIndex가 오버플로(Overflow)되었으며, 메소드 ID(method ID)를 인덱스내에서 발견하지 못했다는 메세지입니다.


해결법

이 경우는 몇 가지 해결책이 있습니다. 보통, 메소드 개수가 65536개를 넘는 경우가 흔하지는 않습니다. 이를 넘긴 경우는 주로 외부 오픈 소스들을 남발한 경우가 대부분일 것입니다. 즉, 이런 경우는 다음과 같은 방법으로 해결할 수 있습니다.

  • 참조하는 외부 라이브러리 개수를 줄이고, 필요한 건 직접 구현해서 사용하는 방법
  • Proguard 등을 이용하여 사용하지 않는 메소드를 삭제하는 방법
  • Multidex 기능을 이용하는 방법

가장 좋은 건 리팩토링을 통해 구조 개선을 하고, 꼭 필요한 라이브러리만 참조하는 방법입니다. 하지만, 이 방법은 시간과 노력이 많이 드는 방법이라 일단 Multidex 기능을 이용하는 방법을 알아보도록 하겠습니다.

참고로, 이 문제에 대한 구글의 공식 설명이 존재합니다.


Multidex 활용한 해결 방법

build.gradle 수정

defaultConfig에 다음과 같이 ‘multiDexEnabled true’ 항목을 추가합니다.

defaultConfig {
    ...
    multiDexEnabled true
    ...
}

그리고 dependencies에도 다음 항목을 추가해줍니다.

dependencies {
    ...
    compile 'com.android.support:multidex:1.0.0'
    ...
}


Application 상속

그리고 메인 Application을 다음과 같이 ‘android.support.multidex.MultiDexApplication’을 상속받도록 수정합니다.

import android.support.multidex.MultiDexApplication;

public class MyApplication extends MultiDexApplication {
  ...
}

만약 Application을 Java 클래스로 구현해서 사용하고 있지 않다면, manifest.xml에서 application을 다음과 같이 수정하면 됩니다.


Manifest.xml

<application
  android:name="android.support.multidex.MultiDexApplication"
  ...>

4차 산업혁명이란

|

요즘 화두가 되고 있는 단어 ‘4차 산업혁명’에 대해서 알아보도록 하겠습니다.


4차 산업혁명(4IR, 4th Industrial Revolution)

간단히 말하면 정보통신기술의 융합으로 가져올 기술 혁신을 말합니다. 대표적인 것들로 인공지능, 로봇공학, IoT(Internet of Things), 무인 시스템, 3D 프린터 등을 들 수 있습니다.

‘4차 산업혁명’이라는 단어는 2016년 1월 20일, 스위스에서 열린 ‘세계 경제 포럼’에서 처음 언급되었습니다. 세계 경제 포럼에서는 4차 산업혁명을 ‘3차 산업혁명을 기반으로 한 디지털과 바이오산업, 물리학 등의 경계를 융합하는 기술혁명’이라고 설명했습니다.


산업혁명의 역사

그러면 산업혁명의 역사에 대해서 잠깐 살펴보도록 하겠습니다.

1차 산업혁명

증기기관의 등장으로 가져온 산업혁명입니다. 가장 유명한 산업혁명입니다. 유럽과 미국에서 18세기에서 19세기에 걸쳐서 일어났습니다.


2차 산업혁명

전기로 공장에서 대량생산이 가능하게 된 산업혁명입니다. 전기, 철강, 석유 등의 산업이 크게 확장되었습니다. 제 1차 세계대전 직전인 1870년에서 1914년 사이에 일어났습니다.


3차 산업혁명

아날로그 장비에서 디지털 장비로 넘어가게 된 산업혁명입니다. 컴퓨터를 이용한 생산 자동화가 이루어지고, 컴퓨터의 보급화, 인터넷이 대중적으로 퍼지게 됩니다.


4차 산업혁명

4차 산업혁명은 대규모의 빅데이터 분석, 인공지능, 그리고 각 사물들의 연결 및 다양한 분야의 융합으로 이루어지리라 예상하고 있습니다.


제조 산업의 패러다임 변화

4차 산업혁명은 제조 산업 전반의 패러다임을 바꾸고 있습니다. 기존의 제조 산업이 공장에서 각 라인을 따라 부품들이 자동으로 조립되어져 나오는 것이었다면, 4차 산업에서는 각 라인을 담당하는 기계들이 서로 연결되어 있고 끊임없이 상태를 체크하고 정보를 교환합니다. 재고량과 생산량을 자동으로 체크하고 조절할 수 있으며, 부품의 상태에 따라 자동으로 부품을 바꾸거나 조립 순서를 바꿉니다. 기계 스스로 학습하고 다양한 제품들을 신속하게 만들어냅니다.

기존에는 생산 시스템을 조금 수정하려면 상당히 긴 시간이 필요했지만, 4차 산업혁명 이후는 이러한 변경이 짧은 시간에 이루어질 수 있고, 다양한 제품들을 쉽게 만들어내어 소비자들의 다양한 욕구를 더욱 쉽게 충족시킬 수 있게 됩니다.

Base64 인코딩

|

Base64

Base64는 64진법이라는 뜻입니다. 화면에 표현하는 문자 종류가 64가지라는 뜻으로 이해하면 됩니다. 전자 메일이나 바이너리 데이터를 전송할 때 많이 사용됩니다. Base64의 정확한 규격은 RFC 1421, RFC 2045에 정의되어 있습니다.


사용하는 문자

Base64에서 사용하는 문자는 A-Z, a-z, 0-9의 총 62가지에 기호 2개로 이루어져 있습니다. 기호 2개는 보통 ‘+’와 ‘/’으로 구성되는데, Base64 변종들에 따라서 조금씩 다르기도 합니다. 그리고 끝을 알리는 코드로 ‘=’를 사용합니다.

image


한계점

Base64의 한계점은 보통 대문자, 소문자, 숫자의 62개 문자외에 추가로 사용하는 기호 2개에서 옵니다. 해당 문자는 파일 이름이나 URL 주소 등으로 사용할 수 없는 특수 문자입니다. 그러다보니 기호 2개를 뺀 62개의 문자로 표현하는 Base62나 눈으로 보면 헷갈리는 문자들(예를 들어 O와 o, 숫자 0 등)을 제외한 Base58 등을 사용하기도 합니다.

URL 주소를 이용하여 ImageView에 이미지 채우기 (Glide 활용)

|

URL 주소를 이용한 이미지 로딩

이미지의 URL 주소를 알고 있을 때, 그 이미지를 ImageView에 그리는 방법은 다음과 같습니다.

  • 이미지를 다운로드한다.
  • 다운로드한 이미지를 Bitmap 로딩하여 ImageView에 채워 넣는다.

이 경우, 구현해야 할 코드의 양도 꽤 많지만, 성능 문제를 위해 여러 가지 기법을 적용해야 합니다. 예를 들면, 다음과 같습니다.

  • ListView 등과 같이 여러 장의 이미지를 동시에 다운로드 할 수 있는 Thread 환경 구현
  • 한 번 다운로드 받은 이미지는 다시 받지 않도록, 그리고 성능을 위해 Image Cache 구현
  • Thread로 다운로드하는 동안 화면에는 ProgressBar를 표시


기본적으로 위의 요소들이 있지만, 하나 하나를 파고들면 고려해볼 것이 상당히 많습니다. 예를 들면 Image Cache에 무한대의 이미지를 저장할 수 없기 때문에 Cache의 리소스 관리도 필요하고, 이미지 다운로드 및 로딩이 끝났을 때 어느 ImageView에 이미지를 그릴 것인지 관리하는 ImageView의 레퍼런스 저장도 필요합니다. 또한 이러한 것들은 WeakReference로 관리하여 해당 ImageView가 메모리에 여전히 남아 있는지 GC(Garbage Collector)로 청소가 끝났는지도 체크해야 합니다.

예전에 프로젝트를 진행하면서 직접 구현한 적이 있었는데, 신경쓸게 너무 많아서 고생을 한 적이 있습니다. 물론, 그만큼 공부는 많이 되긴 했습니다.


Glide

이러한 것들을 한 꺼번에 해소해주기 위해서 오픈 소스 활용을 추천합니다. 오픈 소스는 유명한 것들이 많이 있는데, 5년 정도 전만 하더라도 저는 Android Universal Image Loader를 애용하였습니다. 지금도 무난히 사용하기에 충분히 괜찮은 라이브러리입니다. 하지만, 지금은 Glide 라이브러리를 사용해보려고 합니다. Glide는 구글이 인수한 Bump라는 회사에서 사용한 이미지 로딩 라이브러리입니다.


사용 방법

Gradle에 다음 코드를 추가해줍니다.

dependencies{
    compile'com.github.bumptech.glide:glide:3.7.0'
    ...
}


그리고 별도로 XML 코드를 건드릴 필요 없고, 다음과 같은 Java 코드로 ImageView에 이미지를 불러오게 할 수 있습니다.

Glide
    .with(mContext)
    .load(item.image)
    .centerCrop()
    //.placeholder(R.drawable.loading_spinner)
    .crossFade()
    .into(holder.image);

이렇게만 하면 끝입니다. 더 자세한 사용법은 여기에서 확인할 수 있습니다.

OkHttp를 활용한 GET, POST

|

OkHttp는 HTTP 및 HTTP/2 통신을 보다 쉽게 할 수 있도록 다양한 기능을 제공해주는 Android 및 Java 용 라이브러리입니다. 오픈 소스로 되어 있으며 GitHub에서 소스 확인 및 다운로드할 수 있습니다.


Gradle 설정

build.gradle에 다음 라인을 추가해줍니다.

compile 'com.squareup.okhttp3:okhttp:3.6.0'


GET 예제

public boolean getUserInfo(final Context context) {

  try {
    OkHttpClient client = new OkHttpClient();

    String url = SERVER_CONFIGURATION.ADDRESS + ":" +
        SERVER_CONFIGURATION.PORT + "/v1/userinfo";

    Request request = new Request.Builder()
        .addHeader("Authorization", "TEST AUTH")
        .url(url)
        .build();
    Response response = client.newCall(request)
        .execute();

    String result = response.body().string();

    Gson gson = new Gson();
    UserInfo info = gson.fromJson(result, UserInfo.class);

    Log.i("id: " + info.id);
    Log.i("name: " + info.name);

    return true;
  } catch(Exception e) {
    e.printStackTrace();
  }

  return false;
}


POST 예제

private boolean updatetMetaInfo(JsonItemMetaInfo metaInfo) {

  try {
    OkHttpClient client = new OkHttpClient();

    String url = SERVER_CONFIGURATION.ADDRESS + ":" +
        SERVER_CONFIGURATION.PORT + "/v1/updateMetaInfo";

    Gson gson = new Gson();
    String json = gson.toJson(metaInfo);

    Request request = new Request.Builder()
        .url(url)
        .post(RequestBody.create(MediaType.parse("application/json"), json))
        .build();

    Response response = client.newCall(request).execute();

    Log.i("request : " + request.toString());
    Log.i("Response : " + response.toString());

    return true;
  } catch(Exception e) {
    e.printStackTrace();
  }

  return false;
}


PUT 예제

public boolean registerAppToken(JsonToken token) {
  try {
    OkHttpClient client = new OkHttpClient();

    String url = SERVER_CONFIGURATION.ADDRESS + ":" +
        SERVER_CONFIGURATION.PORT + "/v1/registerAppToken";

    Gson gson = new Gson();
    String json = gson.toJson(token);

    Request request = new Request.Builder()
        .addHeader("key", "Content-Type")
        .addHeader("value", "application/json")
        .addHeader("description", "")
        .url(url)
        .put(RequestBody.create(MediaType.parse("application/json"), json))
        .build();

    Response response = client.newCall(request).execute();

    Log.i("request : " + request.toString());
    Log.i("Response : " + response.toString());

    return true;
  } catch(Exception e) {
    e.printStackTrace();
  }

  return false;
}