2016-06-23 109 views
8

Tôi đã cố đọc tài liệu và hướng dẫn chính thức về cách gửi tin nhắn từ thiết bị này sang thiết bị khác. Tôi đã lưu mã thông báo đăng ký của cả hai thiết bị trong Cơ sở dữ liệu thời gian thực, do đó tôi có mã thông báo đăng ký của một thiết bị khác. Tôi đã thử cách sau để gửi tin nhắnCách gửi một tin nhắn bằng Nhắn tin Firebase

RemoteMessage message = new RemoteMessage.Builder(getRegistrationToken()) 
        .setMessageId(incrementIdAndGet()) 
        .addData("message", "Hello") 
        .build(); 
FirebaseMessaging.getInstance().send(message); 

Tuy nhiên điều này không hoạt động. Thiết bị kia không nhận được bất kỳ tin nhắn nào. Tôi thậm chí không chắc chắn, nếu tôi có thể sử dụng tin nhắn upstream gửi để tiến hành thiết bị để giao tiếp thiết bị.

PS: Tôi chỉ muốn biết liệu nhắn tin từ thiết bị đến thiết bị có thể sử dụng FCM không? Nếu có, thì mã tôi đã sử dụng có vấn đề gì không? Nếu có, thì cách chính xác là gì.

Cập nhật:
Câu hỏi của tôi là để hỏi xem thiết bị để gửi tin nhắn thiết bị mà không sử dụng bất kỳ máy chủ riêng biệt khác hơn là căn cứ hỏa lực có thể gửi tin nhắn là có thể hay không, nếu có hơn như thế nào, vì không có tài liệu về nó. Tôi không hiểu những gì còn lại để giải thích ở đây? Dù sao tôi đã nhận được câu trả lời và sẽ cập nhật nó như là một câu trả lời một khi câu hỏi được mở cửa trở lại.

+2

Vì vậy, bạn nghĩ rằng từ 5 dòng mã và một số giải thích rõ ràng về những gì đã được thực hiện, ai đó sẽ giúp bạn hiểu tại sao * thiết bị khác không nhận được bất kỳ tin nhắn nào *? Bạn là người lạc quan như vậy :) – Selvin

+1

@Selvin Nếu ai đó không thể hiểu mã tôi đã thêm, họ không đủ điều kiện để trả lời anyways. Nếu bạn đã từng sử dụng FCM trước đó, mã này được lấy trực tiếp từ hướng dẫn chính thức, ngoại trừ phần mã thông báo đăng ký. Để hiểu được, vui lòng đọc bản cập nhật. – noob

+0

có vấn đề 'getInstance() send' ... điều này thậm chí sẽ không biên dịch – Selvin

Trả lời

11

Cảnh báo Có một lý do rất quan trọng vì sao chúng tôi không đề cập đến phương pháp này ở bất kỳ đâu. Điều này cho thấy khóa máy chủ của bạn trong APK mà bạn đặt trên mọi thiết bị khách. Nó có thể (và do đó sẽ) được lấy từ ở đó và có thể dẫn đến việc lạm dụng dự án của bạn. Tôi rất khuyên bạn nên chống lại cách tiếp cận này, ngoại trừ các ứng dụng mà bạn chỉ đặt trên thiết bị của riêng bạn. - Frank van Puffelen

Ok, vì vậy câu trả lời của Frank chính xác là Firebase không hỗ trợ thiết bị cho nhắn tin thiết bị. Tuy nhiên có một lỗ hổng trong đó.Máy chủ Firebase không xác định xem bạn đã gửi yêu cầu từ máy chủ thực hay bạn đang thực hiện yêu cầu từ thiết bị của mình.

Vì vậy, tất cả những gì bạn phải làm là gửi máy chủ nhắn tin Post Request tới Firebase cùng với Khóa máy chủ. Chỉ cần lưu ý rằng khóa máy chủ không được cho là trên thiết bị, nhưng không có tùy chọn nào khác nếu bạn muốn nhắn tin từ thiết bị đến thiết bị bằng Nhắn tin Firebase.

Tôi đang sử dụng OkHTTP thay vì cách mặc định để gọi API Rest. Mã này giống như thế này -

