1. 程式人生 > >iOS元件化之AppDelegate中的常見處理。

iOS元件化之AppDelegate中的常見處理。

  在元件化開發的過程中,在app執行期間,app可能處於前臺,後臺,以及兩個之間的過渡狀態。這些狀態的呈現處理一般是在AppDelegate中進行處理的,但是進行元件化後,如果業務元件中的處理邏輯放在AppDelegate中,你會發現解耦無從做起。針對這種情況我們來梳理下需求。

1,存在業務元件在某些時間段需要進入後臺的處理。以及前後臺切換的處理。

  在實際在開發過程中,比如音樂播放器元件,視訊播放器元件,以及下載或者上傳元件。等等相關的業務元件,在進入後臺後,以及從後臺切換到前臺時,需要進行一些額外 的操作才能有比較好的使用者體驗。因此。這些元件需要在AppDelegate中進行鍼對性的處理。

2,某些業務元件只有在進入元件後才會進行前後臺切換相關針對性的處理,退出元件後不再進行相關針對性的處理。

  以音樂播放器業務元件為例,只有在app進入了音樂播放器元件後,才會執行前後臺切換的針對性處理的程式碼。而當app退出音樂播放器業務元件時,前後臺切換的針對性處理的程式碼就不再執行了。
  針對梳理的需求,我這邊寫了一個工具類。能夠實現元件的註冊和取消註冊操作。這個工具類主要是通過method swizzle 來對Appdelegate中的相關方法進行hook操作。將方法交換到註冊過的元件中去實現。程式碼如下:
JKModuleManager.h

//
//  JKModuleManager.h
// Pods // // Created by Jack on 2017/7/17. // // #import <Foundation/Foundation.h> #define JKMODULEMANAGER_EXTERN() \ + (void)load \ { \ [JKModuleManager registerAppDelegateModule:self]; \ if ([self respondsToSelector:@selector(__jkmodule_load)]) { \ [self performSelector:@selector(__jkmodule_load)]; \ } \ } \ + (void
)__jkmodule_load \ @interface JKModuleManager : NSObject + (void) registerAppDelegateModule:(nonnull Class) moduleClass; + (void) registerAppDelegateObject:(nonnull id) obj; + (void) unregisterAppDelegateObject:(nonnull id) obj; @end

JKModuleManager.m

//
//  JKModuleManager.m
//  Pods
//
//  Created by Jack on 2017/7/17.
//
//

#import "JKModuleManager.h"

#import <objc/runtime.h>
#import <objc/message.h>


#define ADD_SELECTOR_PREFIX(__SELECTOR__) @selector(jkmodule_##__SELECTOR__)

#define SWIZZLE_DELEGATE_METHOD(__SELECTORSTRING__) \
Swizzle([delegate class], @selector(__SELECTORSTRING__), class_getClassMethod([JKModuleManager class], ADD_SELECTOR_PREFIX(__SELECTORSTRING__))); \

#define APPDELEGATE_METHOD_MSG_SEND(__SELECTOR__, __ARG1__, __ARG2__) \
for (Class cls in JKModuleClasses) { \
if ([cls respondsToSelector:__SELECTOR__]) { \
[cls performSelector:__SELECTOR__ withObject:__ARG1__ withObject:__ARG2__]; \
} \
} \
\
for (id obj in JKModuleObjects) { \
id target = [obj nonretainedObjectValue];\
if ([target respondsToSelector:__SELECTOR__]) { \
[target performSelector:__SELECTOR__ withObject:__ARG1__ withObject:__ARG2__]; \
} \
}


#define SELECTOR_IS_EQUAL(__SELECTOR1__, __SELECTOR2__) \
Method m1 = class_getClassMethod([JKModuleManager class], __SELECTOR1__); \
IMP imp1 = method_getImplementation(m1); \
Method m2 = class_getInstanceMethod([self class], __SELECTOR2__); \
IMP imp2 = method_getImplementation(m2); \

