首先先上极光推送官网:https://www.jpush.cn/
来个介绍:极光推送,使得开发者可以即时地向其应用程序的用户推送通知或者消息,与用户保持互动,从而有效地提高留存率,提升用户体验。平台提供整合了Android推送、iOS推送的统一推送服务。
废话不多说,先上客户端怎么集成极光推送的功能~
第一步:首先登录上帐号之后创建一个应用,记下里面应用标识(AppKey)、API MasterSecret(服务器端要用到这个)
第二步:http://docs.jpush.cn/pages/viewpage.action?pageId=557214 (Android)
http://docs.jpush.cn/pages/viewpage.action?pageId=2621727 (IOS)按照这里面讲的一步一步来,这里很简单就不多说
第三步:上Demo~
(这里讲一个小小功能,开机启动服务)
Android手机在开机、重启的时候系统会发送一个广播(android.intent.action.BOOT_COMPLETED),我们所要做的就是自定义一个BroadcastReceiver,使用标签去过滤这个广播,然后在onReceive()方法里面,干你该干的事,(我这里去启动了一个服务,初始化极光推送服务,然后可以实现开机不启动应用也可以顺利接收到推送)
BootCompletedReceiver.java
package com.example.b;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class BootCompletedReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
Log.d("yqb", "检测到开机启动,去启动服务");
Intent newIntent = new Intent(context, StartService.class);
context.startService(newIntent);
}
}
}
StartService.java
package com.example.b;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import cn.jpush.android.api.JPushInterface;
public class StartService extends Service {
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
JPushInterface.setDebugMode(true); // 设置开启日志,发布时请关闭日志
JPushInterface.init(this); // 初始化 JPush
JPushInterface.setLatestNotifactionNumber(getApplicationContext(), 10);// 保留多少条通知数
}
}
AndroidManifest.xml
要监听系统开机的那个服务需要一个权限:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" >
</uses-permission>
注册广播和服务:
<!-- 注册广播接收者 -->
<receiver android:name="com.example.b.BootCompletedReceiver" >
<intent-filter>
<!-- 通过标签过滤 -->
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</receiver>
<!-- 注册Service -->
<service android:name="com.example.b.StartService" >
</service>
开始方法:
// 开机启动一个服务,去初始化极光推送
Intent intent = new Intent(MainActivity.this, StartService.class);
startService(intent);
Toast.makeText(MainActivity.this, "服务启动成功", Toast.LENGTH_LONG).show();
注意:记得在AndroidManifest.xml文件里面注册广播和服务;
界面ui:
直接上代码:
<span style="font-size:12px;color:#000000;">package com.example.b;
import java.util.LinkedHashSet;
import java.util.Set;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import cn.jpush.android.api.JPushInterface;
import cn.jpush.android.api.TagAliasCallback;
public class MainActivity extends Activity {
private final String TAG = "yqb";
private TextView mTextView;
private EditText mEditText;
private Button mButton;
public static boolean isForeground = false;
private static final int MSG_SET_TAGS = 1001;//设置tag
private static final int MSG_SET_ALIAS = 1002;//设置设备别名
private Handler mHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case MSG_SET_TAGS:
Log.d(TAG, "Set tags in handler.");
// 调用JPush API设置Tag
JPushInterface.setAliasAndTags(getApplicationContext(), null,
(Set<String>) msg.obj, mTagsCallback);
break;
case MSG_SET_ALIAS:
Log.d(TAG, "Set alias in handler.");
// 调用JPush API设置alias
JPushInterface.setAliasAndTags(getApplicationContext(), (String) msg.obj,
null, mTagsCallback);
break;
default:
Log.i(TAG, "Unhandled msg - " + msg.what);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.textview1);
mEditText = (EditText) findViewById(R.id.editText1);
mButton = (Button) findViewById(R.id.button1);
// 与极光推送绑定tag
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
String tag = mEditText.getText().toString().trim();
// 检查 tag 的有效性
if (TextUtils.isEmpty(tag)) {
Toast.makeText(MainActivity.this, "tag不能为空",
Toast.LENGTH_SHORT).show();
return;
}
// ","隔开的多个 转换成 Set
String[] sArray = tag.split(",");
Set<String> tagSet = new LinkedHashSet<String>();
for (String sTagItme : sArray) {
if (!ExampleUtil.isValidTagAndAlias(sTagItme)) {
Toast.makeText(MainActivity.this, "格式不对",
Toast.LENGTH_SHORT).show();
return;
}
tagSet.add(sTagItme);
}
mHandler.sendMessage(mHandler.obtainMessage(MSG_SET_TAGS,
tagSet));
}
});
// 开机启动一个服务,去初始化极光推送
Intent intent = new Intent(MainActivity.this, StartService.class);
startService(intent);
Toast.makeText(MainActivity.this, "服务启动成功", Toast.LENGTH_LONG).show();
init();
registerMessageReceiver(); // used for receive msg
}
// 初始化 JPush。如果已经初始化,但没有登录成功,则执行重新登录。
private void init() {
JPushInterface.init(getApplicationContext());
JPushInterface.setLatestNotifactionNumber(MainActivity.this, 10);// 保留多少条通知数
}
@Override
protected void onResume() {
isForeground = true;
JPushInterface.onResume(this);
super.onResume();
}
@Override
protected void onPause() {
isForeground = false;
JPushInterface.onPause(this);
super.onPause();
}
@Override
protected void onDestroy() {
unregisterReceiver(mMessageReceiver);
super.onDestroy();
}
// 这个广播是通过MyReceiver来接收到服务端发送给我们的自定义消息(不是通知)然后在通过广播的形式发送过来,之后更新UI
// 这个实现功能是客户端不去向服务器请求,直接服务器推送消息下来,然后直接在界面展示(或者你可以写自己逻辑)
private MessageReceiver mMessageReceiver;
public static final String MESSAGE_RECEIVED_ACTION = "com.example.jpushdemo.MESSAGE_RECEIVED_ACTION";
public static final String KEY_TITLE = "title";
public static final String KEY_MESSAGE = "message";
public static final String KEY_EXTRAS = "extras";
public void registerMessageReceiver() {
mMessageReceiver = new MessageReceiver();
IntentFilter filter = new IntentFilter();
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
filter.addAction(MESSAGE_RECEIVED_ACTION);
registerReceiver(mMessageReceiver, filter);
}
// 接收服务器推送过来过来自定义消息,
public class MessageReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (MESSAGE_RECEIVED_ACTION.equals(intent.getAction())) {
String messge = intent.getStringExtra(KEY_MESSAGE);
String extras = intent.getStringExtra(KEY_EXTRAS);
StringBuilder showMsg = new StringBuilder();
showMsg.append(KEY_MESSAGE + " : " + messge + "\n");
if (!ExampleUtil.isEmpty(extras)) {
showMsg.append(KEY_EXTRAS + " : " + extras + "\n");
}
setCostomMsg(showMsg.toString());
}
}
}
// 更新界面UI逻辑
private void setCostomMsg(String msg) {
if (null != mTextView) {
mTextView.setText(msg);
mTextView.setVisibility(android.view.View.VISIBLE);
}
}
// 通过TagAliasCallback的接口回调来判断tag是否与极光推送服务器绑定成功
private final TagAliasCallback mTagsCallback = new TagAliasCallback() {
@Override
public void gotResult(int code, String alias, Set<String> tags) {
String logs;
switch (code) {
case 0:// 绑定成功
logs = "Set tag and alias success";
Log.i(TAG, logs);
break;
case 6002:// 绑定失败,60s之后再次绑定
logs = "Failed to set alias and tags due to timeout. Try again after 60s.";
Log.i(TAG, logs);
if (ExampleUtil.isConnected(getApplicationContext())) {
mHandler.sendMessageDelayed(
mHandler.obtainMessage(MSG_SET_TAGS, tags),
1000 * 60);
} else {
Log.i(TAG, "No network");
}
break;
default:
logs = "Failed with errorCode = " + code;
Log.e(TAG, logs);
}
// 用户绑定成功之后 toas通知
ExampleUtil.showToast(logs, getApplicationContext());
}
};
}
</span>
下面是自定义的BroadcastReceiver(用来处理所有由服务器发送下来的通知,消息)
package com.example.b;
import org.json.JSONException;
import org.json.JSONObject;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import cn.jpush.android.api.JPushInterface;
/**
* 自定义接收器
*
* 如果不定义这个 Receiver,则: 1) 默认用户会打开主界面 2) 接收不到自定义消息
*/
public class MyReceiver extends BroadcastReceiver {
private static final String TAG = "JPush";
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = intent.getExtras();
Log.d(TAG, "运行自定义的通知");
Log.d(TAG, "[MyReceiver] onReceive - " + intent.getAction()
+ ", extras: " + "---->" + printBundle(bundle));
if (JPushInterface.ACTION_REGISTRATION_ID.equals(intent.getAction())) {
String regId = bundle
.getString(JPushInterface.EXTRA_REGISTRATION_ID);
Log.d(TAG, "[MyReceiver] 接收Registration Id : " + regId);
// send the Registration Id to your server...
} else if (JPushInterface.ACTION_MESSAGE_RECEIVED.equals(intent
.getAction())) {
Log.d(TAG,
"[MyReceiver] 接收到推送下来的自定义消息: "
+ bundle.getString(JPushInterface.EXTRA_MESSAGE));
processCustomMessage(context, bundle);
} else if (JPushInterface.ACTION_NOTIFICATION_RECEIVED.equals(intent
.getAction())) {
Log.d(TAG, "[MyReceiver] 接收到推送下来的通知");
int notifactionId = bundle
.getInt(JPushInterface.EXTRA_NOTIFICATION_ID);
Log.d(TAG, "[MyReceiver] 接收到推送下来的通知的ID: " + notifactionId);
String title = bundle.getString(JPushInterface.EXTRA_NOTIFICATION_TITLE);
String content = bundle.getString(JPushInterface.EXTRA_ALERT);
Log.d(TAG, "Title 标题:: " + title + " " + "Content : 内容:" + content);
} else if (JPushInterface.ACTION_NOTIFICATION_OPENED.equals(intent
.getAction())) {
Log.d(TAG, "[MyReceiver] 用户点击打开了通知");
JPushInterface.reportNotificationOpened(context,
bundle.getString(JPushInterface.EXTRA_MSG_ID));
// 打开自定义的Activity
Intent i = new Intent(context, TestActivity.class);
i.putExtras(bundle);
i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(i);
} else if (JPushInterface.ACTION_RICHPUSH_CALLBACK.equals(intent
.getAction())) {
Log.d(TAG,
"[MyReceiver] 用户收到到RICH PUSH CALLBACK: "
+ bundle.getString(JPushInterface.EXTRA_EXTRA));
// 在这里根据 JPushInterface.EXTRA_EXTRA 的内容处理代码,比如打开新的Activity,
// 打开一个网页等..
} else if (JPushInterface.ACTION_CONNECTION_CHANGE.equals(intent
.getAction())) {
boolean connected = intent.getBooleanExtra(
JPushInterface.EXTRA_CONNECTION_CHANGE, false);
Log.e(TAG, "[MyReceiver]" + intent.getAction()
+ " connected state change to " + connected);
} else {
Log.d(TAG, "[MyReceiver] Unhandled intent - " + intent.getAction());
}
}
// 打印所有的 intent extra 数据
private static String printBundle(Bundle bundle) {
StringBuilder sb = new StringBuilder();
for (String key : bundle.keySet()) {
if (key.equals(JPushInterface.EXTRA_NOTIFICATION_ID)) {
sb.append("\nkey:" + key + ", value:" + bundle.getInt(key));
} else if (key.equals(JPushInterface.EXTRA_CONNECTION_CHANGE)) {
sb.append("\nkey:" + key + ", value:" + bundle.getBoolean(key));
} else {
sb.append("\nkey:" + key + ", value:" + bundle.getString(key));
}
}
return sb.toString();
}
// send msg to MainActivity
private void processCustomMessage(Context context, Bundle bundle) {
if (MainActivity.isForeground) {
String message = bundle.getString(JPushInterface.EXTRA_MESSAGE);
String extras = bundle.getString(JPushInterface.EXTRA_EXTRA);
Intent msgIntent = new Intent(MainActivity.MESSAGE_RECEIVED_ACTION);
msgIntent.putExtra(MainActivity.KEY_MESSAGE, message);
if (!ExampleUtil.isEmpty(extras)) {
try {
JSONObject extraJson = new JSONObject(extras);
if (null != extraJson && extraJson.length() > 0) {
msgIntent.putExtra(MainActivity.KEY_EXTRAS, extras);
}
} catch (JSONException e) {
}
}
context.sendBroadcast(msgIntent);
}
}
}
以上就是客户端的代码,没多少挺简单,主要就是那个自定义的广播接收者来根据不同的参数来判断是什么推送通知;
下面来介绍服务器端代码,怎么使用极光推送服务器端SDK来搭建我们自己的后台推送:
首先需要下载依赖包:
gson-2.2.4.jar、jpush-java-libraryV2.jar
先上代码:
JPushClientExample.java
package com.meritit.tuisong.service;
import java.util.List;
import cn.jpush.api.DeviceEnum;
import cn.jpush.api.ErrorCodeEnum;
import cn.jpush.api.JPushClient;
import cn.jpush.api.MessageResult;
import cn.jpush.api.receive.ReceiveResult;
public class JPushClientExample {
/**
* App的Key (在极光推送控制台创建应用页面上获取)
*/
private static final String appKey = "fc0e14bb62b21314f1b297df"; // 必填,每个应用都对应一个appKey
/**
* App的MasterSecret (在极光推送控制台创建应用页面上获取)
*/
private static final String masterSecret = "38fb3cf02878e7d572a725bc";// 必填,每个应用都对应一个masterSecret
/**
* 保存离线的时长。秒为单位。最多支持10天(864000秒)。 0 表示该消息不保存离线。即:用户在线马上发出,当前不在线用户将不会收到此消息。
* 此参数不设置则表示默认,默认为保存1天的离线消息(86400秒)。
*/
private static final long timeToLive = 60 * 60 * 24;
private static JPushClient jpush = null;
/**
* 发送一个通知
*/
public static final int NOTIFICATION = 001;
/**
* 发送一个消息
*/
public static final int MESSAGE = 002;
/**
* 进行初始化操作
*/
static {
// Example1: 初始化,默认发送给android和ios,同时设置离线消息存活时间
jpush = new JPushClient(masterSecret, appKey, timeToLive);
// Example2: 只发送给android
// jpush = new JPushClient(masterSecret, appKey, DeviceEnum.Android);
// Example3: 只发送给IOS
// jpush = new JPushClient(masterSecret, appKey, DeviceEnum.IOS);
// Example4: 只发送给android,同时设置离线消息存活时间
// jpush = new JPushClient(masterSecret, appKey, timeToLive,
// DeviceEnum.Android);
// Example5: 只发送给IOS,同时设置离线消息存活时间
// jpush = new JPushClient(masterSecret, appKey, timeToLive,
// DeviceEnum.IOS);
/*
* 是否启用ssl安全连接, 可选 参数:启用true, 禁用false,默认为非ssl连接
*/
jpush.setEnableSSL(true);
}
/**
* 对所有安装此应用的用户发送通知消息
*
* @param msgTitle
* 通知标题
* @param msgContent
* 通知内容
* @param tag
* 标识是发送通知还是消息(通知是在客户端通知栏弹出通知、消息是直接在页面上更新UI)
* @return true 成功 false 失败
*/
public static boolean sendAllUserPush(String msgTitle, String msgContent,
int tag) {
// 在实际业务中,建议 sendNo 是一个你自己的业务可以处理的一个自增数字。
// 除非需要覆盖,请确保不要重复使用。详情请参考 API 文档相关说明。
int sendNo = getRandomSendNo();
// IOS设备扩展参数, 设置badge,设置声音
// Map<String, Object> extra = new HashMap<String, Object>();
// IOSExtra iosExtra = new IOSExtra(10, "WindowsLogonSound.wav");
// extra.put("ios", iosExtra);
MessageResult msgResult = null;
switch (tag) {
case NOTIFICATION:
// 对指定标识(tag)用户发送通知
msgResult = jpush.sendNotificationWithAppKey(sendNo, msgTitle,
msgContent);
break;
case MESSAGE:
// 对指定标识(tag)用户发送消息
msgResult = jpush.sendCustomMessageWithAppKey(sendNo, msgTitle,
msgContent);
break;
default:
break;
}
if (null != msgResult) {
System.out.println("服务器返回数据: " + msgResult.toString());
if (msgResult.getErrcode() == ErrorCodeEnum.NOERROR.value()) {
System.out.println(String.format(
"发送成功, sendNo= %s,messageId= %s",
msgResult.getSendno(), msgResult.getMsg_id()));
return true;
} else {
System.out.println("发送失败, 错误代码=" + msgResult.getErrcode()
+ ", 错误消息=" + msgResult.getErrmsg());
switch (msgResult.getErrcode()) {
case 10:
System.out.println("系统内部错误");
break;
case 3001:
System.out.println("HTTP Basic authorization 失败");
break;
case 3002:
System.out.println("msg_ids 参数不存在");
break;
default:
break;
}
return false;
}
} else {
System.out.println("无法获取数据");
return false;
}
}
/**
* 对安装此应用通过绑定标识符(tag)来发送通知消息
*
* @param msgTitle
* 通知标题
* @param msgContent
* 通知内容
* @param userId
* 绑定标识符(tag)
* @param tag
* 标识是发送通知还是消息(通知是在客户端通知栏弹出通知、消息是直接在页面上更新UI)
* @return true 成功 false 失败
*/
public static boolean sendTagUserPush(String msgTitle, String msgContent,
String userId, int tag) {
// 在实际业务中,建议 sendNo 是一个你自己的业务可以处理的一个自增数字。
// 除非需要覆盖,请确保不要重复使用。详情请参考 API 文档相关说明。
int sendNo = getRandomSendNo();
// IOS设备扩展参数, 设置badge,设置声音
// Map<String, Object> extra = new HashMap<String, Object>();
// IOSExtra iosExtra = new IOSExtra(10, "WindowsLogonSound.wav");
// extra.put("ios", iosExtra);
MessageResult msgResult = null;
switch (tag) {
case NOTIFICATION:
// 对指定标识(tag)用户发送通知
msgResult = jpush.sendNotificationWithTag(sendNo, userId, msgTitle,
msgContent);
break;
case MESSAGE:
// 对指定标识(tag)用户发送消息
msgResult = jpush.sendCustomMessageWithTag(sendNo, userId,
msgTitle, msgContent);
break;
default:
break;
}
if (null != msgResult) {
System.out.println("服务器返回数据: " + msgResult.toString());
if (msgResult.getErrcode() == ErrorCodeEnum.NOERROR.value()) {
System.out.println(String.format(
"发送成功, sendNo= %s,messageId= %s",
msgResult.getSendno(), msgResult.getMsg_id()));
return true;
} else {
System.out.println("发送失败, 错误代码=" + msgResult.getErrcode()
+ ", 错误消息=" + msgResult.getErrmsg());
switch (msgResult.getErrcode()) {
case 10:
System.out.println("系统内部错误");
break;
case 3001:
System.out.println("HTTP Basic authorization 失败");
break;
case 3002:
System.out.println("msg_ids 参数不存在");
break;
default:
break;
}
return false;
}
} else {
System.out.println("无法获取数据");
return false;
}
}
/**
* 保持 sendNo 的唯一性是有必要的 It is very important to keep sendNo unique.
*
* @return sendNo
*/
public static int getRandomSendNo() {
final int MAX = Integer.MAX_VALUE;
final int MIN = (int) MAX / 2;
return (int) (MIN + Math.random() * (MAX - MIN));
}
/**
* Received API 以 msg_id 作为参数,去获取该 msg_id 的送达统计数据。 如果一次 API
* 调用推送有很多对象(比如广播推送),则此 API 返回的统计数据会因为持续有客户端送达而持续增加。 每条推送消息的送达统计数据最多保留 10
* 天。即一条消息推送发起 10 天后送达统计将被清除。
*/
public static String getMsgStatistics(String msgId) {
// 获取一条
ReceiveResult receiveResult = jpush.getReceived(msgId);
if (receiveResult == null) {
return ("获取receive 数据失败!" + receiveResult);
} else {
// gson toJson 之后,NULL值的字段会被过滤掉
return ("received result:" + receiveResult.toString());
}
}
/**
* Received API 以 msg_id作为参数,去获取该 msg_id 的送达统计数据。 如果一次
* API调用推送有很多对象(比如广播推送),最多支持100个msg_id,则此 API 返回的统计数据会因为持续有客户端送达而持续增加。
* 每条推送消息的送达统计数据最多保留 10天。即一条消息推送发起 10 天后送达统计将被清除。
*/
public static String getMsgStatistics(String[] msgIds) {
// 获取多条
List<ReceiveResult> receiveResults = jpush.getReceiveds(msgIds);
if (receiveResults == null) {
return ("获取receive 数据失败!");
} else {
return ("成功获取了:" + receiveResults);
}
}
}
注释写的很详细,大家直接看代码吧;
主要就是appKey和masterSecret一定要与我们在极光推送后台建立应用的保持一致,然后初始化JPushClient,我已经在里面封装好了一些公共方法,大家直接调用就可以了,
下面是我写的test.java测试类
package com.meritit.tuisong.service;
public class test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
// if (JPushClientExample.sendAllUserPush("测试标题", "大好人",
// JPushClientExample.NOTIFICATION)) {
// System.err.println("---->发送成功");
// }
if (JPushClientExample.sendTagUserPush("测试标题", "烂好人123", "456",
JPushClientExample.NOTIFICATION)) {
System.err.println("---->发送成功");
}
// msgId :1460604083 1791853663 1791857267
// String msg = JPushClientExample.getMsgStatistics("1460604083");
// System.out.println("统计发送的消息"+msg);
// String[] msgS = { "1460604083", "1791853663", "1791857267" };
// String msg = JPushClientExample.getMsgStatistics(msgS);
// System.out.println("统计发送的消息" + msg);
}
}
全部都测试通过;
有延迟的情况:(服务器的逻辑是,服务器通过外网去连接极光推送的服务器,给极光推送服务器发送消息,然后在通过极光推送服务器的后台来给我们手机发送推送和通知)
一、服务器的外网不给力。解决办法(换跟光钎)
二、手机的网速不给力。解决办法(连wifi,或者换4G!!)
下面附上源码:
http://download.youkuaiyun.com/detail/yang123465/9443497 //csdn资源路径
链接:http://pan.baidu.com/s/1qXrjNPy 密码:mcuv