Khóa học lập trình Android cơ bản

Serial tutorial hướng dẫn lập trình Android cơ bản

Google Cloud Messaging và Push Notification trong Android - Phần 2 Google Cloud Messaging và Push Notification trong Android - Phần 2 Google Cloud Messaging và Push Notification trong Android - Phần 2 Google Cloud Messaging và Push Notification trong Android - Phần 2 Google Cloud Messaging và Push Notification trong Android - Phần 2 0/5 (68 reviews)

Google Cloud Messaging và Push Notification trong Android - Phần 2

Đã đăng 2017-11-14 23:57:53 bởi Kteam
2 bình luận 5613 lượt xem
Google Cloud Messaging và Push Notification trong Android - Phần 2 0 /5 stars (0 reviews)
 

Dẫn nhập

Chào các bạn! Ở các bài học trước, chúng ta đã cùng nhau tìm hiểu về FIREBASE & THÀNH PHẦN CLOUD MESSAGING của nó nói riêng, cũng như bước đầu setup một project trên Google Firebase để làm việc với một project mới toanh: Tạo ứng dụng nhận tin nhắn push từ Firebase.

Ở bài này chúng ta sẽ vào phần 2 của bài học trước, lần này chúng ta sẽ code logic chính của ứng dụng, và chạy thử.


Nội dung

Để đọc hiểu bài này tốt nhất các bạn nên có kiến thức cơ bản về các phần:

Trong bài học này, chúng ta sẽ cùng tìm hiểu các vấn đề:

  • Khởi tạo các hàm cần thiết
  • Instance ID
  • MessagingService
  • NotificationUtils
  • MainActivity

Khởi tạo các hàm cần thiết

Để làm việc với Firebase Google Cloud Messaging hiệu quả, chúng ta cần bố trí các class, các package hợp lý trong project bởi một tin nhắn push từ server sẽ cần khá nhiều thành phần liên quan như Notification, Service, Broadcast Receiver,...

Với những lý do như trên, để có thể mở rộng thêm tính năng về lâu dài, chúng ta sẽ tổ chức các hàm chức năng vào các class chuyên biệt. Đây cũng là cách phân bố chức năng hợp lý mà bạn cũng nên áp dụng cho các project sau này.

  • Các hàm chức năng đưa vào một class riêng có tên NotificationUtils.
  • Lớp chức năng FirebaseMessage đặt riêng. Lớp này là một Service, có chức năng lắng nghe các thông báo được gửi đến thiết bị Android.
  • Lớp chức năng FirebaseInstanceID đặt riêng. Lớp này cũng là một Service, có chức năng lắng nghe thay đổi và xử lý thay đổi liên quan đến ID Firebase đi kèm với thiết bị.
  • Chỉ cần một Activity duy nhất là MainActivity để demo. Sau đó Sử dụng các chức năng đã tạo vào đây.

Lưu ý: Để sử dụng được Firebase / GCM, điện thoại của bạn phải hỗ trợ Google Play Services (tức là có App Store, hoặc Google Chrome, hoặc một app nào đó của Google). Điều này áp dụng cho cả Emulator, nên khi tạo Emulator bạn cũng cần chú ý.

Để biết được máy của bạn có Google Services hay không thì chỉ cần xem nó có sẵn một app của Google nào đó đã được cài chưa là biết, ví dụ: Google Plus / Map / Google Play.

Ở trên chúng ta có các class:

  • HowkFirebaseInstanceIDService: Chứa hàm onTokenRefresh, thực hiện lưu lại thay đổi khi GCM token mới được tạo ra cho thiết bị. Mỗi lần như vậy thì sẽ lưu token vào một vùng nhớ tĩnh trong máy mà chúng ta đã được tìm hiểu ở bài trước là SharedPreferences.
  • HowkFirebaseMessagingService: Class này sẽ nhận tin nhắn từ GCM / FCM rồi chuyển vào hàm onMessageReceived. Tại đây chúng ta sẽ mổ xẻ tin nhắn.
  • NotificationUtils: Chứa các hàm xử lý tin nhắn chính. Ví dụ: Nhận được tin nhắn thì tạo ra Notification đẩy xuống thông báo trên điện thoại,…

Instance ID

Như đã đề cập ở trên, class này chứa hàm onTokenRefresh, thực hiện lưu lại thay đổi khi GCM token mới được tạo ra cho thiết bị.

