[android] GCM (2) – 클라이언트 만들기

Android Studio에서는 이렇게 해야한다.

 

 
이전 글에서 이어짐

 

Google Play Service 연결하기

  1. 이클립스 – Windows – Android SDK Manager 를 연다.
  2. 목록의 제일 아래쪽에 Goole Play Services 선택한다.
  3. 오른쪽 아래의 Install 버튼을 누른다.
  4. 설치가 끝나면 이클립스의 프로젝트 목록에서 오른쪽 클릭해서 import 메뉴를 선택하고,{sdk}/extras/google/google_play_services/ 를 선택한다.
  5. 작성할 프로젝트의 오른쪽 클릭 메뉴 – properties – Android – Library – Add… 버튼을 누른다.
  6. google-play-services_lib 를 선택한다.
    스크린샷 2014-01-13 오후 5.22.01

 

Manifest 편집

kr.mint.testgcm 이라고 적힌 부분은 실제 패키지 경로로 바꿔야 함.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
<uses-permission android:name="android.permission.VIBRATE" />

<permission
    android:name="kr.mint.testgcm.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />

<uses-permission android:name="kr.mint.testgcm.permission.C2D_MESSAGE" />
<activity
	android:name=".MainActivity"
	android:launchMode="singleTask" // notification bar에서 클릭할 때를 위해
	android:label="@string/app_name" >
	<intent-filter>
	<action android:name="android.intent.action.MAIN" />
	<category android:name="android.intent.category.LAUNCHER" />
	</intent-filter>
</activity>
<receiver
    android:name=".receiver.GcmBroadcastReceiver"
    android:permission="com.google.android.c2dm.permission.SEND" >
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <category android:name="kr.mint.testgcm" />
    </intent-filter>
</receiver>

<service android:name=".GcmIntentService" />

<meta-data
    android:name="com.google.android.gms.version"
    android:value="@integer/google_play_services_version" />

 

MainActivity 편집

http://developer.android.com/google/gcm/client.html#sample-register  의 소스를 살짝 바꿨다.

public class MainActivity extends Activity
{
   private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 9000;
   private static final String SENDER_ID = "388136674604";

   private GoogleCloudMessaging _gcm;
   private String _regId;

   private TextView _textStatus;

   @Override
   protected void onCreate(Bundle savedInstanceState)
   {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      _textStatus = (TextView) findViewById(R.id.textView1);

      // google play service가 사용가능한가
      if (checkPlayServices())
      {
         _gcm = GoogleCloudMessaging.getInstance(this);
         _regId = getRegistrationId();

         if (TextUtils.isEmpty(_regId))
            registerInBackground();
      }
      else
      {
         Log.i("MainActivity.java | onCreate", "|No valid Google Play Services APK found.|");
         _textStatus.append("\n No valid Google Play Services APK found.\n");
      }

      // display received msg
      String msg = getIntent().getStringExtra("msg");
      if (!TextUtils.isEmpty(msg))
         _textStatus.append("\n" + msg + "\n");
   }

   @Override
   protected void onNewIntent(Intent intent)
   {
      super.onNewIntent(intent);

      // display received msg
      String msg = intent.getStringExtra("msg");
      Log.i("MainActivity.java | onNewIntent", "|" + msg + "|");
      if (!TextUtils.isEmpty(msg))
         _textStatus.append("\n" + msg + "\n");
   }

   // google play service가 사용가능한가
   private boolean checkPlayServices()
   {
      int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
      if (resultCode != ConnectionResult.SUCCESS)
      {
         if (GooglePlayServicesUtil.isUserRecoverableError(resultCode))
         {
            GooglePlayServicesUtil.getErrorDialog(resultCode, this, PLAY_SERVICES_RESOLUTION_REQUEST).show();
         }
         else
         {
            Log.i("MainActivity.java | checkPlayService", "|This device is not supported.|");
            _textStatus.append("\n This device is not supported.\n");
            finish();
         }
         return false;
      }
      return true;
   }

