研究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可以关闭。 }
相关文章推荐
- linux日志服务器搭建
- linux日志服务器搭建
- 执行shell命令
- Linux系统防止黑客NMAP扫描的方法
- Linux read()函数
- Apache与Tomcat
- opencv内存不足问题
- Flume对Nginx群集日志收集方案
- tomcat中文参数乱码
- BASH中的内置变量
- linux c find grep awk
- 构建高可扩Web架构和分布式系统实战(转载)
- Hadoop学习笔记系列文章导航【持续更新中...】
- WindowsAzure 平台重置Linux密码
- linux c程序获取cpu使用率及内存使用情况关闭
- 高可用Hadoop平台-Flume NG实战图解篇
- Linux文件目录详解
- linux 中特殊符号用法详解
- tomcat中修改server.xml设置虚拟目录后,再删掉那部分内容,虚拟路径还是能访问的。
- java使用ganymed-ssh2执行linux命令