http://www.gfzj.us/series/openshare/
https://github.com/100apps/openshare
因为项目需要同时使用4家的分享。QQ,微信,新浪微博和腾讯微博。按照传统的方法,去各个官方平台的开发者网站,下载SDK,然后集成进去。这样做会导致最后打包的app体积增大不少,而且每个平台API使用方法都不统一,研究每个平台分享、登录功能,也浪费了不少时间。用shareSDK这样类库,也要引入很多的包和库。
终于我发现一个开源的项目openshare,号称100多行代码就可以完成这个任务。
平时一个http的请求有post和get两种。
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);
}
经过作者的研究,返回的数据格式为两种序列化方式:
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
void instrumentObjcMessageSends();
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{
instrumentObjcMessageSends(YES);//设置为NO可以关闭。
}