   // registration  id를 가져온다.
   private String getRegistrationId()
   {
      String registrationId = PreferenceUtil.instance(getApplicationContext()).regId();
      if (TextUtils.isEmpty(registrationId))
      {
         Log.i("MainActivity.java | getRegistrationId", "|Registration not found.|");
         _textStatus.append("\n Registration not found.\n");
         return "";
      }
      int registeredVersion = PreferenceUtil.instance(getApplicationContext()).appVersion();
      int currentVersion = getAppVersion();
      if (registeredVersion != currentVersion)
      {
         Log.i("MainActivity.java | getRegistrationId", "|App version changed.|");
         _textStatus.append("\n App version changed.\n");
         return "";
      }
      return registrationId;
   }

   // app version을 가져온다. 뭐에 쓰는건지는 모르겠다.
   private int getAppVersion()
   {
      try
      {
         PackageInfo packageInfo = getPackageManager().getPackageInfo(getPackageName(), 0);
         return packageInfo.versionCode;
      }
      catch (NameNotFoundException e)
      {
         // should never happen
         throw new RuntimeException("Could not get package name: " + e);
      }
   }

   // gcm 서버에 접속해서 registration id를 발급받는다.
   private void registerInBackground()
   {
      new AsyncTask<Void, Void, String>()
      {
         @Override
         protected String doInBackground(Void... params)
         {
            String msg = "";
            try
            {
               if (_gcm == null)
               {
                  _gcm = GoogleCloudMessaging.getInstance(getApplicationContext());
               }
               _regId = _gcm.register(SENDER_ID);
               msg = "Device registered, registration ID=" + _regId;

               // For this demo: we don't need to send it because the device
               // will send upstream messages to a server that echo back the
               // message using the 'from' address in the message.

               // Persist the regID - no need to register again.
               storeRegistrationId(_regId);
            }
            catch (IOException ex)
            {
               msg = "Error :" + ex.getMessage();
               // If there is an error, don't just keep trying to register.
               // Require the user to click a button again, or perform
               // exponential back-off.
            }

            return msg;
         }

         @Override
         protected void onPostExecute(String msg)
         {
            Log.i("MainActivity.java | onPostExecute", "|" + msg + "|");
            _textStatus.append(msg);
         }
      }.execute(null, null, null);
   }

   // registraion id를 preference에 저장한다.
   private void storeRegistrationId(String regId)
   {
      int appVersion = getAppVersion();
      Log.i("MainActivity.java | storeRegistrationId", "|" + "Saving regId on app version " + appVersion + "|");
      PreferenceUtil.instance(getApplicationContext()).putRedId(regId);
      PreferenceUtil.instance(getApplicationContext()).putAppVersion(appVersion);
   }
}

 

 Received Message

예전에는 브로드캐스트 리시버에서 다 했던 것 같은데, 구글에서 가이드 해줬으니 그거 따라해야지….

public class GcmBroadcastReceiver extends WakefulBroadcastReceiver
{
   @Override
   public void onReceive(Context context, Intent intent)
   {
      Log.i("GcmBroadcastReceiver.java | onReceive", "|" + "================="+"|");
      Bundle bundle = intent.getExtras();
      for (String key : bundle.keySet())
      {
         Object value = bundle.get(key);
         Log.i("GcmBroadcastReceiver.java | onReceive", "|" + String.format("%s : %s (%s)", key, value.toString(), value.getClass().getName()) + "|");
      }
      Log.i("GcmBroadcastReceiver.java | onReceive", "|" + "================="+"|");

      // Explicitly specify that GcmIntentService will handle the intent.
      ComponentName comp = new ComponentName(context.getPackageName(), GcmIntentService.class.getName());
      // Start the service, keeping the device awake while it is launching.
      startWakefulService(context, intent.setComponent(comp));
      setResultCode(Activity.RESULT_OK);
   }
}

 

 

Show in Notification bar

