어떻게 설명해야할지 모르겠지만, 생각보다 많이 힘들었당.. 그래도 시행착오를 기록해보고 싶다!
1. GUI 가져다 쓰기
그냥 단순히 복붙을 하면 된다고 생각했지만 그렇지가 않았다. 근데 사실 지금 생각해보니깐 복붙을 하면 되는 거였다.. 무언가를 보여주는 디자인 틀을 먼저 짜보자고 시작했는데, 복붙을 하고나서 사용법을 코드를 일일이 뜯어봐야 했다.. 설명이 쉽지 않아서 ViewPage에 RecyclerView를 붙인 디자인이었고, 대충 모든걸 다 편집할 수 있어보이는 느낌이었다. 실제로 그게 가능했다. 되게 깔끔하게 잘 짜여져 있는걸 보고 놀랐다. RecyclerView도 여러개 자바 파일을 만들지 않고, 메인 함수에서 ViewHolder, RecyclerView를 만드는걸보고 신기했다.
2. SQLite 데이터
우선 수능, 모의고사 데이터를 사용할거기 때문에 직접 데이터를 구해야했는데, 이걸 어디서 구하고 어떻게 안드로이드에 넣을까 생각해보았다. 내가 가진 기술로는, 안드로이드 자바 파일에서 전역변수로 String배열을 엄청많이 만든다음, 거기다 하나하나 텍스트를 대입하는 방법을 구상했었는데 그것만큼 지저분한건 없어보였다. 그래서 데이터를 관리하는 방법 중 하나인 SQLite를 알게되었고 찾아보았는데, 대충 내용은 사용자가 데이터 파일을 생성하고, 저장하고, 수정하는 내용이었다. 내가 필요한건 개발자가 만든 데이터를 주입시키는? 것이었어서 방법을 찾다가 assets폴더에 db를 옮겨놓고 그걸 스마트폰에 복사하는 코드를 만들어서 사용자가 생성한 것 처럼 사용하는 방법이었다. 1MB를 넘기면 또 새로운 코드가 필요하다고했지만, 절대 넘길일이 없어보였다. 넘기지 않으려고 한다..넘으면 그때가서 생각하겠다.
info class를 만들어줍니다. getter라고하는데 다음과 같이 직접 만들어주면 됩니다.
//info
@IgnoreExtraProperties
public class info {
public int age;
public String fullName;
public String favoriteFoods;
public long nowTime;
public info() {
// Default constructor
}
public info(int age, String fullName, String favoriteFoods, long nowTime) {
this.age = age;
this.fullName = fullName;
this.favoriteFoods = favoriteFoods;
this.nowTime = nowTime;
}
}
//MainActivity
info info_palab = new info(21,"paran laboratory","chicken",System.currentTimeMillis());
mDatabase.child("PA-LAB").setValue(info_palab);
1번과 2번의 결과는 같습니다. 하지만, 2번 방식을 사용해야합니다. 왜냐하면, 더 효율적이니까요.
데이터를 여러개 쓰거나, 읽을 때 엄청난 차이를 보입니다.
Key는 PA-LAB 입니다.
반복의 예시입니다.
info info_palab = new info(21,"paran laboratory","chicken",System.currentTimeMillis());
info info_nolab = new info(21,"norang laboratory","pizza",System.currentTimeMillis()*2);
info info_cholab = new info(21,"chorock laboratory","hamburger",System.currentTimeMillis()*3);
mDatabase.child("PA-LAB").setValue(info_palab);
mDatabase.child("NO-LAB").setValue(info_nolab);
mDatabase.child("CHO-LAB").setValue(info_cholab);
Key는 각각 CHO-LAB, NO-LAB, PA-LAB 입니다.
3) 목록 쓰기 - 데이터 구조화
push() 는 목록을 만들어주는데, 랜덤한 문자열을 Key로 할당합니다.
push 후에 데이터를 넣어주어야 목록이 만들어집니다.
mDatabase.push().setValue(info_palab);
Key는 LgqrkBbir0HEV_A3zxc 입니다.
3. 읽기
데이터베이스에 리스너를 달아주어야 한다. 리스너를 선언하고, 초기화하고, 달아주면된다.
데이터베이스에 리스너를 달아주면됩니다. 데이터가 변경될 시 자동으로 호출되며, 2가지 종류가 존재합니다.
중요한 점은 데이터베이스에 리스너는 꼭 하나만 달려있어야합니다. 중첩 사용시 중첩 작동하여 문제가 발생합니다.
add로 리스너를 달아주고, remove로 리스너를 해제해야합니다. 해제하기 싫으면, 한번만 실행되게 달아줄 수도 있습니다. ValueEventListener의 경우 addListenerForSingleValueEvent()를 해주면 remove할 필요없이 한번만 실행되고 끝납니다.
1) ValueEventListener
변경사항을 한번에 처리한다. 삭제, 추가, 목록 추가 등 하위(자식) 데이터들이 변경될 시 onDataChange가 호출된다.
//선언
ValueEventListener mValueEventListener;
//초기화
ValueEventListener mValueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
String key = postSnapshot.getKey();
info info_each = postSnapshot.getValue(info.class);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
};
//사용
mDatabase.addValueEventListener(mValueEventListener);
//삭제
mDatabase.removeEventListener(mValueEventListener);
아래 예시를 들어보겠습니다.
info info_palab = new info(21,"paran laboratory","chicken",System.currentTimeMillis());
info info_nolab = new info(21,"norang laboratory","pizza",System.currentTimeMillis()*2);
info info_cholab = new info(21,"chorock laboratory","hamburger",System.currentTimeMillis()*3);
mDatabase.child("LAB-LIST").push().setValue(info_palab);
mDatabase.child("LAB-LIST").push().setValue(info_nolab);
mDatabase.child("LAB-LIST").push().setValue(info_cholab);
ValueEventListener mValueEventListener = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
String key = postSnapshot.getKey();
info info_each = postSnapshot.getValue(info.class);
}
}
@Override
public void onCancelled(DatabaseError databaseError) {
}
};
mDatabase.child("LAB-LIST").addValueEventListener(mValueEventListener);
for문은 총 3번 돕니다. 리스너를 LAB-LIST에 달았기때문에, 그 하위 데이터 Key의 데이터들에 접근합니다.
key에는 LgrJctfpcOVGefVUqRT, LgrJctqyCIgf8Gqv0TQ, LgrJctqyCIgf8Gqv0TR 이 담기고,
info_each에는 info_palab, info_nolab, info_cholab 이 담깁니다.
2) ChildEventListener
두 리스너는 모두 비슷하게 동작하며, ChildEventListener은 조금 더 구체적으로 작동합니다. 데이터의 변경 종류에 따라
info_each에는 info클래스가 객체로 담기는데, info클래스를 만들고, getter을 만들면 자동으로 데이터를 매칭시켜줍니다.
만약 class를 만들지 않는다면, 데이터를 가져오는데 아주 복잡해질 것입니다.
4. 왜 사용할까, Log
사용방법은 끝났습니다! 이걸 왜 굳이 사용할까라는 생각이 들어서, 찾아본 내용을 정리해보았습니다.
서버는 큰 비용이 들 수도 있습니다. 작은 서버는 무료로 제공해주는 곳이 많은데 복잡합니다. 데이터를 처리하는게 어렵습니다. Firebase는 우리에게 서버를 제공해주고 관리까지 해줍니다. 앱을 서버 테스트를 하고 싶은데 아마존에서 서버를 구해서, 적용시키기는 복잡하지만 Firebase는 아주 간단합니다. 그리고 또 일정 용량 무료기 때문에 테스트로 아주 좋고, 용량이 넘으면 자동으로 확장시켜주기 때문에 간편합니다. 그 정도 크기가 된다면, 일정 수익이 발생할 것입니다. 왜냐면 10GB나 주거든요..!
생각보다 쉽게 정리된 블로그들이 없어서 처음에 엄청 헤맸습니다..최대한 쉽게 설명하고, 막혔던 부분들을 정리했습니다.
...
public class MainActivity extends AppCompatActivity {
private static final String CLOUD_VISION_API_KEY = ApiKey;
public static final String FILE_NAME = "temp.jpg";
private static final String ANDROID_CERT_HEADER = "X-Android-Cert";
private static final String ANDROID_PACKAGE_HEADER = "X-Android-Package";
private static final int MAX_LABEL_RESULTS = 10;
private static final int MAX_DIMENSION = 1200;
...
}
ApiKey에 생성한 Key를 넣어줍니다.
5. 사용
// add the features we want
annotateImageRequest.setFeatures(new ArrayList<Feature>() {{
Feature labelDetection = new Feature();
labelDetection.setType("LABEL_DETECTION");
labelDetection.setMaxResults(MAX_LABEL_RESULTS);
add(labelDetection);
}});
지금은 LABEL_DETECTION 으로 설정되어있습니다.
하지만 원하는 기능을 사용하고 싶으면 아래 코드를 수정하면 됩니다.
labelDetection.setType("LABEL_DETECTION");
/*
TYPE_UNSPECIFIED Unspecified feature type.
FACE_DETECTION Run face detection.
LANDMARK_DETECTION Run landmark detection.
LOGO_DETECTION Run logo detection.
LABEL_DETECTION Run label detection.
TEXT_DETECTION Run text detection / optical character recognition (OCR). Text detection is optimized for areas of text within a larger image; if the image is a document, use DOCUMENT_TEXT_DETECTION instead.
DOCUMENT_TEXT_DETECTION Run dense text document OCR. Takes precedence when both DOCUMENT_TEXT_DETECTION and TEXT_DETECTION are present.
SAFE_SEARCH_DETECTION Run Safe Search to detect potentially unsafe or undesirable content.
IMAGE_PROPERTIES Compute a set of image properties, such as the image's dominant colors.
CROP_HINTS Run crop hints.
WEB_DETECTION Run web detection.
PRODUCT_SEARCH Run Product Search.
OBJECT_LOCALIZATION Run localizer for object detection.
*/
모든 feature들을 주석처리 했습니다.
만약 feature을 수정했다면, 출력 메세지도 수정해주어야 합니다.
private static String convertResponseToString(BatchAnnotateImagesResponse response) {
StringBuilder message = new StringBuilder("I found these things:\n\n");
List<EntityAnnotation> labels = response.getResponses().get(0).getLabelAnnotations();
if (labels != null) {
for (EntityAnnotation label : labels) {
message.append(String.format(Locale.US, "%.3f: %s", label.getScore(), label.getDescription()));
message.append("\n");
}
} else {
message.append("nothing");
}
return message.toString();
}