Android GCM パラメーター、リミットについてなど

Android

Android GCM パラメーター、リミットについてなど

下記のAndroid developer公式サイトの要点を、まとめてみたので掲載しておきます。私が思うところの要点なので、抜けているところもあります。
http://developer.android.com/guide/google/gcm/adv.html

collapse_keyについて

collapse_key flagをONにしておくと、古いメッセージは廃棄されて、新しいメッセージにとって代わる
collapseなしでストアできる上限は100個
それ以上は廃棄される
collapse_keyについては、下のメッセージタイプにも関わる重要なパラメーター

GCMに端末が接続していない場合

GCMに端末が接続していない場合は、GCMに接続するまで待つ
delay_while_idle flag.をONにしてもしなくてもこれは同じ

たとえば工場出荷時に戻されるなどして、2度とGCMに接続されない場合、4週間は保存される

アプリケーションがインストールされていない端末に接続しようとした場合は、メッセージを廃棄して、registration IDを不正なものにする

スロットル

一度に大量のメッセージが送られないように、スロットルという仕組みがあり、大量にメッセージが送られた場合は、少し遅延時間が発生する

アプリのアップデートやリストア時

アプリケーションがアップデートされる場合は、registration IDが同一である保証はない
推奨されるのは、アプリケーションのバージョンを3rdパーティーのサーバーでも保存しておいて、バージョンが新しくなった場合はregistrationのプロセスをもう一度やらせるなどの対策が必要
同じく、バックアップやリストアで戻った場合、registration IDは違うものになる可能性あり

リトライについて

com.google.android.c2dm.intent.REGISTRATIONがエラーを受け取った場合は、だんだん時間間隔が長くなりながら、リトライする

GCMサーバーからの登録削除について

アプリのアンインストールでGCMサーバーからunregisterされる
ただ、これには多少の時間がかかる
なので、3rd partyでメッセージを送信した時に、NotRegisteredとなって帰ってくる場合もある

メッセージの送信できる上限

4096 bytesまでの送信が可能

“send-to-sync” (collapsible)型と
“message with payload” (non-collapsible message) 型のメッセージと2種類ある
Collapse key で切り替える

“send-to-sync”はたとえば、emailのアプリケーションで、25通メッセージを受け取ったら、25回メッセージは不要で、1回でいいのと同じ

“message with payload”はIMのようなメッセージ。
毎回違うメッセージを表示する、など
Collapse keyを省略すべし
GCMサーバーは100メッセージまでこの”message with payload”をためておける

“send-to-sync”の方が、バッテリーやパフォーマンスの点で軽いので、よろしい

メッセージの複数同時送信できる上限

同一のメッセージを、複数の宛先に送信することが可能。
1から1000まで。

メッセージの有効期限をつけられる

time_to_liveパラメータを利用
期限付きの招待状やチャットのお知らせなど、短い時間しかメッセージが必要でないなどの際に利用

0 to 2,419,200秒まで設定できる
たとえば、これを0に設定しておくと、フルスロットルでメッセージを送信してくれる
しかし、サーバーにはためてくれないので、送信できなかった場合はすぐ消えてしまう。

複数の送信者を設定できる

送信者は100まで。

Android GCMでメッセージを送る

Android GCM サーバーサイド

Android

Android GCM サーバーサイド

GCM(Google Cloud Messagingの略)サーバーサイトのプログラムですが、いつの間にかXMPPでもメッセージが送信できるようになっていたようです。

XMPPでメッセージが送信できるサーバーをCCS(Cloud Connection Server
)と呼んでいるようですね。

XMPPで送信する版と、HTTPでリクエストを投げるのと、違いを書いておきます。

GCMサーバー 

  1. クラウドから端末への一方的な通信のみ
  2. 非同期ではないので、次々とメッセージを送りたい時に、前のメッセージがブロックしてしまう
  3. HTTPでPOST
  4. 複数の送信先に対応

CCSサーバー 

  1. クラウドから端末、端末からクラウドへ両方へ非同期で通信できる
  2. XMPPで通信
  3. 通信に失敗した場合のエラーメッセージも、非同期ですぐ帰ってくる
  4. 複数の送信先に送信できない

http://developer.android.com/google/gcm/ccs.html

サーバーサイドのスクリプトの書き方は、それぞれのやり方によって散らばっています。

