苹果消息推送服务教程:第三部分

这篇文章还可以在这里找到 英语

If you're new here, you may want to subscribe to my RSS feed or follow me on Twitter. Thanks for visiting!

如果你还是没有收到推送的信息,那看看push.php脚本是否仍在运行。然后看看push_development.log文件,这个文件的内容应该和如下类似:

2011-05-06T23:57:29+02:00 Sending message 1 to
 '0f744707bebcf74f9b7c25d48e3358945f6aa01da5ddb387462c7eaf61bbad78', 
payload: '{"aps":{"alert":"SteveJ: Hello, world!","sound":"default"}}'
2011-05-06T23:57:29+02:00 Message successfully delivered

你也应该在phpMyAdmin里的push_queue数据库表格看到一行关于这个信息的记录。
推送信息有时可能需要一段时间才会到达,有时就算到了苹果的服务器后也可能无法发送到设备上。你应该多试几次说不定就可以了。

在app运行的情况下接收推送信息

当你的iPhone收到推送信息后到底会发生什么呢?总共有三种可能性:

  • app在前台运行. 接收到推送信息时屏幕上不会有任何显示,也不会有提示音,但你的app delegate会收到这个推送信息。你可以在这里加入代码来处理接收到的信息。
  • app不在前台运行。iPhone可能停留在主界面或者另一个app正在运行.一个提示窗口会弹出,可能伴随着提示音。用户可以点击Close按钮来关闭这个窗口或者点击View按钮来打开你的app。如果用户点击的时Close按钮,那你的app不会处理这个推送的信息。
  • iPhone在锁屏状态下. 同样一个提示窗口弹出,并伴随着提示音,但是这个窗口不会有Close和View按钮。屏幕解锁后会自动进入你的app。

因为app delegate是接收推送信息的地方,我们对app的最后改动都是在AppDelegate.m文件中。我们需要修改两处:

  1. application:didFinishLaunchingWithOptions:函数. 如果推送信息到达时你的app不在前台运行,而用户在弹出窗口点击了“View”按钮,你的app会重新运行然后这个信息会作为参数注入到application:didFinishLaunchingWithOptions:函数中。
  2. application:didReceiveRemoteNotification:函数. 如果信息到达时你的app正在前台运行,那这个函数就会被调用。在iOS4.0或更新的版本,如果你的app从暂停状态进入前台,这个函数也会被调用。你可以用UIApplication的applicationState属性来检查你的app是否是从暂停状态苏醒。

上述的两个函数都会有一个字典参数其中包含了JSON格式的推送信息内容。OS已经帮我们把JSON格式的信息转换成Objective-C字典了。将下面的代码加到didFinishLaunchingWithOptions:函数的return语句前:

   if (launchOptions != nil)
	{
		NSDictionary* dictionary = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
		if (dictionary != nil)
		{
			NSLog(@"Launched from push notification: %@", dictionary);
			[self addMessageFromRemoteNotification:dictionary updateUI:NO];
		}
	}

我们先确保launchOptions参数不是nil以及launchOptions中包含了推送信息。然后调用addMessageFromRemoteNotification函数来处理这个信息。

把下面的函数加到AppDelegate.m文件中:

  - (void)application:(UIApplication*)application didReceiveRemoteNotification:(NSDictionary*)userInfo
  {
  	NSLog(@"Received notification: %@", userInfo);
	[self addMessageFromRemoteNotification:userInfo updateUI:YES];
  }

这个函数同样依靠addMessageFromRemoteNotification来完成处理信息的工作。
将下面这个函数复制粘贴到didFinishLaunchingWithOptions:函数上面:

- (void)addMessageFromRemoteNotification:(NSDictionary*)userInfo updateUI:(BOOL)updateUI
{
	Message* message = [[Message alloc] init];
	message.date = [NSDate date];
 
	NSString* alertValue = [[userInfo valueForKey:@"aps"] valueForKey:@"alert"];
 
	NSMutableArray* parts = [NSMutableArray arrayWithArray:[alertValue componentsSeparatedByString:@": "]];
	message.senderName = [parts objectAtIndex:0];
	[parts removeObjectAtIndex:0];
	message.text = [parts componentsJoinedByString:@": "];
 
	int index = [dataModel addMessage:message];
 
	if (updateUI)
		[self.chatViewController didSaveMessage:message atIndex:index];
 
	[message release];
}

