KS blog

killins.egloos.com

포토로그



앱들간의 Interaction by KillinS

  - 하나의 앱 안에 있는 여러 액티비티간에 데이터 교환을 위해서는 Intent, startActivity() 등을 사용 : explicit intent
  - Intent는 서로 다른 앱간의 interaction을 위해서도 사용 가능 : implicit intent


1. 다른 앱으로의 전환 및 데이터 송신 : Implicit intent

1) Implicit Intent 생성

  - 시작할 컴포넌트의 이름이 아닌 수행할 액션 및 그 액션과 관계된 데이터를 선언. 안드로이드 시스템이 사용자로 하여금 해당 액션을 받아서 처리할 수 있는 앱으로 등록된 앱들중 하나를 선택하게 함.

  - 데이터가 Uri라면 Intent 컨스트럭터로 액션과 데이터 정의 가능

        Uri number = Uri.parse("tel:5551234");
        Intent callIntent = new Intent(Intent.ACTION_DIAL, number);

    위 인텐트를 startActivity()로 invoke하면 위 번호로 폰 앱이 시작됨

  - putExtra() : 다른 데이터를 추가하기 위해 사용

  - setType() : Uri를 Intent에 사용하지 않는다면, 데이터 타입이 무엇인지를 명확하게 하기 위해 사용

      Intent emailIntent = new Intent(Intent.ACTION_SEND);
      emailIntent.setType(HTTP.PLAIN_TEXT_TYPE);
      emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[] {"jon@example.com"});
      emailIntent.putExtra(Intext.EXTRA_SUBJECT, "Email subject");
      emailIntent.putExtra(Intext.EXTRA_TEXT, "Email text");
      emailIntent.putExtra(Intext.EXTRA_STREAM, Uri.parse("context://path/to/email/attachment"));


2) Intent를 수신할 앱을 확인

  - 수신할 앱이 없다면 app이 crash되므로 반드시 확인

  - PackageManager#queryIntentActivities() : 생성한 Intent를 핸들링할 수 있는 앱의 리스트를 리턴

    PackageManager pMgr = getPackageManager();
    List<ResolveInfo> activities = pMgr.queryIntentActivities(intent, 0);
    boolean isIntentSafe = activities.size() > 0;

3) Intent를 이용한 새로운 앱의 시작

  - startActivity(intent);

  - 해당 Intent를 사용 가능한 복수개의 앱이 있다면 시스템은 다이얼로그를 통해 선택하도록 함

  - 전체 예제

    Uri location = Uri.parse("geo:0,0?q=1600+Amphitheatre+Parkway,+Mountain+View,+California");
    Intent mapIntent = new Intent(Intent.ACTION_VIEW, location);
    PackageManager pMgr = getPackageManager();
    List<ResolveInfo> activities = pMgr.queryIntentActivities(intent, 0);
    boolean isIntentSafe = activities.size() > 0;
    if (isIntentSafe) {
        startActivity(mapIntent);
    }

  - createChooser() : 여러개의 앱을 선택 가능할 경우 디폴트 앱을 정하면 다음부터는 그 앱만 실행되나, 디폴트 앱을 지정하지 않고 매번 선택을 하도록 할 때 사용

    String title = getResources().getText(R.string.chooser_title);    // 선택창의 타이틀 지정 가능
    Intent chooser = Intent.createChooser(intent, title);    startActivity(chooser);



2. 다른 앱으로부터 결과 얻기

   - 다른 앱으로 전환한 뒤, 해당 앱이 종료되면 그 결과를 얻어오도록 할 수 있음. startActivityForResult(), onActivityResult() 사용

   - 다른 앱은 당연히 결과를 리턴하도록 설계되어있어야 함

1) 액티비티 시작

  - 액티비티 전환시 리턴받았을때 확인할 request code를 인자로 준다는것 외에는 일반적인 전환과 동일

        static final int PICK_CONTACT_REQUEST = 1;
        ...
        startActivityForResult(intent, PICK_CONTACT_REQUEST);

