Android C2DM

AndroidC2DMDemo

 

com.google.android.c2dm

 

C2DMBroadcastReceiver

/*
 */
package com.google.android.c2dm;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;

/**
 * Helper class to handle BroadcastReciver behavior.
 * - can only run for a limited amount of time - it must start a real service 
 * for longer activity
 * - must get the power lock, must make sure it's released when all done.
 * 
 */
public class C2DMBroadcastReceiver extends BroadcastReceiver {
    
    @Override
    public final void onReceive(Context context, Intent intent) {
        // To keep things in one place.
        C2DMBaseReceiver.runIntentInService(context, intent);
        setResult(Activity.RESULT_OK, null /* data */, null /* extra */);        
    }
}

 

C2DMBaseReceiver

/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.c2dm;

import java.io.IOException;

import android.app.AlarmManager;
import android.app.IntentService;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.PowerManager;
import android.util.Log;

/**
 * Base class for C2D message receiver. Includes constants for the
 * strings used in the protocol.
 */
public abstract class C2DMBaseReceiver extends IntentService {
    private static final String C2DM_RETRY = "com.google.android.c2dm.intent.RETRY";

    public static final String REGISTRATION_CALLBACK_INTENT = "com.google.android.c2dm.intent.REGISTRATION";
    private static final String C2DM_INTENT = "com.google.android.c2dm.intent.RECEIVE";

    // Logging tag
    private static final String TAG = "C2DM";

    // Extras in the registration callback intents.
    public static final String EXTRA_UNREGISTERED = "unregistered";

    public static final String EXTRA_ERROR = "error";

    public static final String EXTRA_REGISTRATION_ID = "registration_id";

    public static final String ERR_SERVICE_NOT_AVAILABLE = "SERVICE_NOT_AVAILABLE";
    public static final String ERR_ACCOUNT_MISSING = "ACCOUNT_MISSING";
    public static final String ERR_AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED";
    public static final String ERR_TOO_MANY_REGISTRATIONS = "TOO_MANY_REGISTRATIONS";
    public static final String ERR_INVALID_PARAMETERS = "INVALID_PARAMETERS";
    public static final String ERR_INVALID_SENDER = "INVALID_SENDER";
    public static final String ERR_PHONE_REGISTRATION_ERROR = "PHONE_REGISTRATION_ERROR";
    
    // wakelock
    private static final String WAKELOCK_KEY = "C2DM_LIB";

    private static PowerManager.WakeLock mWakeLock;
    private final String senderId;

    /**
     * The C2DMReceiver class must create a no-arg constructor and pass the 
     * sender id to be used for registration.
     */
    public C2DMBaseReceiver(String senderId) {
        // senderId is used as base name for threads, etc.
        super(senderId);
        this.senderId = senderId;
    }
    
    /**
     * Called when a cloud message has been received.
     */
    protected abstract void onMessage(Context context, Intent intent);

    /**
     * Called on registration error. Override to provide better
     * error messages.
     *  
     * This is called in the context of a Service - no dialog or UI.
     */
    public abstract void onError(Context context, String errorId);

    /**
     * Called when a registration token has been received.
     */
    public void onRegistered(Context context, String registrationId) throws IOException {
        // registrationId will also be saved
    }

    /**
     * Called when the device has been unregistered.
     */
    public void onUnregistered(Context context) {
    }

    
    @Override
    public final void onHandleIntent(Intent intent) {
        try {
            Context context = getApplicationContext();
            if (intent.getAction().equals(REGISTRATION_CALLBACK_INTENT)) {
                handleRegistration(context, intent);
            } else if (intent.getAction().equals(C2DM_INTENT)) {
                onMessage(context, intent);
            } else if (intent.getAction().equals(C2DM_RETRY)) {
                C2DMessaging.register(context, senderId);
            }
        } finally {
            //  Release the power lock, so phone can get back to sleep.
            // The lock is reference counted by default, so multiple 
            // messages are ok.
            
            // If the onMessage() needs to spawn a thread or do something else,
            // it should use it's own lock.
            mWakeLock.release();
        }
    }

    
    /**
     * Called from the broadcast receiver. 
     * Will process the received intent, call handleMessage(), registered(), etc.
     * in background threads, with a wake lock, while keeping the service 
     * alive. 
     */
    static void runIntentInService(Context context, Intent intent) {
        if (mWakeLock == null) {
            // This is called from BroadcastReceiver, there is no init.
            PowerManager pm = 
                (PowerManager) context.getSystemService(Context.POWER_SERVICE);
            mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, 
                    WAKELOCK_KEY);
        }
        mWakeLock.acquire();
       
