您的位置:首页 > 运维架构

研究Openshare的原理

2015-07-22 15:45 190 查看

内容来源:

http://www.gfzj.us/series/openshare/

https://github.com/100apps/openshare

起因

因为项目需要同时使用4家的分享。QQ,微信,新浪微博和腾讯微博。按照传统的方法,去各个官方平台的开发者网站,下载SDK,然后集成进去。这样做会导致最后打包的app体积增大不少,而且每个平台API使用方法都不统一,研究每个平台分享、登录功能,也浪费了不少时间。用shareSDK这样类库,也要引入很多的包和库。

终于我发现一个开源的项目openshare,号称100多行代码就可以完成这个任务。

原理

平时一个http的请求有post和get两种。

get就是拼一条url然后发出请求,缺点就是参数都是可见,且数据量有限。

post请求是把参数当成一种请求,提交给服务器。

ps:GET方式提交的数据最多只能是1024字节,理论上POST没有限制,可传较大量的数据,起限制作用的是服务器的处理能力。

经过研究发现各个厂商的分享是先通过openUrl去请求一个Uri,从而可以跳到他们的应用里面去。(比如微信就是浏览器输入:weixin:// 就可以打开微信。)

然后用类似于post的请求来做这个事。post的参数在哪里呢?他们放在iOS系统的粘贴板里。

那我们观察一下是不是。

针对以下的函数hook一下,然后观察分享时他们的值。

1)、对UIApplication的openURL:

2)、pasteboardWithName:create:

3)、粘贴板setData:forPasteboardType:

4)、粘贴板 dataForPasteboardType:


看例子:testSwizzle,主要的代码这里贴一下

//对UIApplication的openURL:方法进行hook
-(void)swizzleOpenUrl{
SEL openUrlSEL=@selector(openURL:);
BOOL (*openUrlIMP)(id,SEL,id) =(BOOL(*)(id,SEL,id))[UIApplication instanceMethodForSelector:openUrlSEL];
static int count=0;
BOOL (^myOpenURL)(id SELF,NSURL * url)=^(id SELF,NSURL *url){
NSLog(@"\n----------open url: %d----------\n%@\n%@\n",count++,url,@"\n"/*[NSThread callStackSymbols]*/);

return (BOOL)openUrlIMP(SELF,openUrlSEL,url);
};
class_replaceMethod([UIApplication class], openUrlSEL, imp_implementationWithBlock(myOpenURL), NULL);
}
//pasteboardWithName:create:方法进行hook,注意这是一个类方法
-(void)swizzlePasteboard{
SEL pasteboardWithNameSEL=@selector(pasteboardWithName:create:);
UIPasteboard* (*pasteboardWithNameIMP)(id,SEL,id,BOOL) =(UIPasteboard* (*)(id,SEL,id,BOOL))[UIPasteboard methodForSelector:pasteboardWithNameSEL];

static int count=0;
UIPasteboard* (^mypasteboardWithName)(id SELF,NSString *name,BOOL create)=^(id SELF,NSString *name,BOOL create){
NSLog(@"\n----------pasteboardWithName: %d----------\n%@\n%d\n",count++,name,create);
return (UIPasteboard*)pasteboardWithNameIMP(SELF,pasteboardWithNameSEL,name,create);
};
class_replaceMethod(/*类方法hook http://stackoverflow.com/a/3267898/3825920*/object_getClass((id)[UIPasteboard class]), pasteboardWithNameSEL, imp_implementationWithBlock(mypasteboardWithName), NULL);
}

//粘贴板setData:forPasteboardType:
-(void)swizzlePasteboardSetData{
SEL swizzlePasteboardSetDataSEL=@selector(setData:forPasteboardType:);
void (*swizzlePasteboardSetDataIMP)(id,SEL,id,id)=(void(*)(id,SEL,id,id))[UIPasteboard instanceMethodForSelector:swizzlePasteboardSetDataSEL];

static int count=0;
void (^mypasteboardSetData)(id SELF,NSData *data,NSString *type)=^(id SELF,NSData *data,NSString *type){

NSLog(@"\n----------swizzlePasteboardSetData: %d----------\n%@\n%@\n%@\n",count++,[((UIPasteboard *)SELF) name], type,[NSPropertyListSerialization propertyListWithData:data options:0 format:0 error:nil]);
swizzlePasteboardSetDataIMP(SELF,swizzlePasteboardSetDataSEL,data,type);
};
class_replaceMethod([UIPasteboard class], swizzlePasteboardSetDataSEL, imp_implementationWithBlock(mypasteboardSetData), NULL);
}

//粘贴板 dataForPasteboardType:
-(void)swizzlePasteboardGetData{
SEL swizzlePasteboardGetDataSEL=@selector(dataForPasteboardType:);
NSData* (*swizzlePasteboardGetDataIMP)(id,SEL,id)=(NSData*(*)(id,SEL,id))[UIPasteboard instanceMethodForSelector:swizzlePasteboardGetDataSEL];

static int count=0;
NSData* (^mypasteboardGetData)(id SELF,NSString *type)=^(id SELF,NSString *type){//
NSData *ret=(NSData*)swizzlePasteboardGetDataIMP(SELF,swizzlePasteboardGetDataSEL,type);
NSLog(@"\n----------pasteboardGetData: %d----------\n%@\n%@\n%@\n%@",count++,[((UIPasteboard *)SELF) name], type,ret,ret);
return ret;
};
class_replaceMethod([UIPasteboard class], swizzlePasteboardGetDataSEL, imp_implementationWithBlock(mypasteboardGetData), NULL);
}


通过以上例子,这时我们就可以通过自己模拟一个这样的请求,也把参数放到粘贴板里面,从而达到像SDK一样的分享效果。

分享后,状态的返回也是利用粘贴板。可以看例子。

经过作者的研究,返回的数据格式为两种序列化方式:
NSData *output=[NSKeyedArchiver archivedDataWithRootObject:data];
NSDictionary *dic=[NSKeyedUnarchiver unarchiveObjectWithData:output;

NSData *output=[NSPropertyListSerialization dataWithPropertyList:data format:NSPropertyListBinaryFormat_v1_0 options:0 error:&err];
NSDictionary *dic=[NSPropertyListSerialization propertyListWithData:output];


总结:

整个分享的步骤为:

1. A程序通过Uri跳转到对应的分享程序B里。

2. 在B里面,他读取从粘贴板里的数据和根据uri做对应的分享处理。

3. 分享完,B把分享的状态结果也放到粘贴板里。然后,根据A之前设好的uri,又跳回到A应用中。

4. 然后A把粘贴板的数据读出来,就知道分享是成功还是失败了。

缺点就是当本地没有装要分享的程序时,无任何响应。

PS:
一种新的监控mac/iOS模拟器中runtime message的方法,这样在mac电脑的/tmp/下目录就能生成/tmp/msgSends-1234类似的文件了。可以用tail -f查看。

#import <objc/runtime.h>
void instrumentObjcMessageSends();

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
instrumentObjcMessageSends(YES);//设置为NO可以关闭。
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: