제임스딘딘의
Tech & Life

개발자의 기록 노트/Android

[안드로이드] 펜딩인텐트 (Pending Intent)

제임스-딘딘 2017. 5. 23. 01:06

안드로이드의 펜딩인텐트 (Pending Intent)


펜딩인텐트 (Pending Intent) 는 인텐트의 일종이다. 

그러면 일반 인텐트와의 차이점은 무엇인지 알아보는 것부터 시작해보자.

컴포넌트에서 다른 컴포넌트에게 작업을 요청하는 인텐트를 사전에 생성시키고 만든다는 점과 "특정 시점"에 자신이 아닌 다른 컴포넌트들이 펜딩인텐트를 사용하여 다른 컴포넌트에게 작업을 요청시키는 데 사용된다는 점이 차이점이다.

수행시킬 작업 및 인텐트(실행의도)와 및 그것을 수행하는 주체를 지정하기 위한 정보를 명시 할 수 있는 기능의 클래스라고 보면 된다. 

이해하기 쉽게 말하자면, 아래와 같이 하고 싶을때 사용할 수 있는 것이다.





A한테 이 B인텐트를 C시점에 실행하라고 해. 지금은 실행하지 말고.

이 클래스의 인스턴스는 getActivity (Context, int, Intent, int), getActivities (Context, int, Intent [], int), getBroadcast (Context, int, Intent, int) 및  getService(Context, int, Intent, int) 가 반환 하는 객체를 다른 응용 프로그램으로 전달 할 수 있으므로 앱 개발자가 명시하는 작업을 수행 할 수 있다.

PendingIntent를 다른 응용 프로그램에 제공하면 다른 응용 프로그램이 자신과 동일한 권한과 ID로 지정된 것처럼 작업을 수행 할 수있는 권한이 부여된다. 따라서 PendingIntent를 작성하는 방법에주의해야한다. 예를 들어, 제공하는 기본적인 인텐트는 컴포넌트 이름이 자신이 가진 컴포넌트들 중 하나를 명시적으로 지정해야 하며,  궁극적으로 그곳으로 보내지는 것을 보장해야한다.

사용되는 몇가지 사례

펜딩인텐트를 사용하는 대표적인 몇가지 예가 있다.

  • 사용자가 Notification을 통해 특정한 동작을 할 때, 실행되는 인텐트를 생성함 (NotificationManager가 인텐트를 실행)
  • 사용자가 AppWidget을 통해 특정한 동작을 할 때,  실행되는 인텐트를 생성함 (홈 스크린이 인텐트를 실행)
  • 미래의 특정 시점에 실행되는 인텐트를 선언함 (안드로이드의 AlarmManager가 인텐트를 실행)

안드로이드 앱을 구현할 때, 인터넷으로부터 파일을 다운로드 하는 로직은 대부분 서비스에서 이루어지도록 구성한다. 
그런데 서비스는 액티비티와 달리 화면에 나타나지 않는다. 따라서 서비스는 다운로드의 진행중이라는 사실 및 진행정도를 화면 상단에 위치한 노티피케이션의 상태바(Status Bar)를 통해서 표현한다. 다운로드가 현재 진행 중이라는 상황을 표시하는 아이콘 등으로 말이다. 그리고 다운로드가 완료된 후에는 아이콘으로 다운로드 완료의 상태를 보여주게 된다.

사용자가 상태바의 아이콘을 확인하고 안드로이드 화면의 상태바를 누르면서 나타나는 바를 잡아 아래로 끌어당기면 나타나는 화면을 노티피케이션 리스트(Notification List)  또는 확장 메시지라 한다. 그리고 만약 서비스가 이 Notification List에 '다운로드 완료' 를 표시를 추가해놓았고, 사용자가 이것을 클릭하면, 노티피케이션은 사전에 서비스에서 작성한 펜딩인텐트를 사용하여 다운로드된 파일을 읽을 수 있는 애플리케이션을 호출하고 다운로드 완료된 파일을 호출된 애플리케이션으로 재생(혹은 보여줌) 하게 된다. 

펜딩인텐트는 안드로이드 App의 각각의 컴포넌트들이 펜딩인텐트를 생성할 수 있도록 다음과 같은 메서드를 제공한다.