GCMでのサーバーサイドスクリプト例

CCSでのサーバーサイドスクリプト例

Android GCM Reg IDが取得できない

Android

Android GCM Reg IDが取得できない

Android GCMでメッセージを送るでGCMの本当にさわりのGoogleさんのコードを紹介しましたが、ちょっとしたことでつまづきやすいので、書いておきます。。。

アクティビティでまずregIdを登録します。

 final String regId = GCMRegistrar.getRegistrationId(this);
    if (regId.equals("")) {
            // Automatically registers application on startup.
            GCMRegistrar.register(this, PlacesConstants.SENDER_ID);
        } else {
            // Device is already registered on GCM, check server.
            if (GCMRegistrar.isRegisteredOnServer(this)) {
                // Skips registration.
                tv.append(getString(R.string.already_registered) + "\n");
        }

しかし、いつまでもregIdが帰ってきません。
どうやらGCMIntentServiceが呼ばれていないようです…

ちなみに今日(2013/07/17)調べたところ、

 GCMRegistrar.getRegistrationId(this)

はDeprecatedされたようです…。
残念。GCM Registration IDの取得を見てみてください。

AndroidManifest.xmlを見直すと

  <receiver
            android:name="com.google.android.gcm.GCMBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND" >
       <intent-filter>
           <!-- Receives the actual messages. -->
          <action android:name="com.google.android.c2dm.intent.RECEIVE" />
           <!-- Receives the registration id. -->
           <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            <category android:name="my.application" />
        </intent-filter>
  </receiver>

category android:name=”my.application”のmy.applicationの部分に、正しくアプリケーション名を書かなければいけなかったのですが、違う名前が入ってしまってました(^_^;

Android GCM

AndroidのGCMという機能はアプリに対してちょっとしたメッセージを送信できる機能です。
GCMはGoogle Cloud Messagingの略です。
お金はかかりません。

http://developer.android.com/guide/google/gcm/gs.html

以前はC2DMというサービスでしたが、これがdeplicateされてGCMになりました。

Android Fragmentの中にあるEditTextの値をActivityに渡す

Android

Fragmentの中にあるEditTextの値を親であるActivityに渡す場合、ちょっと面倒な仕込みが必要になります。

ReviewActivityというActivityの中に、ReviewReceiverFragmentが複数含まれます。

ReviewReceiverFragmentに、点数をカウントするチェックボックスとコメントを記載する欄があります。

まずはFragmentです。

 //ReviewReceiverFragment.java
 public class ReviewReceiverFragment extends Fragment {
 
    private ArrayList<Sender> senderArrayList;
    public CheckBox unloading_wait_time;
    private OnGetFromUserClickListener mListener;
    private int receiver_id;
    private CheckBox unloading_place_info;
    private EditText comment_edit_text;
    private final static String TAG = "ReviewReceiverFragment";
 
    public ReviewReceiverFragment() {
 
    }
 
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
 
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
 
        View v = inflater.inflate(R.layout.review_receiver_fragment, container, false);
 

//中略

 
        unloading_wait_time = (CheckBox) v.findViewById(R.id.unloading_wait_time);
        unloading_place_info = (CheckBox) v.findViewById(R.id.unloading_place_info);
        comment_edit_text = (EditText) v.findViewById(R.id.comment);
 
        unloading_wait_time.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (v.getId() == R.id.unloading_wait_time) {
                    sendRatingToActivity(v);
                }
 
            }
        });
 
        unloading_place_info.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (v.getId() == R.id.unloading_place_info) {
                    sendRatingToActivity(v);
                }
 
            }
        });
 
        comment_edit_text.addTextChangedListener(mTextWatcher);
 
        return v;
    }
 
    private TextWatcher mTextWatcher = new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }
 
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }
 
        @Override
        public void afterTextChanged(Editable s) {
 
            //  String comment = comment_edit_text.getText().toString();
            Log.d(TAG, "afterTextChanged" + s.toString());
            sendCommentToActivity();
            //   sendCommentToActivity();
 
        }
    };
 
 
    /**
     * 点数を返す
     * チェックが付いたものを減点
     *
     * @return
     */
    public String getComment() {
 
        String comment = comment_edit_text.getText().toString();
 
        return comment;
 
    }
 
    /**
     * 点数やコメントを数えるために追加
     */
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            mListener = (OnGetFromUserClickListener) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString() + " must implement OnGetFromUserClickListener");
        }
    }
 
    public void sendRatingToActivity(View v) {
        if (mListener != null) {
            int rate = getRate();
            mListener.getRateFromFragment(rate, receiver_id, false);
        }
    }
 
    public void sendCommentToActivity() {
        if (mListener != null) {
            String comment = getComment();
            mListener.getCommentFromFragment(comment, receiver_id);
        }
    }
 }