노티바에 표시하면서 0.5초 동안 진동하고, 클릭하면 메인화면으로 가는 거 추가

public class GcmIntentService extends IntentService
{
   public static final int NOTIFICATION_ID = 1;

   public GcmIntentService()
   {
      super("GcmIntentService");
   }

   @Override
   protected void onHandleIntent(Intent intent)
   {
      Bundle extras = intent.getExtras();
      GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this);
      // The getMessageType() intent parameter must be the intent you received
      // in your BroadcastReceiver.
      String messageType = gcm.getMessageType(intent);

      if (!extras.isEmpty())
      { // has effect of unparcelling Bundle
         /*
          * Filter messages based on message type. Since it is likely that GCM
          * will be extended in the future with new message types, just ignore
          * any message types you're not interested in, or that you don't
          * recognize.
          */
         if (GoogleCloudMessaging.MESSAGE_TYPE_SEND_ERROR.equals(messageType))
         {
            sendNotification("Send error: " + extras.toString());
         }
         else if (GoogleCloudMessaging.MESSAGE_TYPE_DELETED.equals(messageType))
         {
            sendNotification("Deleted messages on server: " + extras.toString());
            // If it's a regular GCM message, do some work.
         }
         else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE.equals(messageType))
         {
            String msg = intent.getStringExtra("msg");
            // Post notification of received message.
//            sendNotification("Received: " + extras.toString());
            sendNotification("Received: " + msg);
            Log.i("GcmIntentService.java | onHandleIntent", "Received: " + extras.toString());
         }
      }
      // Release the wake lock provided by the WakefulBroadcastReceiver.
      GcmBroadcastReceiver.completeWakefulIntent(intent);
   }

   // Put the message into a notification and post it.
   // This is just one simple example of what you might choose to do with
   // a GCM message.
   private void sendNotification(String msg)
   {
      NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

      Intent intent = new Intent(getApplicationContext(), MainActivity.class);
      intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      intent.putExtra("msg", msg);

      PendingIntent contentIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

      NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_launcher)
                                                                                .setContentTitle("GCM Notification")
                                                                                .setStyle(new NotificationCompat.BigTextStyle().bigText(msg))
                                                                                .setContentText(msg)
                                                                                .setAutoCancel(true)
                                                                                .setVibrate(new long[] { 0, 500 });

      mBuilder.setContentIntent(contentIntent);
      mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build());
   }
}

 

Preference

그냥 구글 소스대로 해놓고 글 작성할 걸 괜히 리팩토링해가지고 불편하게 됐네 ;;;

public class BasePreferenceUtil
{
   private SharedPreferences _sharedPreferences;

   protected BasePreferenceUtil(Context $context)
   {
      super();
      _sharedPreferences = PreferenceManager.getDefaultSharedPreferences($context);
   }

   /**
    * key 수동 설정
    * 
    * @param key
    *           키 값
    * @param value
    *           내용
    */
   protected void put(String $key, String $value)
   {
      SharedPreferences.Editor editor = _sharedPreferences.edit();
      editor.putString($key, $value);
      editor.commit();
   }

   /**
    * String 값 가져오기
    * 
    * @param key
    *           키 값
    * @return String (기본값 null)
    */
   protected String get(String $key)
   {
      return _sharedPreferences.getString($key, null);
   }

   /**
    * key 설정
    * 
    * @param key
    *           키 값
    * @param value
    *           내용
    */
   protected void put(String $key, boolean $value)
   {
      SharedPreferences.Editor editor = _sharedPreferences.edit();
      editor.putBoolean($key, $value);
      editor.commit();
   }

   /**
    * Boolean 값 가져오기
    * 
    * @param key
    *           키 값
    * @param defValue
    *           기본값
    * @return Boolean
    */
   protected boolean get(String $key, boolean $default)
   {
      return _sharedPreferences.getBoolean($key, $default);
   }

