使用新浪微博开放平台api同步微博内容至自己网站(用MBProgressHUD强制给界面设置一个“遮罩”(组图) )

优采云 发布时间: 2022-03-30 18:02

  使用新浪微博开放平台api同步微博内容至自己网站(用MBProgressHUD强制给界面设置一个“遮罩”(组图)

)

  特征

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

  

  进入后:

  

  这个功能实现起来并不难。说白了就是依次调用各个开放平台的“发布”相关API就够了。但是,为用户提供更好的“用户体验”涉及到几个要求,所以不得不使用多线程。要求如下:

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

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

  注意:分享心情界面实际上实现了一个*敏*感*词*的发布界面,可以同步推送,也可以选择你要推送的平台。为了给用户提供方便,点击各个平台(如新浪微博)后,都有单独的发布界面发布到平台,提供更*敏*感*词*,如“@、##”等,见下图。

  

  原创处理

  针对以上需求(1),我原来的做法是使用MBProgressHUD在当前界面强制加一个“遮罩”,让用户点击右上角的发帖按钮后,后退按钮不可点击。目的是停留在当前界面等待各种开放平台提供的“发布成功”、“发布失败”等回调协议的执行,并在回调协议中通过提示信息提示用户是否成功在状态栏中。

  如果返回按钮是可点击的,用户可能会选择发布完成后立即返回,那么当前的 UIViewController 可能已经被释放,回调时应用会崩溃。当然,如果使用MBProgressHUD持有主界面,用户只能等待,而回调协议执行的时机其实取决于很多不确定因素(比如当前的网速,如果有图的话,也要看图片的大小),以及各个开放平台对API调用的响应速度等),可能很快,也可能要等几十秒。一旦你测试了用户的耐心,你就是在为自己埋下一颗定时*敏*感*词*。

  对于需求(2),关闭速度比需求(1))要快。我需要让用户在发布完成后立即关闭发布界面。所以当前的UIViewController等不及了对于回调协议的调用,根本无法释放,无法实现状态栏提示,当然折衷的方法是使用MBProgressHUD显示“请稍候……”,强制界面无法响应用户,并选择在回调完成时关闭。

  从上面的分析可以看出当前的处理方式,希望回调协议与当前发布界面的UIViewController对象的生命周期无关。那么这些“发布”操作只能在主线程(UI线程)上执行,也实现了发布操作与发布函数接口的解耦。

  设计和代码实现

  在 iOS 中有几种实现多线程的方法。这里使用了 NSOperation 和 NSOperationQueue。

  UML图:

  

  大致代码如下:

  发布操作:

  #import

#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

  新浪微博发布运营

  #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]initWithA*敏*感*词*ey:kWBSDKDemoA*敏*感*词*ey

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];

}

  人人发布操作

  #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];

  }

  腾讯微博发布运营

  #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方法中代码的主要目的是保存当前的RunLoop(一般来说就是阻塞当前的执行上下文,直到回调协议执行完毕)。可以看出还是存在回调协议无法执行的问题。为什么会发生这种情况,为什么要保留执行上下文?因为各个平台的API通常都是异步发送请求的(腾讯微博除外),等到有响应后再执行实现的协议。所谓异步,其实是操作系统在后台用另一个线程来处理的。NSOperation的执行方式是在执行完main方法中实现的逻辑后退出,返回到主线程。所以通常情况下,这里的回调协议仍然没有 t 获得执行的机会(实际上获得了机会,但是当前的NSOperation对象已经被释放,导致内存访问错误)。所以这里用了一个while循环来保存当前的主runloop,直到各个平台的异步回调协议完成,然后设置completed或者cancelAfterError为YES退出while循环。请注意,这两个属性是异步设置的,即由另一个线程设置。这里解释一下[super main]之后;在每个平台上调用方法,如果您不在回调协议中(或在另一个线程中),则无法设置这两个属性。因为[超级主];这句话阻塞了当前执行 NSOperation 的线程。所以这里用了一个while循环来保存当前的主runloop,直到各个平台的异步回调协议完成,然后设置completed或者cancelAfterError为YES退出while循环。请注意,这两个属性是异步设置的,即由另一个线程设置。这里解释一下[super main]之后;在每个平台上调用方法,如果您不在回调协议中(或在另一个线程中),则无法设置这两个属性。因为[超级主];这句话阻塞了当前执行 NSOperation 的线程。所以这里用了一个while循环来保存当前的主runloop,直到各个平台的异步回调协议完成,然后设置completed或者cancelAfterError为YES退出while循环。请注意,这两个属性是异步设置的,即由另一个线程设置。这里解释一下[super main]之后;在每个平台上调用方法,如果您不在回调协议中(或在另一个线程中),则无法设置这两个属性。因为[超级主];这句话阻塞了当前执行 NSOperation 的线程。这里解释一下[super main]之后;在每个平台上调用方法,如果您不在回调协议中(或在另一个线程中),则无法设置这两个属性。因为[超级主];这句话阻塞了当前执行 NSOperation 的线程。这里解释一下[super main]之后;在每个平台上调用方法,如果您不在回调协议中(或在另一个线程中),则无法设置这两个属性。因为[超级主];这句话阻塞了当前执行 NSOperation 的线程。

  顺便吐槽一下腾讯微博开放平台。在他们开放的框架中,发出的请求都是同步获取的,并且没有定义任何回调协议,这也让我很困扰。所以腾讯微博运营中的实现会与新浪和人人不同。因为调用是同步的,所以在腾讯微博PublishOperation结束时直接调用[super main]是没有用的(因为会在回调协议之后执行,所以这里不需要调用),这里调用的是线程的Sleep方法, 让线程休眠 5 秒,也就是将当前的 NSOperation 上下文延迟 5 秒。延迟是因为在状态栏中显示提示信息需要2秒。如果没有延迟,那么完成后设置为YES,NSOperation会被执行并退出,

  /*

*状态栏显示完成提示信息

*/

-(void)showOverlayMsg:(NSString*)msg andDuration:(NSTimeInterval)duration andOverlay:(MTStatusBarOverlay*)overlay{

[overlay postImmediateFinishMessage:msg

duration:duration animated:YES];

}

  你在应用的AppDelegate中定义一个NSOperationQueue来执行NSOperation,可以完成发布操作与发布功能接口的Controller的解耦。这样,Controller 可以在发布完成后直接关闭和发布,并且由于对功能的封装,成为一个独立的、可复用的整体。在“Share Mood”功能界面中,如果同步推送多个平台,只需要将各个平台的“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];

}

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线