문제점해결

kitkat(4.4)에서 맞닥뜨린 이슈 및 해결

예쁜꽃이피었으면 2015. 1. 23. 17:33

<script async src="https://static.medium.com/embed.js"></script><a class="m-story" data-collapsed="true" href="https://medium.com/@_marojun/1ecb94c24694">kitkat(4.4)에서 맞닥뜨린 이슈 및 해결</a>


https://medium.com/marojuns-android/kitkat-4-4-%EC%97%90%EC%84%9C-%EB%A7%9E%EB%8B%A5%EB%9C%A8%EB%A6%B0-%EC%9D%B4%EC%8A%88-%EB%B0%8F-%ED%95%B4%EA%B2%B0-1ecb94c24694




업데이트 중 — 아래 내용 외에 발견하신 이슈가 있다면 댓글 남겨주세요. 


1. SMS 이슈

보통 서비스에서 SMS를 보내는 기능을 사용할 경우에는 Intent.ACTION_SEND를 통해서 보내는 것이 일반적이다. 또한 코드상에서 미리 전화번호를 명시하는 것이 아니라 사용자가 직접 SMS을 보내는 화면에서 상대방의 전화번호를 입력하는게 보통이다.

그래서 개발자는 Uri.parse(“smsto:전화번호")를 사용하기보다는 Uri.parse(“sms:”)를 통하여 사용자가 직접 보낼사람을 선택하도록한다. 그러나 4.4에서는 이런방식으로 문자를 보내게되면(지금 내가 사용하는 디폴트 SMS앱은 행아웃이다.) SMS앱에서 계속 전화번호를 검색하여 무한로딩 상황에 빠지게 된다.

이에 이러한 상황을 해결하기 위해 아래와 같이 버전별 분기처리를 하면 정상적으로 사용할 수 있게된다.

// 19(4.4)이상부터는 새로운 코드 적용
if (Build.VERSION.SDK_INT > 18) {
String defaultSmsPackageName = Telephony.Sms.getDefaultSmsPackage(getApplicationContext());
Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setType(“text/plain”);
sendIntent.putExtra(Intent.EXTRA_TEXT, “보낼 메시지”);
if (defaultSmsPackageName != null) {
sendIntent.setPackage(defaultSmsPackageName);
}
startActivity(sendIntent);
} else {
// 18이하는 기존코드
Intent intent = new Intent(Intent.ACTION_SENDTO, Uri.parse(“sms:”));
intent.putExtra(“sms_body”, “보낼 메시지”);
startActivity(intent);
}
참고로 행아웃이 업데이트 되면서 4.3이하에서도 intentChooser를 통해 행아웃을 대표 문자앱으로 사용할 수 있게 되었습니다.

위 코드를 적용하면 4.4의 경우 먼저 보낼 사용자를 선택하고 그 이후에 설정한 메시지가 정의되어있는 메시지 화면으로 진입하며 바로 문자를 보낼 수 있게된다.

이 현상은 해당앱의 타켓버전이 19보다 낮아도 발생하는 현상이므로 꼭 체크하여야한다.

2. 이미지 가져오기 이슈

보통 이미지를 가져오기 위해서 Intent.ACTION_GET_CONTENT를 이용해 intent chooser에서 이미지 리스트를 볼 수 있는 앱 항목 중 갤러리를 통해 이미지를 가지고 온다.

그러나 4.4에서 이를 그대로 사용할 경우 4.4에서 추가된 ‘Storage access framework’에서 제공하는 UI가 나타나고 여기에서 사용할 이미지를 클릭해도 해당 이미지를 가지고 올 수 없게된다.

기존에 사용하던 ACTION_GET_CONTENT가 Storage access framework에 통합되었기 때문이다. 그렇다고 해서 ACTION_GET_CONTENT가 완전한 4.4의 Storage access framework을 이용한다고 보기는 어렵다. 4.4에 새롭게 소개된 intent인 ACTION_OPEN_DOCUMENT와의 차이는 분명히 있다. 자세한 사항은“스토리지 액세스 프레임웍을 통해 파일 마스터가 되자!”를 참조하도록 하자.

물론 코드수정을 통해 새로운 UI를 이용 이미지를 가지고 오게하도록 하는것이 맞지만 부득이하게 시간이 없거나 이전과 같은 사용자 경험을 제공하고 싶다면 아래와 같이 ACTION_GET_CONTENT에서 ACTION_PICK으로 코드를 수정한다.

intent.setAction(Intent.ACTION_GET_CONTENT);
에서
intent = new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
으로 수정한다.

이 현상은 해당앱의 타켓버전이 19보다 낮아도 발생하는 현상이므로 꼭 체크하여야한다.

이미지 크롭이 되지 않는 이슈

위 그림처럼 크롭 후 이미지뷰에 활성화 되지 않는 이슈가 있어 분석해 보았더니 아래와 같은 문제가 복합적으로 적용되어 있었다.

3. 파일패스 이슈

아래와 같은 메서드를 통해 파일 패스를 참조하는 경우.

public static Uri getTempUri(){
return Uri.fromFile(new File(Environment.getExternalStorageDirectory() + “/tmp.jpg”));
}

결과값은 정상적인 경로인 file://storage/emulated/0/tmp.jpg 가 아니라

file:///storage/emulated/0/tmp.jpg 으로 전달 됨.

intent.putExtra(MediaStore.EXTRA_OUTPUT, getTempUri());

이에 위와같이 이미지를 저장하기 위해 파일패스로서 해당 메서드를 사용할 경우 4.3이하 버전의 경우 상관없이 동작하지만 4.4의 경우에는 파일저장에 실패한다.

4. 이미지 크롭 이슈

이미지를 크롭할 경우 크롭한 파일을 특정 경로에 저장하기 위해서 아래와 같이 구현할 경우.

Intent intent = new Intent(“com.android.camera.action.CROP”);
intent.setDataAndType(CommonUtil.getTempUri(), “image/*”);
intent.putExtra(“aspectX”, "넓이");
intent.putExtra(“aspectY”, "높이");
intent.putExtra(MediaStore.EXTRA_OUTPUT, “저장할 파일의 경로”);
intent.putExtra(“outputFormat”, Bitmap.CompressFormat.JPEG.toString());
startActivityForResult(intent, REQ_PICK_FROM_CAMERA);

이미지가 제대로 저장되지 않는 경우가 발생한다.

이에 크롭된 이미지를 번들을 통해 비트맵 파일로 전달 받는 방식으로 변경하여 사용하도록한다.

Intent intent = new Intent(“com.android.camera.action.CROP”);
intent.setDataAndType(CommonUtil.getTempUri(), “image/*”);
intent.putExtra(“aspectX”, "넓이");
intent.putExtra(“aspectY”, "높이");
// 번들을 통해 파일을 전달하는 방식으로 변경
intent.putExtra(“return-data”, true);
startActivityForResult(intent, REQ_PICK_FROM_CAMERA);

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQ_PICK_FROM_CAMERA:
if (resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap cropImg = extras.getParcelable(“data”);

5. 웹뷰 이슈

  • 캐시이슈 — 자세한 사항은 추후 정리
  • mWebView.getSettings().setPluginsEnabled() 사용불가, WebView.getSettings().setPluginState(WebSettings.PluginState.ON);로 대체하여 사용할 수 있으나 앞으로 Plugins을 자체를 지원하지 않을 예정임.

6. 시스템 타임 에러

contentResolver.insert(Images.Media.EXTERNAL_CONTENT_URI, values) 를 사용할때 이미지 저장 시각을 values.put(Images.Media.DATE_TAKEN, new Date().getTime()); 을 사용하면 kitkat 이전 버전에선 제대로 된 시스템 타임이 들어왔으나 kitkat 에선 0으로 저장되는 현상이 발생한다.

7. 외장 메모리 이슈

보통 second Sdcard는 탈착식 sdcard로 외장메모리란 용어로 사용했는데 기존에는 이곳에 많은 앱들이 별반 제약 없이 사용했으나 킷캣부터는 이 것을 허용하지 않으며 자신의 package-specific한 공간에만 퍼미션 없이 자유롭게 사용이 가능하다고 합니다.(외장/android/data/packagename/) 이 공간에 파일을 쓰실땐 해당 패스에 접근하기 위해 4.4부터 새로 제공하는 getExternalFilesDirs() 메서드를 통해서 접근하면 됩니다. 해당 메서드를 통해 전달받는 File[] 형태의 값중 0번째가 첫번째 내장메모리, 1번째가 두번째 외장 메모리 패스를 얻을 수 있습니다.

getExternalFilesDirs() 는 4.4 부터 지원하므로 하위버전에서 해당 메서드를 사용하시기 위해서는 ContextCompat.getExternalFilesDirs()를 사용하시기 바랍니다.

사실 이 현상은 허니콤때부터 나타난 현상으로 제조사에서 해당 부분을 이슈가 없도록 수정하였으며 구글 디바이스의 경우 second Sdcard를 지원하는 것이 없었기에 해당 부분을 인지하지 못하고 있었던 것입니다.

그럼 왜 구글은 second Sdcard에 대한 정책을 변경하였을까요? 추측하건데 구글 크롬북처럼 최대한 구글 클라우드를 이용하도록 유도하기 위해 이러한 정책을 쓴건 아닌가합니다.

확인결과 구글판 갤럭시S4에서도 동일현상이 발생되고 있습니다.

그렇다면 갑자기 G2가 4.4로 업데이트 되면서 해당 이슈가 붉어졌을까요? 제조사에서 이전처럼 해당부분을 변경하면 될텐데요. 그건 아래 관련 스레드에서 설명되어 있듯이 4.4 킷캣부터 구글이 CTS 검증 절차에 이 검사 항목을 추가한것으로 추측됩니다. (구글 CTS 에서 Fail 이 되면 제조사는 해당 디바이스를 출시를 할 수 없습니다) 따라서 어쩔 수 없이 Read 만 가능하도록 그냥 놔두는 것이겠죠.

조금 더 설명해 보자면 안드로이드 시스템 내부를 검색해 보면 system/etc/permissions 폴더에 아래 이미지와 같은 내용의 platform.xml란 파일을 찾을수 있습니다.

이 이미지 항목 중 name= “android.permission.WRITE_EXTERNAL_STORAGE” 의 내용이 바로 이 문제의 원인인 부분입니다. 왼편이 순정, 오른편이 제조사들이 이제까지 수정한 방법입니다. 만약 루팅을 하신 분이 있다면 해당 파일을 위 이미지와 같이 수정하실 경우 이전처럼 문제없이 second Sdcard를 사용하실 수 있으실 겁니다.

ES File Explorer의 경우 second Sdcard 관련 작업이 동작한다는 것을보면 개발적으로 이전처럼 사용할 수 있다는 방법이 있는 것 같기는 합니다만 좀 더 확인해 봐야 할 것 같습니다.

따라서 킷캣 버전 이상부터 App에서 외장메모리 공간 사용 방법과 사용자 안내하는 서비스 시나리오 변경이 필요 할 듯 합니다.

관련 스레드 :

엘지에 g2 킷캣 외장메모리 사용 문의

g2 이외에 4.4 올라간 기기들은 외장메모리 이상 없나요

위 내용을 읽어보셔서 알겠지만 개발자분들께서는 G2 킷캣 업데이트가 본격적으로 진행되기전에 외장메모리 이슈는 확실히 테스트하셔야 할 것 같습니다. 한가지 더 명심하셔야 할 것은 아래 사이트에 나와있듯이

The WRITE_EXTERNAL_STORAGE permission must only grant write access to the primary external storage on a device. Apps must not be allowed to write to secondary external storage devices, except in their package-specific directories as allowed by synthesized permissions. Restricting writes in this way ensures the system can clean up files when applications are uninstalled.
출처 : http://source.android.com/devices/tech/storage/

기존에 이야기 했던 외장메모리 권한 이슈와는 다른 이슈라는 점입니다. 즉, WRITE_EXTERNAL_STORAGE권한을 추가해서 해결될 것이 아니라는 것이죠.

* 참고로 저는 아직 실 단말에서 테스트는 못해보았으며 우려가 있었던Immersive Mode는 G2에서 정상동작함을 확인했습니다. 또한 이번 이슈는 분량이 있어 다른 분들이 공유하기 쉽게 새로운 포스트를 만들었습니다. https://medium.com/marojuns-android/59e57b9a14e7

반응형