public static final String FCM_MESSAGE_URL = "https://fcm.googleapis.com/fcm/send"; 
OkHttpClient mClient = new OkHttpClient(); 
public void sendMessage(final JSONArray recipients, final String title, final String body, final String icon, final String message) { 

     new AsyncTask<String, String, String>() { 
      @Override 
      protected String doInBackground(String... params) { 
       try { 
        JSONObject root = new JSONObject(); 
        JSONObject notification = new JSONObject(); 
        notification.put("body", body); 
        notification.put("title", title); 
        notification.put("icon", icon); 

        JSONObject data = new JSONObject(); 
        data.put("message", message); 
        root.put("notification", notification); 
        root.put("data", data); 
        root.put("registration_ids", recipients); 

        String result = postToFCM(root.toString()); 
        Log.d(TAG, "Result: " + result); 
        return result; 
       } catch (Exception ex) { 
        ex.printStackTrace(); 
       } 
       return null; 
      } 

      @Override 
      protected void onPostExecute(String result) { 
       try { 
        JSONObject resultJson = new JSONObject(result); 
        int success, failure; 
        success = resultJson.getInt("success"); 
        failure = resultJson.getInt("failure"); 
        Toast.makeText(getCurrentActivity(), "Message Success: " + success + "Message Failed: " + failure, Toast.LENGTH_LONG).show(); 
       } catch (JSONException e) { 
        e.printStackTrace(); 
        Toast.makeText(getCurrentActivity(), "Message Failed, Unknown error occurred.", Toast.LENGTH_LONG).show(); 
       } 
      } 
     }.execute(); 
    } 

String postToFCM(String bodyString) throws IOException { 
     RequestBody body = RequestBody.create(JSON, bodyString); 
     Request request = new Request.Builder() 
       .url(FCM_MESSAGE_URL) 
       .post(body) 
       .addHeader("Authorization", "key=" + SERVER_KEY) 
       .build(); 
     Response response = mClient.newCall(request).execute(); 
     return response.body().string(); 
    } 

Tôi hy vọng Firebase sẽ có giải pháp tốt hơn trong tương lai. Nhưng cho đến lúc đó, tôi nghĩ đây là cách duy nhất. Cách khác là gửi tin nhắn chủ đề hoặc nhắn tin nhóm. Nhưng điều đó không nằm trong phạm vi của câu hỏi.

Cập nhật:
Các JSONArray được định nghĩa như thế này -

JSONArray regArray = new JSONArray(regIds); 

regIds là một mảng String của id đăng ký, bạn muốn gửi thông điệp này đến. Hãy nhớ rằng các id đăng ký phải luôn nằm trong một mảng, ngay cả khi bạn muốn nó gửi đến một người nhận.

+0

noob tôi cần xem xét 'người nhận' JsonArray của bạn .. Chỉ cần kiểm tra định dạng của mảng đó .. Cảm ơn giải pháp :) –

+0

@jankigadhiya Đã thêm giải thích – noob

+0

Có một lý do rất quan trọng tại sao chúng tôi không đề cập đến điều này tiếp cận mọi nơi. Điều này cho thấy khóa máy chủ của bạn trong APK mà bạn đặt trên mọi thiết bị khách. Nó có thể (và do đó sẽ) được lấy từ đó và có thể dẫn đến lạm dụng dự án của bạn. Tôi khuyên bạn không nên sử dụng phương pháp này, ngoại trừ các ứng dụng mà bạn chỉ đặt trên thiết bị của riêng mình. –

14

căn cứ hỏa lực có hai tính năng gửi tin nhắn đến các thiết bị:

  • bảng Notifications trong căn cứ hỏa lực Console của bạn cho phép bạn gửi thông báo đến các thiết bị cụ thể, nhóm người dùng, hoặc các chủ đề mà người dùng đã đăng ký.
  • bằng cách gọi API Nhắn tin qua đám mây của Firebase, bạn có thể gửi tin nhắn với bất kỳ chiến lược nhắm mục tiêu nào bạn muốn. Việc gọi API FCM yêu cầu quyền truy cập vào khóa Máy chủ của bạn mà bạn không bao giờ nên để lộ trên thiết bị khách. Đó là lý do tại sao bạn nên luôn chạy mã như vậy trên máy chủ ứng dụng.

Các Firebase documentation thấy điều này trực quan:

The two ways to send messages to device with Firebase

thông điệp gửi từ một thiết bị trực tiếp đến một thiết bị khác không được hỗ trợ thông qua căn cứ hỏa lực Cloud Messaging.

Cập nhật: Tôi đã viết một bài đăng blog chi tiết how to send notifications between Android devices using Firebase Database, Cloud Messaging and Node.js.

