前言:

本文介绍Spring boot 项目中如何搭建极光推送服务,并已将极光推送方法做了封装,实现了三种推送方式:

  • [ ] 服务端控制推送内容方式
  • [ ] 服务端控制推送并带返回值得方式
  • [ ] 服务端仅推送内容,客户端自定义显示的方式

你只需在极光官网申请密钥,使用本文提供的配置和源码,5分钟即可搭建完极光推送的后端微服务。

一.服务端配置文件

在项目的application.properties资源文件中配置极光推送的密钥和缓存时间。

jpush.liveTime为极光服务器对推送消息的缓存时间。

#这两个密钥可以再极光官网申请获得,替换才能生效
jpush.appKey=12340856031dc012a7d456
jpush.masterSecret=123493e8a9be718f023023ze
jpush.liveTime=300000

二.Spring boot 配置文件

添加Maven依赖:

 <dependency>
    <groupId>cn.jpush.api</groupId>
    <artifactId>jpush-client</artifactId>
    <version>3.3.4</version>
</dependency>

读取配置文件,配置极光服务的Bean,基本不用做修改。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;


/**
 * 极光推送配置信息
 *
 * @author 葫芦娃
 */
@Component("jpushConfig")
public class JpushConfig {

    // 读取极光配置信息中的用户名密码
    @Value("${jpush.appKey}")
    private String appkey;
    @Value("${jpush.masterSecret}")
    private String masterSecret;
    @Value("${jpush.liveTime}")
    private String liveTime;

    public String getAppkey() {

        return appkey;
    }

    public String getMasterSecret() {

        return masterSecret;
    }

    public void setLiveTime(String liveTime) {

        this.liveTime = liveTime;
    }

    public void setAppkey(String appkey) {
        this.appkey = appkey;
    }

    public void setMasterSecret(String masterSecret) {
        this.masterSecret = masterSecret;
    }
}

三.封装推送服务类

此类可直接拷贝到项目中使用,已做了3种推送方式的封装和优化,

如:别名去重,同一个别名,不会收到两条相同的信息。

import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.cnegroup.power.common.config.JpushConfig;

import cn.jiguang.common.ClientConfig;
import cn.jiguang.common.ServiceHelper;
import cn.jiguang.common.connection.NettyHttpClient;
import cn.jiguang.common.resp.APIConnectionException;
import cn.jiguang.common.resp.APIRequestException;
import cn.jiguang.common.resp.ResponseWrapper;
import cn.jpush.api.JPushClient;
import cn.jpush.api.push.PushResult;
import cn.jpush.api.push.model.Message;
import cn.jpush.api.push.model.Platform;
import cn.jpush.api.push.model.PushPayload;
import cn.jpush.api.push.model.audience.Audience;
import cn.jpush.api.push.model.notification.AndroidNotification;
import cn.jpush.api.push.model.notification.IosNotification;
import cn.jpush.api.push.model.notification.Notification;
import cn.jpush.api.report.ReceivedsResult;
import cn.jpush.api.report.ReceivedsResult.Received;
import io.netty.handler.codec.http.HttpMethod;

/**
 * 极光推送服务
 * 
 * @author 葫芦娃
 *
 */
@Service("jpushService")
public class JpushService {
    private static final Logger LOG = LoggerFactory.getLogger(JpushService.class);
    @Resource
    JpushConfig jpushConfig;// 注入配置信息

    /**
     * 发送自定义推送,由APP端拦截信息后再决定是否创建通知(目前APP用此种方式)
     * 
     * @param title
     *            App通知栏标题
     * @param content
     *            App通知栏内容(为了单行显示全,尽量保持在22个汉字以下)
     * @param extrasMap
     *            额外推送信息(不会显示在通知栏,传递数据用)
     * @param alias
     *            别名数组,设定哪些用户手机能接收信息(为空则所有用户都推送)
     * @return PushResult
     */
    public PushResult sendCustomPush(String title, String content, Map<String, String> extrasMap, String... alias) {
        ClientConfig clientConfig = ClientConfig.getInstance();
        clientConfig.setTimeToLive(Long.valueOf(jpushConfig.getLiveTime()));
        // 使用NativeHttpClient网络客户端,连接网络的方式,不提供回调函数
        JPushClient jpushClient = new JPushClient(jpushConfig.getMasterSecret(), jpushConfig.getAppkey(), null,
                clientConfig);
        // 设置为消息推送方式为仅推送消息,不创建通知栏提醒
        PushPayload payload = buildCustomPushPayload(title, content, extrasMap, alias);
        PushResult result = null;
        try {
            result = jpushClient.sendPush(payload);
            LOG.info("极光推送结果 - " + result+",接收推送的别名列表:" + String.join(",", alias));
        } catch (APIConnectionException e) {
            LOG.error("极光推送连接错误,请稍后重试 ", e);
            LOG.error("Sendno: " + payload.getSendno());
        } catch (APIRequestException e) {
            LOG.error("极光服务器响应出错,请修复! ", e);
            LOG.info("HTTP Status: " + e.getStatus());
            LOG.info("Error Code: " + e.getErrorCode());
            LOG.info("Error Message: " + e.getErrorMessage());
            LOG.info("Msg ID: " + e.getMsgId());
            LOG.info("以下存在不能识别的别名: " + String.join(",", alias));
            LOG.error("Sendno: " + payload.getSendno());
        }
        return result;
    }

