iOS多线程编程之多社交平台同步推送的设计与实现

简介:

功能介绍

在开发快易博的时候,有一个功能叫做“分享心情”【见下图】。它的主要功能是:用户可以一次发表一个微博(在人人网称之为新鲜事)到所有用户选择的绑定平台(其实就是通常所说的微博同步)。
进入之后:

这个功能实现起来并不难,说白了就是依次调用各个开放平台的关于“发表”相关的API就好了。但牵扯到几个给用户提供更好的“用户体验”的需求,就不得不使用多线程了,需求如下:

(1) 在图二对应的功能界面上,用户在发表完成之后,应该可以立即关闭;

(2) 在各个平台的独立发表界面,在发表完成之后,发表界面自动关闭,显示打开发表界面之前的一个界面。

注:分享心情界面其实实现的是一个多功能的发表界面,可以同步推送,也可以选择你想推送的平台。为了给用户提供方便,在你点进各个平台之后(比如新浪微博),它里面有单独的发表到该平台的发表界面来提供更多的功能,比如“@、##”等,见下图。


原先的处理方式

对于上面的需求(1),我原先的做法是,在用户点击右上角的发表按钮之后。用MBProgressHUD强制给当前界面设置一个“遮罩”,以让返回按钮不可点击。这样做的目的是为了停留在当前界面来等待执行各个开放平台提供的诸如“发表成功”、“发表失败”之类的回调协议,并且在回调协议里,通过在状态栏使用提示信息来提示用户是否成功。

假如让返回按钮可点击,用户有可能在发表完成之后立马选择返回,那么当前的UIViewController可能已经被释放,回调的时候就会导致应用崩溃。当然如果用MBProgressHUD hold主整个界面,用户除了选择等待就没有别的选择,而回调协议执行的时机其实取决于很多不确定因素(比如当前的网速,如果附带了图片,还要看图片的大小,以及各个开放平台对于API调用的响应速度等等),可能很快,也可能会等个好几十秒。而一旦你考验用户的耐心,那就是在给自己埋定时炸弹了。

对于需求(2),比需求(1)的关闭速度更快。我需要让用户在发表完成之后,发表界面立即关闭。所以当前UIViewController根本等不到回调协议的调用就已经释放,状态栏提示也无法实现。当然,妥协的办法还是用MBProgressHUD显示个“请稍后…”之类的强制让界面无法响应用户,等回调完成在选择关闭。

现在的处理方式

从上面的分析可以看得出,我希望回调协议与当前发表界面的UIViewController对象的生命周期没有关系。那么只能让这些“发表”操作都不在主线程(UI线程)上执行,这也就实现了发表操作与发表功能界面的解耦。

设计与代码实现

在iOS中实现多线程的方式有几种,这里使用NSOperation以及NSOperationQueue来实现。

UML简图:


大致代码如下:

PublishOperation:

#import <Foundation/Foundation.h>
#import "MTStatusBarOverlay.h"

@interface PublishOperation : NSOperation
<
MTStatusBarOverlayDelegate
>

@property (nonatomic,copy) NSString* txt;
@property (nonatomic,copy) NSString *publishImgPath;
@property (nonatomic,assign) BOOL completed;
@property (nonatomic,assign) BOOL canceledAfterError;
@property (nonatomic,retain) MTStatusBarOverlay *overlay;

- (void)clearPublishedImage;

- (id)initWithOperateParams:(NSString*)content;

@end

#import "PublishOperation.h"

@implementation PublishOperation

@synthesize completed;
@synthesize canceledAfterError;
@synthesize txt=_txt;
@synthesize overlay=_overlay;
@synthesize publishImgPath=_publishImgPath;

- (void)dealloc{
    [_txt release],_txt=nil;
    [_overlay release],_overlay=nil;
    [_publishImgPath release],_publishImgPath=nil;
    
    [super dealloc];
}

- (id)init{
    if (self=[super init]) {
        completed=NO;
        canceledAfterError=NO;
        [self initMTStatusBarOverlay];
    }
    
    return self;
}