package com.howkteam.pushexample.service;

import android.content.Intent;
import android.content.SharedPreferences;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;

import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
import com.howkteam.pushexample.util.Config;

public class HowkFirebaseInstanceIDService extends FirebaseInstanceIdService {
  private static final String TAG = HowkFirebaseInstanceIDService.class.getSimpleName();

  @Override
  public void onTokenRefresh() {
    super.onTokenRefresh();
    Log.e("RR", "Regresh");
    String refreshedToken = FirebaseInstanceId.getInstance().getToken();

    // Lưu token vào SharedPreferences để sử dụng về sau.
    storeRegIdInPref(refreshedToken);
    Log.e(TAG, refreshedToken);
    // Gửi token lên server.
    sendRegistrationToServer(refreshedToken);

    Intent registrationComplete = new Intent(Config.REGISTRATION_COMPLETE);
    registrationComplete.putExtra("token", refreshedToken);
    LocalBroadcastManager.getInstance(this).sendBroadcast(registrationComplete);
  }

  private void sendRegistrationToServer(final String token) {
    // Hàm gửi token lên server, cần xử lý bằng AsyncTask hoặc thread riêng.
    Log.e(TAG, "sendRegistrationToServer: " + token);
  }

  private void storeRegIdInPref(String token) {
    SharedPreferences pref = getApplicationContext().getSharedPreferences(Config.SHARED_PREF, 0);
    SharedPreferences.Editor editor = pref.edit();
    editor.putString("regId", token);
    editor.apply();
  }
}

Xin lưu lý là hàm sendRegistrationToServer ở trên chỉ là hàm nháp, các bạn có thể mở rộng tính năng của app ra sau này với server phía back-end, còn hiện tại thì như thế này là đủ.


MessagingService

Class này sẽ nhận tin nhắn từ GCM / FCM rồi chuyển vào hàm onMessageReceived. Tại đây chúng ta sẽ mổ xẻ tin nhắn.

package com.howkteam.pushexample.service;

import android.content.Context;
import android.content.Intent;
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;

import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import com.howkteam.pushexample.MainActivity;
import com.howkteam.pushexample.util.Config;
import com.howkteam.pushexample.util.NotificationUtils;

import org.json.JSONException;
import org.json.JSONObject;

public class HowkFirebaseMessagingService extends FirebaseMessagingService {
  public static final String TAG = HowkFirebaseMessagingService.class.getSimpleName();
  private NotificationUtils notificationUtils;

  public HowkFirebaseMessagingService() {
    super();
  }

  @Override
  public void onMessageReceived(RemoteMessage remoteMessage) {
    Log.e(TAG, "From: " + remoteMessage.getFrom());

    if (remoteMessage == null) {
      return;
    }

    // Kiểm tra xem message có chứa notification payload không.
    if (remoteMessage.getNotification() != null) {
      Log.e(TAG, "Notification Body: " + remoteMessage.getNotification().getBody());
      handleNotification(remoteMessage.getNotification().getBody());
    }

    // Kiểm tra xem message có chứa data payload không.
    if (remoteMessage.getData().size() > 0) {
      Log.e(TAG, "Data Payload: " + remoteMessage.getData().toString());

      try {
        JSONObject json = new JSONObject(remoteMessage.getData().toString());
        handleDataMessage(json);
      } catch (Exception e) {
        Log.e(TAG, "Exception: " + e.getMessage());
      }
    }


  }

  private void handleNotification(String message) {
    if (!NotificationUtils.isAppInBackground(getApplicationContext())) {
      // Nếu app đang chạy hiện, broadcast cả push message.
      Intent pushNotification = new Intent(Config.PUSH_NOTIFICATION);
      pushNotification.putExtra("message", message);
      LocalBroadcastManager.getInstance(this).sendBroadcast(pushNotification);
    } else {
      // Nếu app đang chạy ngầm, Firebase sẽ tự xử lý notification.
    }
  }

