轻量级社会化分享openShare源码解析
####開篇 關于社會化分享,一般用友盟比較多,但是也有其他的實現方式,這里介紹一下 openShare ,可以不利用官方SDK,直接進行分享。和友盟相比包小了太多,不過貌似沒法統計,各有特色吧。 ####正文
如上圖openShare的整體結構主要分為兩大部分,openShare 和各大平臺的分類。每個平臺都去擴展OpenShare的類方法,來很好的保證平臺的增加和整體功能的完善等。 我們通過新浪微博和QQ的登錄和分享來介紹openShare的使用以及對源碼的實現方法的理解。 ####新浪微博分享 #####AppDelegate配置 首先導入頭文件并注冊相關的key //第一步:注冊key[OpenShare connectQQWithAppId:@"1103194207"];[OpenShare connectWeiboWithAppKey:@"402180334"];[OpenShare connectWeixinWithAppId:@"wxd930ea5d5a258f4f"];[OpenShare connectRenrenWithAppId:@"228525" AndAppKey:@"1dd8cba4215d4d4ab96a49d3058c1d7f"];[OpenShare connectAlipay];//支付寶參數都是服務器端生成的,這里不需要key.復制代碼然后設置分享的回調方法代碼如下,這里等下分析OpenShare+Weibo文件的時候詳細再說。
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation{//第二步:添加回調if ([OpenShare handleOpenURL:url]) {return YES;}//這里可以寫上其他OpenShare不支持的客戶端的回調,比如支付寶等。return YES; }復制代碼#####ViewController中的調用 在示例代碼中登錄和分享的調用方法如下:
UIView *ret=[[UIView alloc] initWithFrame:frame];UIButton *auth=[self button:@"登錄" WithCenter:CGPointMake(frame.size.width/2, 40)];[ret addSubview:auth];[auth addEventHandler:^(id sender) {[OpenShare WeiboAuth:@"all" redirectURI:@"http://openshare.gfzj.us/" Success:^(NSDictionary *message) {ULog(@"微博登錄成功:\n%@",message);} Fail:^(NSDictionary *message, NSError *error) {ULog(@"微博登錄失敗:\n%@\n%@",message,error);}];} forControlEvents:UIControlEventTouchUpInside];UIButton *textShare=[self button:@"分享純文本" WithCenter:CGPointMake(auth.center.x, calcYFrom(auth)+40)];[ret addSubview:textShare];textShare.tag=1001;[textShare addTarget:self action:@selector(weiboViewHandler:) forControlEvents:UIControlEventTouchUpInside];UIButton *imgShare=[self button:@"分享圖片" WithCenter:CGPointMake(auth.center.x, calcYFrom(textShare)+40)];[ret addSubview:imgShare];imgShare.tag=1002;[imgShare addTarget:self action:@selector(weiboViewHandler:) forControlEvents:UIControlEventTouchUpInside];UIButton *newsShare=[self button:@"分享新聞" WithCenter:CGPointMake(auth.center.x, calcYFrom(imgShare)+40)];[ret addSubview:newsShare];newsShare.tag=1003;[newsShare addTarget:self action:@selector(weiboViewHandler:) forControlEvents:UIControlEventTouchUpInside];//微博分享的實現方法 -(void)weiboViewHandler:(UIButton*)btn{OSMessage *message=[[OSMessage alloc]init];message.title=@"hello openshare (message.title)";if (btn.tag>=1002) {message.image=testImage;}if (btn.tag==1003) {message.link=@"http://openshare.gfzj.us/";}[OpenShare shareToWeibo:message Success:^(OSMessage *message) {ULog(@"分享到sina微博成功:\%@",message);} Fail:^(OSMessage *message, NSError *error) {ULog(@"分享到sina微博失敗:\%@\n%@",message,error);}]; }復制代碼這里首先先說一下登錄的事件添加方式
[auth addEventHandler:^(id sender) {[OpenShare WeiboAuth:@"all" redirectURI:@"http://openshare.gfzj.us/" Success:^(NSDictionary *message) {ULog(@"微博登錄成功:\n%@",message);} Fail:^(NSDictionary *message, NSError *error) {ULog(@"微博登錄失敗:\n%@\n%@",message,error);}];} forControlEvents:UIControlEventTouchUpInside];復制代碼方法的添加是由下圖中的UIControl+Blocks文件中實現的
在.h中創建了- (void)addEventHandler:(ActionBlock)handler forControlEvents:(UIControlEvents)controlEvents; 以及ActionBlock。我們點進.m,可以看到主要由兩個方法組成代碼如下: - (void)addEventHandler:(ActionBlock)handler forControlEvents:(UIControlEvents)controlEvents {objc_setAssociatedObject(self, &UIButtonHandlerKey, handler, OBJC_ASSOCIATION_COPY_NONATOMIC);[self addTarget:self action:@selector(callActionHandler:) forControlEvents:controlEvents];}; } - (void)callActionHandler:(id)sender {ActionBlock handler = (ActionBlock)objc_getAssociatedObject(self, &UIButtonHandlerKey);if (handler) {handler(sender);} }復制代碼首先
objc_setAssociatedObject(self, &UIButtonHandlerKey, handler, OBJC_ASSOCIATION_COPY_NONATOMIC); 復制代碼為runtime的動態屬性的添加,各個參數對應的解釋如下
* @param self 需要添加關聯的對象* @param UIButtonHandlerKey 添加的唯一標識符* @param handler 關聯的對象* @param OBJC_ASSOCIATION_COPY_NONATOMIC 關聯的策略,是個枚舉復制代碼這句話可以理解為,以OBJC_ASSOCIATION_COPY_NONATOMIC的關聯策略為自己添加一個標識符為UIButtonHandlerKey的handler對象。 點進這句話我們可以看到系統對應的關聯策略的枚舉,有以下幾種形式
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {OBJC_ASSOCIATION_ASSIGN = 0, //< Specifies a weak reference to the associated object.OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, //< Specifies a strong reference to the associated object.The association is not made atomically.OBJC_ASSOCIATION_COPY_NONATOMIC = 3, //< Specifies that the associated object is copied.The association is not made atomically.OBJC_ASSOCIATION_RETAIN = 01401, //< Specifies a strong reference to the associated object.The association is made atomically.OBJC_ASSOCIATION_COPY = 01403 //< Specifies that the associated object is copied.The association is made atomically. }; 復制代碼OK,接下來在看對應的添加的callActionHandler事件中的代碼
ActionBlock handler = (ActionBlock)objc_getAssociatedObject(self, &UIButtonHandlerKey);if (handler) {//判斷block是否為null 防止crashhandler(sender);}復制代碼ActionBlock通過標示符UIButtonHandlerKey,得到的之前的管理屬性,然后判斷如果block不為空,進行下一步的操作。
#####OpenShare+Weibo 我們從調用處著手開始分析一下源碼中的一些方法。登錄和分享,最先調起的兩個方法分別為: 登錄
+(void)WeiboAuth:(NSString*)scope redirectURI:(NSString*)redirectURI Success:(authSuccess)success Fail:(authFail)fail;復制代碼分享調起方法
+(void)shareToWeibo:(OSMessage*)msg Success:(shareSuccess)success Fail:(shareFail)fail;復制代碼點擊這兩個方法我們首先進入到的是OpenShare+Weibo,一個專門用來處理微博的分類中,我們可以看到一共有以下4個方法:
+(void)connectWeiboWithAppKey:(NSString *)appKey; +(BOOL)isWeiboInstalled; /*** 分享到微博,微博只支持三種類型:文本/圖片/鏈接。根據OSMessage自動判定想分享的類型。** @param msg 要分享的msg* @param success 分享成功回調* @param fail 分享失敗回調*/ +(void)shareToWeibo:(OSMessage*)msg Success:(shareSuccess)success Fail:(shareFail)fail;/*** 微博登錄OAuth** @param scope scope,如果不填寫,默認是all* @param redirectURI 必須填寫,可以通過http://open.weibo.com/apps/402180334/info/advanced編輯(后臺不驗證,但是必須填寫一致)* @param success 登錄成功回調* @param fail 登錄失敗回調*/ +(void)WeiboAuth:(NSString*)scope redirectURI:(NSString*)redirectURI Success:(authSuccess)success Fail:(authFail)fail; 復制代碼分別用來注冊微博相關的key,判斷是否安裝以及主要的分享登錄。 平臺和key的設置
每個分類中設置不同的schema,區分平臺 +(void)connectWeiboWithAppKey:(NSString *)appKey{[self set:schema Keys:@{@"appKey":appKey}]; } 調用openShare的設置方法 +(void)set:(NSString*)platform Keys:(NSDictionary *)key{if (!keys) {keys=[[NSMutableDictionary alloc] init];}keys[platform]=key; } 復制代碼- 分享的方法 下面通過分享方法,介紹一下核心的分享的實現方式
通過代碼我們可以看到主要步驟為:判斷參數是否存在-->設置分享方式 -->設置messageData并歸檔信息-->把數據放到粘貼板上 -->打開網址( [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]]; 的方式) 下面我們分析是如何設置分享方式的,代碼如下:
if ([msg isEmpty:@[@"link" ,@"image"] AndNotEmpty:@[@"title"] ]) {//text類型分享message= @{@"__class" : @"WBMessageObject",@"text" :msg.title};} 復制代碼msg代表的OSMessage對象包含內容如下:
@interface OSMessage : NSObject @property NSString* title; @property NSString* desc; @property NSString* link; @property UIImage *image; @property UIImage *thumbnail; @property OSMultimediaType multimediaType; //for 微信 @property NSString* extInfo; @property NSString* mediaDataUrl; @property NSString* fileExt; @property (nonatomic, strong) NSData *file; /// 微信分享gif/文件 /*** 判斷emptyValueForKeys的value都是空的,notEmptyValueForKeys的value都不是空的。** @param emptyValueForKeys 空值的key* @param notEmptyValueForKeys 非空值的key** @return YES/NO*/ -(BOOL)isEmpty:(NSArray*)emptyValueForKeys AndNotEmpty:(NSArray*)notEmptyValueForKeys; @end復制代碼OSMessage是用來保存分享的數據信息的對象。可以通過其中包含的內容來進行區分分享的內容類型。而其中:-(BOOL)isEmpty:(NSArray*)emptyValueForKeys AndNotEmpty:(NSArray*)notEmptyValueForKeys;介紹如下:
-(BOOL)isEmpty:(NSArray*)emptyValueForKeys AndNotEmpty:(NSArray*)notEmptyValueForKeys{@try {if (emptyValueForKeys) {for (NSString *key in emptyValueForKeys) {if ([self valueForKeyPath:key]) {//valueForKeyPath 可以獲取OSMessage中相同key的元素return NO; }}if (notEmptyValueForKeys) {for (NSString *key in notEmptyValueForKeys) {if (![self valueForKey:key]) {//取OSMessage對象里 key對應的內容 如果不存在返回NOreturn NO;}}}return YES;}@catch (NSException *exception) {NSLog(@"isEmpty error:\n %@",exception);return NO;} }復制代碼方法實現的目的是 判斷emptyValueForKeys的value都是空的,notEmptyValueForKeys的value都不是空的。valueForKeyPath判斷給定的數組中的內容是否有OSMessage中相同key的元素,如果有的話返回NO,走下一種分享模式。valueForKey判斷給定的內容是否也存在于OSMessage的對象中,如果存在返回YES。 對下面代碼中的翻譯為:
if ([msg isEmpty:@[@"link" ,@"image"] AndNotEmpty:@[@"title"] ]) {//text類型分享message= @{@"__class" : @"WBMessageObject",@"text" :msg.title};} 復制代碼如果OSMessage對象中不包含,link,image,只包含title,則設置的分享格式是純文本分享,同時設置分享的字典格式。
[UIPasteboard generalPasteboard].items=messageData; 復制代碼在新浪微博的分享中以上述方式,把要分享的內容放至剪貼板上。
- 回調方法
接下來看回調函數
+(BOOL)Weibo_handleOpenURL{ } 復制代碼主要代碼如下:
if ([url.scheme hasPrefix:@"wb"]) {NSArray *items=[UIPasteboard generalPasteboard].items;NSMutableDictionary *ret=[NSMutableDictionary dictionaryWithCapacity:items.count];for (NSDictionary *item in items) {for (NSString *k in item) {ret[k]=[k isEqualToString:@"transferObject"]?[NSKeyedUnarchiver unarchiveObjectWithData:item[k]]:item[k];}}NSDictionary *transferObject=ret[@"transferObject"];if ([transferObject[@"__class"] isEqualToString:@"WBAuthorizeResponse"]) {//通過反歸檔 取出的類名 登錄//authif ([transferObject[@"statusCode"] intValue]==0) {if ([self authSuccessCallback]) {[self authSuccessCallback](transferObject);}}else{if ([self authFailCallback]) {NSError *err=[NSError errorWithDomain:@"weibo_auth_response" code:[transferObject[@"statusCode"] intValue] userInfo:transferObject];[self authFailCallback](transferObject,err);}}}else if ([transferObject[@"__class"] isEqualToString:@"WBSendMessageToWeiboResponse"]) {//分享//分享回調if ([transferObject[@"statusCode"] intValue]==0) {if ([self shareSuccessCallback]) {[self shareSuccessCallback]([self message]);}}else{if ([self shareFailCallback]) {NSError *err=[NSError errorWithDomain:@"weibo_share_response" code:[transferObject[@"statusCode"] intValue] userInfo:transferObject];[self shareFailCallback]([self message],err);}}}return YES;} 復制代碼主要邏輯為:取出剪貼板上內容 -->根據遍歷循環取出-->根據之前設置的字典格式區分內容-->設置回調信息
一次分享的整體流程
- 01:對應平臺設置key 和相關平臺設置
- 02:對應平臺取值方法
- 03:創建創建要分享的OSMessage對象
-
04:調去分享微博的方法
-
05:分享方法的調起內部實現
- 06:區分分享方式,設置分享的字典格式,內容以items的形式復制至剪貼板
- 07:拼接uuid 打開軟件
- 08 走OpenShare中回調的hook方法確定是否有回調
- 09分享結束回調,取出剪貼板的數據,做回調處理
實現原理
- A程序通過Uri跳轉到對應的分享程序B里。
- 在B里面,他讀取從粘貼板里的數據和根據uri做對應的分享處理。
- 分享完,B把分享的狀態結果也放到粘貼板里。然后,根據A之前設好的uri,又跳回到A應用中。
- 然后A把粘貼板的數據讀出來,就知道分享是成功還是失敗了。
#####hook方法
+(BOOL)handleOpenURL:(NSURL*)openUrl{returnedURL=openUrl;for (NSString *key in keys) {SEL sel=NSSelectorFromString([key stringByAppendingString:@"_handleOpenURL"]);if ([self respondsToSelector:sel]) {NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:sel]];[invocation setSelector:sel]; [invocation setTarget:self];[invocation invoke]; BOOL returnValue;[invocation getReturnValue:&returnValue];if (returnValue) {return YES;}}else{NSLog(@"fatal error: %@ is should have a method: %@",key,[key stringByAppendingString:@"_handleOpenURL"]);}}return NO; }復制代碼我們主要看一下下面這個方法中的內容
+(BOOL)handleOpenURL:(NSURL*)openUrl{ } 復制代碼創建了一個SEL對象,來獲取之前分類中不同的handleOpenURL方法,通過NSSelectorFromString 來判斷 未實現的方法是不是我們想要動態添加的方法如:
+(BOOL)Weibo_handleOpenURL{} 復制代碼如果實現了,利用NSInvocation來直接調用這個消息,在設置萬簽名,和設置對象之后,執行[invocation invoke],然后根據Weibo_handleOpenURL方法中的返回內容通過getReturnValue來確定有無回調方法。
- NSInvocation介紹 NSInvocation對象只能使用其類方法來初始化,不可使用alloc/init方法。它執行調用之前,需要設置兩個方法:setSelector: 和setArgument:atIndex,簡單的NSInvocation使用方法如下:
有返回值無參數類型的函數,此處的打印數值應該是0,因為Test2的返回值是NO。
//有返回值,有參數SEL selector = @selector(Test3:);NSMethodSignature *method3Sign = [[self class] instanceMethodSignatureForSelector:selector];NSInvocation *method3Invocation = [NSInvocation invocationWithMethodSignature:method3Sign];[method3Invocation setTarget: self];[method3Invocation setSelector:selector];NSString *arg1 = @"testArg1";[method3Invocation setArgument:&arg1 atIndex:2];[method3Invocation invoke];// [method3Invocation setReturnValue:&arg1];[method3Invocation getReturnValue:&arg1];NSLog(@".....%@",arg1);-(NSString *)Test3 :(NSString*)a{a= @"122334";NSLog(@"----%@",a);return a; }復制代碼如果是 [method3Invocation getReturnValue:&arg1] 他的作用是獲取函數的返回結果,打印結果為
2017-06-22 16:14:40.850 01234567899[2884:103358] ----122334 2017-06-22 16:14:40.850 01234567899[2884:103358] .....122334復制代碼如果是 [method3Invocation setReturnValue:&arg1] 他的作用是設置函數的返回結果 函數表達式變為如下:
-(NSString *)Test3 :(NSString*)a{NSLog(@"----%@",a);return a; } 復制代碼打印結果為
2017-06-22 16:17:28.261 01234567899[2924:105348] ----testArg1 2017-06-22 16:17:28.261 01234567899[2924:105348] .....testArg1 復制代碼[invocation invoke]; 這個方法中,只要調用invocation的invoke方法,就代表需要執行NSInvocation對象中制定對象的指定方法,并且傳遞指定的參數。 至此我們基本說完了新浪微博分享的整體流程那么我們在大致說一下QQ分享的時候,與新浪微博的區別。 ####QQ分享的區別 調用分享的主要函數舉例如下:
+(void)shareToQQFriends:(OSMessage*)msg Success:(shareSuccess)success Fail:(shareFail)fail{if ([self beginShare:schema Message:msg Success:success Fail:fail]) {[self openURL:[self genShareUrl:msg to:0]];} } 復制代碼主要的區別在genShareUrl中
+(NSString*)genShareUrl:(OSMessage*)msg to:(int)shareTo{ } 復制代碼數據的歸檔并放置到剪貼板的方法為[[UIPasteboard generalPasteboard] setData:data forPasteboardType:key];不同于新浪微博的 [UIPasteboard generalPasteboard].items=messageData;具體實現如下:
/**數據的歸檔 并放置剪貼板*/ +(void)setGeneralPasteboard:(NSString*)key Value:(NSDictionary*)value encoding:(OSPboardEncoding)encoding{if (value&&key) {NSData *data=nil;NSError *err;switch (encoding) {case OSPboardEncodingKeyedArchiver://歸檔方式存儲data=[NSKeyedArchiver archivedDataWithRootObject:value];break;case OSPboardEncodingPropertyListSerialization://序列化data=[NSPropertyListSerialization dataWithPropertyList:value format:NSPropertyListBinaryFormat_v1_0 options:0 error:&err];default:NSLog(@"encoding not implemented");break;}if (err) {NSLog(@"error when NSPropertyListSerialization: %@",err);}else if (data){[[UIPasteboard generalPasteboard] setData:data forPasteboardType:key];///*data類型的數據放在粘貼板中,pasteboardType 類型 字符串*/}} }/**從剪貼板取出數據 并解檔*/ +(NSDictionary*)generalPasteboardData:(NSString*)key encoding:(OSPboardEncoding)encoding{NSData *data=[[UIPasteboard generalPasteboard] dataForPasteboardType:key];//從剪貼板取出數據NSDictionary *dic=nil;if (data) {NSError *err;switch (encoding) {case OSPboardEncodingKeyedArchiver:dic= [NSKeyedUnarchiver unarchiveObjectWithData:data];break;case OSPboardEncodingPropertyListSerialization:dic=[NSPropertyListSerialization propertyListWithData:data options:0 format:0 error:&err];default:break;}if (err) {NSLog(@"error when NSPropertyListSerialization: %@",err);}}return dic; }復制代碼并在打開軟件的過程中使用了base64編碼等,其余的原理大致相同。 到這里,對openShare實現的過程和源碼有了大致的說明,可以開心的使用啦。
####后記 這篇介紹,主要是自己對openShare,實現的一種認識,以及對源碼的理解,可能存在一定的錯誤和理解偏差,希望積極指出。
總結
以上是生活随笔為你收集整理的轻量级社会化分享openShare源码解析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用ASIFormDataRequest
- 下一篇: DeprecationWarning