아래의 메서드들을 통해 펜딩인텐트를 사용하고자 하는 컴포넌트 유형을 지정해야 한다는 뜻이다.


  • getActivity(Context, int, Intent, int) -> Activity를 시작하는 인텐트를 생성함
  • getBroadcast(Context, int, Intent, int) -> BroadcastReceiver를 시작하는 인텐트를 생성함
  • getService(Context, int, Intent, int)  -> Service를 시작하는 인텐트를 생성함

잘 보면, 3개의 메서드는 모두 Context를 필요로 한다. 이 Context는 '현재 App의 Context'이다.


예제 코드

실제로 PendingIntent를 활용해 Notification 발생시키는 예제 코드를 살펴보겠다.

이 예제코드를 따라가면 아래 화면과 같은 앱을 얻게될 것이다.





예제코드를 살펴보자.

총 5개의 파일(AndroidManifest.xml, MainActivity.java, NotificationSomething.java, activity_main.xml, notification_something.xml)  을 보게 될 것이다.

하나의 android application project를 생성하고, 아래와 같이 파일을 구성하자.

layout 2개와 java코드 2개를 만들면 된다.




AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.pendingintent"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="16" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
        <activity android:name=".NotificationSomething">
        </activity>
    </application>

</manifest>


앱이 실행되면 처음 보여지는 메인 Activity를 구성하는 layout이다. 1개의 버튼을 가지고 있다.

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.tistory.webnautes.notification.MainActivity">
 
    <Button
        android:text="노티피케이션 발생시키기"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:id="@+id/button" />
</RelativeLayout>


앱이 실행되면 처음 보여지는 메인 Activity의 소스코드이다. 버튼을 눌렀을 때, NotificationSomethings( )라는 메서드를 호출하며, PendingIntent를 통해 Notification을 발생시키고 있다. Notification을 발생시키는 intent에서 putExtra( ) 메서드를 통해 몇가지 데이터를 넘기는 것을 주목하자. 특히 notificationId는 나중에 Notification을 목록에서 지울 때 사용한다.

MainActivity.java

package com.example.pendingintent;

import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.support.v4.app.NotificationCompat;


public class MainActivity extends Activity {
	Button button = null;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		 
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View arg0) {
                NotificationSomethings();
            }
        });
	}

    public void NotificationSomethings() {
        Resources res = getResources();
        int notfi_id = 1;
 
        Intent notificationIntent = new Intent(this, NotificationSomething.class);
        notificationIntent.putExtra("notificationId", notfi_id); //전달할 값
        notificationIntent.putExtra("extraString", "Hello PendingIntent"); //전달할 값
        PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
 
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
 
        builder.setContentTitle("상태바 드래그시 보이는 타이틀")
                .setContentText("상태바 드래그시 보이는 서브타이틀")
                .setTicker("상태바 한줄 메시지")
                .setSmallIcon(R.drawable.ic_launcher)
                .setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.ic_launcher))
                .setContentIntent(contentIntent)
                .setAutoCancel(true)
                .setWhen(System.currentTimeMillis())
                .setDefaults(Notification.DEFAULT_ALL);
 
        
 /*
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
            builder.setCategory(Notification.CATEGORY_MESSAGE)
                    .setPriority(Notification.PRIORITY_HIGH)
                    .setVisibility(Notification.VISIBILITY_PUBLIC);
        }
 */
        NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        nm.notify(notfi_id, builder.build());
    }
}


첫 화면에서 발생한 Notification을 눌렀을 때 보여지는 Activity의 layout이다.

notification_something.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
 
    <TextView
        android:text="TextView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/textView" />
</LinearLayout>


첫 화면에서 발생한 Notification을 눌렀을 때 보여지는 Activity의 소스코드이다.

NotificationSomething.java

package com.example.pendingintent;

import android.app.Activity;
import android.app.NotificationManager;
import android.content.Context;
import android.os.Bundle;
import android.widget.TextView;

public class NotificationSomething extends Activity {

	@Override
	public void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		setContentView(R.layout.notification_something);
        CharSequence s = "전달 받은 값: ";
        int id=0;
        String extraData = "";
 
        Bundle extras = getIntent().getExtras();
        if (extras == null) {
            s = "error";
        }
        else {
            id = extras.getInt("notificationId");
            extraData = extras.getString("extraString");
        }
        TextView t = (TextView) findViewById(R.id.textView);
        s = s+" "+ extraData + "id:" + id;
        
        t.setText(s);
        NotificationManager nm =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
 
        //노티피케이션 제거
        nm.cancel(id);
	}
}



관련 Reference Document 바로가기

https://developer.android.com/reference/android/app/PendingIntent.html