   /**
    * key 설정
    * 
    * @param key
    *           키 값
    * @param value
    *           내용
    */
   protected void put(String $key, int $value)
   {
      SharedPreferences.Editor editor = _sharedPreferences.edit();
      editor.putInt($key, $value);
      editor.commit();
   }

   /**
    * int 값 가져오기
    * 
    * @param key
    *           키 값
    * @param defValue
    *           기본값
    * @return int
    */
   protected int get(String $key, int $default)
   {
      return _sharedPreferences.getInt($key, $default);
   }
}

 

public class PreferenceUtil extends BasePreferenceUtil
{
   private static PreferenceUtil _instance = null;

   private static final String PROPERTY_REG_ID = "registration_id";
   private static final String PROPERTY_APP_VERSION = "appVersion";

   public static synchronized PreferenceUtil instance(Context $context)
   {
      if (_instance == null)
         _instance = new PreferenceUtil($context);
      return _instance;
   }

   protected PreferenceUtil(Context $context)
   {
      super($context);
   }

   public void putRedId(String $regId)
   {
      put(PROPERTY_REG_ID, $regId);
   }

   public String regId()
   {
      return get(PROPERTY_REG_ID);
   }

   public void putAppVersion(int $appVersion)
   {
      put(PROPERTY_APP_VERSION, $appVersion);
   }

   public int appVersion()
   {
      return get(PROPERTY_APP_VERSION, Integer.MIN_VALUE);
   }
}

 

Registration ID 복사해두기

정상적으로 등록이 됐다면 logcat 에 RegID가 나올텐데, 이걸 복사해뒀다가 송신 서버 만들 때 쓰자.

 

 

Error – SERVICE_NOT_AVAILABLE

도움받은 곳 : http://aroundck.tistory.com/2353

java.io.IOException: SERVICE_NOT_AVAILABLE
	at com.google.android.gms.gcm.GoogleCloudMessaging.register(Unknown Source)
	at com.mintshop.candibox.activities.MainActivity$3.doInBackground(MainActivity.java:151)
	at com.mintshop.candibox.activities.MainActivity$3.doInBackground(MainActivity.java:1)
	at android.os.AsyncTask$2.call(AsyncTask.java:288)
	at java.util.concurrent.FutureTask.run(FutureTask.java:237)
	at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)

이런 에러가 나오면 다른 방법으로 regId를 받아와야한다.

<receiver
    android:name=".receiver.GcmBroadcastReceiver"
    android:permission="com.google.android.c2dm.permission.SEND" >
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" /> <!-- add this line -->

        <category android:name="kr.susemi99.testgcm" />
    </intent-filter>
</receiver>

menifest.xml 에 저 부분을 추가하면 broadcast receiver에서 regId를 받아올 수 있다.

public class GcmBroadcastReceiver extends WakefulBroadcastReceiver
{
  @Override
  public void onReceive(Context context, Intent intent)
  {
    Log.i("GcmBroadcastReceiver.java | onReceive", "|" + "=================" + "|");
    Bundle bundle = intent.getExtras();
    for (String key : bundle.keySet())
    {
      Object value = bundle.get(key);
      Log.i("GcmBroadcastReceiver.java | onReceive", "|" + String.format("%s : %s (%s)", key, value.toString(), value.getClass().getName()) + "|");
      if (key.equalsIgnoreCase("registration_id"))
      {
        String regId = bundle.getString(key);
        PreferenceUtil.instance(context).putRedId(regId);
      }
    }
    Log.i("GcmBroadcastReceiver.java | onReceive", "|" + "=================" + "|");
    
    // Explicitly specify that GcmIntentService will handle the intent.
    ComponentName comp = new ComponentName(context.getPackageName(), GcmIntentService.class.getName());
    // Start the service, keeping the device awake while it is launching.
    startWakefulService(context, intent.setComponent(comp));
    setResultCode(Activity.RESULT_OK);
  }
}

preference에 저장하는 부분을 여기서 하면 된다.

 

예제소스

https://github.com/susemi99/GCM-client-sample