  private void handleDataMessage(JSONObject json) {
    Log.e(TAG, "push json: " + json.toString());

    try {
      JSONObject data = json.getJSONObject("data");

      String title = data.getString("title");
      String message = data.getString("message");
      boolean isBackground = data.getBoolean("is_background");
      String imageUrl = data.getString("image");
      String timestamp = data.getString("timestamp");
      JSONObject payload = data.getJSONObject("payload");

      Log.e(TAG, "title: " + title);
      Log.e(TAG, "message: " + message);
      Log.e(TAG, "isBackground: " + isBackground);
      Log.e(TAG, "payload: " + payload.toString());
      Log.e(TAG, "imageUrl: " + imageUrl);
      Log.e(TAG, "timestamp: " + timestamp);


      if (!NotificationUtils.isAppInBackground(getApplicationContext())) {
        // Nếu app đang chạy hiện, broadcast cả push message.
        Intent pushNotification = new Intent(Config.PUSH_NOTIFICATION);
        pushNotification.putExtra("message", message);
        LocalBroadcastManager.getInstance(this).sendBroadcast(pushNotification);
      } else {
        // Nếu app đang chạy ngầm, Firebase sẽ tự xử lý notification.
        Intent resultIntent = new Intent(getApplicationContext(), MainActivity.class);
        resultIntent.putExtra("message", message);

        // Kiểm tra xem có ảnh không.
        if (TextUtils.isEmpty(imageUrl)) {
          showNotificationMessage(getApplicationContext(), title, message, timestamp, resultIntent);
        } else {
          // image is present, show notification with image
          showNotificationMessageWithBigImage(getApplicationContext(), title, message, timestamp, resultIntent, imageUrl);
        }
      }
    } catch (JSONException e) {
      Log.e(TAG, "Json Exception: " + e.getMessage());
    } catch (Exception e) {
      Log.e(TAG, "Exception: " + e.getMessage());
    }
  }

  /**
   * Hiển thị notification chỉ có text.
   */
  private void showNotificationMessage(Context context, String title, String message, String timeStamp, Intent intent) {
    notificationUtils = new NotificationUtils(context);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    notificationUtils.showNotificationMessage(title, message, timeStamp, intent);
  }

  /**
   * Hiển thị notification có cả text và hình.
   */
  private void showNotificationMessageWithBigImage(Context context, String title, String message, String timeStamp, Intent intent, String imageUrl) {
    notificationUtils = new NotificationUtils(context);
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
    notificationUtils.showNotificationMessage(title, message, timeStamp, intent, imageUrl);
  }
}

NotificationUtils

Class chứa các method cần thiết để hiển thị message (tên, nội dung, ảnh, và ngày giờ gửi).

package com.howkteam.pushexample.util;

import android.app.ActivityManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.support.v4.app.NotificationCompat;
import android.text.Html;
import android.text.TextUtils;
import android.util.Patterns;

import com.howkteam.pushexample.R;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * Class chứa các method cần thiết để hiển thị message (tên, nội dung, ảnh, và ngày giờ gửi).
 */
public class NotificationUtils {
  private static final String TAG = NotificationUtils.class.getSimpleName();
  private Context context;

  public NotificationUtils(Context context) {
    this.context = context;
  }

  public void showNotificationMessage(String title, String message, String timeStamp, Intent intent) {

  }

