U8SDK——关于渠道自定义Application的适配
我们知道,每个android应用程序中都有一个唯一的上下文Application对象,这个Application一般我们不需要去关心他,应用启动时,系统会自动创建一个默认的Application实例。但是,因为Application在整个应用中是唯一的,也就是说,他是一个单例。所以,有的应用就可能希望利用Application来完成一些工作。
好在,在android中,实现一个自定义的Application是很简单的。直接自定义一个类继承Application,然后在AndroidManifest.xml的application节点属性里将android:name设置为你自定义的这个application类即刻。
那么,Application和U8SDK又有什么关系呢?
这个是因为部分渠道SDK(比如百度SDK),正是在Application级别做了些事情,使得接入他们的游戏,需要使用他们的Application,或者自定义一个Application去继承SDK的Application,在Application对应的接口里调用他们的方法。
但是,现在问题来了,因为U8SDK整套框架的核心思想就是,兼顾所有渠道。不可能直接在游戏的AndroidManifest.xml中配置上某个渠道的Application或者自定义一个Application,去继承某一个渠道的Application。然而,渠道的要求很明确的横在中间,我们必须想办法越过去。
幸运的是,方法总是有的。在这里,我在U8SDK抽象层中定义了一个Application监听器IApplicationListener,同时定义一个继承了Application类的U8Application。在U8Application类中,维护了一个IApplicationListener实例。这样在U8Application的onCreate,attackBaseContext等方法中,会间接的调用IApplicationListener中相应的接口。
这样,在具体接入渠道SDK的时候,我们就定一个适配器模式的类来继承渠道自己的Application,同时实现U8SDK抽象层的IApplicationListener接口。然后在IApplicationListener的接口实现中,直接调用基类(渠道SDK的Application)的对应方法。
然后,游戏层如果有自己的Application,那么需要将该Application继承U8Application,如果没有自定义Application,那么就直接将U8Application配置到AndroidManifest.xml的application节点的android:name属性中。(怎么配置自定义Application可以百度科普一下)。**
现在关键的问题是,在U8SDK抽象层的U8Application中,我们怎么知道当前需要实例化哪个IApplicationListener的实现类呢?也就是说,如果百度SDK和小米SDK里我都实现了IApplicaitonListener的实现类。那么,在生成渠道包的时候,怎么样U8Application实例化对应的实现类呢?问题很简单,我们在各个渠道接入的时候,已经定义了一个sdk_manifest.xml配置。在这个配置中,我们再加入一个配置。就是来配置这个IApplicationListener的实现类。在applicationConfig节点的keyword后面,我们再加一个属性:proxyApplication.这样我们可以这样来配置这个属性,比如:proxyApplication=com.u8.sdk.BDProxyApplication.
这样,我们在打包工具的脚本在合并Manifest文件时,加入这一段配置的解析就可以了。关于打包工具详细的机制和原理,后续的文章会说道。如果已经购买视频拿到源码在使用的童鞋。可以将打包工具脚本apk_utils.py中的mergeManifest方法改为如下形式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
def mergeManifest(targetManifest, sdkManifest): if not os.path.exists(targetManifest) or not os.path.exists(sdkManifest): file_utils.printF("The manifest file is not exists. targetManifest:%s, sdkManifest:%s", targetManifest, sdkManifest) return False ET.register_namespace('android', androidNS) targetTree = ET.parse(targetManifest) targetRoot = targetTree.getroot() ET.register_namespace('android', androidNS) sdkTree = ET.parse(sdkManifest) sdkRoot = sdkTree.getroot() f = open(targetManifest) targetContent = f.read() f.close() bRet = False appConfigNode = sdkRoot.find('applicationConfig') appNode = targetRoot.find('application') if appConfigNode != None and len(appConfigNode) > 0: proxyApplicationName = appConfigNode.get('proxyApplication') if proxyApplicationName != None and len(proxyApplicationName) > 0: metaNode = SubElement(appNode, 'meta-data') key = '{' + androidNS + '}name' val = '{' + androidNS + '}value' metaNode.set(key, "U8_APPLICATION_PROXY_NAME") metaNode.set(val, proxyApplicationName) appKeyWord = appConfigNode.get('keyword') if appKeyWord != None and len(appKeyWord) > 0: keyIndex = targetContent.find(appKeyWord) if -1 == keyIndex: bRet = True for child in list(appConfigNode): targetRoot.find('application').append(child) permissionConfigNode = sdkRoot.find('permissionConfig') if permissionConfigNode != None and len(permissionConfigNode) > 0: for child in list(permissionConfigNode): key = '{' + androidNS + '}name' val = child.get(key) if val != None and len(val) > 0: attrIndex = targetContent.find(val) if -1 == attrIndex: bRet = True targetRoot.append(child) targetTree.write(targetManifest, 'UTF-8') return bRet |
这样,我们就实现了在U8SDK中支持了渠道自定义Application。同时,各个渠道自定义的Application也不会影响到U8SDK整体的框架。在U8SDK中,我们实现的这几个类的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
package com.u8.sdk; import android.content.Context; import android.content.res.Configuration; /*** * * 定义一个Application接口,这样我们就可以通过该接口去间接调用渠道的Application类。 * 因为在u8sdk这套框架中,我们没有办法直接继承或者直接使用某个渠道的Application。 * * @author xiaohei * */ public interface IApplicationListener { public void onProxyCreate(); public void onProxyAttachBaseContext(Context base); public void onProxyConfigurationChanged(Configuration config); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
package com.u8.sdk; import com.u8.sdk.utils.SDKTools; import android.app.Application; import android.content.Context; import android.content.res.Configuration; /** * 我们在u8sdk抽象层中,定义一个我们自己的Application实现类。在这个类中,我们主要是通过间接的调用 * IApplicationListener接口的方法来完成实际各个渠道Application中方法的调用。 * * 如果上层游戏,有自己的Application。那么可以让该Application继承U8Application即可。 * 如果没有,则可以直接将U8Application配置到应用的AndroidManifest.xml的application节点 * 的android:name属性中。 * * @author xiaohei * */ public class U8Application extends Application { private static final String DEFAULT_PKG_NAME = "com.u8.sdk"; private static final String PROXY_NAME = "U8_APPLICATION_PROXY_NAME"; private IApplicationListener listener; public void onCreate() { super.onCreate(); if (listener != null) { listener.onProxyCreate(); } } public void attachBaseContext(Context base) { super.attachBaseContext(base); this.listener = initProxyApplication(); if (this.listener != null) { this.listener.onProxyAttachBaseContext(base); } } public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (this.listener != null) { this.listener.onProxyConfigurationChanged(newConfig); } } @SuppressWarnings("rawtypes") private IApplicationListener initProxyApplication() { String proxyAppName = SDKTools.getMetaData(this, PROXY_NAME); if (proxyAppName == null || SDKTools.isNullOrEmpty(proxyAppName)) { return null; } if (proxyAppName.startsWith(".")) { proxyAppName = DEFAULT_PKG_NAME + proxyAppName; } try { Class clazz = Class.forName(proxyAppName); return (IApplicationListener) clazz.newInstance(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } return null; } } |
同时,为了,验证这套东西可行,我模拟了百度SDK的场景。加入两个类。BaiduApplication和BDProxyApplication.其中,BaiduApplication是模拟百度SDK自己的Application,而BDProxyApplication则是IApplicationListener的实现类,同时也继承BaiduApplication。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
package com.u8.sdk.bd; import android.app.Application; import android.content.Context; import android.util.Log; /** * 这个类 模拟百度SDK里面自带的Application类。 * * @author xiaohei * */ public class BaiduApplication extends Application { public void onCreate() { super.onCreate(); Log.e("BaiduApplication", "The onCreate of BaiduApplication called."); } @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); Log.e("BaiduApplication", "The attachBaseContext of BaiduApplication called."); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
package com.u8.sdk.bd; import com.u8.sdk.IApplicationListener; import android.content.Context; import android.content.res.Configuration; /*** * * 通过定义一个代理类,继承百度SDK的Application,同时实现u8sdk里,我们定义的Application监听器接口。这样,在监听器方法的实现中 * 我们调用基类也就是BaiduApplication的相应方法。 * * 这样后面,我们就可以通过调用IApplicationListener接口,实现各个渠道Application中相应方法的调用 * * @author xiaohei */ public class BDProxyApplication extends BaiduApplication implements IApplicationListener { @Override public void onProxyCreate() { super.onCreate(); } @Override public void onProxyAttachBaseContext(Context base) { super.attachBaseContext(base); } @Override public void onProxyConfigurationChanged(Configuration config) { super.onConfigurationChanged(config); } } |
本文出自 U8SDK技术博客,转载时请注明出处及相应链接。
本文永久链接: http://www.uustory.com/?p=1434