Cập nhật 2: Bây giờ bạn cũng có thể sử dụng Cloud Functions for Firebase để gửi thư một cách an toàn mà không cần phải quay lên máy chủ. Xem this sample use-case để bắt đầu.

+3

Cảm ơn câu trả lời. Tôi hiểu rằng chìa khóa máy chủ không nên được tiếp xúc với thiết bị khách, nhưng tôi chỉ đang tạo ra các nguyên mẫu và không muốn có một máy chủ ứng dụng ở giai đoạn này, vì vậy nếu tôi để lộ chìa khóa và gửi tin nhắn bằng POST từ máy khách thiết bị, nó có hoạt động không? Tôi sẽ giới thiệu một máy chủ ứng dụng sau, nhưng không muốn điều này ngay bây giờ. – noob

+0

Vì vậy, về cơ bản một máy chủ ứng dụng là cần thiết chủ yếu để bảo mật. Điều gì xảy ra nếu tôi không sử dụng một máy chủ ứng dụng ở tất cả chỉ là thư viện XMPP để gửi stanzas tới FCM CCS và không lưu trữ khóa máy chủ trong mã ứng dụng của tôi, nhưng yêu cầu nó thông qua một số tập lệnh PHP được lưu trữ trên cơ sở dữ liệu trực tuyến khi tôi cần nó. @frankvanpuffelen –

+0

Liên quan đến bản cập nhật 2, trường hợp sử dụng nào liên quan đến việc sử dụng Nhắn tin qua đám mây của Firebase với Chức năng đám mây? Hay bạn đang đề cập đến ví dụ giới hạn trò chuyện Cơ sở dữ liệu thời gian thực? – zavtra

3

Tôi cũng đã sử dụng thiết bị trực tiếp để nhắn tin gcm thiết bị trong nguyên mẫu của mình. Nó đã hoạt động rất tốt. Chúng tôi không có máy chủ nào. Chúng tôi trao đổi id reg GCM bằng sms/văn bản và sau đó giao tiếp bằng GCM sau đó. Tôi đặt ở đây liên quan đến tiến GCM xử lý

************** Gửi tin nhắn GCM *************

//Sends gcm message Asynchronously 
public class GCM_Sender extends IntentService{ 
    final String API_KEY = "****************************************"; 

    //Empty constructor 
    public GCM_Sender() { 
     super("GCM_Sender"); 
    } 

    //Processes gcm send messages 
    @Override 
    protected void onHandleIntent(Intent intent) { 

     Log.d("Action Service", "GCM_Sender Service Started"); 
     //Get message from intent 
     String msg = intent.getStringExtra("msg"); 
     msg = "\"" + msg + "\""; 
     try{ 
      String ControllerRegistrationId = null;     
      //Check registration id in db  
      if(RegistrationIdAdapter.getInstance(getApplicationContext()).getRegIds().size() > 0) { 
       String controllerRegIdArray[] = RegistrationIdAdapter.getInstance(getApplicationContext()).getRegIds().get(1); 
       if(controllerRegIdArray.length>0) 
        ControllerRegistrationId = controllerRegIdArray[controllerRegIdArray.length-1]; 

       if(!ControllerRegistrationId.equalsIgnoreCase("NULL")){ 
        // 1. URL 
        URL url = new URL("https://android.googleapis.com/gcm/send"); 
        // 2. Open connection 
        HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection(); 
        // 3. Specify POST method 
        urlConnection.setRequestMethod("POST"); 
        // 4. Set the headers 
        urlConnection.setRequestProperty("Content-Type", "application/json"); 
        urlConnection.setRequestProperty("Authorization", "key=" + API_KEY); 
        urlConnection.setDoOutput(true); 
        // 5. Add JSON data into POST request body 
        JSONObject obj = new JSONObject("{\"time_to_live\": 0,\"delay_while_idle\": true,\"data\":{\"message\":" + msg + "},\"registration_ids\":[" + ControllerRegistrationId + "]}"); 
        // 6. Get connection output stream 
        OutputStreamWriter out = new OutputStreamWriter(urlConnection.getOutputStream()); 
        out.write(obj.toString()); 
        out.close(); 
        // 6. Get the response 
        int responseCode = urlConnection.getResponseCode(); 

        BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream())); 
        String inputLine; 
        StringBuffer response = new StringBuffer(); 
        while ((inputLine = in.readLine()) != null){ 
         response.append(inputLine); 
        } 
        in.close(); 
        Log.d("GCM getResponseCode:", new Integer(responseCode).toString()); 
       }else{ 
        Log.d("GCM_Sender:","Field REGISTRATION_TABLE is null"); 
       } 
      }else { 
       Log.d("GCM_Sender:","There is no Registration ID in DB ,please sync devices"); 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 
      //MessageSender.getInstance().sendMessage(msg, Commands.SMS_MESSAGE); 
     } 
    } 

    //Called when service is no longer alive 
    @Override 
    public void onDestroy() { 
     super.onDestroy(); 
     //Do a log that GCM_Sender service has been destroyed 
     Log.d("Action Service", "GCM_Sender Service Destroyed"); 
    } 
} 

