教你快速高效接入SDK——關於Application的適配和代理,sdkapplication
我們知道,每個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