- (id)initWithOperateParams:(NSString*)content{
    if (self=[self init]) {
        self.txt=content;
    }
    
    return self;
}

/*
 *实例化工具栏提示组件
 */
-(void)initMTStatusBarOverlay{
    _overlay = [MTStatusBarOverlay sharedInstance];
    _overlay.animation = MTStatusBarOverlayAnimationFallDown;  
    _overlay.detailViewMode = MTDetailViewModeHistory;         
    _overlay.delegate=self;
}

- (void)main{
    //操作既没有完成也没有因为发生错误而取消,则hold住当前run loop
    //防止返回主线程使得代理方法无法执行
    while (!(self.completed||self.canceledAfterError)) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode 
                                 beforeDate:[NSDate distantFuture]];
        DebugLog(@"running!");
    }
    
}

#pragma mark - other methods -
- (void)clearPublishedImage{
    //清除緩存圖片
    if (fileExistsAtPath(self.publishImgPath)) {
        removerAtItem(self.publishImgPath);
    }
}

@end

SinaWeiboPublishOperation

#import "PublishOperation.h"
#import "WBEngine.h"

@interface SinaWeiboPublishOperation : PublishOperation
<
WBEngineDelegate
>

@end

#import "SinaWeiboPublishOperation.h"

@interface SinaWeiboPublishOperation()

@property (nonatomic,retain) WBEngine *engine;

@end

@implementation SinaWeiboPublishOperation

@synthesize engine=_engine;

- (void)dealloc{
    [_engine setDelegate:nil];
    [_engine release],_engine=nil;
    
    [super dealloc];
}

/*
 *business logic
 */
- (void)main{
    self.publishImgPath=PUBLISH_IMAGEPATH_SINAWEIBO;
    _engine = [[WBEngine alloc]initWithAppKey:kWBSDKDemoAppKey 
                                    appSecret:kWBSDKDemoAppSecret];
    _engine.delegate=self;
    [self.engine sendWeiBoWithText:self.txt 
                        image:[UIImage imageWithContentsOfFile:
                               PUBLISH_IMAGEPATH_SINAWEIBO]];
    
    [self.overlay postImmediateMessage:@"新浪微博发表中..." animated:YES];
    
    [super main];
}

#pragma mark - WBEngineDelegate Methods -
- (void)engine:(WBEngine *)engine requestDidSucceedWithResult:(id)result{
    [self clearPublishedImage];
    [GlobalInstance showOverlayMsg:@"新浪微博发表成功!" 
                       andDuration:2.0 
                        andOverlay:self.overlay];
    
     self.completed=YES;
     [NSThread sleepForTimeInterval:5];
}

- (void)engine:(WBEngine *)engine requestDidFailWithError:(NSError *)error{
    [self clearPublishedImage];
    [GlobalInstance showOverlayErrorMsg:@"新浪微博发表失败!" 
                            andDuration:2.0 
                             andOverlay:self.overlay];
    
    self.canceledAfterError=YES;
    [NSThread sleepForTimeInterval:5];
}

RenRenPublishOperation

#import "PublishOperation.h"

@interface RenRenPublishOperation : PublishOperation
<
RenrenDelegate
>

@end

#import "RenRenPublishOperation.h"

@implementation RenRenPublishOperation

/*
 *business logic
 */
- (void)main{
    self.publishImgPath=PUBLISH_IMAGEPATH_RENREN;
    BOOL hasImage=[[NSFileManager defaultManager]fileExistsAtPath:PUBLISH_IMAGEPATH_RENREN];
    if (hasImage) {
        ROPublishPhotoRequestParam *photoParams=[[[ROPublishPhotoRequestParam alloc]init]autorelease];
        photoParams.imageFile=[UIImage imageWithContentsOfFile:PUBLISH_IMAGEPATH_RENREN];
        photoParams.caption=self.txt;
        [[Renren sharedRenren] publishPhoto:photoParams andDelegate:self];
        [self.overlay postImmediateMessage:@"人人图片上传中..." animated:YES];
    }else {
        NSMutableDictionary *params=[NSMutableDictionary dictionaryWithCapacity:10];
        [params setObject:@"status.set" forKey:@"method"];
        [params setObject:self.txt
                   forKey:@"status"];
        [[Renren sharedRenren]requestWithParams:params andDelegate:self];
        [self.overlay postImmediateMessage:@"人人状态发表中..." animated:YES];
    }
    
    [super main];
}

