문제점 : 

for문 안에서 AsyncTask.execute() 실행 시 병렬로 처리되는 게 아니라 순차적으로 실행되는 오류가 있었음.


for(int i=0;i<50;i++){

    new AsyncTask<Void, Void, Void> {

        protected Void doInBackground(Void... voids) {

            connect(view.getId() == R.id.button_release);

            return null;

        }

    }.execute();
}


그러니까 위의 코드를 실행했다고 하면



                ↗ 일1 ↘

일 3개 시작 -> 일2  → 종료

                ↘ 일3  ↗


가 아닌


일 3개 시작 ->일1->일2->일3->종료


로 진행됨.



해결 : 


for(int i=0;i<50;i++){

    new AsyncTask<Void, Void, Void> {

        protected Void doInBackground(Void... voids) {

            connect(view.getId() == R.id.button_release);

            return null;

        }

    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}


execute();

메소드 대신 

executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

를 사용해야 정상적으로 병렬로 처리가 된다.



참고 :


https://trend21c.tistory.com/1715

https://gist.github.com/benelog/5954649

'개발 > JAVA' 카테고리의 다른 글

JAVA SHA1 암호화  (0) 2018.07.20

Firestore를 사용하게 되어 메모 겸 요약본을 작성한다.

문서가 꽤 잘되있어 처음 시작은 어렵지 않을듯.


** firestore 지원 DataType

https://firebase.google.com/docs/firestore/manage-data/data-types?authuser=0



** firestore 데이터 가져오기

https://firebase.google.com/docs/firestore/query-data/get-data?authuser=0



** 시작하기 전에 권한이 없다고 나온다면..


권한에 들어가서 저 부분을 true로 바꾸면 된다. (저렇게 하면 읽기 쓰기 허용)








예를 들어 아래와 같은 데이터를 등록했다고 할 때..




database.document("notice/main").get().addOnCompleteListener(task -> {

    if(task.isSuccessful()){

        //성공

        DocumentSnapshot document = task.getResult();

        List list = (List) document.getData().get("list");

        for(int i=0;i<list.size();i++){

            Log.i("TEST", "data["+i+"] > " + list.get(i).toString());

            HashMap map = (HashMap) list.get(i);

            NoticeData data = new NoticeData();

            data.setTitle(map.get("title").toString());

            data.setContent(map.get("content").toString().replace("\\n", "\n"));

            data.setRegDate((Date) map.get("regdate"));

            Log.i("TEST", "["+i+"] > " + (list.get(i) instanceof HashMap) + " / " + (list.get(i).getClass().getName()) + " / " + list.get(i).toString());

        }

    }else{

         //실패

    }

};



한 줄씩 보자면..

** database.document("notice/main").get().addOnCompleteListener(task -> {

- Document를 가져올 것이라서 document(Colloction이름/Document이름).get()을 사용

- task 는 Task<DocumentSnapShot>이다.


** DocumentSnapshot document = task.getResult();

- 해당 문서 내부의 항목을 가져옴. (HashMap<String, Object> 형태


** List list = (List) document.getData().get("list");


- list라는 이름으로 등록된 필드를 가져옴



** HashMap map = (HashMap) list.get(i);


- Object 형식으로 저장한 데이터를 가져올 때는 Map을 사용함.


** data.setContent(map.get("content").toString().replace("\\n", "\n"));

- 개행문자가 그대로 입력되기 때문에 개행문자를 다시 한 번 변환해준다. (\\으로 입력해야 "\" 문자로 인식됨)


** data.setRegDate((Date) map.get("regdate"));

- 서버에 등록된 Timestamp를 바로 Date로 변환하기 위해서는 아래와 같은 코드를 사전에 입력해두어야 한다.

        database = FirebaseFirestore.getInstance();

        FirebaseFirestoreSettings.Builder settings = new FirebaseFirestoreSettings.Builder();

        settings.setTimestampsInSnapshotsEnabled(true);

        database.setFirestoreSettings(settings.build());

        database.document(....).get()....




** 

문서 내부에 list가 아니라 특정 오브젝트의 형태로 만들어져있다면 

toObject 메소드를 사용하여 손쉽게 Custom Object로 변환할 수 있다.


** 문제점

- 26버전 아래의 TextView에 app:autoSizeTextType 관련 속성이 동작 안 하는 오류 발생

- java 파일에서 TextViewCompat을 사용하는 방법으로도 적용되지 않았음.

- xml파일에서 prefix를 android로 하면 동작하나 app으로 하면 동작하지 않았음.



** 해결

- TextView의 height 값에 제한을 준다. (wrap_content는 쓰지 않는 게 좋음. 만약 wrap_content를 쓰는 경우에는 maxLine 등과 함께 사용)

- singleLine 속성 대신 maxLine 속성을 사용한다.

- TextView 대신 android.support.v7.widget.AppCompatTextView를 사용


** 참고

https://stackoverflow.com/questions/44118002/autosizing-of-textview-doesnt-work-android-o

** 방법

그냥 한 파일에 같이 적으면 됨.





이게 뭐라고 나는 고민을 했던 걸까...


** 사용 이유

기존에 테스트 서버와 연결되는 앱과 실제 서비스 서버와 연결되는 앱이 따로 설치되기를 희망하였음.

이번에 마이그레이션 작업 진행하며 기존 앱과 따로 설치하여 테스트하기를 희망함.

(기존 앱은 이클립스로 작업)




** 방법

1. 해당 모듈의 build.gradle에 아래와 같은 형식으로 추가

- 나는 마이그레이션/DEV/SERVICE로 나누었음.


flavorDimensions "mode"

    productFlavors {

        migration {

            dimension "mode"

            applicationIdSuffix ".migration"

            buildConfigField 'boolean', 'isTestMode', "true"

        }

//        dev {

//            dimension "mode"

//            applicationIdSuffix ".dev"

//            buildConfigField 'boolean', 'isTestMode', "true"

//        }


        service {

            dimension "mode"

            applicationIdSuffix ""

            buildConfigField 'boolean', 'isTestMode', "false"

        }

    }


flavorDimensions - 무엇인지는 모르겠지만.. flavor 설정 시 모든 항목이 같은 flavorDimensions에 속해야 한다는 에러메세지가 떠서 추가


migration/dev/service - 그냥 자기가 마음대로 설정 가능. 이후 폴더를 생성할 때 동일한 이름으로 생성해야 한다. (대문자가 되는지는 아직 확인하지는 못했다.) 이 때 Flavor가 하나 이상 등록되어있으면 나중에 APK 뽑을 때 Flavor를 무조건 선택해야되서 service를 추가하였음.


applicationIdSuffix - 식별자를 정하는 것 같음. Suffix 설정 시 applicationId 뒤에 붙여지게 된다. 예를 들어 마이그레이션용 앱의 applicationId는 com.example.flavortest.migration 이 되는 식. 나중에 google-services.json 복사 시 문서 내의 applicationID와 동일하게 적어야 한다. service는 기본 설정과 동일하게 해 주기 위해서 빈 값으로 설정하였다.


buildConfigField - 설정하게 되면 BuildConfig 내부에 static 변수로 등록이 되서 소스 코드 내에서 사용이 가능하다. 예를 들어 migration과 dev의 앱에서 BuildConfig.isTestMode는 true지만 service에서는 false가 된다.




2. 각 flavor 별로 폴더 추가



위에서 만든 flavor별로 폴더를 만들고, 내부에 google-services.json을 복사해서 넣는다.

(이제와서 보니 service 폴더랑 main 안의 google-services.json 파일은 없어도 될듯..)


각 flavor별로 리소스 관리를 할 수 있다.

따로 설정하지 않으면 기본(main)폴더 내의 리소스를 사용하는 듯.



이런식으로 앱 아이콘을 따로하고 싶을 때 사용한다. (우측은 플레이 스토어에 등록된 앱, 좌측은 migration flavor로 설치한 앱)




3. google-services.json 수정


google-services.json 파일은 



이렇게 package_name 부분만 변경해주면 된다.


gradle의 applicationId에 위에서 추가했던 applicationIdSuffix 를 더한 문자열을 쓰면 됨.

예를 들어 migration 폴더의 package_name은 "com.example.flavortest.migration"로 변경하면 됨.






4. Manifest 수정(FileProvider 사용 시)


마지막으로 혹시 FileProvider을 정의해서 사용한다면, 혹은 Manifest 파일에서 android:authorities 속성을 사용한다면 android:authorities 속성의 값을 변경해야 한다.

(이렇게 안 하면 앱이 동시에 설치가 안 된다.)


android:authorities="${applicationId}.fileprovider"



이와 같이 applicationId를 변수로 가져오게 수정하여야 한다. 





5. 빌드 시 flavor 선택하기





Build Variants 메뉴를 통해 Run 및 Instant Run 시 어떤 flavor로 실행할 지 선택할 수 있다.







APK 추출 시에는 Flavors 부분에서 선택할 수 있다.

(그런데 Flavors가 하나 이상 등록되어있을 때 선택 해제가 안 된다. 그래서 service Flavor를 따로 만들었음.)








** 참고

http://gun0912.tistory.com/74

https://stackoverflow.com/questions/16267785/install-shows-error-in-console-install-failed-conflicting-provider 

https://medium.com/@iammert/android-product-flavors-1ef276b2bbc1

+ Recent posts