[转载]IOS监测其他APP是否打开的思路

之所以写这篇文章是因为碰到一个问题,因为最近要做一个app去鼓励用户下载其他的app,所以需要我们去监测用户是否下载了指定的软件并且运行试玩了,重点就是我们的软件在用户点击去appstore下载之后是在后台运行的,软件状态就是在后台运行情况下去监测其他app的安装运行,因为ios是沙盒运行,所以自己的app去检测其他软件肯定是被苹果禁止的,现在总结下曲线救国的一点思路。

一、获取所有已经安装的软件

可以参考这个文章《ios开发之UIDevice使用总结》中的方案,可以获取到手机中所有已经安装的软件。

通过这个方法可以获得所有已经安装的软件和路径

//这个头文件记得包含,不然有可能会报objc_getClass没定义的错误

#include 

Class LSApplicationWorkspace_class = objc_getClass("LSApplicationWorkspace");

SEL selector=NSSelectorFromString(@"defaultWorkspace");

NSObject* workspace = [LSApplicationWorkspace_class performSelector:selector];

SEL selectorALL = NSSelectorFromString(@"allApplications");

NSLog(@"apps: %@", [workspace performSelector:selectorALL]);

得到的结果是每个软件的budleid和路径

    " com.tencent.xin ",

    " com.tencent.mipadqq ",

    " com.apple.MobileReplayer ",

    " com.jdnet.kuaifa ",

    " damon.testsssss ",

    " com.electriclabs.IOKitBrowser "

但是这个只是得到了所有已经安装的软件

二、判断是否有指定的软件

如果想获取某个软件安装没有,可以参考这个文章《IOS的软件之间的调用(URL Schemes)》中的,通过调用scheme的来获取又没有

NSString * url = @"damon://ssss";

