跳至主要内容

iOS 推送处理的的一种实践

前段时间的WWDC推了 iOS 7 出来,作为一个穷苦的码农我还没有好的idea迫使我去申请一个开发者账号(因果关系搞反了=_=),所以就没有beta版可以玩了,对于它的风格改动,我还是持支持的,毕竟苹果把很多越狱插件的功能都做进系统中了,我也没什么越狱的欲望了。言归正传,后面的内容是只针对 iOS 6 的。

本文首先会介绍一下iOS推送通知的使用和推送流程,然而文中重点是我对iOS接收到通知后的处理方法。一般而言,推送通知可以只做文本提示,应用可以完全不处理推送通知的内容,但我们也可以利用推送通知去做更多的操作,如界面跳转。比如微信,我通过推送通知打开微信的时候,微信会直接帮我条到对话界面,而不需要我慢慢手动点进去。

推送流程

完成推送通知需要后端服务器的支持,主要过程是:

  1. 用户启动应用后,选一个时机获取苹果的token
  2. 收到这个token后,系统会application:didRegisterForRemoteNotificationsWithDeviceToken:这个方法,让我们处理这个方法
  3. 把token发给后端存好
  4. 后端使用各种连接apns的框架就可以实现消息推送了

这里只说明大概的过程,但后面的内容比较专注于处理推送消息的内容,后端的内容可以找其他文章。

推送注册

调用这个方法,就可以获取一个推送token,这个token,是与应用、手机相关联的。