#pragma mark - RenrenDelegate (ren ren) -
-(void)renren:(Renren *)renren requestDidReturnResponse:(ROResponse *)response{
    [self clearPublishedImage];
    [GlobalInstance showOverlayMsg:@"人人新鲜事发表成功!" 
                       andDuration:2.0 
                        andOverlay:self.overlay];
    self.completed=YES;
    [NSThread sleepForTimeInterval:5];
}

TencentWeiboPublishOperation

#import "PublishOperation.h"
#import "TencentWeiboDelegate.h"

@interface TencentWeiboPublishOperation : PublishOperation
<
TencentWeiboDelegate
>

@end

#import "TencentWeiboPublishOperation.h"
#import "TencentWeiboManager.h"
#import "OpenApi.h"

@implementation TencentWeiboPublishOperation

/*
 *business logic
 */
- (void)main{
    [self.overlay postImmediateMessage:@"腾讯微博发表中..." animated:YES];
    self.publishImgPath=PUBLISH_IMAGEPATH_TENCENTWEIBO;
    BOOL hasImage=[[NSFileManager defaultManager]fileExistsAtPath:PUBLISH_IMAGEPATH_TENCENTWEIBO];
    OpenApi *myApi=[TencentWeiboManager getOpenApi];
    myApi.delegate=self;
    if (!hasImage) {
        [myApi publishWeibo:self.txt 
                       jing:@"" 
                        wei:@"" 
                     format:@"json" 
                   clientip:@"127.0.0.1" 
                   syncflag:@"0"];
    }else{
        [myApi publishWeiboWithImageAndContent:self.txt 
                                          jing:@"" wei:@"" 
                                        format:@"json" 
                                      clientip:@"127.0.0.1" 
                                      syncflag:@"0"];
    }
    
}

#pragma mark - tencent weibo delegate -
- (void)tencentWeiboPublishedSuccessfully{
    [self clearPublishedImage];
    [GlobalInstance showOverlayMsg:@"腾讯微博发表成功!" 
                       andDuration:2.0 
                        andOverlay:self.overlay];
    [NSThread sleepForTimeInterval:5];
}

- (void)tencentWeiboRequestFailWithError{
    [self clearPublishedImage];
    [GlobalInstance showOverlayErrorMsg:@"腾讯微博发表失败!" 
                            andDuration:2.0 
                             andOverlay:self.overlay];
    
    [NSThread sleepForTimeInterval:5];
}


@end

注解:NSOperation在非并发的执行方式下,只需要重新实现它的main方法即可。

在PublishOperation的main方法中的那段代码的主要目的hold主当前的RunLoop(通俗一点讲其实就是阻塞当前执行的上下文,直到回调协议被执行),由此可见这里仍然存在回调协议无法执行的问题。为什么会出现这种情况,为什么要hold执行的上下文?因为各个平台的API通常都是异步发送请求的(腾讯微博除外),等到有响应了再来执行实现了的协议。而所谓的异步,其实也是操作系统在后台用另一个线程来处理的。而NSOperation的执行模式是它执行完实现在main方法里的逻辑就会退出,返回主线程。所以通常情况下这里的回调协议还是没有得到机会执行(机会其实是得到了,只是当前的NSOperation对象已被释放,导致内存访问错误)。所以这里用一个while循环hold主当前的runloop,一直保持到各个平台的异步回调完协议之后,设置completed或者canceledAfterError为YES,以退出while循环。注意这两个属性是异步设置的,也就是说是另一个线程来设置的。这里说明一下,在各个平台调用了[super main];方法之后,其实你不在回调协议中(或者说在另一个线程中)是无法设置这两个属性的。因为[super main];这句让当前执行NSOperation的线程阻塞了。