if ([[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:url]]) {

     [[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];

}

如果canOpenURL返回值为true,那么就是安装了这个软件,但是前提是需要知道软件的scheme,否则这个方法是不可以的,并不是每个软件都有scheme,需要和他们的开发确认。测试了下,如果你的app在后台运行状态下去调用其他app,比如微信,那么canOpenURL会在有微信的情况下立马返回值为true,但是openURL并不会返回true,只有当你的app转为活跃状态才可以,就是只有当你的app从后台切换到前台,oepnURL才有效。

总结

通过第一步和第二步可以知道软件的安装与否,如果之前没有,后面有了就知道是新安装的,如果没有,就是还没有安装,如果原来就有了,那就是老软件了,就是用户之前下载过的软件。

三、获取软件是否已经运行

这个是一个难点,搜索了很多都没有解决方案,这个也是我们软件最重要的一步判断,当然如果对方app在启动时可以自己调用我们的app,或者给我们服务器发送一个请求,那是最简单的,但是现在就是想在不修改对方软件包的前提下去获得软件运行状况,所以这个有点蛋疼。

所以思路是这样的

1、通过软件后台的进程来判断

2、通过私有库来判断

3、通过文件读写来判断

4、通过网络请求来判断

5、通过网页调用来判断

系统在ios9.0以下可以获得进程(只适合ios9.0以下)

如果手机的系统在ios9.0以下,那么可以参考这个文章《ios开发之UIDevice使用总结》里面,通过sys/sysctl.h这系统方法来获取软件的进程,从而得到这个软件是否运行,但是现在ios10.2都发布了,所以如果只想让软件给ios9.0以下的用户用不现实,所以这个方案是有缺陷的。

通过私有库来判断(ios9测试无效果)

网上有一个使用FrontBoard.framework这个库的方案《iOS私有库的正确使用》,这个库的路径在/System/Library/PrivateFrameworks/FrontBoard.framework/FrontBoard这个,但是这个库并没有,所以在github上搜了下,在github上面搜索到了

Github下载:

https://github.com/nst/iOS-Runtime-Headers

https://github.com/nst/RuntimeBrowser/

但是看这个库下面的介绍,ios8.0之上好像也不支持了,通过调用发现读取不到这个库

NSBundle *b = [NSBundle bundleWithPath:@"/System/Library/PrivateFrameworks/FrontBoard.framework/FrontBoard"];

//    NSBundle *b = [NSBundle bundleWithPath:@"/Users/damon/github/iOS-Runtime-Headers/PrivateFrameworks/FrontBoard.framework/FrontBoard"];

    BOOL success = [b load];

    NSLog(@"%d",success);

    Class FBProcessManager = NSClassFromString(@"FBProcessManager");

    id manager = [FBProcessManager valueForKey:@"sharedInstance"];

在ios9上面,我用ipad和iphone测试,读取库的log一直失败,所以估计这个库也不行了

通过文件读写来判断(ios10已失效)

这个方法是使用IOKIT.Framework这个库,来读取软件是否读取了资源来判断,参考问答《ios9屏蔽了sysctl方法,现在有什么办法可以获取进程呢?》。

这个库有一个demo,demo的github地址:https://github.com/matthiasgasser/IOKitBrowser

demo说明地址:http://www.lyonanderson.org/blog/2014/02/12/ios-iokit-browser/

通过这个demo可以获取到后台有操作的程序的进程pid和工程的target名字

但是网友测试

既然是IOKit ,我推断目标app必须要有IO动作才会被这个IOKit扫出来。拿一个空壳APP做测试,发现:

1。进行写文件操作。 结果 : 扫不出。

2。进行读文件操作。 结果 : 扫不出。

3。加载一个webview。 结果 : 扫出。

4。加载一个UIImageView并赋上图。 结果 : 扫出。

我试了下,使用[[NSUserDefaults standardUserDefaults] setObject:@"sss" forKey:@"sss"];存数据也读取不到,给button赋值等,都不行,所以这个虽然可以扫出一部分,但是还是有局限的。

通过网络请求(仅限于思路)

这个只是我在想,但是并没有实施方案,我的思路就是能不能抓包来获取哪个app发送了数据请求,从而判断这个app在后台运行,但是这个我现在也没有想好怎么做,仅仅是一个思路,但是根据实际情况来看,好像单从网络请求来说是判断不出来哪个请求的。

通过网页调用来判断

网页调用这个方法就是通过oc去调用html的网页,通过网页逻辑来实现调用软件的方案,

有用一个a标签同样跳转app的scehemes方式去手动调用,但是这种手动调用是要用户点击的,不是去自动打开的,所以不符合要求。


打开APP

使用苹果官方的在meta信息中添加字段也是需要用户手动点击,并且仅限于safari浏览器,也是不符合要求的

meta"apple-itunes-app"content"app-id=myAppStoreID, affiliate-data=myAffiliateData, app-argument=myURL"

所以大部分逻辑都是类似于这样调用

window.location = 'weixin://';

setTimeout(function() {

    window.location = 'itms-apps://itunes.apple.com/cn/app/nuan-dao/id222222?mt=8'

}, 30);

通过app的schemes码去调用,或者使用frame去调用app

//创建一个隐藏的iframe

var ifr = document.createElement('iframe');

ifr.src = 'com.baidu.tieba://';

ifr.style.display = 'none';

document.body.appendChild(ifr);

//记录唤醒时间

var openTime = +new Date();

window.setTimeout(function(){

    document.body.removeChild(ifr);

    //如果setTimeout 回调超过2500ms,则弹出下载

    if( (+new Date()) - openTime > 2500 ){

        window.location = 'http://exam.com/xxxx.apk';

    }

},2000)

但是经过测试这些方案在ios9.0之后,苹果增加了确认机制,都会弹出一个提示框,只有用户点击确认之后才会去打开对应的app,否则就直接超时打开失败。

Universal Links方式

Universal Links是另外一个可以想到的解决方案,在apple的官方说明中,Universal Links是一种能够方便的通过传统 HTTP 链接启动应用程序, 使用相同的网址打开网站和App。通过一个通用的链接便可实现,当移动设备里面已经有了某个应用,在点击了这个链接后便可实现深度链接而直接进入应用内的某个特定页面;如果手机内并没有该应用,可打开设定的网址(例如应用的落地下载页面)。可以不用提供app的scheme码,并且会自动判断手机有没有该软件,一个链接就搞定了app和网站。

但是这个看了开发过程之后,发现还是不可行,有一步是在 Xcode的capabilities里添加你的 APP 域名, 必须用applinks: 前置它,意思就是去修改别人的软件,并不是只用修改自己的软件就可以搞定的,其实已经算跑题了。

通过后台不停调用scheme方式

我们的需求是判断软件是否打开,但是并没有强调运行期间的动作,考虑到判断仅仅是打开了,所以想到了后台定时调用scheme,定时判断手机里面有没有这个指定的app,这样的话只要我们的app检测到手机存在其他app,就自动打开他,打开之后发送请求标识已经打开

这样的话就是后台运行时定时,比如2秒,去一直调用打开,比如微信

[[UIApplication sharedApplication] openURL:[NSURL URLWithString:@"weixin://"]];

但是发现在后台运行状态下,这个函数一直返回的是false,一直打开失败

但是判断有没有微信这个函数却在安装微信之后,立马返回true

[[UIApplication sharedApplication] canOpenURL:[NSURL URLWithString:@"weixin://"]]

但是这个canOpenUrl函数在ios9之上只能加50个白名单,所以也是很无奈

暂时采用的方案在前台调用scheme方式

因为openURL方法在前台调用才可以,所以最后我们还是决定做一个按钮,去让手动提交了。

因为如果在前台自动调用的话,会在手机有该软件的情况下自动打开,比如用户在用其他无关应用的时候,会自动切换出来,影响用户体验。

比如我们的软件a让用户下载软件b,用户下载软件b的同时在使用软件c,但是一旦软件b下载完毕,软件a就自动检测到b安装了,直接打开b,这样用户就从c切换出来了,所以不完美,但是这个是现在不去修改软件b包的前提下,知道的最佳的方案了。

四、参考文章

ios开发之UIDevice使用总结

IOS的软件之间的调用(URL Schemes)

iOS私有库的正确使用

ios9屏蔽了sysctl方法,现在有什么办法可以获取进程呢?

Is it possible to get information about all apps installed on iPhone?

使用 Charles 抓取 App 网络请求

About Apple URL Schemes

Promoting Apps with Smart App Banners

iOS使用schema协议调起APP

iOS IOKit Browser

Mobile Safari调用本地App, 否则进入App Store下载

从浏览器或者Webview 中唤醒APP

使用通用链接(Universal Link)打通深度链接 - 适用iOS 9或者以上版本

[推荐]腾讯云云服务器新购特惠2折起!

版权属于:胡东东博客

本文链接:http://www.hudongdong.com/talk/379.html

交流QQ群:点击加入APP推广运营群

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,881评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,052评论 1 301
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,598评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,407评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,823评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,872评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,037评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,778评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,505评论 1 247
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,745评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,233评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,231评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,141评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,939评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,954评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,784评论 2 275

推荐阅读更多精彩内容

  • 第一次见到那场熊熊大火,他约莫十五六岁,远远地观望那慎人的火势,把天都烧出了个窟窿来。 周围的环境是如此熟悉,这儿...
    迦渡阅读 337评论 3 6
  • 2017年4月21号去扬州跑鉴真半程马拉松,途中遇到邻座的姐姐,一路上相谈甚欢,聊饮食、健身、跑步、家庭、工作,还...
    lovejasmine阅读 207评论 1 1
  • 一、 dumpsys命令 1.1 服务列表 不同的Android系统版本支持的命令有所不同,可通过下面命令查看当前...
    tiger桂阅读 2,082评论 1 0
  • 能克服脆弱, 明确自己是谁, 想要什么, 应该怎么做, 就会非常坚定, 对生活充满掌控感。 -完- ©严正法律声明...
    更好时代阅读 87评论 0 0