[[UIApplication sharedApplication] registerForRemoteNotificationTypes:(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];

在刚刚提到的方法中自由处理一下就可以了

- (void)application:(UIApplication *)app didFailToRegisterForRemoteNotificationsWithError:(NSError *)error 
{    
    //do something
}

接受推送通知方法

点击推送通知打开应用,会出现以下两个情况:

  • 应用没有启动过,或呆在后台已经有一段时间(应用可能已经被挂起或内存被系统清空)

      - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
      {
          //userInfo就是推送通知内容
          NSDictionary* userInfo = [launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
    
          //do something
    
          return YES;
      }
    
  • 后台激活应用

      - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
      {
          //userInfo就是推送通知内容
          //do something
      }
    

这两个不同入口的区别就是应用启动过和应用未启动过,这个值得注意的是,有的界面的loadView和viewDidLoad方法是没有执行过的,如果直接利用推送通知的信息进行界面跳转处理的话,应用并不会做出任何反应。

推送协议

推送通知的内容都由 NSDictionary 封装,官方使用的有badge(icon显示的数字), sound(推送消息声音), msg(推送内容)等字段。推送通知有256字节的限制,所以一次推送不能推送太多内容。另外,我们可以定义更多的kv对,作为参数带入应用,从而做相应的ui跳转。

推送处理

对于简单的推送通知,比如弹出一个UIAlertView显示消息并让用户确认,可以直接在以上提到的两个方法中直接处理。但如果需要对特定的界面做操作,我们需要判断界面是不是已经运行了viewDidLoad方法,这个界面是不是当前正在显示的界面,这可能会引起相当复杂的逻辑判断。

因此,我在这里考虑的是继承UIViewContrller写一个自己应用的ViewController(应用的基本风格也可以在这里处理好,好处相当多)。在这里我把推送通知的处理放到viewDidAppear方法中,这样我就不用考虑太多事情了,利用ViewController本身的生命周期就能完成这些事情。

这里的处理过程只能称之为方法,不能得上是框架。

  • Message:推送通知的封装

      @interface Message : NSObject
      {
          NSDictionary *_pushDict;
      }
    
      //Message工厂
      + (Message *)createMessage:(NSDictionary *)dict;
    
      //根据传入的推送通知内容构造Message对象
      - (id)initWithPushDict:(NSDictionary *)dict;
      - (void)execInterface:(UIViewController*)vc;
    
      @end
    
  • Executor:执行推送通知的执行器

      @interface Executor : NSObject
      - (void)execute:(Message *)msg viewController:(UIViewController*)vc
    
      @end
    
      @implementation Executor
      - (void)execute:(Message *)msg viewController:(UIViewController*)vc
      {
          [msg execInterface:vc];
      }
    
      @end
    
  • PCViewController:
    继承自UIViewController,这个ViewController拥有Execotor的对象和Message的对象,我们准备用利用它的生命周期嵌入推送处理的过程。而后续开发的过程,所有用到的ViewController都继承这个PCViewController。

    这里我利用的是viewDidAppear方法

      - (void)viewDidAppear:(BOOL)animated
      {
          [super viewDidAppear:animated];
          if(message!=nil)
          {                       
              [executor execute:message viewController:self];
              message = nil;
          }
      }
    
  • PCTabBarController

  • PCNavigationController

以上两个通常作为rootViewController使用,可以根据自己业务逻辑摆放执行execotor的位置。

界面跳转的实践

接到推送通知后,提取通知内容,新建下面这个方法,在适当的位置调用

-(void)pushToJump:(NSDictionary*)userInfo withFirstStart:(BOOL)firstStart
{   
    PCTabBarController* rootVC = (PCTabBarController*)self.window.rootViewController;
    Message *msg = [Message createMessage:userInfo];
    rootVC.message = msg;
    if(!firstStart){
        //如果不是第一次启动,就没法利用生命周期来自动执行页面跳转
        [rootVC executeJump];
    }
}

createMessage是一个工厂方法,userInfo是推送通知,可以利用userInfo中比如type字段来定义推送通知的类型,并利用反射构造相应的message。

在Message的execInterface方法可以定义如下内容:

- (void)execInterface:(UIViewController*)vc
{
    PCViewController *nextVC = [[[PCViewController alloc] init] autorelease];
    nextVC.message = [Message createMessage:[NSDictionary dictionary…]];

    [vc.navigationController pushViewController:nextVC animated:YES];
}

这种做法同样可以做成点击点击按钮,切换segment,但需要注意的是,这里不应该进行需要长时间等待的操作,除非用多线程。

小结

以上提到的方法,同样可以用于应用间跳转(handle open url),需要注意的是,iOS 应用的页面跳转和网页跳转还是有区别的,iOS应用更强调界面跳转逻辑,不能没头没尾的就跳到一个与当前操作无关的页面,就是说不能点着点着就突然回到首页,除非提供了一个回到首页的按钮。

评论

此博客中的热门博文

Spark 矩阵相乘实现

矩阵相乘计算的意义十分重要,比如我们做数据分析经常使用到的join操作就可以理解成为矩阵相乘的一个部分,矩阵相乘在很多分布式计算框架都有自己的实现,这些实现也可以根据不同大小的矩阵做不同的优化,比如在MapReduce上就有两种基本的实现: 大矩阵乘小矩阵 我可以把小矩阵放到DistrubutedCache,让每个mapper读取,这就是hive的MapJoin的实现。 两个大矩阵相乘 相乘中两个矩阵都是超大矩阵的话,为了减少MapReduce过程中产生的巨大数据,使用的带宽。都会采用矩阵分块的做法,以下会详细介绍这种实现方法。 Spark介绍 官网介绍 超大矩阵拆分计算方法 单机(单线程)矩阵相乘 假设有矩阵 A , B ,相乘的结果为 C ,那么相乘的伪代码: C = 0 for(i <- 0 to A.lenght) for(k <- 0 to A[i].lenght) if(A[i][k]!=0) for(j <- B[k].lenght) { C[i][j] += A[i][k] * B[k][j] } 单机的计算中,时间复杂度是 O(n^3),如果 n 增长到一定程度的时候,那么计算用时就会非常大,所以下面会说一下并行计算方法。 并行矩阵相乘 在计算的过程中,我们很容易发现每个 A[i][k] * B[k][j] 都可以单独计算,因此我也可以单独计算这些组合,最后做一个累计就可以获得 C 矩阵了,相乘伪代码如下: A = ((row, column), value) => (column, (row, value)) B = ((row, column), value) => (row, (column, value)) Temp = A.join(B) => (k, (rowA, valueA), (columnB, valueB)) => ((rowA, columnB), valueA * valueB) => ((rowC, columnC), valueC) C = Temp.reduceByKey =...

iphone 自动打包脚本

最近做ios开发,经常需要给老大打ipa包,这个虽然在xcode中编译并打包是很简单的事,不过每次都得花几分钟的时间做一些手动的放入Payload并压缩成zip包的操作。比较麻烦的是,在开发过程中,突然就说要一个可以执行的包做测试。那么,思路断了,正在写的代码要注释掉,这样持续下去浪费的时间会很多,所以还是需要写一个打包脚本。 打包具体用到的命令是这些: xcodebuild: 主要用于编译项目 xcrun: 主要用于打ipa包 具体打包流程就是编译,然后打一个发布包,一个ipa包,其实用脚本来说话就好了。另外,我用一个conf.dat来存放target和configuration,这些都在xcode里面指定好了,用xxx:xxx这样的格式来存放,xcodebuild在编译的时候会自动找到对应的配置。 打包脚本如下: #!/bin/sh basePath=`pwd` distDir="target" distDir="${basePath}/${distDir}" rm -rdf "$distDir" mkdir -p "$distDir" baseName="xxx" #.app 的名字 projectDir=$(cd ../mobile/xxx; pwd) # 进入xcode工程目录 cd $projectDir for line in $(cat ${basePath}/conf.dat) do targetName=`echo $line | cut -f1 -d':'` conf=`echo $line | cut -f2 -d':'` releaseDir="${projectDir}/build/${conf}-iphoneos" rm -rdf "$releaseDir" echo "======build ${baseName}.app start..." echo "======clean ${conf}..." xcodebuild clean -configuration "$...

【转】ELO-对弈与排名(有触感的排名)

原文: https://www.cnblogs.com/leoin2012/p/4854442.html ELO介绍 ELO等级分制度 是指由 匈牙利 裔 美国 物理学家 Elo创建的一个衡量各类对弈活动水平的评价方法,是当今对弈水平评估的公认的权威方法。被广泛用于 国际象棋 、 围棋 、 足球 、 篮球 等运动。网络游戏 英雄联盟 、 魔兽世界 内的竞技对战系统也采用此分级制度。 历史 ELO等级分制度是基于 统计学 的一个评估棋手水平的方法。 美国 国际象棋 协会在1960年首先使用这种计分方法。由于它比先前的方法更公平客观,这种方法很快流行开来。1970年国际棋联正式开始使用等级分制度。 Elo模型原先采用 正态分布 。但是实践显明棋手的表现并非呈正态分布,所以现在的等级分计分系统通常使用的是 Logistic distribution 。 计分方法 假设棋手A和B的当前等级分分别为 和 ,则按Logistic distribution A对B的胜率期望值当为 类似B对A的胜率为 假如一位棋手在比赛中的真实得分 (胜=1分,和=0.5分,负=0分)和他的胜率期望值 不同,则他的等级分要作相应的调整。具体的数学公式为 公式中 和 分别为棋手调整前后的等级分。在大师级比赛中 通常为16。 例如,棋手A等级分为1613,与等级分为1573的棋手B战平。若K取32,则A的胜率期望值为 ,约为0.5573,因而A的新等级分为1613 + 32 · (0.5 − 0.5573) = 1611.166 国际象棋中的等级分 国际象棋中,等级分和棋联称号的大致对应为 2500分以上:国际特级大师 2400-2499分:国际大师 2300-2399分:棋联大师