Things take time

[Android] 인텐트 필터, 커스텀 스키마, 그에 따른 액티비티 중복 제거 본문

Android(기능)

[Android] 인텐트 필터, 커스텀 스키마, 그에 따른 액티비티 중복 제거

겸손할 겸 2017. 7. 12. 18:11

[인텐트 필터]

 

공식 : https://developer.android.com/guide/components/intents-filters.html

매니페스트안에 <activity>~ </activity> 사이에 넣는 설정 값인 <intent-filter> ~ </intent-filter>에 대해서 얘기해보려 한다.

 

흔히 안드로이드에서 액티비티를 전환할 때는 인텐트를 통해서 전환하는데, 명시적 인텐트 (특정 액티비티를 지정하는 것)과 암시적 인텐트(ACTION_VIEW와 같은 안드로이드 상수값)이 있다. 명시적 인텐트는 현재 패키지 내의 액티비티끼리만 이동하는 인텐트다.

 

이런 인텐트들로는 각각의 액티비티를 전환하고 flag속성들을 활용하여 액티비티 전환 시 스택 구조를 사용하는 등 다양하게 할 수 있다.

이것을 응용하여 외부에서(앱이든 웹이든..) 암시적 인텐트(ACTION_VIEW, ACTION_SEND 등), 혹은 URL을 이용했을 때 호출될 수 있도록 하게 하는 것이 이번 목적이다.

<activity android:name=".SplashActivity" android:launchMode="singleTask"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <!-- 외부 앱에서 Intent.ACTION_SEND + text/plain 혹은 image 인텐트 사용시, 현재 앱이 검색 결과에 나타나도록 세팅 --> <action android:name="android.intent.action.SEND"/> <category android:name="android.intent.category.DEFAULT"/> <data android:mimeType="text/plain"/> <data android:mimeType="image/*"/> </intent-filter> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <!-- 스키마처리 : 웹에서 abc://def(scheme://host)이란 스키마 호출, 카카오 스키마 값으로 카카오 링크 공유하기 호출 시 이 액티비티가 호출 --> <data android:host="def" android:scheme="abc" /> <data android:host="@string/kakaolink_host" android:scheme="@string/kakao_scheme" /> </intent-filter> </activity>

매니페스트에 있는 SplashActivity라는 액티비티 클래스에 대한 설정 값이다.

인텐트 필터가 3개 있는 것이 보일 것이다.

 

첫 번째 인텐트 필터가 들어가 있는 액티비티는 무조건 로딩 액티비티, 처음 켜지는 액티비티가 된다. 참고용

그리고 혹시나 이 인텐트  필터에 다른 데이터들을 넣게 되면, 홈화면 자체에도 앱 아이콘이 생성되지 않는다(!). 갑자기 아이콘이 생성안되길래 생성 코드넣고 했는데, 결국 이 문제였음.

이 인텐트 필터안에 데이터나 다른 애들은 넣지 말 것.. 이 예제에서는 첫 번째 인텐트 필터는 예제에서 제외한다. 디폴트 값이기 때문

 