        // Use a naming convention, similar with how permissions and intents are 
        // used. Alternatives are introspection or an ugly use of statics. 
        String receiver = context.getPackageName() + ".C2DMReceiver";
        intent.setClassName(context, receiver);
        
        context.startService(intent);

    }
    
    
    private void handleRegistration(final Context context, Intent intent) {
        final String registrationId = intent.getStringExtra(EXTRA_REGISTRATION_ID);
        String error = intent.getStringExtra(EXTRA_ERROR);
        String removed = intent.getStringExtra(EXTRA_UNREGISTERED);

        if (Log.isLoggable(TAG, Log.DEBUG)) {
            Log.d(TAG, "dmControl: registrationId = " + registrationId +
                ", error = " + error + ", removed = " + removed);
        }

        if (removed != null) {
            // Remember we are unregistered
            C2DMessaging.clearRegistrationId(context);
            onUnregistered(context);
            return;
        } else if (error != null) {
            // we are not registered, can try again
            C2DMessaging.clearRegistrationId(context);
            // Registration failed
            Log.e(TAG, "Registration error " + error);
            onError(context, error);
            if ("SERVICE_NOT_AVAILABLE".equals(error)) {
                long backoffTimeMs = C2DMessaging.getBackoff(context);
                
                Log.d(TAG, "Scheduling registration retry, backoff = " + backoffTimeMs);
                Intent retryIntent = new Intent(C2DM_RETRY);
                PendingIntent retryPIntent = PendingIntent.getBroadcast(context, 
                        0 /*requestCode*/, retryIntent, 0 /*flags*/);
                
                AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
                am.set(AlarmManager.ELAPSED_REALTIME,
                        backoffTimeMs, retryPIntent);

                // Next retry should wait longer.
                backoffTimeMs *= 2;
                C2DMessaging.setBackoff(context, backoffTimeMs);
            } 
        } else {
            try {
                onRegistered(context, registrationId);
                C2DMessaging.setRegistrationId(context, registrationId);
            } catch (IOException ex) {
                Log.e(TAG, "Registration error " + ex.getMessage());
            }
        }
    }
}

 

C2DMessaging

/*
 * Copyright 2010 Google Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.google.android.c2dm;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;

/**
 * Utilities for device registration.
 *
 * Will keep track of the registration token in a private preference.
 */
public class C2DMessaging {
    public static final String EXTRA_SENDER = "sender";
    public static final String EXTRA_APPLICATION_PENDING_INTENT = "app";
    public static final String REQUEST_UNREGISTRATION_INTENT = "com.google.android.c2dm.intent.UNREGISTER";
    public static final String REQUEST_REGISTRATION_INTENT = "com.google.android.c2dm.intent.REGISTER";
    public static final String LAST_REGISTRATION_CHANGE = "last_registration_change";
    public static final String BACKOFF = "backoff";
    public static final String GSF_PACKAGE = "com.google.android.gsf";


    // package
    static final String PREFERENCE = "com.google.android.c2dm";
    
    private static final long DEFAULT_BACKOFF = 30000;