ポイント①はリスナーを作って、チェックボックスを押したり、コメント欄を編集した場合に、それを呼び出すことです。
ポイント②はaddTextChangedListener でTextWatcherを利用することです。
これがないと、クリックだけだと、テキストボックスを編集した後、すぐにActivityで送信ボタンを押されたりする場合、編集した後のテキストが取得できません。

 //OnGetFromUserClickListener.java
 public interface OnGetFromUserClickListener {
 
    void getRateFromFragment(int rate, int receiver_id, boolean is_sender);
    void getCommentFromFragment(String comment, int receiver_id);
 
 }
 //ReviewActivity.java
 public class ReviewActivity extends UtilsActivity implements PostJsonTask.PostJsonTaskCallback, OnGetFromUserClickListener {
 
    private static final String TAG = "ReviewActivity";
 
    private Button accept;
    private CheckBox baggage_info;
    private CheckBox receipt_goods_wait_time;
    private CheckBox receipt_goods_place_info;
    private ProgressDialog m_ProgressDialog;
    private Context context = this;
 
    private Offer offer;
    private String mOfferId;
    private String mCompanyId;
    private int mSender_id;
    private ArrayList<Review> reviewArrayList;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_review);
 
       //中略
       
       //Offer offerを取得している部分を略
       makeView(offer);
    }
 
    public void makeView(Offer offer) {
 
        ArrayList<Delivery> deliveryList = offer.getDeliveryList();
 
        FragmentManager fragMan = getFragmentManager();
        FragmentTransaction fragTransaction = fragMan.beginTransaction();
 
        reviewArrayList = new ArrayList<>();
        mCompanyId = Integer.toString(offer.getCompany_id());
        Log.d(TAG, "会社ID:" + mCompanyId);
 
        for (int count = 0; count < deliveryList.size(); count++) {
 
 
            Fragment review_fragment = new ReviewFragment();
 
            Delivery delivery = deliveryList.get(count);
            Bundle bundle = new Bundle();
            bundle.putParcelable("delivery", delivery);
            bundle.putInt("delivery_number", count);
            review_fragment.setArguments(bundle);
 
            fragTransaction.add(R.id.each_delivery, review_fragment, "review" + count);
 
            //レビューの初期化
            ArrayList<Receiver> receiverArrayList = delivery.getReceiverList();
            for (int count_receiver = 0; count_receiver < receiverArrayList.size(); count_receiver++) {
                Receiver receiver = receiverArrayList.get(count_receiver);
 
                ArrayList<Sender> senderList = delivery.getSenderList();
 
                //発荷主は一つと仮定
                Sender sender = senderList.get(0);
                mSender_id = sender.getCompany_id();
                Review review = new Review(Integer.parseInt(mOfferId), receiver.getCompany_id(), mSender_id);
                // reviewArrayList.add(receiver.getCompany_id());
                reviewArrayList.add(review);
            }
 
        }
 
        fragTransaction.commit();
 
 
    }
 
    @Override
    public void postJsonCancel() {
 
        // プログレスダイアログを閉じる
        if (m_ProgressDialog != null || m_ProgressDialog.isShowing()) {
            m_ProgressDialog.dismiss();
        }
    }
 
    @Override
    public void getRateFromFragment(int rate, int receiver_id, boolean is_sender) {
 
        Log.d(TAG, "点数:" + rate + " レシーバーID:" + receiver_id);
 
        reviewArrayList = ReviewUtils.changeRate(rate, receiver_id, reviewArrayList, is_sender);
    }
 
    @Override
    public void getCommentFromFragment(String comment, int receiver_id) {
 
        Log.d(TAG, "コメント:" + comment + " レシーバーID:" + receiver_id);
 
        reviewArrayList = ReviewUtils.changeComment(comment, receiver_id, reviewArrayList);
 
    }
 }