그리고 그 밑에 있는 두 번째 인텐트 필터에는 SEND라는 액션 값, DEFAULT라는 카테고리 값, text/plain과 image/*라는 마임타입이 들어가있는 DATA가 있다. 인텐트 필터에는 액션, 카테고리, 데이터라는 세 가지 속성 값이 있다는 것을 알 수 있다. 참고로 이 세가지 옵션을 모두 써야하는 것이 아니라 액션이 필수이며 카테고리, 데이터는 선택이다.

 

액션에는 불러지는 앱이나 웹에서 하는 인텐트의 행동양식이랄까.. 카테고리는 DEFAULT가 무조건 들어가야하며, 추가로 BROWSABLE과 같은 값들이 들어갈 수 있고.. DATA는 마임타입이 들어갈 수도 있고, 스키마 값이 들어갈 수 있다.

 

마지막 세 번째 인텐트 필터에는 커스텀 스키마라 하여, 사용자가 지정한 스키마의 값을 갖고 특정 액티비티를 호출할 수 있다. 카카오링크와 같은 외부 API도 이 방식을 사용하며, 현재 개발중인 앱에도 커스텀 적용할 수 있다.

 

1) 두 번째 인텐트 필터

 

소스를 간단히 해석하면, 다른 외부 앱에서 intent를 통해 ACTION_SEND + text/plain으로 타입을 정해진 경우, 데이터를 받을 수 있도록 한다.

                Intent sharingIntent = new Intent(Intent.ACTION_SEND);
                sharingIntent.setType("text/plain");
                sharingIntent.putExtra(Intent.EXTRA_TEXT, "안뇽안뇽");
                startActivity(Intent.createChooser(sharingIntent, "앱으로 갑시다"));

이 소스는 지금 실습중인앱 외의 또 다른앱에서 사용하는 것이다. 즉, 외부 앱 기준으로 설명하는 것

createChooser에 의해 위의 인텐트 속성을 갖고 있는 현재 앱이 뜨게 된다. 그러므로 매니페스트에 인텐트 필터를 넣어놓은 앱들의 목록이 뜨게 된다. 실제로 테스트해보면, 위의 매니페스트 코드를 넣은 앱 + 카카오톡이나 다른 소셜 앱들이 뜨게 된다. 즉, 카톡에서도 위의 인텐트 필터를 넣어 개발했을 것이다.

 

2) 세 번째 인텐트 필터

 

세 번째는 웹이나 앱에서 다 호출이 가능한데, Uri값을 이용해서 한다고 생각하면 된다. 결론적으로 말하면 scheme://host 란 양식을 바탕으로 이 앱의 액티비티를 호출할 수 있다. (아래 소스는 외부 앱에서 작성해야한다)

                String url ="abc://def";
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                startActivity(intent);

이렇게 앱에서도 호출이 가능하고

Location.href="abc://def";

이런 웹페이지에서도 호출이 가능하단 뜻이다. 

 

 

[응용]

 

인텐트 필터는 다양하게 응용할 수 있기 때문에, 위의 예는 간단한 예일 뿐이다. 어쨌든 이 예를 활용하여, 그냥 이 앱의 액티비티만을 여는게 아니라 데이터, 파라미터를 전달할 수 있다.

 

간단히 말하면 getIntent()를 통해서 두 예제 모두 다 값을 받을 수 있다. 다만 첫 번째 예에서는 putExtra를 통해서 값을 받은 경우이고, 두 번째 앱에서는 따로 파라미터를 전달하는 것을 보여주지 않았으나.. 저 scheme://host?param=abc 라고 받았다고 생각해보자. 그렇다면 abc라는 값을 얻어와야할 것이다.

 

첫 번째 예제에서는 getIntent().getType()라는 것이 key이고, 두 번째 예제는 getIntent().getData(), getIntent().getData().getQueryParameter("파라미터")가 답이다.

@Override protected void onResume() { super.onResume(); Log.i("Splash Resume", "CALL"); // Get the intent that started this activity String type = null; Intent intent = getIntent(); Uri data = intent.getData(); type = intent.getType(); // 인텐트의 text/plain양식을 가진 외부 앱의 공유하기를 통해 현재 앱으로 온 경우 if(type != null){ Log.i("getType", type); Bundle bundle = intent.getExtras(); String textStr = (String) bundle.get(Intent.EXTRA_TEXT); Log.i("getType", textStr); SharedPreferences prefs = getSharedPreferences("SHARE", MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putString("TextStr", textStr); editor.putString("Bidx", ""); editor.putString("GoCbidx", "'"); editor.commit(); } // 외부 스키마를 통해 들어온 경우 (카카오톡은 예외) else { Log.i("getType", "null"); if(data != null) { Log.i("data", data.toString()); String bidx = data.getQueryParameter("bidx"); SharedPreferences prefs = getSharedPreferences("SHARE", MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putString("Bidx", bidx); editor.putString("TextStr", ""); editor.commit(); } // 아무런 것 없이 들어온 경우 else { Log.i("data", "data null"); SharedPreferences prefs = getSharedPreferences("SHARE", MODE_PRIVATE); SharedPreferences.Editor editor = prefs.edit(); editor.putString("Bidx", ""); editor.putString("TextStr", ""); editor.commit(); } } }

 

 

onCreate든 onResume이든 앱의 실행주기에 따라 개발한 앱을 생각해서 넣으면 된다.

어쨌든 폰 DB중 하나인 SharedPreference에 넣게 된다. SQLite와 같은 폰 DB에 넣기엔 사치다.

 

중요한 것은 getIntent()와 getIntent().getData(), getIntent().getType()를 응용하란 것이다. 외부앱의 ACTION_SEND로 온 데이터를 구분할 때는 getType()로 해야하며, 외부 스키마를 통해 온 경우에는 getData()를 사용한다. 그러므로 구분짓는 코드가 필요하다.

 

[다음 작업]

 

이 다음 작업은 개발 앱에 따라 자유롭게 사용하면 된다. 나같은경우 SplashActivity는 인트로화면을 보여주는 단순 액티비티이며, 일정 시간뒤에 MainActivity로 넘어간다. 그래서 MainActivity에서 SharePreference를 검사하여 값이 있다면, 그 값을 바탕으로 웹뷰를 전환시키며.. 없을 경우 홈 화면으로 가는 것이다.

 

 

[예외 상황]

위의 코드는 풀소스긴 하지만, 매니페스트에 액티비티에는 특이한 속성이 있다. launchMode라는 속성에 singleTask라는 것인데.. 외부의 스키마, 외부의 인텐트를 통해 앱을 실행시키면, 각각의 액티비티가 스택구조로 쌓이게 된다.

 

 

이렇게 실행중인 앱을 보면, 하나의 앱에 액티비티가 2개쌓이게 된다. 이런 경우에 저 launchMode속성을 이용하는 것이다. SplashActivity는 singleTask방식으로 열리게 되는데, 이 방식은 액티비티는 무조건 1개만 유지하며, 새로운애가 오면 onCreate부터 다시 시작하라는 의미이다.

 

그래서 위의 SharedPreference는 onCreate에 넣어도 된다는 의미이다.

 

어쨌든 이런 작업까지 마쳐주면, 기본적인 용법은 문제 없다.