我顺便吐槽一下腾讯微博开放平台,它们开放的Framework中,发出的请求居然是同步获取结果的,而且它们居然都没有定义任何的回调协议,这里也让我很费神。所以在TencentWeiboOperation中的实现会跟新浪、人人有所不同。因为调用是同步的,所以在TencentWeiboPublishOperation最后直接调用[super main];是没用的(因为它会在回调协议之后才会执行,所以这边不需要调用它) ,这边调用线程的Sleep方法,让线程睡眠5秒钟,也就是延迟当前的NSOperation上下文5秒,延迟是因为在状态栏显示提示信息需要2秒,如果不延迟,那么在completed设置为YES之后NSOperation就会执行完成并退出,那MTStatusOverlayDelegate也没有机会执行。

/*
 *状态栏显示完成提示信息
 */
-(void)showOverlayMsg:(NSString*)msg andDuration:(NSTimeInterval)duration andOverlay:(MTStatusBarOverlay*)overlay{
    [overlay postImmediateFinishMessage:msg 
                               duration:duration animated:YES];
}

你在应用程序的AppDelegate中定义一个NSOperationQueue,用来执行NSOperation,就可以完成对发表操作与发表功能界面的Controller解耦。这样就可以在发表完成之后,直接关闭并释放Controller,并且由于功能的封装,它变成一个独立的、可复用的整体。在“分享心情”功能界面里,多平台同步推送的话,你只需要把各个平台的“PublishOperation”丢到NSOperationQueue去,其他就不用管,直接关闭或者返回即可。

新浪微博单独发表界面示例代码:

- (void)publishBtn_handle:(id)sender{	
    NSString *txt=self.publishTxtView.text;
    PublishOperation *publishOperation=[[[SinaWeiboPublishOperation alloc]initWithOperateParams:txt]autorelease];
    [((FastEasyBlogAppDelegate*)appDelegateObj).operationQueue addOperation:publishOperation];
    
    [self dismissModalViewControllerAnimated:YES];
}

分享心情发表界面示例代码:

/*
 *发送
 */
-(void)publish_click{    
	//移除高亮效果
	UIButton *btn=(UIButton*)self.navigationItem.rightBarButtonItem.customView;
	[btn setBackgroundImage:[UIImage imageNamed:@"publishBtn.png"] forState:UIControlStateNormal];
	
	if ([self.publishTxtView.text length]==0) {
		[GlobalInstance showMessageBoxWithMessage:@"请说点什么吧!"];
		return;
	}
    
    NSString *weiboTextContent=self.publishTxtView.text;
    
    if (plaformSwitch.isRenRenOpen) {
        PublishOperation *renrenOperation=[[[RenRenPublishOperation alloc]initWithOperateParams:weiboTextContent]autorelease];
        
        [((FastEasyBlogAppDelegate*)appDelegateObj).operationQueue addOperation:renrenOperation];
    }
    
    if (plaformSwitch.isTencentWeiboOpen) {
        PublishOperation *tencentOperation=[[[TencentWeiboPublishOperation alloc]initWithOperateParams:weiboTextContent]autorelease];
        
        [((FastEasyBlogAppDelegate*)appDelegateObj).operationQueue addOperation:tencentOperation];
    }
	
	//清空內容
    [self clearInputFromTextView];
    
    UIButton *imgBtn=(UIButton*)[self.view viewWithTag:4321];
    [imgBtn removeFromSuperview];
    
    [self dismissModalViewControllerAnimated:YES];
}

最后,为自己打个广告:


这是本人的第一个“个人iOS应用”,它是一款能够帮助你同时在多个社交平台和好友沟通交流以及分享的应用。支持一键发表状态/心情/照片到所有绑定平台,快速浏览/回复/分享,以及多终端同时在线浏览好友微博与好友互动,为你提供更为便捷的社交体验。




原文发布时间为:2012-11-18


本文作者:vinoYang


本文来自云栖社区合作伙伴CSDN博客,了解相关信息可以关注CSDN博客。

