Log for everything - Day

안드로이드 개발시에만 로그 출력하기

|

사용씬

디버그로 빌드할때만 로그가 출력되고, release로 빌드했을때는 로그가 출력되지 않도록 하려고 한다.

사용 방법

Log 클래스의 Wrapper class를 생성한 후, BuildConfig.BUILD_TYPE 값을 체크하여 로그를 출력한다.

public class LogUtils {
    public static boolean LOGGING_ENABLED = !BuildConfig.BUILD_TYPE.equalsIgnoreCase("release");

    public static void LOGD(final String tag, String message) {
        if (LOGGING_ENABLED){
            if (Log.isLoggable(tag, Log.DEBUG)) {
                Log.d(tag, message);
            }
        }
    }

    public static void LOGD(final String tag, String message, Throwable cause) {
        if (LOGGING_ENABLED){
            if (Log.isLoggable(tag, Log.DEBUG)) {
                Log.d(tag, message, cause);
            }
        }
    }

    public static void LOGV(final String tag, String message) {
        if (LOGGING_ENABLED) {
            if (Log.isLoggable(tag, Log.VERBOSE)) {
                Log.v(tag, message);
            }
        }
    }

    public static void LOGV(final String tag, String message, Throwable cause) {
        if (LOGGING_ENABLED) {
            if (Log.isLoggable(tag, Log.VERBOSE)) {
        Log.v(tag, message, cause);
            }
        }
    }

    public static void LOGI(final String tag, String message) {
        if (LOGGING_ENABLED) {
            Log.i(tag, message);
        }
    }

    public static void LOGI(final String tag, String message, Throwable cause) {
        if (LOGGING_ENABLED) {
            Log.i(tag, message, cause);
        }
    }

    public static void LOGW(final String tag, String message) {
        Log.w(tag, message);
    }

    public static void LOGW(final String tag, String message, Throwable cause) {
        Log.w(tag, message, cause);
    }

    public static void LOGE(final String tag, String message) {
        Log.e(tag, message);
    }

    public static void LOGE(final String tag, String message, Throwable cause) {
        Log.e(tag, message, cause);
    }

    private LogUtils() {
    }

}

참고로 INFO 레벨 미만은 Log.isLoggable을 이용해 로그가 출력 가능한지 확인해줘야 한다.
디폴트 로그 레벨 출력값은 Info 이상이다.

이스터에그? Log.wtf

API 문서를 참조하다. 로그레벨 wtf이 있다는 것을 알게되었다.
“What a Terrible Failure: Report a condition that should never happen.”라고 설명하고 있지만…
우리는 모두 진실을 알고있다.

안드로이드 SQLite에 대량의 레코드 삽입하기

|

사용씬

안드로이드의 로컬 저장소인 SQLite에 대용량의 레코드를 한번에 삽입하려 한다.
ArrayList<Data> data 안에 4만개의 레코드가 있다.

통상정인 방법

SqliteOpenHelper 내 메소드

public long insertData(Data data) {
    ContentValues cv = new ContentValues();
    cv.put(COLUMN_A, dataA);
    cv.put(COLUMN_B, dataB);
    cv.put(COLUMN_B, dataC);

    return getWritableDatabase().insert(TABLE_A, null, cv);
}

하나의 레코드를 삽입하는 메소드를 정의했다.

insertData 사용

for (int i=0; i<data.size(); i++) {
    dbHelper.insertData(data.get(i));
}

for 문을 돌려서 Data를 삽입한다.

문제점

만약 4만개의 레코드를 한번에 저장하려면 위의 방법으로는 대략 1분이 넘어간다.

long startTime = System.nanoTime();
    for (int i=0; i<data.size(); i++) {
    dbHelper.insertData(data.get(i));
}
long finishTime = System.nanoTime();
Log.e(TAG, "Write to DB takes: "+ (finishTime-startTime)/1e9 +" seconds");

위와 같이 시간을 확인해 보았을 때, 94.216414초가 걸렸다.

원인

이는 SQlite가 하나의 레코드를 생성할때 Transaction을 수행하며 저널 파일을 생성하기 때문인데,
위의 방법으로는 4만여개의 저널파일을 생성하며 DB의 삽입 속도가 느려지게 된다.

해결책

하나의 Transaction으로 처리해 4만여개의 레코드를 한번에 삽입하며 저널파일을 한번만 생성하게 되면 된다.

public void insertAllDatas(ArrayList<Data> datas) {
SQLiteDatabase db = getWritableDatabase();
db.beginTransaction();
try {
    for (int i=0; i<datas.size(); i++ ){
        ContentValues cv = new ContentValues();
        cv.put(COLUMN_A, dataA);
        cv.put(COLUMN_B, dataB);
        cv.put(COLUMN_B, dataC);
        db.insert(TABLE_A, null, cv);
    }
    db.setTransactionSuccessful();
} finally {
    db.endTransaction();
}

위의 방법으로 4만개의 레코드를 삽입할 때에는 2.813887초가 걸렸다.

안드로이드 Activity에서 Fragment로부터 데이터 전달 받기

|

사용씬

액티비티에서 Fragment Transaction을 이용하여 DialogFragment 등을 호출했다.
이 Dialog가 종료시에 Activity가 결과값을 돌려받고자 한다.

문제점

Fragment - Fragment 면 setTargetFragmentonActivityResult를 통해 인텐트로 전달가능하다.
하지만 Activity가 Fragment를 호출했을때는 Dialog가 종료 시 onActivityResult가 호출되지 않는다.

해결책

액티비티에서 인터페이스를 생성하여 구현하거나 기타등등 해결법이 있지만 제일 간단한 방법은…

  1. 액티비티에 public 메소드를 구현한다.
  2. DialogFragment에서 다음과 같이 액티비티의 메소드를 인자와 같이 호출한다.
    ((MainActivity)getContext()).someMethod(args);
    

엄밀히 따지면 데이터 전달은 아니다.

안드로이드 startActivity 시에 기존 activity 제거하기

|

인텐트 전달하며 기존 액티비티 제거하기

액티비티에서 액티비티로 갈때에는 finish(); 를 호출하면 간단하다.

비동기 태스크와 같이 액티비티가 아닌 곳에서 기존 액티비티를 없애려면 다음과 같이 한다.

Intent i = new Intent(context, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(i);

사용법

Splash 화면 만들어놓고 비동기로 작업 해놓고 그 작업이 다 완료되면 다음 액티비티로 넘어가며,
Back Stack에 Splash Activity가 남지 않게 하려면 사용하면 된다.

안드로이드 레이아웃 XML에서 위젯에 물결 이펙트 넣기

|

요즘 바빠서 블로깅하기가 힘들다.
그냥 간단한 것이라도 남기기로 한다.

XML에 물결 이펙트 넣기

위젯이 터치 이벤트에 반응하여 움직이는 것처럼 보이게 하려면 위젯의 XML attribute에
android:background="?attr/selectableItemBackgroundBorderless" 를 추가하면 된다.

이펙트 크기는 padding값을 적절히 조절하면 된다.