  public void showNotificationMessage(final String title, final String message, final String timeStamp, Intent intent, String imageUrl) {
    if (TextUtils.isEmpty(message)) {
      return;
    }

    final int icon = R.mipmap.ic_launcher;

    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
    final PendingIntent resultPendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
    final NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context);
    if (TextUtils.isEmpty(imageUrl)) {
      // Kiểm tra xem đường dẫn ảnh có hợp lệ hay không.
      if (imageUrl != null && imageUrl.length() > 4 && Patterns.WEB_URL.matcher(imageUrl).matches()) {
        Bitmap bitmap = getBitmapFromUrl(imageUrl);
        if (bitmap != null) {

        }
      }
    }
  }

  /**
   * Hiển thị thông báo dạng nhỏ (chỉ có 1-2 dòng text).
   */
  public void showSmallNotification(NotificationCompat.Builder builder,
                                    int icon,
                                    String title,
                                    String message,
                                    String timeStamp,
                                    PendingIntent resultTendingIntent) {
    NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
    inboxStyle.addLine(message);
    Notification notification;
    notification = builder.setSmallIcon(icon).setTicker(title).setWhen(0)
            .setAutoCancel(true)
            .setContentTitle(title)
            .setContentIntent(resultTendingIntent)
            .setStyle(inboxStyle)
            .setWhen(getTimeMilliSec(timeStamp))
            .setSmallIcon(R.mipmap.ic_launcher)
            .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), icon))
            .setContentText(message)
            .build();
  }

  /**
   * Hiển thị thông báo dạng lớn (có hình).
   */
  private void showBigNotification(Bitmap bitmap, NotificationCompat.Builder mBuilder, int icon, String title, String message, String timeStamp, PendingIntent resultPendingIntent) {
    NotificationCompat.BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle();
    bigPictureStyle.setBigContentTitle(title);
    bigPictureStyle.setSummaryText(Html.fromHtml(message).toString());
    bigPictureStyle.bigPicture(bitmap);
    Notification notification;
    notification = mBuilder.setSmallIcon(icon).setTicker(title).setWhen(0)
            .setAutoCancel(true)
            .setContentTitle(title)
            .setContentIntent(resultPendingIntent)
            .setStyle(bigPictureStyle)
            .setWhen(getTimeMilliSec(timeStamp))
            .setSmallIcon(R.mipmap.ic_launcher)
            .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), icon))
            .setContentText(message)
            .build();

    NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.notify(Config.NOTIFICATION_ID_BIG_IMAGE, notification);
  }

  /**
   * Chuyển đổi url thành ảnh bitmap (nếu url là ảnh hợp lệ).
   * Hàm này được gọi trong service nên không bị chặn bởi UI Thread.
   */
  public Bitmap getBitmapFromUrl(String inputUrl) {
    try {
      URL url = new URL(inputUrl);
      HttpURLConnection connection = (HttpURLConnection) url.openConnection();
      connection.setDoInput(true);
      connection.connect();
      InputStream inputStream = connection.getInputStream();
      Bitmap b = BitmapFactory.decodeStream(inputStream);
    } catch (IOException e) {
      e.printStackTrace();
      return null;
    }

    return null;
  }

  /**
   * Kiểm tra xem app có đang ở background (chạy nền) không.
   */
  public static boolean isAppInBackground(Context context) {
    boolean isInBackground = true;
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
      List<ActivityManager.RunningAppProcessInfo> runningProcesses = activityManager.getRunningAppProcesses();
      for (ActivityManager.RunningAppProcessInfo processInfo : runningProcesses) {
        if (processInfo.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
          for (String activeProcess : processInfo.pkgList) {
            if (activeProcess.equals(context.getPackageName())) {
              isInBackground = false;
            }
          }
        }
      }
    } else {
      List<ActivityManager.RunningTaskInfo> taskInfo = activityManager.getRunningTasks(1);
      ComponentName componentInfo = taskInfo.get(0).topActivity;
      if (componentInfo.getPackageName().equals(context.getPackageName())) {
        isInBackground = false;
      }
    }

    return isInBackground;
  }

  /**
   * Xóa tất cả các Notifications đang hiển thị trong Notification Center.
   */
  public static void clearNotifications(Context context) {
    NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
    notificationManager.cancelAll();
  }

  public static long getTimeMilliSec(String timeStamp) {
    SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    try {
      Date date = format.parse(timeStamp);
      return date.getTime();
    } catch (ParseException e) {
      e.printStackTrace();
    }
    return 0;
  }
}

MainActivity

Cuối cùng, trong MainActivity, chúng ta đem ra sử dụng các thứ đã code ở trên. Các bạn xem kỹ phần comment nhé:

package com.howkteam.pushexample;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.widget.TextView;
import android.widget.Toast;

import com.google.firebase.messaging.FirebaseMessaging;
import com.howkteam.pushexample.util.Config;
import com.howkteam.pushexample.util.NotificationUtils;

public class MainActivity extends AppCompatActivity {

  private static final String TAG = MainActivity.class.getSimpleName();
  private BroadcastReceiver mRegistrationBroadcastReceiver;
  private TextView txtRegId, txtMessage;

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

    txtRegId = (TextView) findViewById(R.id.txt_reg_id);
    txtMessage = (TextView) findViewById(R.id.txt_push_message);

    mRegistrationBroadcastReceiver = new BroadcastReceiver() {
      @Override
      public void onReceive(Context context, Intent intent) {

        // Kiểm tra Intent Filter có khớp cái nào không.
        if (intent.getAction().equals(Config.REGISTRATION_COMPLETE)) {
          // GCM đã được đăng ký thành công.
          // Đăng ký vào topic có tên "Global".
          FirebaseMessaging.getInstance().subscribeToTopic(Config.TOPIC_GLOBAL);
          displayFirebaseRegId();

        } else if (intent.getAction().equals(Config.PUSH_NOTIFICATION)) {
          // Khi có tin nhắn mới về.
          String message = intent.getStringExtra("message");
          Toast.makeText(getApplicationContext(), "Push notification: " + message, Toast.LENGTH_LONG).show();
          txtMessage.setText(message);
        }
      }
    };