    /**
     * 原生方式推送
     * 
     * @param title
     *            App通知栏标题
     * @param content
     *            App通知栏内容(为了单行显示全,尽量保持在22个汉字以下)
     * @param extrasMap
     *            额外推送信息(不会显示在通知栏,传递数据用)
     * @param alias
     *            别名数组,设定哪些用户手机能接收信息(为空则所有用户都推送)
     * 
     */
    public PushResult sendPush(String title, String content, Map<String, String> extrasMap, String... alias) {
        ClientConfig clientConfig = ClientConfig.getInstance();
        clientConfig.setTimeToLive(Long.valueOf(jpushConfig.getLiveTime()));
        // 使用NativeHttpClient网络客户端,连接网络的方式,不提供回调函数
        JPushClient jpushClient = new JPushClient(jpushConfig.getMasterSecret(), jpushConfig.getAppkey(), null,
                clientConfig);
        // 设置推送方式
        PushPayload payload = buildPushPayload(title, content, extrasMap, alias);
        PushResult result = null;
        try {
            result = jpushClient.sendPush(payload);
            LOG.info("极光推送结果 - " + result);
        } catch (APIConnectionException e) {
            LOG.error("极光推送连接错误,请稍后重试 ", e);
            LOG.error("Sendno: " + payload.getSendno());
        } catch (APIRequestException e) {
            LOG.error("极光服务器响应出错,请修复! ", e);
            LOG.info("HTTP Status: " + e.getStatus());
            LOG.info("Error Code: " + e.getErrorCode());
            LOG.info("Error Message: " + e.getErrorMessage());
            LOG.info("Msg ID: " + e.getMsgId());
            LOG.info("以下存在不能识别别名: " + alias);
            LOG.error("Sendno: " + payload.getSendno());
        }
        return result;
    }

    /**
     * 异步请求推送方式
     * 
     * @param title
     *            通知栏标题
     * @param content
     *            通知栏内容(为了单行显示全,尽量保持在22个汉字以下)
     * @param extrasMap
     *            额外推送信息(不会显示在通知栏,传递数据用)
     * @param alias
     *            需接收的用户别名数组(为空则所有用户都推送)
     * 
     * @see 使用NettyHttpClient,异步接口发送请求,通过回调函数可以获取推送成功与否情况
     */
    public void sendPushWithCallback(String title, String content, Map<String, String> extrasMap, String... alias) {
        ClientConfig clientConfig = ClientConfig.getInstance();
        clientConfig.setTimeToLive(Long.valueOf(jpushConfig.getLiveTime()));
        String host = (String) clientConfig.get(ClientConfig.PUSH_HOST_NAME);
        NettyHttpClient client = new NettyHttpClient(
                ServiceHelper.getBasicAuthorization(jpushConfig.getAppkey(), jpushConfig.getMasterSecret()), null,
                clientConfig);
        try {
            URI uri = new URI(host + clientConfig.get(ClientConfig.PUSH_PATH));
            PushPayload payload = buildPushPayload(title, content, extrasMap, alias);
            client.sendRequest(HttpMethod.POST, payload.toString(), uri, new NettyHttpClient.BaseCallback() {
                @Override
                public void onSucceed(ResponseWrapper responseWrapper) {
                    if (200 == responseWrapper.responseCode) {
                        LOG.info("极光推送成功");
                    } else {
                        LOG.info("极光推送失败,返回结果: " + responseWrapper.responseContent);
                    }
                }
            });
        } catch (URISyntaxException e) {
            e.printStackTrace();
        } finally {
            // 需要手动关闭Netty请求进程,否则会一直保留
            client.close();
        }

    }

