教你快速高效接入SDK——關於Application的適配和代理

 

U8SDK技術博客:http://www.uustory.com/,歡迎來坐坐。

百度傳課已經停運,最新U8SDK視頻教程已經轉移至B站U8SDK最新視頻教程

 

我們知道,每個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
 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章