#define DEF_APPDELEGATE_METHOD_CONTAIN_RESULT(__ARG1__, __ARG2__) \
BOOL result = YES; \
SEL jk_selector = NSSelectorFromString([NSString stringWithFormat:@"jkmodule_%@", NSStringFromSelector(_cmd)]); \
SELECTOR_IS_EQUAL(jk_selector, _cmd) \
if (imp1 != imp2) { \
result = !![self performSelector:jk_selector withObject:__ARG1__ withObject:__ARG2__]; \
} \
APPDELEGATE_METHOD_MSG_SEND(_cmd, __ARG1__, __ARG2__); \
return result; \

#define DEF_APPDELEGATE_METHOD(__ARG1__, __ARG2__) \
SEL jk_selector = NSSelectorFromString([NSString stringWithFormat:@"jkmodule_%@", NSStringFromSelector(_cmd)]); \
SELECTOR_IS_EQUAL(jk_selector, _cmd) \
if (imp1 != imp2) { \
[self performSelector:jk_selector withObject:__ARG1__ withObject:__ARG2__]; \
} \
APPDELEGATE_METHOD_MSG_SEND(_cmd, __ARG1__, __ARG2__); \




#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"

#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wundeclared-selector"

void Swizzle(Class class, SEL originalSelector, Method swizzledMethod)
{
    Method originalMethod = class_getInstanceMethod(class, originalSelector);
    SEL swizzledSelector = method_getName(swizzledMethod);

    BOOL didAddMethod =
    class_addMethod(class,
                    originalSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));

    if (didAddMethod && originalMethod) {
        class_replaceMethod(class,
                            swizzledSelector,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
    //
    class_addMethod(class,
                    swizzledSelector,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));
}

@implementation UIApplication (JKModuleManager)
- (void)module_setDelegate:(id<UIApplicationDelegate>) delegate
{

    static dispatch_once_t delegateOnceToken;
    dispatch_once(&delegateOnceToken, ^{
        SWIZZLE_DELEGATE_METHOD(applicationDidFinishLaunching:);
        SWIZZLE_DELEGATE_METHOD(application: willFinishLaunchingWithOptions:);
        SWIZZLE_DELEGATE_METHOD(application: didFinishLaunchingWithOptions:);
        SWIZZLE_DELEGATE_METHOD(applicationDidBecomeActive:)
        SWIZZLE_DELEGATE_METHOD(applicationWillResignActive:)
        SWIZZLE_DELEGATE_METHOD(application: openURL: options:)
        SWIZZLE_DELEGATE_METHOD(application: handleOpenURL:)
        SWIZZLE_DELEGATE_METHOD(application: openURL: sourceApplication: annotation:)
        SWIZZLE_DELEGATE_METHOD(applicationDidReceiveMemoryWarning:)
        SWIZZLE_DELEGATE_METHOD(applicationWillTerminate:)
        SWIZZLE_DELEGATE_METHOD(applicationSignificantTimeChange:);
        SWIZZLE_DELEGATE_METHOD(application: didRegisterForRemoteNotificationsWithDeviceToken:)
        SWIZZLE_DELEGATE_METHOD(application: didFailToRegisterForRemoteNotificationsWithError:)
        SWIZZLE_DELEGATE_METHOD(application: didReceiveRemoteNotification:)
        SWIZZLE_DELEGATE_METHOD(application: didReceiveLocalNotification:)
        SWIZZLE_DELEGATE_METHOD(application: handleEventsForBackgroundURLSession: completionHandler:)
        SWIZZLE_DELEGATE_METHOD(application: handleWatchKitExtensionRequest: reply:)
        SWIZZLE_DELEGATE_METHOD(applicationShouldRequestHealthAuthorization:)
        SWIZZLE_DELEGATE_METHOD(applicationDidEnterBackground:)
        SWIZZLE_DELEGATE_METHOD(applicationWillEnterForeground:)
        SWIZZLE_DELEGATE_METHOD(applicationProtectedDataWillBecomeUnavailable:)
        SWIZZLE_DELEGATE_METHOD(applicationProtectedDataDidBecomeAvailable:)

        SWIZZLE_DELEGATE_METHOD(remoteControlReceivedWithEvent:)
    });
    [self module_setDelegate:delegate];
}
@end

