添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

需求说明:

一个web页面,需要嵌入到App进行文件上传,但是App在一段时间就会息屏,息屏类似于进入后台,会停止App的一些活动。上传大型文件时需要很长时间,经常息屏,所以看下App能不能实现在不改动web代码的前提下让web在后台上传文件。

问题解决的思路。

  • 首先是让App进入后台不被挂起(杀死),可以在后台进行网络请求。
  • 拦截需要上传的文件,再拦截web的文件上传的链接(网络请求拦截),App在本地进行文件上传,上传成功之后告诉网页上传成功了。
  • 补充方法,让屏幕保持常亮 [[UIApplication sharedApplication] setIdleTimerDisabled: YES] 不过要记得在 dealloc 中设置为 NO .
  • 在iOS16下,即使App一直在后台存活,WKWebView会停止网络请求,所以必须进行拦截,然后App进行文件上传操作,然后及时返回给网页,这样就可以保证WKWebView的网络请求存活.

    在iOS17beta3中,不进行拦截的情况下,只要保证App在后台存活,WKWebView可以进行网络请求。后续再iOS17正式版发布之后在进行测试是否真的支持。

    拦截后文件上传有两种方案,

    方案一
    由于拦截到的文件formData丢失,但是可以拦截到上传的 range,也就是文件范围,App对文件进行分段,然后自己组装formData数据进行实时上传。

    截取部分文件代码

    NSFileHandle * readFileHandle;
    //offset偏移量  size长度
    [readFileHandle seekToFileOffset:offset];
    NSData * fileFragmentData = [fileModel.readFileHandle readDataOfLength:size];
    

    方案二:
    App通过AFNetworking进行文件上传,并通知记录上传进度,在拦截到网页的接口时获取网页当前进度,如果App进度超过网页进度,那么直接返回网页,告诉他当前已经上传成功,形成一个“伪同步”。

    例如一个文件10M,通过表单分成10段上传,
    App如果网速很快,1秒上传了8M, 那么在拦截的网页中,前八次请求都返回成功。
    第九次上传,进入隧道,App信号不好了。上传了20秒,那么拦截到web第九次请求就一直等待,直到App请求成功,在进行web接口的响应。

    iOS7以后提供的后台接口模式

    1、Background Audio,这是后台的音频,类似于各种音乐播放器。

    2、Location Services,这是后台的定位,类似于各种地图导航应用。

    3、VoIP,后台语音服务,类似于各种聊天软件。

    4、Newsstand,报刊杂志后台自动下载更新,其能够自动实时更新。

    我选择的是第一种,可以做到无感知。简而言之就是在后台循环播放一段无声地音乐。

    /// 创建音乐播放器
    - (**void**)creatAVAudioSessionObject{
       //设置后台模式和锁屏模式下依然能够播放
       [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback withOptions:AVAudioSessionCategoryOptionMixWithOthers error:**nil**];
       [[AVAudioSession sharedInstance] setActive: **YES** error: **nil**];
       //初始化音频播放器
       NSError *playerError;
       NSURL *urlSound = [[NSURL alloc]initWithString:[[NSBundle mainBundle]pathForResource:@"laojie" ofType:@"mp3"]];
       _audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:urlSound error:&playerError];
       _audioPlayer.numberOfLoops = -1;//无限播放
       _audioPlayer.volume = 0;
    /// 开始播放声音
    - (**void**)startPlayAudioSession{
       **BOOL**  isPlay = [_audioPlayer play];
        NSLog(@"isPlay===%id", isPlay);
    /// 停止播放声音
    - (**void**)stopPlayAudioSession{
       [_audioPlayer stop];
    

    使用方式。

    首先,打开后台播放音乐开关。

    1.在didFinishLaunchingWithOptions中 调用 creatAVAudioSessionObject

    2.在 applicationDidEnterBackground中 调用 startPlayAudioSession

    3.在 applicationWillEnterForeground中 调用 stopPlayAudioSession

    进入后台时,可以写一个倒计时,通过下面代码后台剩余活跃时间。
    [UIApplication sharedApplication].backgroundTimeRemaining

    怎么拦截(Hook)要上传的文件

    添加分类方法,在内部通过Runtime更换掉系统的获取文件的方法。从而拦截从文件系统/相机相册获取的文件。

    具体代码看文件:
    拦截UIImagePickerController
    拦截UIDocumentPickerViewController
    拦截PHPickerViewController(iOS14之后从文件管理里面获取文件使用这个)。

    使用方式:

    1、在viewDidLoadhookDelegate 例如 [UIImagePickerController hookDelegate]
    2、在deallocunHookDelegate

    由于文件过大,所以文件管理使用了 NSFileHandle

    怎么拦截文件上传链接(网络请求)

    iOS11之后系统提供了:WKURLSchemeHandler来拦截WKWenView中的网络请求。

    首先创建一个对象遵守WKURLSchemeHandler代理,用于进行请求的拦截处理。
    .h代码查看
    .m代码查看

    WKWebViewConfiguration * con = [[WKWebViewConfiguration alloc] init];
    con.allowsInlineMediaPlayback = YES;
    con.allowsPictureInPictureMediaPlayback = YES;
    GSKHFURLSchemeHandler *Scheme = [GSKHFURLSchemeHandler new];
    [con setURLSchemeHandler:Scheme forURLScheme:@"https"];
    _webview = [[WKWebView alloc] initWithFrame:CGRectZero configuration:con];
    

    这里拦截 https 会 crash,因为系统不支持,所以还需要做一下处理。

    给WkWebView添加一个分类,在分类里面通过Runtime,把handlesURLScheme替换成自己的方法,对拦截的https进行单独的处理。

    代码如下:

    #import "WKWebView+SchemeHandle.h"
    #import <objc/runtime.h>
    @implementation WKWebView (SchemeHandle)
    + (void)load {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            Method originalMethod1 = class_getClassMethod(self, @selector(handlesURLScheme:));
            Method swizzledMethod1 = class_getClassMethod(self, @selector(yyhandlesURLScheme:));
            method_exchangeImplementations(originalMethod1, swizzledMethod1);
    + (BOOL)yyhandlesURLScheme:(NSString *)urlScheme {
        if ([urlScheme isEqualToString:@"http"] || [urlScheme isEqualToString:@"https"] || [urlScheme isEqualToString:@"file"]) {
            return NO;  //这里让返回NO,应该是默认不走系统断言或者其他判断啥的
        } else {
            return [self handlesURLScheme:urlScheme];
    

    到这里就基本结束了。

    © 版权声明
    THE END
    喜欢就支持一下吧
    点赞0