    /**
     * Initiate c2d messaging registration for the current application
     */
    public static void register(Context context,
            String senderId) {
        Intent registrationIntent = new Intent(REQUEST_REGISTRATION_INTENT);
        registrationIntent.setPackage(GSF_PACKAGE);
        registrationIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT,
                PendingIntent.getBroadcast(context, 0, new Intent(), 0));
        registrationIntent.putExtra(EXTRA_SENDER, senderId);
        context.startService(registrationIntent);
        // TODO: if intent not found, notification on need to have GSF
    }

    /**
     * Unregister the application. New messages will be blocked by server.
     */
    public static void unregister(Context context) {
        Intent regIntent = new Intent(REQUEST_UNREGISTRATION_INTENT);
        regIntent.setPackage(GSF_PACKAGE);
        regIntent.putExtra(EXTRA_APPLICATION_PENDING_INTENT, PendingIntent.getBroadcast(context,
                0, new Intent(), 0));
        context.startService(regIntent);
    }

    /**
     * Return the current registration id.
     *
     * If result is empty, the registration has failed.
     *
     * @return registration id, or empty string if the registration is not complete.
     */
    public static String getRegistrationId(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        String registrationId = prefs.getString("dm_registration", "");
        return registrationId;
    }

    public static long getLastRegistrationChange(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        return prefs.getLong(LAST_REGISTRATION_CHANGE, 0);
    }
    
    static long getBackoff(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        return prefs.getLong(BACKOFF, DEFAULT_BACKOFF);
    }
    
    static void setBackoff(Context context, long backoff) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putLong(BACKOFF, backoff);
        editor.commit();

    }

    // package
    static void clearRegistrationId(Context context) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putString("dm_registration", "");
        editor.putLong(LAST_REGISTRATION_CHANGE, System.currentTimeMillis());
        editor.commit();

    }

    // package
    static void setRegistrationId(Context context, String registrationId) {
        final SharedPreferences prefs = context.getSharedPreferences(
                PREFERENCE,
                Context.MODE_PRIVATE);
        Editor editor = prefs.edit();
        editor.putString("dm_registration", registrationId);
        editor.commit();

    }
}

 

org.wp.activity

 

AndroidC2DMDemo 

package org.wp.activity;

import com.google.android.c2dm.C2DMessaging;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;

public class AndroidC2DMDemo extends Activity {
	private static final String TAG = "AndroidC2DMDemo";
	public static final String SENDER_ID = "wp.android.c2dm.demo@gmail.com";
	public static final String MESSAGE_KEY = "onewayonelife";

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);

		Log.i(TAG, "start");
		C2DMessaging.register(this, SENDER_ID);
	}
}

 

C2DMReceiver

package org.wp.activity;

import java.io.IOException;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

import com.google.android.c2dm.C2DMBaseReceiver;

public class C2DMReceiver extends C2DMBaseReceiver {
	private static final String TAG = "C2DMReceiver";

	public C2DMReceiver() {
		super(AndroidC2DMDemo.SENDER_ID);
	}

	@Override
	public void onRegistered(Context context, String registrationId)
			throws IOException {
		Log.i(TAG, "registrationId:" + registrationId);
	}

	@Override
	public void onUnregistered(Context context) {
	}