BOOL JKModuleManagerClassIsRegistered(Class cls)
{
    return [objc_getAssociatedObject(cls, &JKModuleManagerClassIsRegistered) ?: @YES boolValue];
}

BOOL JKModuleManagerObjectIsRegistered(id x)
{
    return [objc_getAssociatedObject(x, &JKModuleManagerObjectIsRegistered) ?: @YES boolValue];
}

static NSMutableArray<Class> *JKModuleClasses;

static NSMutableArray<id> *JKModuleObjects;

@interface JKModuleManager()

@property (nonatomic) NSMutableDictionary *routes;

@end

@implementation JKModuleManager


+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Swizzle([UIApplication class], @selector(setDelegate:), class_getInstanceMethod([UIApplication class], @selector(module_setDelegate:)));
    });
}

+ (instancetype)sharedIsntance
{
    static JKModuleManager *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[self alloc] init];
    });
    return instance;
}

+ (void)registerAppDelegateModule:(Class) moduleClass
{

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        JKModuleClasses = [NSMutableArray new];
    });

    // Register module
    [JKModuleClasses addObject:moduleClass];

    objc_setAssociatedObject(moduleClass, &JKModuleManagerClassIsRegistered,
                             @NO, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

+ (void) registerAppDelegateObject:(nonnull id) obj
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        JKModuleObjects = [NSMutableArray new];
    });

    // Register module
    [JKModuleObjects addObject:[NSValue valueWithNonretainedObject:obj]];

    objc_setAssociatedObject(obj, &JKModuleManagerObjectIsRegistered,
                             @NO, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

+ (void) unregisterAppDelegateObject:(nonnull id) obj
{
    for (int i = 0; i < JKModuleObjects.count ; i++) {
        NSValue *currentValue = JKModuleObjects[i];
        id currentObj = [currentValue nonretainedObjectValue];
        if (currentObj == nil || currentObj == obj) {
            [JKModuleObjects removeObject:currentValue];
            i--;
        }
    }

    //以安全的方式移除相關關聯,而不是移除所有關聯
    objc_setAssociatedObject(obj, &JKModuleManagerObjectIsRegistered,
                             nil, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

#pragma mark - AppDelegate

+ (void)jkmodule_applicationDidFinishLaunching:(UIApplication *)application
{
    DEF_APPDELEGATE_METHOD(application, NULL);
}

+ (BOOL)jkmodule_application:(UIApplication *)application willFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions
{
    DEF_APPDELEGATE_METHOD_CONTAIN_RESULT(application, launchOptions);
}

+ (BOOL)jkmodule_application:(UIApplication *)application didFinishLaunchingWithOptions:(nullable NSDictionary *)launchOptions
{
    DEF_APPDELEGATE_METHOD_CONTAIN_RESULT(application, launchOptions);
}

+ (void)jkmodule_applicationDidBecomeActive:(UIApplication *)application
{
    DEF_APPDELEGATE_METHOD(application, NULL);
}
+ (void)jkmodule_applicationWillResignActive:(UIApplication *)application
{
    DEF_APPDELEGATE_METHOD(application, NULL);
}
+ (BOOL)jkmodule_application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options NS_AVAILABLE_IOS(9_0); // no equiv. notification. return NO if the application can't open for some reaso
{
    BOOL result = YES;
    SEL jk_selector = NSSelectorFromString([NSString stringWithFormat:@"jkmodule_%@", NSStringFromSelector(_cmd)]);
    SELECTOR_IS_EQUAL(jk_selector, _cmd)
    if (imp1 != imp2) {
        result = ((BOOL (*)(id, SEL, id, id, id))(void *)objc_msgSend)(self, jk_selector, app, url, options);
    }
    BOOL (*typed_msgSend)(id, SEL, id, id, id) = (void *)objc_msgSend;
    for (Class cls in JKModuleClasses) {
        if ([cls respondsToSelector:_cmd]) {
            typed_msgSend(cls, _cmd, app, url, options);
        }
    }

    for (NSValue * obj in JKModuleObjects) {
        id target = [obj nonretainedObjectValue];
        if ([target respondsToSelector:_cmd]) {
            typed_msgSend(target, _cmd, app, url, options);
        }
    }
    return result;
}

+ (BOOL)jkmodule_application:(UIApplication *)application handleOpenURL:(NSURL *)url
{
    DEF_APPDELEGATE_METHOD_CONTAIN_RESULT(application, url);
}

+ (BOOL)jkmodule_application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation
{
    BOOL result = YES;
    SEL jk_selector = NSSelectorFromString([NSString stringWithFormat:@"jkmodule_%@", NSStringFromSelector(_cmd)]);
    SELECTOR_IS_EQUAL(jk_selector, _cmd)
    if (imp1 != imp2) {
        result = ((BOOL (*)(id, SEL, id, id, id, id))(void *)objc_msgSend)(self, jk_selector, application, url, sourceApplication, annotation);
    }
    BOOL (*typed_msgSend)(id, SEL, id, id, id, id) = (void *)objc_msgSend;
    for (Class cls in JKModuleClasses) {
        if ([cls respondsToSelector:_cmd]) {
            typed_msgSend(cls, _cmd, application, url, sourceApplication, annotation);
        }
    }

    for (NSValue * obj in JKModuleObjects) {
        id target = [obj nonretainedObjectValue];
        if ([target respondsToSelector:_cmd]) {
            typed_msgSend(target, _cmd, application, url, sourceApplication, annotation);
        }
    }
    return result;
}

+ (void)jkmodule_applicationDidReceiveMemoryWarning:(UIApplication *)application;      // try to clean up as much memory as possible. next step is to terminate ap
{
    DEF_APPDELEGATE_METHOD(application, NULL);
}
+ (void)jkmodule_applicationWillTerminate:(UIApplication *)application
{
    DEF_APPDELEGATE_METHOD(application, NULL);
}
+ (void)jkmodule_applicationSignificantTimeChange:(UIApplication *)application;        // midnight, carrier time update, daylight savings time chang
{
    DEF_APPDELEGATE_METHOD(application, NULL);
}
+ (void)jkmodule_application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken NS_AVAILABLE_IOS(3_0)
{
    DEF_APPDELEGATE_METHOD(application, deviceToken);
}
+ (void)jkmodule_application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error NS_AVAILABLE_IOS(3_0)
{
    DEF_APPDELEGATE_METHOD(application, error);
}
+ (void)jkmodule_application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo NS_AVAILABLE_IOS(3_0)
{
    DEF_APPDELEGATE_METHOD(application, userInfo);
}
+ (void)jkmodule_application:(UIApplication *)application didReceiveLocalNotification:(NSDictionary *)userInfo NS_AVAILABLE_IOS(3_0)
{
    DEF_APPDELEGATE_METHOD(application, userInfo);
}
+ (void)jkmodule_application:(UIApplication *)application handleEventsForBackgroundURLSession:(NSString *)identifier completionHandler:(void (^)(void))completionHandler NS_AVAILABLE_IOS(7_0)
{
    SEL jk_selector = NSSelectorFromString([NSString stringWithFormat:@"jkmodule_%@", NSStringFromSelector(_cmd)]);
    SELECTOR_IS_EQUAL(jk_selector, _cmd)
    if (imp1 != imp2) {
        ((void (*)(id, SEL, id, id, id))(void *)objc_msgSend)(self, jk_selector, application, identifier, completionHandler);
    }
    void (*typed_msgSend)(id, SEL, id, id, id) = (void *)objc_msgSend;
    for (Class cls in JKModuleClasses) {
        if ([cls respondsToSelector:_cmd]) {
            typed_msgSend(cls, _cmd, application, identifier, completionHandler);
        }
    }

    for (NSValue * obj in JKModuleObjects) {
        id target = [obj nonretainedObjectValue];
        if ([target respondsToSelector:_cmd]) {
            typed_msgSend(target, _cmd, application, identifier, completionHandler);
        }
    }
}
+ (void)jkmodule_application:(UIApplication *)application handleWatchKitExtensionRequest:(nullable NSDictionary *)userInfo reply:(void(^)(NSDictionary * __nullable replyInfo))reply NS_AVAILABLE_IOS(8_2)
{
    SEL jk_selector = NSSelectorFromString([NSString stringWithFormat:@"jkmodule_%@", NSStringFromSelector(_cmd)]);
    SELECTOR_IS_EQUAL(jk_selector, _cmd)
    if (imp1 != imp2) {
        ((void (*)(id, SEL, id, id, id))(void *)objc_msgSend)(self, jk_selector, application, userInfo, reply);
    }
    void (*typed_msgSend)(id, SEL, id, id, id) = (void *)objc_msgSend;
    for (Class cls in JKModuleClasses) {
        if ([cls respondsToSelector:_cmd]) {
            typed_msgSend(cls, _cmd, application, userInfo, reply);
        }
    }

    for (NSValue * obj in JKModuleObjects) {
        id target = [obj nonretainedObjectValue];
        if ([target respondsToSelector:_cmd]) {
            typed_msgSend(target, _cmd, application, userInfo, reply);
        }
    }
}
+ (void)jkmodule_applicationShouldRequestHealthAuthorization:(UIApplication *)application NS_AVAILABLE_IOS(9_0)
{
    DEF_APPDELEGATE_METHOD(application, NULL);
}
+ (void)jkmodule_applicationDidEnterBackground:(UIApplication *)application NS_AVAILABLE_IOS(4_0)
{
    DEF_APPDELEGATE_METHOD(application, NULL);
}
+ (void)jkmodule_applicationWillEnterForeground:(UIApplication *)application NS_AVAILABLE_IOS(4_0)
{
    DEF_APPDELEGATE_METHOD(application, NULL);
}
+ (void)jkmodule_applicationProtectedDataWillBecomeUnavailable:(UIApplication *)application NS_AVAILABLE_IOS(4_0)
{
    DEF_APPDELEGATE_METHOD(application, NULL);
}
+ (void)jkmodule_applicationProtectedDataDidBecomeAvailable:(UIApplication *)application    NS_AVAILABLE_IOS(4_0)
{
    DEF_APPDELEGATE_METHOD(application, NULL);
}

+ (void)jkmodule_remoteControlReceivedWithEvent:(UIEvent *)event{
    DEF_APPDELEGATE_METHOD(event, NULL);
}


@end


#pragma clang diagnostic pop

#pragma clang diagnostic pop

以音樂播放器為例,我在音樂播放器元件中實現如下:
JKMusicPlayerModule.h

//
//  JKMusicPlayerModule.h
//  Pods
//
//  Created by Jack on 2017/7/17.
//
//

#import <Foundation/Foundation.h>

@interface JKMusicPlayerModule : NSObject
@property (nonatomic,assign) BOOL isAppInMusicModule;   ///< app是否在音樂播放器模組
+ (instancetype)shareInstance;
@end

JKMusicPlayerModule.m

//
//  JKMusicPlayerModule.m
//  Pods
//
//  Created by Jack on 2017/7/17.
//
//

#import "JKMusicPlayerModule.h"

#import "MCDataEngine.h"
#import "FMSingerModel.h"


#import <JKBasicProvider/JkBasicProvider.h>
#import <AVFoundation/AVFoundation.h>

#if (__IPHONE_OS_VERSION_MIN_REQUIRED >= 40000)
#import <AudioToolbox/AudioToolbox.h>
#endif

@interface JKMusicPlayerModule()
@property (nonatomic,assign) NSUInteger bgTaskId; ///< 後臺任務Id
@end

@implementation JKMusicPlayerModule
static JKMusicPlayerModule *_musicPlayerModule = nil;
+ (instancetype)shareInstance{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _musicPlayerModule = [JKMusicPlayerModule new];
    });
    return _musicPlayerModule;
}

JKMODULEMANAGER_EXTERN()
{

}

+ (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

    return YES;
}

+ (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{

}


+ (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings{

}


+ (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{

}


+ (void)applicationWillResignActive:(UIApplication *)application {
    // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
    // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game.
}

+ (void)applicationDidEnterBackground:(UIApplication *)application {
    if ([JKMusicPlayerModule shareInstance].isAppInMusicModule) {
        [JKMusicPlayerModule shareInstance].bgTaskId=[[self class] backgroundPlayerID:[JKMusicPlayerModule shareInstance].bgTaskId];
    }

}

+(UIBackgroundTaskIdentifier)backgroundPlayerID:(UIBackgroundTaskIdentifier)backTaskId
{
    //設定並激活音訊會話類別
    AVAudioSession *session=[AVAudioSession sharedInstance];
    [session setCategory:AVAudioSessionCategoryPlayback error:nil];
    [session setActive:YES error:nil];
    UIResponder *appDelegate = (UIResponder *)[UIApplication sharedApplication].delegate;
    [appDelegate becomeFirstResponder];
    //設定後臺任務ID
    UIBackgroundTaskIdentifier newTaskId=UIBackgroundTaskInvalid;
    newTaskId=[[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:nil];
    if(newTaskId!=UIBackgroundTaskInvalid&&backTaskId!=UIBackgroundTaskInvalid)
    {
        [[UIApplication sharedApplication] endBackgroundTask:backTaskId];
    }
    return newTaskId;
}


+ (void)remoteControlReceivedWithEvent:(UIEvent *)event
{
    NSString * statu = nil;
    if (event.type == UIEventTypeRemoteControl) {
        switch (event.subtype) {
            case UIEventSubtypeRemoteControlPause:
                statu = @"UIEventSubtypeRemoteControlPause";
                break;
            case UIEventSubtypeRemoteControlPreviousTrack:
                statu = @"UIEventSubtypeRemoteControlPreviousTrack";
                break;
            case UIEventSubtypeRemoteControlNextTrack:
                statu = @"UIEventSubtypeRemoteControlNextTrack";
                break;
            case UIEventSubtypeRemoteControlPlay:
                statu = @"UIEventSubtypeRemoteControlPlay";
                break;
            default:
                break;
        }
    }

    [[NSNotificationCenter defaultCenter] postNotificationName:JKAudioPlayerRemoteOperationNotifiation object:statu userInfo:nil];
}

+ (void)applicationWillEnterForeground:(UIApplication *)application {
    // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background.
}

+ (void)applicationDidBecomeActive:(UIApplication *)application {
    // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.

}

+ (void)applicationWillTerminate:(UIApplication *)application {
    // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
    // Saves changes in the application's managed object context before the application terminates.

}

@end

大家可以看到我在.m檔案實現了

JKMODULEMANAGER_EXTERN()
{

}

同時將進入後臺的操作都在JKMusicPlayerModule.m中進行了處理,明顯的感覺到了,程式碼被完全解耦了。另外還可以實現註冊和解除註冊的操作。是不是很奇妙啊。嘿嘿。

更多優質文章,可以微信掃碼關注:
這裡寫圖片描述