我保证这是最后一点代码了。然我们解释一下这段代码。

Message* message = [[Message alloc] init];
	message.date = [NSDate date];

首先我们创建一个Message对象。我们会把推送信息的内容提取出来,填入到这个对象中然后将这个对象加入到DataModel中。

	NSString* alertValue = [[userInfo valueForKey:@"aps"] valueForKey:@"alert"];

上面的代码从推送信息中获取了信息的内容。推送信息的JSON的格式看起来是这样的:

{
	"aps":
	{
		"alert": "SENDER_NAME: MESSAGE_TEXT",
		"sound": "default"
	},
}

服务器把信息内容以及信息作者的昵称放到了“alert”栏中。我们对这个字典中的其他内容并不感兴趣。

	NSMutableArray* parts = [NSMutableArray arrayWithArray:[alertValue componentsSeparatedByString:@": "]];
	message.senderName = [parts objectAtIndex:0];
	[parts removeObjectAtIndex:0];
	message.text = [parts componentsJoinedByString:@": "];

上面的代码将发送者的昵称和信息内容分解出来放入到Message对象中。发送者昵称是分号和空格之前的字符串。

	int index = [dataModel addMessage:message];

现在我们可以把这个Message对象加入到DataModel中了。

	if (updateUI)
		[self.chatViewController didSaveMessage:message atIndex:index];

最后,我们让ChatViewController加入这个新的信息。但是,如果推送信息是在didFinishLaunchingWithOptions函数中收到的,那我们就不能刷新这个视图,因为那时ChatViewController的表格还没有加载。视图加入这个信息会导致系统崩溃的。

就这些了。编译并运行现有的程序。用test_message.html中的表格来发送一些信息。你应该在app的聊天视图中看到这些信息气泡出现。

自定义提示信息

你应该还记得我们之前在介绍推送信息时曾说过你可以自定义提示设置。比如你可以在有信息时播放一个自定义的提示音。我在app的resources文件夹中放了一个音频文件叫做beep.caf。

打开api.php文件并在makePayload()函数中将下面这行代码:

$payload = '{"aps":{"alert":"' . $nameJson . ': ' . $textJson . '","sound":"default"}}';

改为:

$payload = '{"aps":{"alert":"' . $nameJson . ': ' . $textJson . '","sound":"beep.caf"}}';

你不需要改变app本身的任何代码,甚至不用重新编译。但你还是应该在设备上关闭打开了的app。因为如果我们的app正在前台运行,那提示音是不会响的。现在用test_message.html给app发一个信息。当提示窗口出现时,提示音是不是不同了?

你也可以实验修改其他的选项。比如提供自定义按钮,或者给app设定数量小图标。(如果你想实验数量小图标,别忘了让app注册接收数量图标。现在我们的app只会有提示音和提示窗口。)

反馈服务器

APNS is happy to give you feedback!

APNS is happy to give you feedback!

你现在应该已经累得想睡觉了吧,但是我们还是要给服务器再加一些代码,然我们的服务器更加完善。

现象一下下面这个情况:你的数据库中有一个表格记录了很多用户的设备编码。但有的用户会移除你的app。很悲伤的情况但是无可避免。

但是,你的服务器并不会知道这个变化。服务器会继续将信息推送到他们的设备上。

这当然是一个及其不应该的情况,所以我们需要这个反馈服务器。你需要定期联系这个服务器来下载无效的设备编码列表。你应该停止给这些设备推送信息。

在PushChatServer/push文件夹中你应该看到feedback.php这个文件。这个文件中的代码会连接反馈服务器并下载相应的设备编码。你应该还看到了feedback_config.php文件。这个文件含有feedback.php脚本所需要的选项值。这个脚本会使用你的PEM文件,SSL证书和密钥来链接苹果服务器。

与push.php不同的是,你不应该在后台线程中不断运行反馈脚本。你应该建立一个cron job让服务器每小时运行这个脚本一次。
Unlike push.php, the feedback script should not be run continuously as a background process. Instead, you should set up a cron job on the server that launches it every hour or so.