目录
相关文章
|
7天前
|
安全 Java 调度
Java编程时多线程操作单核服务器可以不加锁吗?
Java编程时多线程操作单核服务器可以不加锁吗?
21 2
|
12天前
|
Java 调度 开发者
Java中的多线程编程:从基础到实践
本文旨在深入探讨Java多线程编程的核心概念和实际应用,通过浅显易懂的语言解释多线程的基本原理,并结合实例展示如何在Java中创建、控制和管理线程。我们将从简单的线程创建开始,逐步深入到线程同步、通信以及死锁问题的解决方案,最终通过具体的代码示例来加深理解。无论您是Java初学者还是希望提升多线程编程技能的开发者,本文都将为您提供有价值的见解和实用的技巧。
15 2
|
13天前
|
Java 数据处理
Java中的多线程编程:从基础到实践
本文旨在深入探讨Java中的多线程编程,涵盖其基本概念、创建方法、同步机制及实际应用。通过对多线程基础知识的介绍和具体示例的演示,希望帮助读者更好地理解和应用Java多线程编程,提高程序的效率和性能。
19 1
|
17天前
|
安全 Java 调度
Java 并发编程中的线程安全和性能优化
本文将深入探讨Java并发编程中的关键概念,包括线程安全、同步机制以及性能优化。我们将从基础入手,逐步解析高级技术,并通过实例展示如何在实际开发中应用这些知识。阅读完本文后,读者将对如何在多线程环境中编写高效且安全的Java代码有一个全面的了解。
|
6天前
|
Java
COMATE插件实现使用线程池高级并发模型简化多线程编程
本文介绍了COMATE插件的使用,该插件通过线程池实现高级并发模型,简化了多线程编程的过程,并提供了生成结果和代码参考。
|
9天前
|
Java 数据处理 调度
Java中的多线程编程:从基础到实践
本文深入探讨了Java中多线程编程的基本概念、实现方式及其在实际项目中的应用。首先,我们将了解什么是线程以及为何需要多线程编程。接着,文章将详细介绍如何在Java中创建和管理线程,包括继承Thread类、实现Runnable接口以及使用Executor框架等方法。此外,我们还将讨论线程同步和通信的问题,如互斥锁、信号量、条件变量等。最后,通过具体的示例展示了如何在实际项目中有效地利用多线程提高程序的性能和响应能力。
|
9天前
|
安全 算法 Java
Java中的多线程编程:从基础到高级应用
本文深入探讨了Java中的多线程编程,从最基础的概念入手,逐步引导读者了解并掌握多线程开发的核心技术。无论是初学者还是有一定经验的开发者,都能从中获益。通过实例和代码示例,本文详细讲解了线程的创建与管理、同步与锁机制、线程间通信以及高级并发工具等主题。此外,还讨论了多线程编程中常见的问题及其解决方案,帮助读者编写出高效、安全的多线程应用程序。
|
10天前
|
安全 Java 调度
python3多线程实战(python3经典编程案例)
该文章提供了Python3中多线程的应用实例,展示了如何利用Python的threading模块来创建和管理线程,以实现并发执行任务。
12 0
|
11天前
|
存储 缓存 Java
JAVA并发编程系列(11)线程池底层原理架构剖析
本文详细解析了Java线程池的核心参数及其意义,包括核心线程数量(corePoolSize)、最大线程数量(maximumPoolSize)、线程空闲时间(keepAliveTime)、任务存储队列(workQueue)、线程工厂(threadFactory)及拒绝策略(handler)。此外,还介绍了四种常见的线程池:可缓存线程池(newCachedThreadPool)、定时调度线程池(newScheduledThreadPool)、单线程池(newSingleThreadExecutor)及固定长度线程池(newFixedThreadPool)。
|
17天前
|
安全 数据库连接 API
C#一分钟浅谈:多线程编程入门
在现代软件开发中,多线程编程对于提升程序响应性和执行效率至关重要。本文从基础概念入手,详细探讨了C#中的多线程技术,包括线程创建、管理及常见问题的解决策略,如线程安全、死锁和资源泄露等,并通过具体示例帮助读者理解和应用这些技巧,适合初学者快速掌握C#多线程编程。
49 0
下一篇
无影云桌面