教你快速高效接入SDK——关于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方法改为如下形式:

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中,我们实现的这几个类的源码:

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);
     
}

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。

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.");
     }
     
}

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);
     }
}


本文由小黑发表于本博客,转载请注明出处.
更多精彩文章欢迎访问小黑的博客:http://www.uustory.com

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。