这个脚本会连接到苹果的反馈服务,下载无效的设备编码列表,关闭连接。然后它会到active_users表格中删除列表中的用户。

改进和限制

PushChat用推送信息的方式作为传递信息的唯一渠道。在我们的教程中看起来不错,但却有一个潜在的问题。信息是否能成功推送到用户设备并没有被保障。所以如果推送服务丢失了任何一个信息,用户就永远无法收到这个信息了。还有就是如果用户在弹出提示时点击了“Close”按钮,我们的app就不会接收到这则信息。在我们的app中,就不会有关于这个信息的气泡出现在聊天界面中了。

更好地方法是在服务器上保持一个数据库。我们的API可以把每个信息都放入一个叫“messages”的数据库表格中。当app启动时(因为用户手动开启,或者通过信息提示窗口),我们会从服务器下载这些信息。这样就不会有信息丢失的情况了。

push.php脚本功能齐全,但是如果你的app有很多用户的话就不应该使用了。PHP远没有C或者C++的效率高,速度快。还有就是你可以看看这些PHP脚本:http://code.google.com/p/php-apns/

下一步做什么?

这是我们的教程所有的代码

虽然这个教程以及可以赶得上一个短篇小说了,但是关于推送服务,我们还有许多没有提到的。如果你的app非常依赖推送服务,我建议你再看看下面这些资源:

如果你有任何问题,意见或者建议,请登陆下面的论坛发表评论!

Matthijs Hollemans

这个教程是由iOS教程组成员 Matthijs Hollemans编写的。Matthijs是一个经验丰富的自由iOS 开发者,可以接收合同工作!


翻译分类:

我希望听到你的想法!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在信号处理领域,DOA(Direction of Arrival)估计是一项关键技术,主要用于确定多个信号源到达接收阵列的方向。本文将详细探讨三种ESPRIT(Estimation of Signal Parameters via Rotational Invariance Techniques)算法在DOA估计中的实现,以及它们在MATLAB环境中的具体应用。 ESPRIT算法是由Paul Kailath等人于1986年提出的,其核心思想是利用阵列数据的旋转不变性来估计信号源的角度。这种算法相比传统的 MUSIC(Multiple Signal Classification)算法具有较低的计算复杂度,且无需进行特征值分解,因此在实际应用中颇具优势。 1. 普通ESPRIT算法 普通ESPRIT算法分为两个主要步骤:构造等效旋转不变系统和估计角度。通过空间平移(如延时)构建两个子阵列,使得它们之间的关系具有旋转不变性。然后,通过对子阵列数据进行最小二乘拟合,可以得到信号源的角频率估计,进一步转换为DOA估计。 2. 常规ESPRIT算法实现 在描述中提到的`common_esprit_method1.m`和`common_esprit_method2.m`是两种不同的普通ESPRIT算法实现。它们可能在实现细节上略有差异,比如选择子阵列的方式、参数估计的策略等。MATLAB代码通常会包含预处理步骤(如数据归一化)、子阵列构造、旋转不变性矩阵的建立、最小二乘估计等部分。通过运行这两个文件,可以比较它们在估计精度和计算效率上的异同。 3. TLS_ESPRIT算法 TLS(Total Least Squares)ESPRIT是对普通ESPRIT的优化,它考虑了数据噪声的影响,提高了估计的稳健性。在TLS_ESPRIT算法中,不假设数据噪声是高斯白噪声,而是采用总最小二乘准则来拟合数据。这使得算法在噪声环境下表现更优。`TLS_esprit.m`文件应该包含了TLS_ESPRIT算法的完整实现,包括TLS估计的步骤和旋转不变性矩阵的改进处理。 在实际应用中,选择合适的ESPRIT变体取决于系统条件,例如噪声水平、信号质量以及计算资源。通过MATLAB实现,研究者和工程师可以方便地比较不同算法的效果,并根据需要进行调整和优化。同时,这些代码也为教学和学习DOA估计提供了一个直观的平台,有助于深入理解ESPRIT算法的工作原理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值