************** Nhận Tin nhắn GCM *************

public class GCM_Receiver extends WakefulBroadcastReceiver { 
    public static final String RETRY_ACTION ="com.google.android.c2dm.intent.RETRY"; 
    public static final String REGISTRATION ="com.google.android.c2dm.intent.REGISTRATION"; 
    public SharedPreferences preferences; 

    //Processes Gcm message . 
    @Override 
    public void onReceive(Context context, Intent intent) { 
     ComponentName comp = new ComponentName(context.getPackageName(), 
       GCMNotificationIntentService.class.getName()); 
     //Start GCMNotificationIntentService to handle gcm message asynchronously 
     startWakefulService(context, (intent.setComponent(comp))); 
     setResultCode(Activity.RESULT_OK); 

     /*//Check if DatabaseService is running . 
     if(!DatabaseService.isServiceRunning) { 
      Intent dbService = new Intent(context,DatabaseService.class); 
      context.startService(dbService); 
     }*/ 
     //Check if action is RETRY_ACTION ,if it is then do gcm registration again . 
     if(intent.getAction().equals(RETRY_ACTION)) { 
      String registrationId = intent.getStringExtra("registration_id"); 

      if(TextUtils.isEmpty(registrationId)){ 
       DeviceRegistrar.getInstance().register(context); 
      }else { 
       //Save registration id to prefs . 
       preferences = PreferenceManager.getDefaultSharedPreferences(context); 
       SharedPreferences.Editor editor = preferences.edit(); 
       editor.putString("BLACKBOX_REG_ID",registrationId); 
       editor.commit(); 
      } 
     } else if (intent.getAction().equals(REGISTRATION)) { 
     } 

    } 
} 

//Processes gcm messages asynchronously . 
public class GCMNotificationIntentService extends IntentService{ 
    public static final int NOTIFICATION_ID = 1; 
    private NotificationManager mNotificationManager; 
    String gcmData; 
    private final String TAG = "GCMNotificationIntentService"; 

    //Constructor with super(). 
    public GCMNotificationIntentService() { 
     super("GcmIntentService"); 
    } 

    //Called when startService() is called by its Client . 
    //Processes gcm messages . 
    @Override 
    protected void onHandleIntent(Intent intent) { 

     Log.d("GCMNotificationIntentService", "GCMNotificationIntentService Started"); 
     Bundle extras = intent.getExtras(); 
     //Get instance of GoogleCloudMessaging . 
     GoogleCloudMessaging gcm = GoogleCloudMessaging.getInstance(this); 
     //Get gcm message type . 
     String messageType = gcm.getMessageType(intent); 

     if (!extras.isEmpty()) { 
      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()); 
      } else if (GoogleCloudMessaging.MESSAGE_TYPE_MESSAGE 
        .equals(messageType)) { 
       Log.i(TAG, "Completed work @ " + SystemClock.elapsedRealtime()); 

       gcmData = extras.getString("message"); 
       Intent actionService = new Intent(getApplicationContext(),Action.class);  
       actionService.putExtra("data", gcmData); 
       //start Action service . 
       startService(actionService); 

       //Show push notification . 
       sendNotification("Action: " + gcmData); 
       //Process received gcmData. 

       Log.d(TAG,"Received Gcm Message from Controller : " + extras.getString("message")); 
      } 
     } 
     GCM_Receiver.completeWakefulIntent(intent); 
    } 

    //Shows notification on device notification bar . 
    private void sendNotification(String msg) { 
     mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); 
     Intent notificationIntent = new Intent(this, BlackboxStarter.class); 
     //Clicking on GCM notification add new layer of app. 
     notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); 
     PendingIntent contentIntent = PendingIntent.getActivity(this, 0,notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT); 
     NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(
       this).setSmallIcon(R.drawable.gcm_cloud) 
       .setContentTitle("Notification from Controller") 
       .setStyle(new NotificationCompat.BigTextStyle().bigText(msg)) 
       .setContentText(msg); 
     mBuilder.setContentIntent(contentIntent); 
     mNotificationManager.notify(NOTIFICATION_ID, mBuilder.build()); 
     //Play default notification 
     try { 
      Uri notification = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); 
      Ringtone r = RingtoneManager.getRingtone(getApplicationContext(), notification); 
      r.play(); 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 

    //Called when service is no longer be available . 
    @Override 
    public void onDestroy() { 
     // TODO Auto-generated method stub 
     super.onDestroy(); 
     Log.d("GCMNotificationIntentService", "GCMNotificationIntentService Destroyed"); 
    } 

} 
+0

