메인 액티비티 실행이 안돼요

일반적으로 앱은 아이콘이 있다. 아이콘을 누르면 진입점 역할의 액티비티가 실행된다. 이 액티비티를 편의상 메인 액티비티라고 부르자.

메인 액티비티를 만드는 법은 간단하다. AndroidManifest.xml에 선언된 액티비티 중 메인으로 만들고 싶은 것을 골라 아래와 같이 MAIN 액션LAUNCHER 카테고리가 포함된 인텐트 필터를 추가하면 된다.

<intent-filter>
     <action android:name="android.intent.action.MAIN" /> 
     <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

위와 같이 인텐트 필터를 선언하면, 런쳐는 메인 액티비티로 판단하여 앱 슬롯에 아이콘을 표시한다.

그럼, 코드로 메인 액티비티를 실행해보자. 내 앱이라면 패키지 이름과 컴포넌트 이름으로 명시적 호출도 가능하다. 여기서는 암시적 인텐트를 이용해 보자.

adb shell am start <패키지이름>

패키지 이름만 건네면 앱의 메인 액티비티를 찾아 실행하는 명령어다. 위 명령어로 앱들을 실행해보면 어떤 앱은 실행되고 어떤 앱은 실행되지 않는다. 왜 그럴까?

실행여부는 <intent-filter>에 android.intent.category.DEFAULT가 있어야 있느냐에 따라 달라진다. DEFAULT 카테고리가 있어야 패키지 이름만으로 실행할 수 있는 것이다.

숨겨진 액티비티도 아니고 아이콘이 노출된 앱인데 코드로 실행할 수는 없다니 이상하다. 게다가, 메인 액티비티는 공개된 액티비티이므로 android:exported=”true”도 강제라고 한다. 그럼 외부에서 실행하는 것을 보장하겠다는 뜻이 아닌가?

이런 앞뒤가 맞지 않는 상황 때문인지 안드로이드의 PackageManager 클래스는  API3부터 getLaunchIntentForPackage()라는 설명이 재미있는 API를 제공한다.

Returns a "good" intent to launch a front-door activity in a package. 
...(이하 생략)

메인 액티비티를 실행하는 “좋은” 인텐트를 만들어 반환한다고 API 설명이 되어 있다. 하지만, 이 API도 MAIN 액션, LAUNCHER 카테고리를 가진 액티비티를 찾아 반환하는 것이므로 여전히 인텐트 필터는 필요하다.

메인 액티지티를 만들 때 DEFAULT 카테고리도 잊지 말고 추가하도록 하자. 다른 앱이 내 앱을 한 번이라도 더 실행하도록 말이다.

그 밖에

  • 한 앱에서 MAIN, LAUNCHER 속성을 2개 이상의 액티비티에 동시에 선언할 수 있으며, 이 때 아이콘이 각각 생긴다.
  • 아이콘의 레이블은 액티비티의 레이블을 따라간다. 그러므로, 아이콘을 다르게 여러 개 만들 수도 있다.

참조

FAILED BINDER TRANSACTION

안드로이드에서 액티비티나 서비스같은 컴포넌트 간 데이터를 전달할 때 인텐트를 쓴다. 정확히는 인텐트 객체 안의mExtras라는Bundle 형 객체를 활용하는데 이 때 전송할 수 있는 데이터의 제한이 있을까?

결론만 이야기하면 있다. 안드로이드 공식문서에 인텐트의 extras의 크기 제한으로 언급된 내용은 없지만, 일정 크기 이상의 데이터를 intent에 넣어 전달하려고 하면 아래와 같은 에러가 발생한다.

09-06 11:04:42.329 6839-6839/? E/JavaBinder: !!! FAILED BINDER TRANSACTION !!!  (parcel size = 6966844)
...
Caused by: android.os.TransactionTooLargeException: data parcel size 6966844 bytes
...

참고로, 이는 Bundle이나 Intent 클래스의 제약이 아니다. 안드로이드의 컴포넌트 들간 인텐트 전달을 위해  안드로이드 시스템을 거칠 때, 전달되는 아규먼트나 리턴 값은 바인더 binder transaction buffer 를 거치는데, 이 버퍼의 크기가 1mb로 제한되어 있고,  이 버퍼는 현재 수행중인 모든 트랜잭션이 공유하여 사용한다. 그러므로, 이 버퍼 안에 저장할 수 없을 만큼 아규먼트나 리턴 값이 클 때 TransactionTooLargeException이 발생하는 것이다.

다른 트랜잭션이 없다면 1mb 가까이 intent에 데이터를 넣어도 되겠지만, 보통은 100kb 미만의 데이터만 전달하도록 권장한다. 안드로이드 시스템과 하드웨어가 향상되어 버퍼의 크기는 커질 수 있지만, 크게 바뀔 가능성은 별로 없을 것 같다. 그러므로, 개발자들은 이 버퍼의 크기를 감안하여 데이터 전송량을 최대한 줄이는 것이 좋겠다. 혹시, 이미지나 파일 같은 크기가 큰 데이터를 전송해야 한다면, 파일의 위치를 uri를 통해 전달하는 방식을 고려해보자.

참고