	@Override
	protected void onMessage(Context context, Intent intent) {
		Bundle extras = intent.getExtras();
		if (extras != null) {
			String msg = extras.getString(AndroidC2DMDemo.MESSAGE_KEY);
			NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
			PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, AndroidC2DMDemo.class), 0);
			Notification notification = new Notification(R.drawable.icon, msg, System.currentTimeMillis());
			notification.setLatestEventInfo(this, getString(R.string.app_name), msg, contentIntent);
			notification.flags |= Notification.FLAG_AUTO_CANCEL;
			notificationManager.notify(0, notification);
		}
	}

	@Override
	public void onError(Context context, String errorId) {
	}
}

 

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
	package="org.wp.activity" android:versionCode="1" android:versionName="1.0">
	<application android:icon="@drawable/icon" android:label="@string/app_name">
		<activity android:name=".AndroidC2DMDemo" android:label="@string/app_name">
			<intent-filter>
				<action android:name="android.intent.action.MAIN" />
				<category android:name="android.intent.category.LAUNCHER" />
			</intent-filter>
		</activity>
		<!-- In order to use the c2dm library, an application must declare a class 
			with the name C2DMReceiver, in its own package, 
			extending com.google.android.c2dm.C2DMBaseReceiver 
			It must also include this section in the manifest. -->
		<service android:name=".C2DMReceiver" />
		<!-- Only C2DM servers can send messages for the app. If permission is 
			not set - any other app can generate it -->
		<receiver android:name="com.google.android.c2dm.C2DMBroadcastReceiver"
				  android:permission="com.google.android.c2dm.permission.SEND">
			<!-- Receive the actual message -->
			<intent-filter>
				<action android:name="com.google.android.c2dm.intent.RECEIVE" />
				<category android:name="org.wp.activity" />
			</intent-filter>
			<!-- Receive the registration id -->
			<intent-filter>
				<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
				<category android:name="org.wp.activity" />
			</intent-filter>
		</receiver>
	</application>
	<uses-sdk android:minSdkVersion="8" />

	<!-- Only this application can receive the messages and registration result -->
	<permission android:name="org.wp.activity.permission.C2D_MESSAGE"
		android:protectionLevel="signature" />
	<uses-permission android:name="org.wp.activity.permission.C2D_MESSAGE" />
	<!-- This app has permission to register and receive message -->
	<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
	<!-- Send the registration id to the server -->
	<uses-permission android:name="android.permission.INTERNET" />
	<!-- App must have this permission to use the library -->
	<uses-permission android:name="android.permission.WAKE_LOCK" />
	<uses-permission android:name="android.permission.GET_ACCOUNTS" />
	<uses-permission android:name="android.permission.USE_CREDENTIALS" />
</manifest>

 

AndroidC2DMServerDemo

 

AndroidC2DMServer