2) 결과 수신

  - 결과가 리턴되면 시스템은 onActivityResult() 메소드를 콜. 3개의 인자가 있음

    1) 전환시 보냈던 requset code
    2) 실행결과코드 : RESULT_OK 또는 RESULT_CANCELED
    3) 결과 데이터가 있는 Intent

        protected void onActivityResult(int reqCode, int resCode, Intent data) {
          if (reqCode == PICK_CONTACT_REQUEST) {
            if (resCode == RESULT_OK) {
              ...
            }
          }
        }

  - 결과 Intent는 많은 표준 프로그램의 API에 정의되어 있음. 개별 프로그램 각자 알아내야 함

  - 전화번호 얻기 예제

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == PICK_CONTACT_REQUEST) {
            if (resultCode == RESULT_OK) {
                Uri contactUri = data.getData();
                // We only need the NUMBER column, because there will be only one row in the result
                String[] projection = {Phone.NUMBER};
                // query() 메소드는 blocking을 막기 위해 앱이 UI 쓰레드와 다른 별도의 쓰레드에서 수행해야 함. 여기선 그냥
                Cursor cursor = getContentResolver().query(contactUri, projection, null, null, null);
                cursor.moveToFirst();
                int column = cursor.getColumnIndex(Phone.NUMBER);
                String number = cursor.getString(column);
            }
        }
    }



3. 다른 앱이 내 앱을 실행하게 하기

  - manifest 파일의 <activity> 요소에 <intent-filter> 요소를 추가 -> 설치 시 시스템이 이것을 보고 internal catalog에 추가함

1) Intent filter 추가

  - 최대한 상세해야 함

  - 아래 세가지 요건이 정의되어야 함
    1) Action : 수행할 액션 이름. 보통 ACTION_SEND 또는 ACTION_VIEW. <action> 요소로 추가
    2) Data : intent와 연관된 데이터. <data> 요소로 추가. MIME type, URI prefix, URI scheme의 조합이 될 수 있음. URI 외의 데이터를 쓰려면 android:mimeType 애트리뷰트에 앱에서 다룰 데이터 타입을 선언해야 함(text/plain 등)
    3) Category : 액티비티의 종류를 뜻함. 여러가지가 있는데 implicit intent를 수신하기 위해선 CATEGORY_DEFAULT를 사용

  - ACTION_SEND intent를 텍스트나 이미지 데이터와 함께 사용할 앱의 예제

    <activity android:name="ShareActivity">
        <intent-filter>
            <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>
    </activity>

  - 각 <intent-filter>안에 복수개의 <action>, <data>, <catgory> 쌍 존재 가능. 상호 배타적인 쌍이라면 다른 필터로 지정해야함

  - 아래 예제의 경우, ACTION_SEND와 ACTION_SENDTO에 모두 이미지/텍스트를 사용하나, ACTION_SENDTO의 경우 URI를 사용해야만 하므로 서로 다른 필터로 지정

    <activity android:name="ShareActivity">
        <!-- filter for sending text; accepts SENDTO action with sms URI schemes -->
        <intent-filter>
            <action android:name="android.intent.action.SENDTO"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:scheme="sms" />
            <data android:scheme="smsto" />
        </intent-filter>
        <!-- filter for sending text or images; accepts SEND action and text or image data -->
        <intent-filter>
            <action android:name="android.intent.action.SEND"/>
            <category android:name="android.intent.category.DEFAULT"/>
            <data android:mimeType="image/-"/>
            <data android:mimeType="text/plain"/>
        </intent-filter>
    </activity>


2) Intent 사용

  - getIntent() 메소드로 intent를 얻어와 사용

  - onCreate() 또는 onStart() 콜백에서 사용해야 함 : 인텐트 수신은 앱이 foreground로 나오면서 이루어지므로

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        Intent intent = getIntent();
        Uri data = intent.getData();
        // intent type에 따라 실행
        if (intent.getType().indexOf("image/") != -1) {
            // Handle intents with image data ...
        } else if (intent.getType().equals("text/plain")) {
            // Handle intents with text ...
        }
    }


3) 결과 송신

  - 내 앱을 invoke한 앱에 결과를 보내려면 setResult()로 결과 코드 설정 후 finish() 메소드를 콜

  - 결과 코드 기본값은 RESULT_CANCELED. 0 보다 큰 특정 정수값을 임의로 보낼 수 있음

  - 결과를 받는 앱, 즉 지금 앱을 invoke한 앱에서 결과값이 필요없다면 알아서 무시하므로 내 앱을 startActivity()로 불렀는지, startActivityForResult() 불렀는지는 중요하지 않음


* 출처 : http://developer.android.com/training/basics/intents/index.html