    /**
     * 构建Android和IOS的推送通知对象
     * 
     * @return PushPayload
     */
    private PushPayload buildPushPayload(String title, String content, Map<String, String> extrasMap, String... alias) {
        if (extrasMap == null || extrasMap.isEmpty()) {
            extrasMap = new HashMap<String, String>();
        }
        // 批量删除数组中空元素
        String[] newAlias = removeArrayEmptyElement(alias);
        return PushPayload.newBuilder().setPlatform(Platform.android_ios())
                // 别名为空,全员推送;别名不为空,按别名推送
                .setAudience((null == newAlias || newAlias.length == 0) ? Audience.all() : Audience.alias(alias))
                .setNotification(Notification.newBuilder().setAlert(content)
                        .addPlatformNotification(
                                AndroidNotification.newBuilder().setTitle(title).addExtras(extrasMap).build())
                        .addPlatformNotification(IosNotification.newBuilder().incrBadge(1).addExtras(extrasMap).build())
                        .build())
                .build();
    }

    /**
     * 构建Android和IOS的自定义消息的推送通知对象
     * 
     * @return PushPayload
     */
    private PushPayload buildCustomPushPayload(String title, String content, Map<String, String> extrasMap,
            String... alias) {
        // 批量删除数组中空元素
        String[] newAlias = removeArrayEmptyElement(alias);
        return PushPayload.newBuilder().setPlatform(Platform.android_ios())
                .setAudience((null == newAlias || newAlias.length == 0) ? Audience.all() : Audience.alias(alias))
                .setMessage(Message.newBuilder().setTitle(title).setMsgContent(content).addExtras(extrasMap).build())
                .build();
    }

    /**
     * 查询记录推送成功条数(暂未使用)
     * 
     * @param msg_id
     *            在推送返回结果PushResult中保存
     */
    public void countPush(String msg_id) {
        JPushClient jpushClient = new JPushClient(jpushConfig.getMasterSecret(), jpushConfig.getAppkey());
        try {
            ReceivedsResult result = jpushClient.getReportReceiveds(msg_id);
            Received received = result.received_list.get(0);
            LOG.debug("Android接受信息:" + received.android_received + "\n IOS端接受信息:" + received.ios_apns_sent);
            LOG.debug("极光推送返回结果 - " + result);
        } catch (APIConnectionException e) {
            LOG.error("极光推送连接错误,请稍后重试", e);
        } catch (APIRequestException e) {
            LOG.error("检查错误,并修复推送请求", e);
            LOG.info("HTTP Status: " + e.getStatus());
            LOG.info("Error Code: " + e.getErrorCode());
            LOG.info("Error Message: " + e.getErrorMessage());
        }
    }

    /**
     * 删除别名中的空元素(需删除如:null,""," ")
     * 
     * @param strArray
     * @return String[]
     */
    private String[] removeArrayEmptyElement(String... strArray) {
        if (null == strArray || strArray.length == 0) {
            return null;
        }
        List<String> tempList = Arrays.asList(strArray);
        List<String> strList = new ArrayList<String>();
        Iterator<String> iterator = tempList.iterator();
        while (iterator.hasNext()) {
            String str = iterator.next();
            // 消除空格后再做比较
            if (null != str && !"".equals(str.trim())) {
                strList.add(str);
            }
        }
        // 若仅输入"",则会将数组长度置为0
        String[] newStrArray = strList.toArray(new String[strList.size()]);
        return newStrArray;
    }
}

四.如何使用

 @Autowired
JpushService jpushService;

String title ="推送标题";
String content = "推送内容";
Map<String, String> extrasMap = new HashMap<String, String>();
//此Map必须创建和实例化,但可以不添加内容
extrasMap.put("额外附加传递内容","App可以解析到,没有值仅实例化即可");

//方式一:服务端控制推送内容方式
jpushService.sendPush(title, content, extrasMap, “你业务中的别名1”);
//方式二:服务端控制推送并带返回值得方式
jpushService.sendPushWithCallback(title, content, extrasMap, “你业务中的别名1”);
//方式三:服务端仅推送内容,客户端自定义显示的方式
jpushService.sendCustomPush(title, content, extrasMap, “你业务中的别名1”);
Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