package org.wp.activity;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class AndroidC2DMServer {
	private static String clientLoginUrl = "https://www.google.com/accounts/ClientLogin";
	// 因为使用https的方式会提示出错,因此使用http的方式
	private static String pushUrl = "http://android.apis.google.com/c2dm/send";
	private static String registrationId = "APA91bHeWVX8t0hE-mhks0539soI9JaNFcU1r0_BEtDShnMy0lDDF3U2nR5NxC8F18y_oqj3nbzE3Pn9PpcilYRsK09lnsAaFhRcVCfS0zaztHt6BRXveHey73ipRBeJzpdd9xegxwsQih1B-Dr9Pj903tiorIZbcQ";

	public static void main(String args[]) {
		// 获取注册使用C2DM服务的用户账号的ClientLogin权限Auth值
		String auth = getAuthToken(clientLoginUrl, getClientLoginParams());
		if (auth != null && !"".equals(auth)) {
			// 按格式给C2DM服务器发送要Push的数据
			Map<String, String> data = new HashMap<String, String>();
			data.put("onewayonelife", "onewayonelife!" + new Date());
			sendPushMessage(pushUrl, getPushParams(registrationId, "wp", data, true), auth);
		}
	}
	
	public static String getClientLoginParams() {
		StringBuilder clsb = new StringBuilder();
		/** accountType 请求授权的帐户类型 **/
		clsb.append("accountType=HOSTED_OR_GOOGLE");
		/** Email 邮箱账号 **/
		clsb.append("&Email=wp.android.c2dm.demo@gmail.com");
		/** Passwd 邮箱账号密码 **/
		clsb.append("&Passwd=wpandroidc2dmdemo");
		/** service 请求授权的服务名称,C2DM服务的值为ac2dm **/
		clsb.append("&service=ac2dm");
		/** source 用来表示我们应用的字符串,类似companyName-applicationName-versionID **/
		clsb.append("&source=wp-c2dmdemo-1.0");
		return clsb.toString();
	}

	public static String getAuthToken(final String url, final String params) {
		String auth = null;
		try {
			byte[] postData = params.getBytes();
			
			URL requestUrl = new URL(url);
			HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
			// 上传数据,需要将setDoOutput方法的参数值设为true
			connection.setDoOutput(true);
			// Post请求不能使用缓存
			connection.setUseCaches(false);
			connection.setRequestMethod("POST");
			connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			connection.setRequestProperty("Content-Length", Integer.toString(postData.length));
			connection.setRequestProperty("Charset", "UTF-8");
			OutputStream out = connection.getOutputStream();
			// 写入POST数据
			out.write(postData);
			out.flush();
			out.close();

			// 请求成功
			if (connection.getResponseCode() == 200) {
				BufferedReader br = new BufferedReader(new InputStreamReader(
						connection.getInputStream()));
				String responseLine;
				StringBuilder sb = new StringBuilder();
				while ((responseLine = br.readLine()) != null) {
					sb.append(responseLine);
				}
				auth = sb.substring(sb.indexOf("Auth=") + 5);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return auth;
	}
	
	public static String getPushParams(String registration_id, String collapse_key, 
			Map<String, String> data, boolean delay_while_idle) {
		StringBuilder phsb = new StringBuilder();
		/** registration_id Android应用程序注册获得的id **/
		phsb.append("registration_id=" + registration_id);
		/** collapse_key 用一个任意的字符串来标识一组类似的信息
		 * 当设备由离线状态重新上线以后,只有最后一条消息被发送到客户端
		 * 这是为了避免当设备重新上线后有太多的信息发送到手机 **/
		phsb.append("&collapse_key=" + collapse_key);
		/** data.<key> 推送的信息,以键值对形式保存
		 * 将会被包含在intent当中发送到android应用程序
		 * 对于个数上并没有限制,只是在数据总大小上有要求 **/
		for (String key : data.keySet()) {
			phsb.append("&data." + key + "=" + data.get(key));
		}
		/** delay_while_idle 如果包含这个值
		 * 表示不会在设备处于闲置状态的时候就立即发送推送消息
		 * 服务器会等待设备变为活动状态的时候发送
		 * 并且只有collapse_key标识的最后一条信息才会被发送 **/
		if (delay_while_idle)
			phsb.append("&delay_while_idle=0");
		return phsb.toString();
	}

	public static void sendPushMessage(final String url, final String params, final String auth) {
		try {
			byte[] postData = params.getBytes();
			
			URL requestUrl = new URL(url);
			HttpURLConnection connection = (HttpURLConnection) requestUrl.openConnection();
			// 上传数据,需要将setDoOutput方法的参数值设为true
			connection.setDoOutput(true);
			// Post请求不能使用缓存
			connection.setUseCaches(false);
			connection.setRequestMethod("POST");
			connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
			connection.setRequestProperty("Content-Length", Integer.toString(postData.length));
			connection.setRequestProperty("Charset", "UTF-8");
			connection.setRequestProperty("Authorization", "GoogleLogin auth=" + auth);
			OutputStream out = connection.getOutputStream();
			out.write(postData);
			out.flush();
			out.close();

			int responseCode = connection.getResponseCode();
			if (responseCode == 200) {
				BufferedReader br = new BufferedReader(new InputStreamReader(
						connection.getInputStream()));
				String responseLine;
				StringBuilder sb = new StringBuilder();
				while ((responseLine = br.readLine()) != null) {
					sb.append(responseLine);
				}
				if (sb.toString().startsWith("id=")) {
					System.out.println("推送信息发送成功!");
				}
			} else if (responseCode == 503) {
				System.out.println("Indicates that the server is temporarily unavailable (i.e., because of timeouts, etc ). Sender must retry later, honoring any Retry-After header included in the response. Application servers must implement exponential back off. Senders that create problems risk being blacklisted.");
			} else if (responseCode == 401) {
				System.out.println("Indicates that the ClientLogin AUTH_TOKEN used to validate the sender is invalid.");
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值