    displayFirebaseRegId();
  }

  // Hiển thị Registration ID.
  private void displayFirebaseRegId() {
    SharedPreferences pref = getApplicationContext().getSharedPreferences(Config.SHARED_PREF, 0);
    String regId = pref.getString("regId", null);

    Log.e(TAG, "Firebase reg id: " + regId);

    if (!TextUtils.isEmpty(regId))
      txtRegId.setText("Firebase Reg Id: " + regId);
    else
      txtRegId.setText("Firebase Reg Id is not received yet!");
  }

  @Override
  protected void onResume() {
    super.onResume();

    // Đăng ký receiver vào LocalBroadcastManager.
    LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
            new IntentFilter(Config.REGISTRATION_COMPLETE));

    // Đăng ký bộ nhận tin nhắn.
    LocalBroadcastManager.getInstance(this).registerReceiver(mRegistrationBroadcastReceiver,
            new IntentFilter(Config.PUSH_NOTIFICATION));

    // Xóa các notification khi app được bật.
    NotificationUtils.clearNotifications(getApplicationContext());
  }

  @Override
  protected void onPause() {
    LocalBroadcastManager.getInstance(this).unregisterReceiver(mRegistrationBroadcastReceiver);
    super.onPause();
  }
}

Test thôi… !

Compile và chạy app. Các bạn cần ghi lại Registration ID của máy sau khi đăng ký thành công với server FCM.

Sử dụng registration này, các bạn truy cập vào trang Pushtry.com và thử gửi một tin nhắn đến chính máy của mình:

(Server API key có dạng  AIzaSyBayaAYuvudghyw4kh67G… ), các bạn vào Google Dev console (xem lại bài trước) để biết thêm chi tiết.


Source code tham khảo

Nhằm giúp các bạn thao tác dễ dàng hơn trong quá trình theo dõi bài viết, Kteam hỗ trợ source code tham khảo ngay trong link bên dưới 

PUSHEXAMPLE.ZIP


Kết luận

Qua bài này chúng ta đã nắm được cách tích hợp GCM / FCM cơ bản vào ứng dụng Android. Cũng như cách test push notification.

Cảm ơn các bạn đã theo dõi bài viết. Hãy để lại bình luận hoặc góp ý của mình để phát triển bài viết tốt hơn. Đừng quên “Luyện tập – Thử thách – Không ngại khó”.


Tài liệu 

Nhằm phục vụ mục đích học tập Offline của cộng đồng, Kteam hỗ trợ tính năng lưu trữ nội dung bài học Google Cloud Messaging và Push Notification trong Android - Phần 2 dưới dạng file PDF trong link bên dưới.

Ngoài ra, bạn cũng có thể tìm thấy các tài liệu được đóng góp từ cộng đồng ở mục TÀI LIỆU trên thư viện Howkteam.com

Đừng quên like hoặc +1 Google để ủng hộ Kteam và tác giả nhé! 


Thảo luận

Nếu bạn có bất kỳ khó khăn hay thắc mắc gì về khóa học, đừng ngần ngại đặt câu hỏi trong phần BÌNH LUẬN bên dưới hoặc trong mục HỎI & ĐÁP trên thư viện Howkteam.com để nhận được sự hỗ trợ từ cộng đồng. 

 

Chia sẻ:
Thảo luận Hỏi và đáp Báo lỗi bài viết
Hủy bỏ   hoặc  
Google Cloud Messaging và Push Notification trong Android - Phần 2
nguyenvanthinh1992 2018-09-21 23:08:59

File Config kia tự tạo hay gì thế Ad ơi, cho mình xin mã nguồn nội dung 2 file java đó với

0 bình chọn
Reply
Google Cloud Messaging và Push Notification trong Android - Phần 2
nguyenvanthinh1992 2018-09-21 23:08:12

ádasd

0 bình chọn
Reply
Hủy bỏ   hoặc  
Hủy bỏ   hoặc  

Chiến dịch

Kteam - Howkteam Free Education