精华内容
下载资源
问答
  • iOS 开发之实现 App 消息推送(最新)

    万次阅读 多人点赞 2014-11-19 21:13:10
    今天就由本菜鸟给大家做一个简单的IOSApp消息推送教程吧!一切从0开始,包括XCode6, IOS8, 以及苹果开发者中心最新如何注册应用,申请证书以及下载配置概要文件,相信很多刚开始接触ios的人会很想了解一下。(ps:...

    好久没有写过博客啦,今天就由本菜鸟给大家做一个简单的IOSApp消息推送教程吧!一切从0开始,包括XCode6, IOS8, 以及苹果开发者中心最新如何注册应用,申请证书以及下载配置概要文件,相信很多刚开始接触ios的人会很想了解一下。(ps:网上看了一下虽然有很多讲述推送的好教程,我也是看着一步步学会的,但是这些教程的时间都是去年或者更早时期的,对引导新手来说不是很合适)

    第一部分

    首先第一步当然是介绍一下苹果的推送机制(APNS)咯(ps:其实每一篇教程都有),先来看一张苹果官方对其推送做出解释的概要图。

    Provider是给你手机应用发出推送消息的服务器,而APNS(Apple Push Notification Service)则是苹果消息推送服务器。你本地的服务器当需要给应用推送一条消息的时候,先要将消息发出到苹果推送服务器,然后再由苹果推送服务器将消息发到安装了该应用的手机。

    接下来再看一张解释图:

     

    根据上图的逻辑我来给大家解释一下:

    1.你的IOS应用需要去注册APNS消息推送功能。

    2.当苹果APNS推送服收到来自你应用的注册消息就会返回一串device token给你(很重要)

    3.将应用收到的device Token传给你本地的Push服务器。

    4.当你需要为应用推送消息的时候,你本地的推送服务器会将消息,以及Device Token打包发送到苹果的APNS服

    5.APNS再将消息推送给目的iphone

     

    第二部分

    1.从证书颁发机构颁发证书

    打开你mac的钥匙串访问: 然后点击钥匙串访问

     

    随后它会弹出一个窗口用户电子邮件信息就填写你苹果开发者账号的名称即可(应该是一个邮件名称),点击保存到磁盘的选

    项,点击继续,显示如下:

     

    点击存储,文件名为:CertificateSigningRequest.certSigningRequest 随后将他放在一个文件夹中我们取名push吧!

     

    第三部分

    访问苹果开发者网址:https://developer.apple.com/

     

    选中MemberCenter选项,进入登陆页面,用你的苹果开发者账号登陆,过一会网页就会自动跳转到下图。

    点击红色所选部分内容进行下一步的操作。

    选择Certificates选项,设置证书,如图所示先解释一下:

     

     

    Development选项的作用顾名思义就是用来作为开发使用的证书,Production选项则是用来发布产品使用的,名称很陌生是不是,之

    前的开发者网页是没有这一选项的,可能是苹果把他修改了,用这个名称更加能让人理解吧(字面上解释就是产品么)。两个选项生

    成证书的步骤是一样的,现在我们使用开发者的选项进行证书的制作,步骤如下:选择Development选项:

     

    点击上面的加号选项,

     

    选择APNS选项(开发么当然是在沙盒环境下了,模拟真实情况),然后Continue

     

    这个AppID我们在下一部分讲如何生成,现在我用的是已经生成好的一个应用ID,继续Continue

     

    这边就要选择在钥匙串访问环节下载下来的CertificateSigningRequest.certSigningRequest文件了,选择并生成:

    点击下载,得到aps_development .cer,保存到push文件中去。

     

    第四部分

    新建一个AppID,选择网页上的AppIDs,然后点击右上角的 “加号”

     

    App的取名只要按照苹果要求的就可以了

     

    然后BundleID是比较重要的,在提交审核以及测试(苹果的TestFlight)和付费环节都需要用到,也只需按照苹果要求来写就好了。

     

    接下来就是对你的应用需要使用苹果的哪些服务进行选择就行了,例如广告,游戏中心,推送,付费等等情况。

    最后选择“Submit”选项,在下一个界面中选择“done”选项,这样我们设置AppID的步骤我们就完成了。


    第五部分:生成Provisioning Profiles

    这个配置概要文件分为两种,一种是为开发使用的,还有一种则是为发布到appStore上面。

    创建发布版的ProvisioningProfile与开发版的流程相同,点击Development然后点击右上角的加号

     

    会进入选择何种配置概要文件的界面:

     

    我们现在时测试,所以选择“IOS App Development”的选项,在下面的Distribution发布选项中有两个选择,“App Store”以及

    “Ad hoc”,你可以根据下面的描述选择你发布所需的选项。点击Continue进入下一步。

    选择你上一步创建的AppID,点击Continue 进行下一步

    选择你的开发者账号,Continue进行下一步

    在这一步上选择你的设备(你只有在这一步上勾选了你的设备,你才能在设备上用这个签名进行调试)。关于如何将你的设备号添加

    进去也是非常简单的,选择左侧的"Devices",然后点击右上角的加号,在随后出来的页面上添加你设备的UUID(在XCode中可以查

    看到)以及name( 可以随便取,自己看的懂就行)然后Register一下,照着流程走到最后一步就完成了。

    好咱们继续回到上面的Provisioning Profile配置环节,当你选好了你的设备后点击“Continue”进入下一页,

     

    输入一个文件名(最好是起的能看懂是干嘛的,当然也可以随便起),点击“Generate”进入下一个页面,在这个页面中就会有一个下

    载按钮让你下载这个文件,我们把它下载下来放在Push文件夹中。

     

    第六部分

    好了,前期的准备工作都已经做完了,现在让我们开始推送吧!(吼吼)

    首先双击我们生成的 “aps_development .cer” 文件,进入钥匙串访问,找到我们的专用秘钥(根据在第二部分中从证书机构颁发证书操作中填写的常用名)

     

    我在第二部分填写的是“silicon”,由于换了一台mac之前安装的没有了,之前没有截图,所以随便找了个图给大家看一下,凭大家的聪明才智应该不难理解吧。

    然后右击导出,会弹出如下所示的图。

     

    将他存储到push文件夹中,命名为“push.p12”,在这一步中导出会让你输入密码并验证,你可以自定义一个密码,例如abc123

    现在push文件夹中应该有几个文件“aps_development .cer” ,"push.p12",“CertificateSigningRequest.certSigningRequest”以及

    刚才下下来的配置概要文件。

     

    接下来我们打开终端将他们生成.pem文件

    1.把aps_development .cer文件生成.pcm文件,cd到push文件夹下

     

    2.把push.p12文件生成为.pem文件

    上边输入的密码则是你导出证书所设的密码,即abc123.接着还会让你输入.pem文件的密码,还是使用abc123好了,防止混淆。

    这样我们在push文件夹中就又得到了两个文件,PushChatCert.pem和PushChatKey.pem。

     

    3.把PushChatCert.pem和PushChatKey.pem合并为一个pem文件,

     

    在push文件夹中又多了一个ck.pem文件,以上我们把需要使用的文件都准备好了

     

    接下来就要测试一下啦,是不是很激动~

    为了测试证书工作的状况,我们可以使用“telnet gateway.sandbox.push.apple.com 2195”来检测一下,如果显示下图则表示成功了。

     

    然后,我们使用我们生成的证书和私钥来设置一个安全的链接去链接苹果服务器

    在终端输入如下命令:openssl s_client -connect gateway.sandbox.push.apple.com:2195 -cert PushChatCert.pem -key PushChatKey.pem

    需要输入密码(abc123 我们刚才所设置的)。

    然后他会返回一系列的数据,这里我就粘贴一部分啦:

     

    CONNECTED(00000003)

    depth=1 /C=US/O=Entrust, Inc./OU=www.entrust.net/rpa is incorporated by reference/OU=(c) 2009 Entrust, Inc./CN=Entrust Certification Authority - L1C

    verify error:num=20:unable to get local issuer certificate

    verify return:0

    ---

    Certificate chain

    。。。。。(省略)

    。。。。。(省略)

    。。。。。(省略)

     

        Start Time: 1416389389

        Timeout   : 300 (sec)

        Verify return code: 0 (ok)

    ---

    测试就到这里啦。。。

     

    第七部分

    1.建立推送项目

     

    //
    //  AppDelegate.m
    //  TestPushNotifiy
    //
    //  Created by silicon on 14-10-30.
    //  Copyright (c) 2014年 silicon. All rights reserved.
    //
    
    #import "AppDelegate.h"
    
    @implementation AppDelegate
    @synthesize mainView = _mainView;
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
        if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)])
        {
            //IOS8
            //创建UIUserNotificationSettings,并设置消息的显示类类型
            UIUserNotificationSettings *notiSettings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIRemoteNotificationTypeSound) categories:nil];
            
            [application registerUserNotificationSettings:notiSettings];
            
        } else{ // ios7
            [application registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge										 |UIRemoteNotificationTypeSound										 |UIRemoteNotificationTypeAlert)];
        }
        
        self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
        // Override point for customization after application launch.
        self.window.backgroundColor = [UIColor whiteColor];
        [self.window makeKeyAndVisible];
        
        self.mainView = [[MainViewController alloc] initWithNibName:@"MainViewController" bundle:nil];
        self.window.rootViewController = self.mainView;
        return YES;
    }
    
    - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)pToken{
        NSLog(@"---Token--%@", pToken);
    }
    
    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
        
        NSLog(@"userInfo == %@",userInfo);
        NSString *message = [[userInfo objectForKey:@"aps"]objectForKey:@"alert"];
        
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:message delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
        
        [alert show];
    }
    
    - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
    
        NSLog(@"Regist fail%@",error);
    }
    
    - (void)applicationWillResignActive:(UIApplication *)application
    {
        // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
        // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
    }
    
    - (void)applicationDidEnterBackground:(UIApplication *)application
    {
        // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 
        // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
    }
    
    - (void)applicationWillEnterForeground:(UIApplication *)application
    {
        // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
    }
    
    - (void)applicationDidBecomeActive:(UIApplication *)application
    {
        // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
    }
    
    - (void)applicationWillTerminate:(UIApplication *)application
    {
        // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    }
    
    @end
    

     

    在appdelegate.m中加入以上代码,

     

     

     

    if ([application respondsToSelector:@selector(isRegisteredForRemoteNotifications)])
        {
            //IOS8
            //创建UIUserNotificationSettings,并设置消息的显示类类型
            UIUserNotificationSettings *notiSettings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge | UIUserNotificationTypeAlert | UIRemoteNotificationTypeSound) categories:nil];
            
            [application registerUserNotificationSettings:notiSettings];
            
        } else{ // ios7
            [application registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge										 |UIRemoteNotificationTypeSound										 |UIRemoteNotificationTypeAlert)];
        }

    由于ios8的推送跟ios7及以下的不一样,所以需要加判断来注册消息推送。

    函数:

     

    - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)pToken{
        NSLog(@"---Token--%@", pToken);
    }

     

    会接收来自苹果服务器给你返回的deviceToken,然后你需要将它添加到你本地的推送服务器上。(很重要,决定你的设备能不能接收到推送消息)。

     

    - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
        
        NSLog(@"userInfo == %@",userInfo);
        NSString *message = [[userInfo objectForKey:@"aps"]objectForKey:@"alert"];
        
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"提示" message:message delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"确定", nil];
        
        [alert show];
    }

     

    这个函数则是当设备接收到来自苹果推送服务器的消息时触发的,用来显示推送消息。

     

     

    - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
    
        NSLog(@"Regist fail%@",error);
    }

    当注册失败时,触发此函数。

     

     

    2.PHP服务端

    将simplepush.php这个推送脚本也放在push文件夹中

     

    <?php
    
    // ??????????deviceToken???????????????
    $deviceToken = 'c95f661371b085e2517b4c12cc76293522775e5fd9bb1dea17dd80fe85583b41';
    
    // Put your private key's passphrase here:
    $passphrase = 'abc123';
    
    // Put your alert message here:
    $message = 'My first push test!';
    
    
    
    $ctx = stream_context_create();
    stream_context_set_option($ctx, 'ssl', 'local_cert', 'ck.pem');
    stream_context_set_option($ctx, 'ssl', 'passphrase', $passphrase);
    
    // Open a connection to the APNS server
    //??????????
     //$fp = stream_socket_client(?ssl://gateway.push.apple.com:2195?, $err, $errstr, 60, //STREAM_CLIENT_CONNECT, $ctx);
    //?????????????appstore??????
    $fp = stream_socket_client(
    'ssl://gateway.sandbox.push.apple.com:2195', $err,
    $errstr, 60, STREAM_CLIENT_CONNECT|STREAM_CLIENT_PERSISTENT, $ctx);
    
    if (!$fp)
    exit("Failed to connect: $err $errstr" . PHP_EOL);
    
    echo 'Connected to APNS' . PHP_EOL;
    
    // Create the payload body
    $body['aps'] = array(
    'alert' => $message,
    'sound' => 'default'
    );
    
    // Encode the payload as JSON
    $payload = json_encode($body);
    
    // Build the binary notification
    $msg = chr(0) . pack('n', 32) . pack('H*', $deviceToken) . pack('n', strlen($payload)) . $payload;
    
    // Send it to the server
    $result = fwrite($fp, $msg, strlen($msg));
    
    if (!$result)
    echo 'Message not delivered' . PHP_EOL;
    else
    echo 'Message successfully delivered' . PHP_EOL;
    
    // Close the connection to the server
    fclose($fp);
    ?>

    deviceToken填写你接收到的token,passPhrase则填写你的ck.pem设置的密码。

    此刻就是见证奇迹的时候了,使用终端进入到push文件夹,在终端输入 php simplepush.php

     

    若显示以上提示则表示推送成功了。附上一张成功图。

     

    推送就到这边吧!

    感谢这篇博客的指导:http://blog.csdn.net/showhilllee/article/details/8631734

     

    simplepush.php以及XCode工程我会上传到我的资源里去,大家可以去那边下载。有什么不明白的地方大家可以留言或者私信我,我会第一之间回复的~

     

     

    大家可以关注我的微信公众号与我互动,相关问题也可以直接用公众号联系我:

     

     

     

     

     

     

     

     

     

     

    展开全文
  • 市面上目前做免费推送服务的有很多,友盟、极光、百度、小米、华为等,由于android机型的多样性,在使用单独的一种推送时,往往会造成一些机型无法获取(当然,内部原因可能很复杂)。Android 第三方 Push 推送方案...

    市面上目前做免费推送服务的有很多,友盟、极光、百度、小米、华为等,由于android机型的多样性,在使用单独的一种推送时,往往会造成一些机型无法获取(当然,内部原因可能很复杂)。

    Android 第三方 Push 推送方案使用调查

    因此往往需要同时集成多个第三方推送,并且能做到:

    • 自动识别机型以选取对应的SDK
    • 也希望设定阀门,在多次建联失败时,可以自动切换SDK,以尝试不同的推送服务
    • 记录状态,自动选定上次记录的推送服务SDK

    目前,采用了百度、小米和华为的推送SDK,层次上以百度为主覆盖大量机型,小米和华为为辅覆盖专有机型。
    三种SDK在基本的使用方法上大同小异,现简单加以说明:

    1. 什么是推送

    在移动互联网时代,移动端的开发都必须涉及 客户端/服务端的概念,但是从字面上看,服务端和客户端是完全独立的两套逻辑

    客户端可以主动的向服务端请求数据,譬如最常见的登录,用户输入账户密码,点击确定按钮—客户端主动把信息抛给给服务端,并等待服务端返回—-服务端返回校验状态–客户端拿到,并呈现相应的UI状态

    注意,上述流程的起点是客户端主动发起请求那么问题来了,一旦服务端的数据发生了变更,如何主动告知客户端

    那么问题来了,一旦服务端的数据发生了变更,如何主动告知客户端呢? 譬如,你的服务器收到了一个紧急邮件,但是你没在电脑前边,服务器如果通知到你呢?

    这种服务器主动发起的信息交互,就是推送

    关于推送的具体概念和不同实现方式,可以自行查看http://www.cnblogs.com/liangqihui/p/3539984.html

    1. 轮训方式 客户端不断向服务器主动请求
    2. 长连接方式 客户端和服务端维持一个长连接,类似一个双向流通的管道,有信息就丢进去,对方一直在保持监听

    1.1 端内推送、端外推送、第三方推送这些名词到底是啥意思的?

    在进行一下概念之前,先声明一点,也存在一些app的端内推送和端外推送是混合在一起的,譬如完全使用第三方的推送构建自己的聊天或业务逻辑,再譬如完全不依赖第三方,把自己的端内推送做的牛逼到可以用作端外

    其实这些概念的界限很模糊,我们就kdzl本身来谈一下:

    这里写图片描述

    • 端内推送和端外推送是一对性质相同的概念,这里的端其实指的就是我们的应用app本身

      • 端内推送,意味是我们应用自己构建了与自己服务器的长连接,这个长连接有很大程度上依赖于我们应用进程本身,譬如我们kdzl使用的PB模式的通道,就是建立在底层自己实现了的一个TCP连接上,时刻保持着与我们自己的服务器的连接状态,接收相关信息
      • 端外推送,往往意味着独立于应用实现了的推送服务,这个服务不依赖于我们自身,可以是google android提供的GCM,也可以是公司自己开发的一套推送机制,但就国内而言往往是有其他的公司开发的SDK,我们直接集成就可以,也
    • 第三方推送则是一个独立的概念,主要描述了推送的一种实现方式,主要涉及推送整个流程中是否涉及第三方的服务器

    好了,我们结合实际的kdzl分析下:

    • 端内推送 = 我们自己的实现了的PB格式的信息长连接,主要涉及我们自己的业务逻辑,,,譬如用户A给用户B发了一条聊天信息,或者A处理了B的某个流程,都需要端内推送告知用户B状态改变了
    • 端外推送 = 第三方推送(百度推送、华为推送、小米推送),只实现 位于通知栏的状态提醒,不牵扯业务逻辑

    现在可以看出来,kdzl的推送分为了两种,而且基本是相互独立的,这点大家要先明白

    由此,我们就可以解答这样一个疑问了:
    “为什么会出现,通知栏我看到了X给我发的聊天信息,但是我打开应用却没看到,等了一段时间才看到?”
    “为什么X给我发的信息,我在应用里的IM聊天界面已经看多了,过了一会,又收到了位于通知栏的新信息提醒?”

    2. 既然已经有了端内推送,为什么要使用第三方推送?

    我们要知道端内推送的TCP通道是依赖于 应用进程而存在的,进程在活跃状态下(前台或者后台)下,那么TCP通道的推送是基本稳定的;但是我们知道,android会在内存不足的状况下,会回收进程,并且 大量android用户甚至会主动通过系统一键清除或者第三方的一键清除kill我们的进程,这自然就导致我们端内长连接关闭,在此时我们如何通知用户一些信息呢? 这就需要第三方推送了
    第三方推送其实也是维持了一个与第三方服务器的长连接服务,同样它也面临同样的问题,但是这个长连接服务比较稳定:

    • 手机厂商自定义的长连接,不会被kill
      譬如小米手机运行的小米推送,小米推送所在进程为系统常驻进程,这个进程不会被系统回收,而且一键清除也不会关闭,所以稳定

    • 厂商合作
      微信作为超级应用,其实是手机厂主动和微信合作,这些我们不细说;厂商合作的结果就是 应用所在进程 被作为系统进程或者高权限进程存活,从而不能或者很难被 一键清除等操作kill, 应用不会被kill,那么自然长连接稳定。
      系统进程衍生的进程还是系统进程,微信主进程和推送进程分离,即便主进程被偶尔kill,推送进程不见得被kill;
      大家可以验证下,我们知道微信其实是有logo界面的,就是那个地球;如果你能看到地球,说明应用是从死亡状态重新激活;如果你一键清除后,再打开微信,还是么考到logo,那就说明应用根本没被kill

    • 平台适配
      做一款稳定的推送是十分耗费人力物力的,尤其是android这样机型千千万,各种自定义的,要在不同的机型上尽量保证存活;

    • 全家桶的相互唤醒
      百度推送作为非手机厂商的推送,在一键清除时候长连接也会很大几率被kill,但是当你启动集成百度推送的其他应用的时候,会把百度推送的长连接唤醒。
      譬如kdzl 和 百度地图都集成了百度推送,当kdzl被kill的时候,长连接也会被系统kill,这时候自然无法收到第三方的推送;但是当你打开百度地图的时候,由于kdzl和百度地图实际上在一台手机上共享一个长连接通道,kdzl的推送就也可收到了;
      这种全家桶由于集成用户众多,相互唤醒概率很大,从而保证长连接的自启动,进而实现稳定

    3. 什么是第三方推送

    “第三方推送”向开发者提供的消息推送服务;通过利用云端与客户端之间建立稳定、可靠的长连接来为开发者提供向客户端应用推送实时消息服务。

    为了使用第三方推送,往往需要继承对应第三方平台的相关资源文件,这些文件一般被封装为一个SDK,借助SDK,应用的开发者可以通过少量的代码,就可以将推送服务接入的APP应用中,同时可选择使用(1)自己搭建服务器使用对应第三方平台服务API 或者(2)直接使用第三方平台服务控制台的方式进行通知信息的发送。

    为了实现推送服务,实质上需要以下4个模块:

    • App应用(App Client)
      自己开发的应用,需要接入push,只需要和pushService打交道提出请求即可

    • pushService(Push Client)
      运行在手机上的推送服务,直接代理App应用与“第三方推送服务器”打交道
      用于与服务器建立稳定push通道,上报标签、 LBS信息,通知推送(含富媒体)等
      集成SDK后,一般由SDK维护

    • 第三方推送服务器(推送控制台,Push Server)
      pushService监听的服务器,该服务器用于发送通知或者信息,同时也会处理设置标签、用户记录等操作

    • 自己的业务服务器(App Server)
      实现自己的业务功能,往往需要接入“第三方推送服务器”所设定的一些API,从而可以自动的发送通知,而不再需要人工登录“推送控制台”进行信息推送

    4. 典型应用交互流程

    典型应用交互流程

    这里写图片描述

    4.1 参数说明

    • TMID
      用户标识唯一标识,一部用户终端。
      此标识在应用通过开放接口注册到PUSH平台时会获得,需要应用客户端将此标识传递给应用服务端
      在不同的平台有不同的叫法:小米regid,百度channeid+userid,华为token

    4.2 流程简要

    4.2.1 注册流程

    1. 请求TMID—-注册请求,委托推送服务去开启推送服务
      一般在app启动(activity或者application的onCreate等函数中,不确定,不同平台有不同设定)时,通过调用对应的SDK方法,请求PushService,为当前设备注册并开启推送服务

    2. 请求分配TMID—-接收到应用请求,向“第三方服务器”发起注册请求
      该方法一般由SDK默认实现和控制,应用开发者只需要在步骤1中调用对应的接口方法后,pushservice会向服务器提出申请
      该申请一般是异步申请

    3. TMID应答
      该pushService一直运行在手机中,该service在接收到“第三方服务器”给出的应答后,会发送广播,通知对应的接受者,并转发信息
      该方法一般由SDK默认实现和控制,

    4. TMID响应
      App应用往往需要继承SDK提供“receiver接收器”,并进行注册。
      pushService发出的广播,会被所继承的“接收器”中的对应方法接收,并调用

    5. 上报TMID
      App应用获取到“第三方推送服务器”分配给自己的TMID时,需要将TMID上传给“自己的业务服务器”,从而可以长久保存该ID,并且告知业务服务器通过该TMID,可向对应的业务用户发送通知。
      譬如,1:1的聊天信息,需要做定向推送,A发送给B的信息,需要得知B的TMID码
      再譬如,非定向信息,覆盖全部用户的热定新闻通知,不需要TMID码,全部推送即可。然而实际上,虽然“业务服务器”没有给出TMID码,其实是第三方服务器对“所有该App的TMID码”进行了推送。实质上和上个例子一样

    4.2.2 推送流程

    我们以“1:1的聊天信息”为例,A发送给B信息:

    1. 根据TMID推送信息
      业务服务器调用“第三方平台”给出的对应API方法,向“推送服务器”发出推送信息的申请,定向推送需要给出B用户的TMID码

    2. 根据TMID找到手机并下发
      第三方的推送服务器会通过TMID码找到对应的手机,并定向发出推送信息

    3. PushService转发信息
      该service一直运行在手机中,监听“ 第三方的推送服务器”的推送,获取到推送信息, 会发送广播,通知对应的接受者,并转发信息

    4. 信息响应
      App应用往往需要继承SDK提供“receiver接收器”,并进行注册。
      pushService发出的广播,会被所继承的“接收器”中的对应方法接收,并调用。
      可以在该方法中实现自己的业务操作。

    事件流程

    App应用中设定标签,设定别名等操作的过程,与注册流程基本一致
    

    4.2.3 自拟的平台优先级 百度>小米>华为

    为什么百度排第一呢,后台和客服反映说百度推送一堆问题,为什么还是采用百度第一呢? 原因两点:

    1. 百度推送的注册流程成功率高
    2. 百度推送的相互启动概率高,,,也就是说pushService能保持长久存活

    5. 非典型交互流程

    5.1 应用死掉

    一直想找个机会,好好的分析下,这些很浅显的问题:

    • 什么叫Deaded?
      • 整个进程组被手动或第三方管理软件kill 了,这是死掉了么?(Yes)
      • 整个应用没有一个Activity在Task栈中,甚至没有Task栈,但是却有后台service在运行,这个应用是Deaded 么?(No)
      • 整个应用没有一个Service在启动,没有Task栈,但是有Receiver正在活动期间,那么这个应用是Deaded 么?(No)

    如果,我们非要从一个外在形式上来评判一个应用是Alive还是Deaded,那么我们可以使用当前进程的Application是否存在作为评判,那么唤醒一个应用其实就是意味着做了初始化Application的行为:

    • 什么叫唤醒Alive一个应用?
      • 启动Application下的任意一个Activity,会导致Application的初始化;
      • 启动Application下的任意一个Service,会导致Application的初始化;
      • 启动Application下的任意一个Receiver,会导致Application的初始化;
      • 看上去四大组件都会导致Application的初始化;

    事实上,我们经常说“有些应用会后台自启动”,“后台偷跑流量”,这里所谓的自启动,其实并没有启动任何一个Activity,也就是根本没有构建Task,而只是启动了某个Service或者Receiver,从而势必引发至少一个进程和对应Applicaiton,我们视作Alive状态

    好了,现在我们得知Application可以作为应用是唤醒状态Alive 或者 死亡deaded状态 的标准了:

    1. 如果主进程的Application存在,那么我们就认为app是Alive,否则app是deaded
    2. 如果当前进程Application存在(针对Service或Receive可能跨进程),那么当前进程是Alive,和主进程app的存活无关系

    那么,应用被唤醒了,就意味着 应用可能维持的通信长连接已经建立了么?

    • 唤醒一个应用,只意味着启动某个service或者receiver,和调用 Application.OnCreate函数里的操作
      • 如果你的长连接通道的建立是在以上模块中构建的,那么长连接会被建立
      • 如果不是,那么就不会被建立。譬如,你是在启动页Activity中建立长连接,并把相关信息存储到application,那么你用启动service方式,就不会导致长连接的建立

    相信看到这里你基本也清楚了,应用被唤醒 与 应用具体的业务逻辑没有半毛钱关系(长连接的建立也属于业务逻辑分部),如果你想实现某个业务部分,那就必须在被唤醒的模块中 调用或者初始化

    5.2 设置中的“允许 应用 自启动”对于推送到底是影响了什么?

    如何唤醒被杀死的android app

    上篇文章里其实也有提及,唤醒应用的方式

    1. 广播,而且是静态注册的广播
    2. 服务,启动一个service

    在本处,我们主要关注下,广播这一块。我们知道,第三方推送SDK维护的PushService其实往往是运行在一个单独的进程中的(但是很大可能和app主进程同组,这点注意下,一会会提及) ,这个进程和我们应用app进程是分割开的

    我们注册推送SDK的时候,所有的官方文档都会让我静态一个Receiver类,作为监听器。回顾下上文提到的“推送流程 3-4”,PushService维持一个长连接,如果收到了来自PushServer的信息,则跨进程的发送广播给我们的应用,试图唤醒我们的的应用

    5.2.1 第三方PushService唤醒App Receiver的方式

    我们可以大胆猜测,这种inten方式应该如《如何唤醒被杀死的android app》一文中所讲:

    我们一般发广播都是局限在app内部,所以通常都是这么发的:

    Intent intent = new Intent();
    intent.setAction("my.broadcast.test");
    sendBroadcast(intent);
    
    或者这么发:
    
    Intent intent = new Intent(context, TestBroadcastReceiver.class);
    sendBroadcast(intent);

    静态的系统广播,例如:开机广播,用户开屏广播,USB插入和拔出广播等,在app运行期间可以用静态注册的接收器正常接收,但是在app被杀死后就无法收到了,Android系统做了屏蔽,把被杀死的app的系统静态广播都过滤了,所以想让app被杀死后仍然通过静态注册的接收器接收系统广播是做不到的

    静态注册的自定义广播也会遇到类似问题,尤其是在定制版的系统中

    采用下面这种方式发送广播即使app被杀死后,静态广播也能正常收到:

    Intent intent = new Intent();
                Context c = null;
                try {
                    c = createPackageContext("com.example.broadcasttest", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
                } catch (PackageManager.NameNotFoundException e) {
                    e.printStackTrace();
                }
    //            intent.setPackage(getPackageName());
    //            intent.setComponent(pkgName, className);
    //            intent.setComponent(pkgNameContext, className);
                intent.setClassName(c, "com.example.broadcasttest.TestBroadcastReceiver");
    //            intent.setClassName("com.example.broadcasttest", "com.example.broadcasttest.TestBroadcastReceiver");
                intent.setAction("my.broadcast.test");
                intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
                sendBroadcast(intent);

    接收放广播的配置(app内的自定义推送Receiver)要把exported设置成true,否则就无法收到app以外的广播发送,只能收到app内部的广播发送

    5.2.2 广播唤醒的缺陷

    正如文中所说:

    以上通过广播唤醒在一些手机上可以正常唤醒app,例如小米3;但是在魅族手机上就没办法唤醒了,需要到安全中心把app的自启动权限开启后才能正常唤醒,由此可见,一些手机厂商可能对于静态广播的接收做了一些优化导致静态广播还是没办法被接收,所以会唤醒失败

    即便 intent.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES)

    相信到这里大家也有所明悟了,为什么推送有时候收不到,我们让客户开启自启动权限,就可以收到了,原因如下:

    • App的自启动权限,会限制 PushService的自启动权限;

      • App被kill的时候,PushService作为同组进程,会被同时kill了,如果自启动被限制,则PushService都无法重启,从而导致 整个“推送流程”在环节3就被停止,app自然无法收到推送
    • App的自启动权限,会影响自身被唤醒的限制,也就是receiver的正常接收

      • App被kill了,如果自启动被限制,则其静态注册的Receiver可能被系统屏蔽掉,以防止其自动唤醒,从而导致在 “推送流程”在环节4上被停止

    这也是为什么有时开启app的自启动权限,就能导致第三方推送恢复正常的原因了。 至于app被唤醒后,触发通知栏,但是通知栏信息未显示的原因,我们在后续文章中会讲解。

    6. 推送的“信息”

    信息推送是一个动词,我们上边主要关注了“推送”这个动作的实现方式,我们知道了实现这个动作所需要的 四大天王,缺一不可

    那么作为这个动作的受体,到底是什么呢?

    我们可以简单地认为是一串文本信息,譬如一句话: “你好”

    到这里,我们回归下最开始的意图,我们使用第三方推送,是期望能在手机的通知栏弹出一栏信息,根据 执行弹出动作的主体可以分为两种类型:

    1. 通知类型 弹出通知栏的动作由PushService完成,由服务端直接拟定
    2. 透传信息类型 弹出通知栏的动作由应用自身完成,先解析数据,再弹窗

    相对而言,透传信息的自由度更高,我们可以自行处理收到信息后的状态,譬如弹窗,开启引用,或者不弹窗

    我们现在在百度和华为平台上使用了透传信息,而小米则使用了通知

    6.1 为什么还要使用通知呢?

    其实这和我们之前说的“非典型交互流程”有关

    我们知道, pushservice 在收到信息后,会发送广播,试图唤醒我们的应用,然后将信息传递过去

    那么唤醒不成功呢? 自然无法将信息传递过去,应用本身不知道信息,处于dead状态,自然就不会弹窗提醒了

    然而通知类型则不需要唤醒了,因为弹窗动作直接由pushservice完成啦,管你app是死是活,都能弹出来提示

    而在小米手机上,pushservice是一种系统支持的服务,你杀都杀不死,所以我们在一般在小米手机上使用通知方式

    6.2 那通知有这个好处,为什么我们在三个平台上,都使用通知呢?

    很简单呀,一旦使用通知,相当于把全部责任给 pushservice, 如果pushservice越坚挺,我们就都使用通知

    然而实际上,只有小米和华为这种手机厂商自拟的pushservice,在自己手机上 pushservice 才有这么牛逼

    因为,是小米手机我们就用小米推送

    6.3 华为为什么不用通知?

    华为的透传信息也是系统级别的,能保持 pushservice 坚挺就足够了,,,我们还是期望多些自由度

    6.4 透传或通知到了,就一定会弹窗么?

    肯定不是了,弹窗只是一个动作,很有可能被“通知中心”之类的限制,你弹,它限制,你弹,它限制,自然出不来了

    7. Android推送遇到的尴尬

    目前作为移动设备的两大巨头 Android 和 iOS 平台, 也分别在系统级别各自集成相应的推送模块(GCM, APNs), 然而由于 Google 服务器在国内所存在的流量限制和Android 系统存在的定制多样性, 使得信息推送在 Android 设备上成为一件不得不大费周折的事情。

    保证信息推送的即时性( 信息发送后能在 Minutes 级别被响应收到) 和高到达率( 信息发送后能稳定的到达对应设备) 成为信息推送所需要解决的首要问题。

    目前市面上已经有很多关于 Android、 iOS、 Windows Phone 的信息推送平台, 基本上我们可以分为三种类型, 不同的推送平台都拥有着不同的优势, 然而也存在着不同层面上的问题, 无法有效保证信息推送的即时性和高到达率:

    • 官方推送 Google Cloud Messaging, 在 Android 系统级别进行支持的推送服务, 需要 Google 服务器支持。

    由于是从原生 Android 的系统层次进行推送支持, 从而保证了移动端推送运转的畅通和稳定性, 可以有效且即时的响应信息, 但是由于国内对 Google 服务器的流量限制, 导致了服务端的 GCM推送服务的不确定性, 这也导致了国内的终端厂商大部分移除了 GCM 模块。 综合来说, GCM 对于主要面向国外市场的移动设备依然是一种优先考虑的选择, 对于国内设备只能作为信息推送的辅助手段, 无法承担实际信息推送的任务。

    • 手机厂商推送, 例如小米系统的 MIPush, 华为系统HuaWeiPush, 魅族系统的 FlyMePush 等.作为定制系统的手机厂商, 从自定制系统级别进行了推送支持。

    国内的终端厂商往往在原生 Android系统的基础上进行了进一步定制, 实现了各自不同的终端系统, 并在各自的系统级别实现了推送服务,但是各自的推送平台仅能在各自相应的系统上作为系统级别的推送服务,例如 MiPush 仅能在小米系统上作为系统级服务存在, 在其他终端系统上退化为普通推送服务。

    • 第三方推送, 诸如极光、 个推、 信鸽、 友盟、 阿里、 百度、 小米、 华为等, 不具备系统级别的服务支持。

    作为应用级的信息推送平台, 支持跨系统平台的推送服务, 且往往依赖于集成同款应用之间的相互唤醒, 具有一定的稳定性, 例如集成百度推送的移动应用可以被另一款集成该推送的移动应用所唤醒, 但是移动端推送服务被系统回收和 kill 的概率相对较高, 往往需要赋予自启权限,且很难保证推送的稳定性。

    如何合理且高效的运用这些推送平台, 以保证信息推送的即时性和高到达率?

    8. 多平台通道的构因

    为方便理解,我举一个例子,让大家感受下

    场景预设:农民伯伯种地,同一土地的酸碱度和水分程度会随时间变化,同时不同的土地含矿物质不同。农民伯伯只能种下唯一一颗种子,种子一分钟就会开会结果,而后落地成种子继续开花结果。不同的土地适合不同的种子,种子在不适合的土地上生长会减产或者死亡。

    场景预设的等价描述:不同的系统平台上启动推送服务,同一系统平台适用的推送服务可能会不同,不同的系统平台适用的推送服务也会不同。系统平台上同时运行的有且只能有一个推送服务。 推送服务的选择与开启在每次应用开启时都会触发。

    涉及元素:土地 = 终端设备的系统平台、种子 = 推送平台

    问题: 如何保证产量 = 如何提供信息的到达率?

    • 环节1:为什么要集成多个平台? = 为什么要集成多个品种的种子成一个种子库;

    土地会随着时间导致酸碱度和水分程度不同—-不同的酸碱度和水分程度适宜不同的种子—–>时间会导致 土地适用于不同的种子 —–> 农民伯伯只能种一个种子—->那么当然期望一个超级种子:它内部包含多个种子;能体现不同性质,而长成苹果 or 梨子等

    • 环节2: 在不同的土地上第一次播种,能自动变成对应最优种子 因地制宜 == 【核心】智能初始化

    不同的土地含矿物质不同 — 不同矿物质含量适宜不同的种子 —> 不同的土地 导致 适用于不同的种子 —–>农民伯伯只能种一个种子—->那么当然期望一个超级种子:它内部包含多个种子;第一次在某个土地上落地时,能先抽取下土地的土壤并分析,而后根据矿物质的不同,选择出包含的最优适配的种子作为自身的性质;记录该种子标识,并在下一分钟的再次 落地开花结果中能够直接转化为对应种子,而不是再次抽取土地土壤分析;
    不同的手机系统 有 不同的最佳适用推送服务,小米系统适用小米推送,华为系统适用华为推送,vivo适用百度推送 —> 应用一旦被安装在手机上,只能同时运行一款推送服务——>那么当然期望一个超级推送:它内部包含多个推送平台;第一次在某个终端安装并运行应用时,智能的在不同手机平台上从“推送库”中选择不同的推送服务,作为首要选择并缓存记录;同时下次启动应用时,直接按照此时记录的推送平台进行启动;

    • 环节3:同一土地的酸碱度和水分变化时,自动调整种子 = 【核心】智能切换

    土地会随着时间导致酸碱度和水分程度不同—-不同的酸碱度和水分程度适宜不同的种子—–>时间会导致 土地适用于不同的种子 —–> 农民伯伯只能种一个种子—->那么当然期望一个超级种子:它内部包含多个种子;能够及时响应自身 减产或死亡的变化而选择出来对应的最佳种子,如果在当前土地上减产或死亡多次,能自动切换为下一个种子类型;
    应用在运行的过程中,根据相关运行时状态(例如无响应和失败响应),动态的对推送服务进行切换,同时缓存记录该切换后的平台标识,下次启动应用时,直接按照此时记录的推送平台进行启动;

    • 环节4:农民伯伯觉得自动调整有些问题,他就想种苹果种子 == 命令行控制方式作为自动化切换的人工补偿。

    • 其他:所有土地都适合中土豆,但是土豆在国内不值钱,国外值钱 = 所有android的GCM是最原生的,但是GCM在国内被限制,国外可用

    土地在国外则直接中土豆,否则土豆作为催化剂
    手机设备支持GCM且不受限制则使用GCM,否则GCM作为辅助通道

    • 其他:不同的酸碱度与最优种子计算算法 == 优先级因子计算算法

    思考下“既然各个推送平台各个有缺,为什么我不把全部推送都集成进来”

    展开全文
  • 小米推送,华为推送,个推,阿里云推送集成(服务端JAVA开发)

    小米推送,华为推送,个推,阿里云推送集成(服务端JAVA开发)

    公司新任务中要求集成以上四种推送方式,所以我在这里做下随堂笔记:
    这里是官方的文档url:
    小米:https://dev.mi.com/mipush/docs/server-sdk/introduction/
    华为:http://developer.huawei.com/consumer/cn/service/hms/catalog/huaweipush.html?page=hmssdk_huaweipush_devguide_s
    个推:http://docs.getui.com/server/java/template/
    阿里云:https://help.aliyun.com/document_detail/48048.html?spm=5176.doc30092.6.604.ivLvNV
    博主:qq - 3447767388(不喜勿喷,欢迎交流,下述内容来如有误,请留言指出,雷同的应该是没有,我自己写的)


    java.jar(小米、个推、阿里云官方很直接给出了maven,个推的maven我本人无法引入,所以我将官方jar打包到私有库里,无法引用的朋友也可以学我使用)

    <!--MiPush-->
        <dependency>
          <groupId>com.mi</groupId>
          <artifactId>mipush-sdk-server</artifactId>
          <version>2.2.18</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.googlecode.json-simple/json-simple -->
        <dependency>
          <groupId>com.googlecode.json-simple</groupId>
          <artifactId>json-simple</artifactId>
          <version>1.1.1</version>
        </dependency>
    	<!--MiPush-->
    
    <!--GePush-->
    <dependency>
      <groupId>com.gexin</groupId>
      <artifactId>rp-sdk-base</artifactId>
      <version>4.0.0.14</version>
    </dependency>
    <dependency>
      <groupId>com.gexin</groupId>
      <artifactId>rp-fastjson</artifactId>
      <version>1.0.0.0</version>
    </dependency>
    <dependency>
      <groupId>com.gexin</groupId>
      <artifactId>rp-sdk-http</artifactId>
      <version>4.0.1.9</version>
    </dependency>
    <dependency>
      <groupId>com.gexin</groupId>
      <artifactId>rp-sdk-template</artifactId>
      <version>4.0.0.8</version>
    </dependency>
    <dependency>
      <groupId>com.protobuf</groupId>
      <artifactId>rp-protobuf-java</artifactId>
      <version>2.5.0</version>
    </dependency>
    <!--GePush-->
    
    <!--alipush-->
        <dependency>
          <groupId>com.aliyun</groupId>
          <artifactId>aliyun-java-sdk-push</artifactId>
          <version>3.5.1</version>
        </dependency>
        <dependency>
          <groupId>com.aliyun</groupId>
          <artifactId>aliyun-java-sdk-core</artifactId>
          <version>3.2.5</version>
        </dependency>
    <!--alipush-->
    

    华为 有个自己的http/https方式很便捷

    • 华为官方给出
      String HV_TOKEN_URL= “https://login.vmall.com/oauth2/token”;
      String HV_API_URL= “https://api.push.hicloud.com/pushsend.do”;

    代码展示

    1.小米推送(我只推送Android且只应用regId发起推送,所以下面只有推送Android的代码)

    1.1构建message

    private Message buildMessageForAndroid(String title, String description, String messagePayload,
                                               Integer passThrough, Integer actionType, String actionUrl, Integer notifyType, String extendContent) {
            Message message;
            Message.Builder builder = new Message.Builder()
                    .title(title)//标题(注意16字真言限制长度,这段画上重点考)
                    .description(description)//描述(注意128限制长度,这段画上重点考,这个描述,我理解为副标题,而且在手机客户端呈现的也是标题+描述,内容不会自己显示出来,如果只是为了通知用户信息,我们可以将信息内容放在此处,显示效果比较明显。但是三个文字区域都不可空。需要补充文字方可使用)
                    .payload(messagePayload)//内容(这个长度够你用了)
                    .restrictedPackageName(AggregationPushContent.MI_ANDROID_PACKAGE_NAME)//APP包名
                    .passThrough(passThrough)//是否透传
                    .notifyType(notifyType)//设置震动,响铃等等
                    .extra("extend_content", extendContent);//这里要注意下,你可以通过自定义的key传给客户端一段透传参数
    
            if (actionType == 1) {//动作跳转根据自己定义的方式去跳转url,if里的两组.extra是必须要放入的,这段画上重点
                builder.extra(Constants.EXTRA_PARAM_NOTIFY_EFFECT, Constants.NOTIFY_ACTIVITY)
                        .extra(Constants.EXTRA_PARAM_INTENT_URI, actionUrl);
            } else if (actionType == 2) {
                builder.extra(Constants.EXTRA_PARAM_NOTIFY_EFFECT, Constants.NOTIFY_WEB)
                        .extra(Constants.EXTRA_PARAM_WEB_URI, actionUrl);
            }
            message = builder.build();
            return message;
        }
    

    1.2开始发送

    public Result sendMessageToRegId(String messageId, String title, String description,
                                         String messagePayload, Integer passThrough, Integer actionType, String actionUrl, Integer notifyType,
                                         String extendContent, String regId) {
    
            Constants.useOfficial();//这里要注意,这是正式-启动方式,支持IOS跟Android,Constants.useSandbox();这是测试-启动方式,不支持Android,尽量申请正式APP,利用正式环境测试
            Sender sender = new Sender("你需要传入你所申请appsecret");
            Message message = buildMessageForAndroid(title, description, messagePayload, passThrough, actionType, actionUrl, notifyType, extendContent);
            Result result = null;
            try {
                result = sender.send(message, regId, RETRIES);
            } catch (Exception e) {
    		    //log
            }
            return result;
        }
    

    1.3返回值 根据下述返回值自己定义成功与失败吧!

    		/**
             * 发送消息返回的错误码,如果返回ErrorCode.Success表示发送成功,其他表示发送失败。
             */
            ErrorCode errorMessage = result.getErrorCode();
            /**
             * 如果消息发送成功,服务器返回消息的ID;如果发送失败,返回null。
             */
            String platformMessageId = result.getMessageId();
    
            /**
             * 	如果消息发送失败,服务器返回错误原因的文字描述;如果发送成功,返回null。
             */
            String reason = result.getReason(); 
    

    2.华为推送(我只推送Android且只应用deviceToken发起推送,所以下面只有推送Android的代码)

    2.1获取应用accessToken(这个token放在redis或者数据库里保存,失效时重新获取,不要发一次,获取一次,因为频繁获取token很有可能获取失败,这也是好的开发规则,自定义失效时间要比实际失效时间减少3-5分钟,把系统耗时放在内,规避掉巧合失效的情况)

     /**
         * 获取下发通知消息的认证Token
         */
        public Map<String, String> refreshToken() {
            String accessToken;//下发通知消息的认证Token
            long tokenExpiredTime;  //accessToken的过期时间
            Map<String, String> data = Maps.newHashMap();
            try {
                String msgBody = MessageFormat.format(
                        "grant_type=client_credentials&client_secret={0}&client_id={1}",
                        URLEncoder.encode(appSecret, "UTF-8"), appId);
                String response = HttpUtil.post(tokenUrl, msgBody);
                JSONObject obj = JSONObject.parseObject(response);
                accessToken = obj.getString("access_token");
                tokenExpiredTime = obj.getLong("expires_in");
    
                data.put(redisKey, accessToken);
                data.put(tokenTimeKey, tokenExpiredTime + "");
            } catch (Exception e) {
                logger.error("HvPush -- >> 认证Token获取失败!原因:e = {}", ExceptionUtil.getMessage(e));
            }
            return data;
        }
    

    2.2构建payLoad

    这里发一张我的payLoad结构,官方有现成的内容,我的比较实惠

    {
    		    "hps": {
    		        "msg": {
    		            "action": {
    			            "type": 3,
    		                "param": {
    		                    "appPkgName": "自定义的路径"
    		                }
    		            },
    		            "type": 1,//这里要注意,华为暂时推荐1或3,3就是通知,1是透传模式,有的小朋友用的方式1,是收不到通知消息的,可以自己多自测下,画上波浪线是重点
    		            "body": {
    		                "extend_content": "透传参数",
    		                "title": "消息标题",
    		                "content": "消息内容"
    		            }
    		        },
    		        "ext": {
    		            "biTag": "Trump",
    		            "icon": "自定义推送消息在通知栏的图标,value为一个公网可以访问的URL"
    		        }
    		    }
    }
    

    2.3发起推送请求并解析返回值

    String postBody;
    
            Map<String, Object> req = Maps.newHashMap();
            try {
                postBody = MessageFormat.format(
                        "access_token={0}&nsp_svc={1}&nsp_ts={2}&device_token_list={3}&payload={4}",
                        URLEncoder.encode(accessToken, "UTF-8"),
                        URLEncoder.encode("openpush.message.api.send", "UTF-8"),
                        URLEncoder.encode(String.valueOf(System.currentTimeMillis() / 1000), "UTF-8"),
                        URLEncoder.encode(deviceTokens.toString(), "UTF-8"),
                        URLEncoder.encode(payload.toString(), "UTF-8"));
    
    
                String postUrl = apiUrl + "?nsp_ctx=" + URLEncoder.encode("{\"ver\":\"1\", \"appId\":\"" + appId + "\"}", "UTF-8");
    
                String request = HttpUtil.post(postUrl, postBody);
    
                logger.info("HvPush ---- >> 推送结果:messageId = {}, result = {}", messageId, request);
    
                req = JSON.parseObject(request);
    
            } catch (Exception e) {
                logger.error("HvPush -- 华为推送失败!messageId = {}, 原因:e = {}", messageId, ExceptionUtil.getMessage(e));
            }
    
            if (req.get("code").equals("80000000") && req.get("msg").equals("success")) {
                return ResultModel.success("推送成功!");
            } else {
                return ResultModel.commonError("推送失败!");
            }
    

    3.个推官方给的太详细了http://docs.getui.com/server/java/template/大家自己复制吧,可能出现的问题就是官方jar引用问题。

    4.阿里云推送(官方SDK,拿来即用)

    IClientProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessKeySecret);
            DefaultAcsClient client = new DefaultAcsClient(profile);
            PushRequest pushRequest = new PushRequest();
            // 推送目标
            pushRequest.setAppKey(appKey);
            pushRequest.setTarget("DEVICE"); //推送目标: DEVICE:按设备推送 ALIAS : 按别名推送 ACCOUNT:按帐号推送  TAG:按标签推送; ALL: 广播推送
            pushRequest.setTargetValue(deviceIds); //根据Target来设定,如Target=DEVICE, 则对应的值为 设备id1,设备id2. 多个值使用逗号分隔.(帐号与设备有一次最多100个的限制)
    //        pushRequest.setTarget("ALL"); //推送目标: DEVICE:推送给设备; ACCOUNT:推送给指定帐号,TAG:推送给自定义标签; ALL: 推送给全部
    //        pushRequest.setTargetValue("ALL"); //根据Target来设定,如Target=DEVICE, 则对应的值为 设备id1,设备id2. 多个值使用逗号分隔.(帐号与设备有一次最多100个的限制)
            pushRequest.setPushType("NOTICE"); // 消息类型 MESSAGE NOTICE
            pushRequest.setDeviceType("ALL"); // 设备类型 ANDROID iOS ALL.
            // 推送配置
            pushRequest.setTitle("ALi Push Title"); // 消息的标题
            pushRequest.setBody("Ali Push Body"); // 消息的内容
            // 推送配置: iOS
            pushRequest.setIOSBadge(5); // iOS应用图标右上角角标
            pushRequest.setIOSMusic("default"); // iOS通知声音
            pushRequest.setIOSSubtitle("iOS10 subtitle");//iOS10通知副标题的内容
            pushRequest.setIOSNotificationCategory("iOS10 Notification Category");//指定iOS10通知Category
            pushRequest.setIOSMutableContent(true);//是否允许扩展iOS通知内容
            pushRequest.setIOSApnsEnv("DEV");//iOS的通知是通过APNs中心来发送的,需要填写对应的环境信息。"DEV" : 表示开发环境 "PRODUCT" : 表示生产环境
            pushRequest.setIOSRemind(true); // 消息推送时设备不在线(既与移动推送的服务端的长连接通道不通),则这条推送会做为通知,通过苹果的APNs通道送达一次。注意:离线消息转通知仅适用于生产环境
            pushRequest.setIOSRemindBody("iOSRemindBody");//iOS消息转通知时使用的iOS通知内容,仅当iOSApnsEnv=PRODUCT && iOSRemind为true时有效
            pushRequest.setIOSExtParameters("{\"_ENV_\":\"DEV\",\"k2\":\"v2\"}"); //通知的扩展属性(注意 : 该参数要以json map的格式传入,否则会解析出错)
            // 推送配置: Android
            pushRequest.setAndroidNotifyType("NONE");//通知的提醒方式 "VIBRATE" : 震动 "SOUND" : 声音 "BOTH" : 声音和震动 NONE : 静音
            pushRequest.setAndroidNotificationBarType(1);//通知栏自定义样式0-100
            pushRequest.setAndroidNotificationBarPriority(1);//通知栏自定义样式0-100
            pushRequest.setAndroidOpenType("URL"); //点击通知后动作 "APPLICATION" : 打开应用 "ACTIVITY" : 打开AndroidActivity "URL" : 打开URL "NONE" : 无跳转
            pushRequest.setAndroidOpenUrl("http://www.aliyun.com"); //Android收到推送后打开对应的url,仅当AndroidOpenType="URL"有效
            pushRequest.setAndroidActivity("com.alibaba.push2.demo.XiaoMiPushActivity"); // 设定通知打开的activity,仅当AndroidOpenType="Activity"有效
            pushRequest.setAndroidMusic("default"); // Android通知音乐
            pushRequest.setAndroidPopupActivity("com.ali.demo.PopupActivity");//设置该参数后启动辅助弹窗功能, 此处指定通知点击后跳转的Activity(辅助弹窗的前提条件:1. 集成第三方辅助通道;2. StoreOffline参数设为true)
            pushRequest.setAndroidPopupTitle("Popup Title");
            pushRequest.setAndroidPopupBody("Popup Body");
            pushRequest.setAndroidExtParameters("{\"k1\":\"android\",\"k2\":\"v2\"}"); //设定通知的扩展属性。(注意 : 该参数要以 json map 的格式传入,否则会解析出错)
            // 推送控制
            Date pushDate = new Date(System.currentTimeMillis()) ; // 30秒之间的时间点, 也可以设置成你指定固定时间
            String pushTime = ParameterHelper.getISO8601Time(pushDate);
            pushRequest.setPushTime(pushTime); // 延后推送。可选,如果不设置表示立即推送
            String expireTime = ParameterHelper.getISO8601Time(new Date(System.currentTimeMillis() + 12 * 3600 * 1000)); // 12小时后消息失效, 不会再发送
            pushRequest.setExpireTime(expireTime);
            pushRequest.setStoreOffline(true); // 离线消息是否保存,若保存, 在推送时候,用户即使不在线,下一次上线则会收到
            PushResponse pushResponse = client.getAcsResponse(pushRequest);
            System.out.printf("RequestId: %s, MessageID: %s\n",
            pushResponse.getRequestId(), pushResponse.getMessageId());
    

    以上四种推送,针对Android&IOS都做到了很优质的推送服务,在开发中可能要满足多样式的产品需求,去变动传输参数,例如:透传给APP客户端的订单信息,需要播报,需要跳转链接等等。其中小米设备,华为设备也有自己的强硬的推送。我个人认为服务端这边坑还是很小的,至少淹不死人,开发前别急于求成,想找文档,把最官方最全面的API都拿出来,我想都会迎刃而解,服务端需要注意的事项:

    1.透传方式(通知 - 服务端直接唤醒APP客户端的通知栏;透传模式 - 服务端将配置的参数传给APP客户端,由开发者自定义)
    2.动作&路径
    3.设备号要正确,且APP在各个相应平台已经上线
    4.透传参数要对正放入,阿里云推送中,对应的透传JSON-MAP 格式要符合客户端的需要
    

    2019年3月20日 我将我写的api代码上传了,仅供参考https://download.csdn.net/download/wendy_loves/11041699
    2020年07月16日10:46:20 我发现下载的积分我设置了0还是会自己涨所以网盘大法好,链接: https://pan.baidu.com/s/1QgftxqNXloFbmxWxK-eeTw 密码: q1cn
    如何还是想用csdn下载的朋友如果发现积分涨了请联系我

    展开全文
  • 本地推送.极光推送.APNs推送

    千次阅读 2015-12-30 14:47:38
    1.本地推送的简单应用 2.APNs介绍 3.使用极光推送向你的APP推送一条信息 一.本地推送 本地通知推送也叫闹钟通知, 这是通知中比较简单的一部分. 如果将app比喻成一个人, 某个controller比喻成身体的某一部分(手臂), ...

    1.本地推送的简单应用
    2.APNs介绍
    3.使用极光推送向你的APP推送一条信息

    一.本地推送

    本地通知推送也叫闹钟通知, 这是通知中比较简单的一部分.
    如果将app比喻成一个人, 某个controller比喻成身体的某一部分(手臂), 那么由手臂注册通知并且发送通知, 最后被大脑(AppDelegate)收到这个通知并作出处理.

    file-list

    下面我们模拟一个情景, 在ViewController中创建一个通知(闹钟)并且激活它, 然后在Appdelegate中收到这个通知, 并且弹窗AlertView显示.

    ViewController.h

    1
    2
    
    // 本地注册通知的方法
    + (void)registerLocalNotification:(NSInteger)alertTime;
    

    ViewController.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    + (void)registerLocalNotification:(NSInteger)alertTime
    {
        // 实例化一个本地通知对象.
        UILocalNotification * notification = [[UILocalNotification alloc] init];
        
        // 设置什么时间点(具体的时间)触发本地通知. 距离现在多少秒之后.
        notification.fireDate = [NSDate dateWithTimeIntervalSinceNow:alertTime];
        
        // 设置时区,default手机时区
        notification.timeZone = [NSTimeZone defaultTimeZone];
        
        // 设置重复间隔, 按秒计算.
        notification.repeatInterval = kCFCalendarUnitSecond;
        
        // 弹出的通知内容, 下拉菜单的通知.
        notification.alertBody = @"快迟到了!";
        // 气泡个数
        notification.applicationIconBadgeNumber = 1;
        
        // 通知被触发时播放声音吗?
        notification.soundName = UILocalNotificationDefaultSoundName;
        
        // 通知的参数
        NSDictionary * userDict = [NSDictionary dictionaryWithObject:@"起床洗漱了" forKey:@"hurry"];
        notification.userInfo = userDict;
        
        // 最后一步, 将这个通知激活.
        [[UIApplication sharedApplication] scheduleLocalNotification:notification];
    }
    

    事实上, 这么写已经将闹钟激活了, 他确实会在alerttime秒之后向发送一个通知, 但是, 我们没有在Appdelegate中配置接受这个通知并作出响应. 那么这个通知实际上被我们丢弃了.

    那么如何在Appdelegate中处理这个通知呢?, 有一个代理方法application:didReceiveLocalNotification是处理这个通知的.

    AppDelegate.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    #pragma mark本地通知代理
    // 做过iOS 开发的人对这个函数都会很熟悉,这是在程序结束启动,并即将运行时调用的,通常一些初始化的工作可以在这个函数中处理。
    - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification*)notification{
        //在此时设置解析notification,并展示提示视图
        NSString * message = [notification.userInfo valueForKey:@"hurry"];
        UIAlertView * alert = [[UIAlertView alloc] initWithTitle:@"aaaa" message:message delegate:self cancelButtonTitle:@"取消" otherButtonTitles: nil];
        [alert show];
        
        // 既然已经查看了这个通知, 那么气泡就要减少一个
        NSInteger badge = [[UIApplication  sharedApplication] applicationIconBadgeNumber];
        badge --;
        [UIApplication sharedApplication].applicationIconBadgeNumber = badge;
    }
    

    以上代码并不完整.
    我们并没有为app申请本体通知的权限:

    AppDelegate.m的didFinishLaunchingWithOptions方法中

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
        
        // ios8后,需要添加这个注册,才能得到授权
        if ([[UIApplication sharedApplication] respondsToSelector:@selector(registerUserNotificationSettings:)]) {
            UIUserNotificationType type =  UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound;
            UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:type categories:nil];
            [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
        }else{ // ios8之前的申请权限方法.
            [application registerForRemoteNotificationTypes:UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound];
        }
        return YES;
    }
    

    这样基本就完成了, 但是有一种情况, 比如闹钟响了, 我们点击了起床, 此时闹铃不应该再响了, 我们应该从本地通知中取消这个通知(根据key).

    AppDelegate.m的application:didReceiveLocalNotification方法中, 我们在气泡减一操作之后, 调用这个页面的取消通知方法.

    1
    2
    
    // 取消通知
    [ViewController cancelLocalNotificationWithKey:@"hurry"];
    

    ViewController.h

    1
    2
    
    // 根据传入的key, 移除本地通知
    + (void)cancelLocalNotificationWithKey:(NSString *)key;
    

    VidwController.m

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
    + (void)cancelLocalNotificationWithKey:(NSString *)key
    {
        // 从当前的app中找出所有的本地通知.
        NSArray * notificationArrray = [UIApplication sharedApplication].scheduledLocalNotifications;
        // 如果按照key寻找userInfo, 不为空则说明找到了.
        for (UILocalNotification * not in notificationArrray) {
            if ([not.userInfo valueForKey:key]) {
                [[UIApplication sharedApplication] cancelAllLocalNotifications];
            }
        }
    }
    

    以上就是我们比较完整的需求.


    二.苹果自家推送–APNs服务

    下面来讲一下远程推送, 上面的推送不联网也能执行, 但是远程推送则不行.

    等一下, 这个远程推送是什么意思? 为什么会有远程推送?

    苹果的推送功能是ios3.0之后推出的, 也就是说, 一开始的苹果操作系统是没有这个功能. 那么为什么后来要加上这个推送呢? 没有推送行不行呢?

    这个还真不行.

    原因很简单, 苹果ios操作系统不支持真正意义上的多任务.
    假设一种应用场景, 如果不支持多任务的话, 你正在聊qq, 忽然想去微信摇一摇, 切换到微信后, qq程序进入后台, 但是再也收不到消息了.

    早期(2007年)的iphone就是这个样子. 除了几个系统工具, 如打电话, 发短信, 看地图, 发邮件外, 你不能保证用户自己的app同时运行. 等等, 说错了! 妈蛋那个时候iphone还没有app store呢, 你想装app都不行.

    于是乎, 苹果推出了自己家的推送服务, 由苹果服务器手机推送一条通知, 手机根据推送过来的消息, 判断属于发给谁(发给哪个app), 然后向该app发送通知, 调用相应的代理方法, 最终的结果是, 你桌面上的app图标冒起了气泡, 桌面通知栏弹出一个条消息.

    file-list

    这就是远程推送, 看似不经意的一个动作, 包含了一整套的设计方案–APNs, 凝结了无数人的心血.

    APNs(英文全称:Apple Push Notification Service),中文翻译为:苹果推送通知服务。顾名思义, 该服务由苹果提供并且维护.

    在这里要特别的注意, 如果服务器使用推送的方式与APP发生交互, 那么你必须走APNs, 换而言之, 三方服务不能直接向你的手机推送消息, 必须通过APNs.

    那么我们该如何理解APNs服务呢?

    我们通过三张图来简单说明一下APNs的推送过程, 此图来自APNs百度百科.

    • 手机与服务器建立连接:
      file-list

      1.手机向APNs服务器发出初连接请求. 手机与服务器开始握手.
      2.服务器向设备返回服务器证书(Server Cerficate). 此证书的目的是为了表明服务器的身份.
      3.手机验证刚刚收到的证书合法性(Validate Server Cerficate), 如果验证成功, 则表明手机连接到了正确的服务器.(Validate: 证实,验证;确认;使生效, 它还有隐藏含义, 使证书生效).
      4.下面轮到手机向服务器发送证书(Device Cerficate), 此证书表明了手机的身份–有没有越狱? 是不是苹果机器?
      5.服务器验证手机是否合法.
      6.身份验证完毕.


    • 推送过程:

      1.你开发的APP, 从当前的手机系统iOS中读取手机的推送证书, 之后, iOS操作系统向APNs Server申请Device token.
      2.APNs Server返回Device token到你的手机, 再由iOS通知给你的APP.
      3.你的APP将获得的Device token, 发送到你的服务器, 可以是极光服务器, 可以是你们公司自己的服务器, 不管怎样, 你的服务器中要维护一张Device token的表. 这个表中, 记录了所有使用你APP的Device token.
      4.当你需要向某个用户发送推送时, 根据3维护的表, 找到该用户的Device token, 然后将Device token和需要推送的消息, 一起发送给 APNs Server
      5.最终, APNs将这条推送消息推送到用户APP中, 完成推送过程.

    注意以上的两个过程建立连接推送过程, 建立连接是为了验证通信双方的身份, 我们关注的点应该在推送.


    • 你的服务器如何向用户群推送

      此过程是上图中的4,5步骤.不做赘述.

    下面我们通过使用极光推送来具体的演示一下过程, 并且分析它.


    三.极光推送

    极光推送主要功能

    • 为 JPush Server 上报 Device Token,免除开发者管理 Device Token 的麻烦
    • 应用运行时,应用内 JPush 长连接可以持续地收到推送消息

    极光推送的集成过程以及使用方法在官方文档中有着极为详细的说明, 这里引用一下, 不做赘述.

    下面讨论一些莫名其妙的东西.

    * 一.didFinishLaunching

    前面提到, 这个方法会在程序启动完成之后执行, 通常一些程序的初始化动作可以在这里执行.
    所以, 推送的初始化也要在这里完成, 推送的注册(申请权限)根据iOS的版本不同略有不同, iOS8之前只需要一句话, 之后的版本则多几行.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Required, 注册通知
        if ([[UIDevice currentDevice].systemVersion floatValue] >= 8.0) {
            //可以添加自定义categories
            [APService registerForRemoteNotificationTypes:( UIUserNotificationTypeBadge | UIUserNotificationTypeSound | UIUserNotificationTypeAlert) categories:nil];
        } else {
            //categories 必须为nil
            [APService registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert) categories:nil];
        }
        
        // Required
        [APService setupWithOption:launchOptions];
        
        return YES;
    }
    

    我们看最后一句:[APService setupWithOption:launchOptions];
    launchOptions(下段文字来自百度知道:怎样判断app是通过推送消息启动的)
    当应用程序启动时执行,应用程序启动入口。只在应用程序启动时执行一次。application参数用来获取应用程序的状态、变量. 字典参数:(NSDictionary *)launchOptions,该参数存储程序启动的原因。

    • 若用户直接启动,lauchOptions内无数据;
    • 若由其他应用程序通过openURL:启动,则UIApplicationLaunchOptionsURLKey对应的对象为启动URL(NSURL),UIApplicationLaunchOptionsSourceApplicationKey对应启动的源应用程序的bundle ID (NSString);
    • 若由本地通知启动,则UIApplicationLaunchOptionsLocalNotificationKey对应的是为启动应用程序的的本地通知对象(UILocalNotification);
    • 若由远程通知启动,则UIApplicationLaunchOptionsRemoteNotificationKey对应的是启动应用程序的的远程通知信息userInfo(NSDictionary);
    • 其他key还有UIApplicationLaunchOptionsAnnotationKey, UIApplicationLaunchOptionsLocationKey,
      UIApplicationLaunchOptionsNewsstandDownloadsKey.

    如果要在启动时,做出一些区分,那就需要在下面的代码做处理。
    所以, [APService setupWithOption:launchOptions];方法, 会根据启动类型, 对极光推送做一些配置.

    * 二.获得token
    1
    2
    3
    
    - (void)application:(UIApplication *)applicationdidRegisterForRemoteNotificationsWithDeviceToken:(NSData *)pToken
    // &
    - (void)application:(UIApplication *)applicationdidFailToRegisterForRemoteNotificationsWithError:(NSError *)error
    

    这两个方法是将设备的token传送到苹果的服务器的回调方法,在成功之后, 我们应该调用:[APService registerDeviceToken:deviceToken];方法, 将这个token发送到极光服务器(这里一定要注意, 我们选择由极光替我们维护这张token表, 如果你的公司有自己的推送服务器, 应该将此token发送到你公司的服务器, 自行维护这张表.)

    * 三.接收处理方法.

    如果程序在运行期间或者后台状态, 则如果推送消息会在下面的两个方法中执行, 注意, 两个方法作用一样, 你应该从中二选一, 苹果推荐你选择后者, 因为他提供了一个回调的句柄. 这个block的特别之处在于, 回调方法不是严格等待处理结束, 开始处理30s后, 此句柄会被调起.

    1
    2
    3
    
    -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
    // &
    -(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
    

    userInfo结构

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    {
        "aps" : {
            "alert" : "You got your emails.",
            "badge" : 9,
            "sound" : "bingbong.aiff"
        },
        "acme1" : "bar",
        "acme2" : 42
    }
    展开全文
  • thinkphp5嵌入极光推送群发实例

    千次阅读 2018-06-02 11:21:33
    一、安装jpush 极光推送 在composer的配置文件中加入 &quot;require&quot;: { &quot;jpush/jpush&quot;: &quot;^3.5&quot; } 使用composer 安装极光推送 cmd 命令 composer install...
  • 极光推送 java 服务端推送api

    千次阅读 2018-06-14 12:43:30
    极光推送 java 服务端推送api 1 极光推送的核心类如下: import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.assertj.core.util.Strings; import pre.cyy....
  • 极光推送 sdk 集成 全部推送 与别名标签推送
  • 1.本地推送 关于本地推送的作用及意义:【后期完善】 实现步骤 大致4步:1创建通知、2注册通知、3实现函数、4删除通知 注意! 下面的内容中没有提到:在使用通知的时候,为了避免重复注册:最好在生命周期的函数...
  • 第一种:用户拒绝了, 进入小程序后,点击右上角三个点,点击设置, 打开推送开关 第二种:非常重点研究一个星期的重点:不是自己在发送前执行一次wx.requestSubscribeMessage(),而是需要接收方执行了wx....
  • ios推送之远程推送

    千次阅读 2016-04-11 12:09:06
    IOS中消息的推送有两种方式,分别是本地推送和远程推送,本文主要讨论远程推送的流程与配置过程。 二、远程推送的相关原理 1、远程推送的相关机制 单设备 图中所描述的大致意思是这样:你的应用服务端(Provider...
  • Java如何给极光推送发消息

    千次阅读 2016-09-08 20:59:58
    一,项目背景,我是负责App端开发的,不过由于公司小人手不足,因此也要写一些后台的小程序,国内的推送平台还是比较多的,有极光、个推、信鸽等等,下面聊一下服务端如何来给极光的服务端推送消息,以达到让极光...
  • 推送之个推推送

    千次阅读 2016-11-21 11:20:35
    做完推送这个需求已经很长时间了,一直以来也没时间来写写笔记及新得,做完这个需求收货还是颇多的 集成类似于个推这种第三方框架首先要对其官方的sdk熟悉一番,个推的官方文档写的着实详细呢,每个名词都有详细的...
  • 极光推送,是一个面向普通开发者开放的,免费的第三方消息推送服务。本篇博客将结合案例介绍极光推送自定义消息的使用方法,利用自定义消息实现项目中特定的消息推送需求。 本案例将实现如图效果:  ...
  • 说Android端外推送比较烦,实际有两层意思:首先是说实现上比较麻烦,至今业界也没有找到一种完美的解决方案,Android程序员通常需要同时集成多家推送平台(如果有自己的端内推送,还要考虑与端内推送的配合);...
  • 极光推送,是一个面向普通开发者开放的,免费的第三方消息推送服务。本篇博客将结合案例介绍极光推送自定义消息的使用方法,利用自定义消息实现项目中特定的消息推送需求。
  • 前沿 集成极光推送大概有七八回了吧,本以为轻车熟路的,结果你永远干不过PM的脑洞。...可是后台推送就这个语言呀,结果开始互相甩锅了。。。。。 解决方案 本地负责的态度决定自己本地做处理吧,自己的思路是首...
  • 在上一章节,我们了解了推送的基本概念,在本章节,我们开始使用百度推送、小米推送、华为推送,并分析他们在使用上的不同第三方推送的官方网址 友盟推送 极光推送 百度推送 小米推送 华为推送 Pushbullet 集成第三...
  • 消息推送在APP应用中越来越普遍,来记录一下项目中用到的一种推送方式,对于Andriod它并没有自己的原生推送机制,一种简单的推送方式是采用第三方推送服务的方式,即通过嵌入SDK使用第三方提供的推送服务,主流的有...
  • 推送原理 极光推送使用详解
  • 今天来带大家学习下微信模版消息推送。 先看效果图: 核心代码只有下面几行,即可轻松实现微信模版消息推送 //1,配置 WxMpInMemoryConfigStorage wxStorage = new WxMpInMemoryConfigStorage(); wxStorage....
  • 极光推送正式环境收不到推送消息

    千次阅读 2016-12-07 17:40:38
    做极光推送线上版本迭代几版后,发现生产环境收不到推送消息,测试环境却可以收到推送消息。各种搜集资料,猜测是服务端的配置出了问题。是这样的,服务端和客户端都是需要配置极光推送的,服务端的配置代码,默认是...
  • 微信公众号开发消息推送以及图文推送

    万次阅读 热门讨论 2018-10-30 17:11:44
    今天给大家分享的关注公众号自动推送图文消息,以及做一个超牛逼的机器人。 先看看效果。 错图了。。。这是我昨天开发的一款机器人chu了会骂人啥都不会了。 我今天将它词库进行了更新和升级,接入了...
  • 微信封号推送卡包消息方法揭秘

    千次阅读 2019-06-14 01:14:27
    众所周知,微信封号之后是不能消息的,这就大大的影响了正常的网络沟通,在诸多微信被封人群中有各色各样的职业,特别是微商封号或者永久封号联系不到客户都是极大损失;所谓树跟被断、鱼鳞被揭、微信被封作为由古...
  • 消息推送

    千次阅读 2016-11-16 00:16:01
    消息推送最简单的方法就是使用第三方的,比如现在使用比较多的是小米推送、极光推送,消息推送的技术原理是:移动无线网络长连接移动互联网络的现状因为手机平台本身、电量、网络流量的限制,移动互联网应用在设计上...
  • 之前的项目跟推送有关的,我一直使用的都是极光推送,集成非常简单,只要按照官方文档的步骤一步步的进行,基本就没有什么问题。但是这个项目因为要向个人推送消息,而之前的推送都是以广播的方式向所有人推送内容。...
  • 安卓Jpush推送简单集成

    千次阅读 2017-03-21 20:19:24
    一、前言  这篇文章主要来讲解推送的使用,例如我们一打开... 推送的原理大都相似,都是通过一个中转服务器,初始化的时候客户端和服务端都在服务器上注册好自己的设备信息,一旦服务端需要发推送消息时,就会给
  • 这篇文章介绍一下目前企业微信所支持的推送消息的格式和使用方法,大部分内容与消息示例均来源于目前钉钉的开发文档。
  • 前段时间公司需要 Android 端的手机群功能,我们就通过 MQTT 来实现了该功能。 MQTT 的官网如下http://mqtt.org/ 在 MQTT 官网中 http://mqtt.org/software 列举出了实现了该协议的开源客户端和服务器工程和库。...
  • Android番外02_JPush极光推送集成

    千次阅读 2016-12-16 15:10:59
    一、前言 这篇文章主要来讲解推送的使用...推送的原理大都相似,都是通过一个中转服务器,初始化的时候客户端和服务端都在服务器上注册好自己的设备信息,一旦服务端需要发推送消息时,就会给中转服务器发送一条请求,

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 105,559
精华内容 42,223
关键字:

如何发推送