Chào mừng bạn đến với stackoverflow. Bạn cần phải đặt các giải pháp thực tế và tốt nhất là viết mã trong câu trả lời của bạn cho nó để được coi là một. Nếu không, bạn sẽ nhận được downvotes và/hoặc loại bỏ các câu trả lời của bạn hoàn toàn. Nếu bạn muốn chia sẻ ý kiến, bạn có thể bình luận (Sau khi đạt tới danh tiếng 50). Kiểm tra phần trợ giúp để biết thêm chi tiết – noob

0

Tôi đã giải pháp muộn nhưng đã giúp tôi ghi lại điều này Câu trả lời đơn giản, bạn có thể gửi tin nhắn của bạn trực tiếp đến các thiết bị Android từ ứng dụng Android, đây là việc thực hiện đơn giản tôi đã làm và nó hoạt động tuyệt vời cho tôi.

  1. biên dịch thư viện bóng android

    compile 'com.android.volley:volley:1.0.0' 
    
  2. Chỉ cần sao chép dán chức năng đơn giản này;) và cuộc sống của bạn sẽ trở nên mịn màng giống như con dao trong bơ. : D

    public static void sendPushToSingleInstance(final Context activity, final HashMap dataValue /*your data from the activity*/, final String instanceIdToken /*firebase instance token you will find in documentation that how to get this*/) { 
    
    
    final String url = "https://fcm.googleapis.com/fcm/send"; 
    StringRequest myReq = new StringRequest(Request.Method.POST,url, 
         new Response.Listener<String>() { 
          @Override 
          public void onResponse(String response) { 
           Toast.makeText(activity, "Bingo Success", Toast.LENGTH_SHORT).show(); 
          } 
         }, 
         new Response.ErrorListener() { 
          @Override 
          public void onErrorResponse(VolleyError error) { 
           Toast.makeText(activity, "Oops error", Toast.LENGTH_SHORT).show(); 
          } 
         }) { 
    
        @Override 
        public byte[] getBody() throws com.android.volley.AuthFailureError { 
         Map<String, Object> rawParameters = new Hashtable(); 
         rawParameters.put("data", new JSONObject(dataValue)); 
         rawParameters.put("to", instanceIdToken); 
         return new JSONObject(rawParameters).toString().getBytes(); 
        }; 
    
        public String getBodyContentType() 
        { 
         return "application/json; charset=utf-8"; 
        } 
        @Override 
        public Map<String, String> getHeaders() throws AuthFailureError { 
         HashMap<String, String> headers = new HashMap<String, String>(); 
         headers.put("Authorization", "key="+YOUR_LEGACY_SERVER_KEY_FROM_FIREBASE_CONSOLE); 
         return headers; 
        } 
    
    }; 
    
    Volley.newRequestQueue(activity).add(myReq); 
    } 
    

Note Nếu bạn muốn gửi thông điệp tới chủ đề, do đó bạn có thể thay đổi thông số instanceIdToken một cái gì đó giống như /chủ đề/topicName. Đối với nhóm triển khai giống nhau nhưng bạn chỉ cần chăm sóc các tham số. checkout Firebase documentation and you can pass those parameters. cho tôi biết nếu bạn gặp phải bất kỳ sự cố nào.

+0

nhận được lỗi không 400 để chuyển định dạng sai trong json – Panache

+0

hãy kiểm tra nó bằng "datavalue" đơn giản. –

+0

{data = {"title": "Testing"}, to = diduAXILZtY: APA91bHcIBllVz_liOnF-RQa4ei} đây là những gì tôi gửi, giá trị được cắt – Panache

Các vấn đề liên quan