졸업프로젝트 week20
ContentValues 일부분석
아래의 코드는 ContentValues 함수에서 타입별 get 작업을 하는 일부 코드이다.
/**
* Gets a value. Valid value types are {@link String}, {@link Boolean}, and
* {@link Number} implementations.
*
* @param key the value to get
* @return the data for the value
*/
public Object get(String key) {
return mValues.get(key);
}
/**
* Gets a value and converts it to a String.
*
* @param key the value to get
* @return the String for the value
*/
public String getAsString(String key) {
Object value = mValues.get(key);
return value != null ? value.toString() : null;
}
/**
* Gets a value and converts it to a Long.
*
* @param key the value to get
* @return the Long value, or null if the value is missing or cannot be converted
*/
public Long getAsLong(String key) {
Object value = mValues.get(key);
try {
return value != null ? ((Number) value).longValue() : null;
} catch (ClassCastException e) {
if (value instanceof CharSequence) {
try {
return Long.valueOf(value.toString());
} catch (NumberFormatException e2) {
Log.e(TAG, "Cannot parse Long value for " + value + " at key " + key);
return null;
}
} else {
Log.e(TAG, "Cannot cast value for " + key + " to a Long: " + value, e);
return null;
}
}
}
처음에 타입별로 getAs의 형태로 함수가 나뉘어져 있는 것을 보고, put 함수를 Object 타입의 value를 갖도록 하나로 줄이는 것이 괜찮은걸까 의문을 가졌다. 그리하여 해당 부분들을 분석해보았고, 우리는 각 getAs 함수들에서 value들을 타입에 맞게 변환해준다고 이해하여 put 함수를 하나로 만드는 것이 문제가 없다고 생각하였다.
코드개선 및 UnitTest
지난 시간에 확인했었던 개선하려는 코드를 전체 빌드를 하기 전에 따로 빼내어 단위 테스트를 진행했다.
ContentValues에 필드값인 Hashmap의 mValues에 put하는 함수들이 인스턴스별로 나누어져 있는 상태의 코드를 확인했다.
public void put(String key, String value) {
mValues.put(key, value);
}
/**
* Adds all values from the passed in ContentValues.
*
* @param other the ContentValues from which to copy
*/
public void putAll(ContentValues other) {
mValues.putAll(other.mValues);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Byte value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Short value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Integer value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Long value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Float value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Double value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Boolean value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, byte[] value) {
mValues.put(key, value);
}
/**
* Adds a null value to the set.
*
* @param key the name of the value to make null
*/
public void putNull(String key) {
mValues.put(key, null);
}
위의 코드를 호출하는 ContentProviderOperation 클래스의 withValue 함수의 코드에도, 같은 형태로 인스턴스별로 나누어져 있는 불필요한 코드를 확인하였다.
public Builder withValue(String key, Object value) {
if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
throw new IllegalArgumentException("only inserts and updates can have values");
}
if (mValues == null) {
mValues = new ContentValues();
}
if (value == null) {
mValues.putNull(key);
} else if (value instanceof String) {
mValues.put(key, (String) value);
} else if (value instanceof Byte) {
mValues.put(key, (Byte) value);
} else if (value instanceof Short) {
mValues.put(key, (Short) value);
} else if (value instanceof Integer) {
mValues.put(key, (Integer) value);
} else if (value instanceof Long) {
mValues.put(key, (Long) value);
} else if (value instanceof Float) {
mValues.put(key, (Float) value);
} else if (value instanceof Double) {
mValues.put(key, (Double) value);
} else if (value instanceof Boolean) {
mValues.put(key, (Boolean) value);
} else if (value instanceof byte[]) {
mValues.put(key, (byte[]) value);
} else {
throw new IllegalArgumentException("bad value type: " + value.getClass().getName());
}
return this;
}
이처럼 불필요하다고 생각되는 코드들을 개선하기로 하였고, 실제 빌드 전 단위 테스트를 진행하였다. 단위테스트를 진행한 코드는 아래와 같다.
//개선전 ContentValues 클래스
import java.util.HashMap;
public class ContentValues {
private HashMap<String, Object> mValues;
public ContentValues() {
mValues = new HashMap<String,Object>();
}
public void put(String key, String value) {
mValues.put(key, value);
}
/**
* Adds all values from the passed in ContentValues.
*
* @param other the ContentValues from which to copy
*/
public void putAll(ContentValues other) {
mValues.putAll(other.mValues);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Byte value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Short value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Integer value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Long value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Float value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Double value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Boolean value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, byte[] value) {
mValues.put(key, value);
}
/**
* Adds a value to the set.
*
* @param key the name of the value to put
* @param value the data for the value to put
*/
public void put(String key, Object value) {
mValues.put(key, value);
}
/**
* Adds a null value to the set.
*
* @param key the name of the value to make null
*/
public void putNull(String key) {
mValues.put(key, null);
}
}
//개선전 ContentProviderOperation
public static ContentValues mValues;
public static void withValue(String key, Object value) {
if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
throw new IllegalArgumentException("only inserts and updates can have values");
}
if (mValues == null) {
mValues = new ContentValues();
}
if (value == null) {
mValues.putNull(key);
} else if (value instanceof String) {
mValues.put(key, (String) value);
} else if (value instanceof Byte) {
mValues.put(key, (Byte) value);
} else if (value instanceof Short) {
mValues.put(key, (Short) value);
} else if (value instanceof Integer) {
mValues.put(key, (Integer) value);
} else if (value instanceof Long) {
mValues.put(key, (Long) value);
} else if (value instanceof Float) {
mValues.put(key, (Float) value);
} else if (value instanceof Double) {
mValues.put(key, (Double) value);
} else if (value instanceof Boolean) {
mValues.put(key, (Boolean) value);
} else if (value instanceof byte[]) {
mValues.put(key, (byte[]) value);
} else {
throw new IllegalArgumentException("bad value type: " + value.getClass().getName());
}
}
}
//개선 후 ContentValues 단위 코드
import java.util.HashMap;
public class ContentValues {
private HashMap<String, Object> mValues;
public ContentValues() {
mValues = new HashMap<String,Object>();
}
public void put(String key, Object value) {
mValues.put(key, value);
}
public void putAll(ContentValues other) {
mValues.putAll(other.mValues);
}
public void putNull(String key) {
mValues.put(key, null);
}
}
//개선 후 ContentProviderOperation 단위 코드
public static ContentValues mValues;
public static void withValue(String key, Object value) {
if (mType != TYPE_INSERT && mType != TYPE_UPDATE && mType != TYPE_ASSERT) {
throw new IllegalArgumentException("only inserts and updates can have values");
}
if (mValues == null) {
mValues = new ContentValues();
}
if (value == null) {
mValues.putNull(key);
}
else if(value !=null) {
mValues.put(key,value);
}
else {
throw new IllegalArgumentException("bad value type: " + value.getClass().getName());
}
}
}
//테스트를 위한 Main코드
public static void main(String[] args) {
// TODO Auto-generated method stub
mValues = new ContentValues();
Random random = new Random();
ArrayList<String> testKeySet = new ArrayList<String>();
ArrayList<Object> testObjectSet = new ArrayList<Object>();
for(int i=0;i<100;i++) {
testKeySet.add("a"+i);
switch(i%6) {
case 0:
testObjectSet.add("a"+random.nextInt());//String타입
break;
case 1:
testObjectSet.add(random.nextInt());
break;
case 2:
testObjectSet.add(random.nextDouble());
break;
case 3:
testObjectSet.add(random.nextFloat());
break;
case 4:
testObjectSet.add(random.nextLong());
break;
case 5:
testObjectSet.add(random.nextBoolean());
break;
}
}
long startTimeBefore = System.nanoTime();
for(int j=0;j<10000000;j++) {
withValueBefore(testKeySet.get(random.nextInt(100)), testObjectSet.get(random.nextInt(100)));
}
long EndTimeBefore = System.nanoTime();
System.out.printf("before Time: %d\n",EndTimeBefore - startTimeBefore);
long startTime = System.nanoTime();
for(int j=0;j<10000000;j++) {
withValue(testKeySet.get(random.nextInt(100)), testObjectSet.get(random.nextInt(100)));
}
long EndTime = System.nanoTime();
System.out.printf("after Time:%d",EndTime - startTime);
위와 같이 임의의 String 형태의 Key값을 100개와 Java의 Random 클래스를 통한 String, Int, Long, Double, Float, Boolean 형태의 value 값 100개를 생성하여 key, value 쌍을 만들어 위에서 개선한 HashMap<String,Object> 형태의 변수에 put하는 과정을 테스트해보았다. 결과는 다음과 같다.
빌드
개선한 코드로 전체 빌드는 성공하였으나, 에뮬레이터 상에서 계속해서 에러들이 떠 테스트 애플리케이션을 사용하는데에 어려움을 겪고있다. 에뮬레이터에서 왜 이러한 에러들이 발생하는지 확인하고 해결방법을 찾아야 할 것 같다.