插件自助开发——客户端概述

来自AnySDK 文档
跳转至: 导航搜索
本文概括性介绍AnySDK 插件自助开发的一些容易混淆的定义,开发步骤和流程,配置,插件工程及工程记录等。在介绍过程中,简略说明相关的常用接口。

config.xml详细说明

在插件文件里,有一个必然存在的 config.xml文件,这是插件与客户端打包工具交互的配置文件。打包工具根据config.xml来显示SDK参数,配置插件内注册的插件类,判断插件更新等。
根节点是config,属性pluginId表示插件 ID,是插件唯一标志,与代码中保持一致。
子节点paramLs里面是第三方sdk需要用到的所有参数;子节点pluginLs是插件拥有的模块,比如用户模块、支付模块等等;子节点version是插件版本和更新内容等;子节点operateLs是预留操作字段。

安智插件中的config.xml全文如下:

<?xml version="1.0" encoding="UTF-8"?>
<config pluginId="13">
    <paramLs>
        <param name="AnzhiAppKey" required="1" showName="AppKey" desc="应用的唯一标示,用于标示应用" bWriteIntoManifest="0" bUserOffer="1" bWriteIntoClient="1"/>
        <param name="AnzhiAppSecret" required="1" showName="AppSecret" desc="应用私钥" bWriteIntoManifest="0" bUserOffer="1" bWriteIntoClient="1"/>
        <param name="callback" required="1" desc="anysdk支付结果通知" showName="支付通知地址" bUserOffer="1" bWriteIntoClient="0"/>
    </paramLs>
    
    <operateLs>
    </operateLs>
    
    <pluginLs>
        <plugin name="UserAnZhi" typePlugin="0" desc="安智用户插件 "/>
        <plugin name="IAPOnlineAnZhi" typePlugin="2" desc="安智支付插件"/>
    </pluginLs>

    <version code = "1013" showVersion = "2.1.3_3.2" >
        <requestVersion minClientVer="2.0" minFrameworkVer="2.0"/>
        <![CDATA[更新框架2.0]]>
    </version>
</config>

子节点paramLs

子节点paramLs里面是第三方sdk需要用到的所有参数,这些参数配置显示在客户端打包工具的SDK参数列表中,上述安智插件参数显示如下图:

SDK参数显示截图.jpg

在插件代码中通过 PluginHelper 类中的getParamsInfo()接口获取用户输入的值。在这些参数中,有些是不能随便填写,只能选择一个可选值,每一个可选值是一个子标签option;有些不是填写字符串,而是选择文件的(如图片),需要添加一个子标签input。其配置分别如下:
        <param
            name="TCMSDKPayMode"
            bUserOffer="1"
            bWriteIntoClient="1"
            bWriteIntoManifest="0"
            desc="APP:使用应用传递的充值金额;SDK:由SDK提供充值默认值"
            required="1"
            showName="充值默认值" >

            <option value="APP" />

            <option value="SDK" />
        </param>
        <param
            name="TCMSDKCoinIcon"
            bUserOffer="1"
            bWriteIntoClient="1"
            bWriteIntoManifest="0"
            desc="支付时的道具图标,图标像素要求:48*48"
            placeholderText="图标像素要求:48*48"
            required="1"
            showName="道具图标" >

            <input
                data1="Image File(*.png)"
                data2="game_coin_icon.png"
                desc="支付时的道具图标,图标像素要求:48*48"
                height="48"
                type="file"
                width="48" />
        </param>

说明:

  • name:参数名,为防止多插件时,参数名冲突,定义参数名格式:插件名+参数名,新插件必须遵守此规则。已发布插件更新时,如果不是必须修改时,参数名不做修改,因为参数名的修改可能引起服务端取值错误,从而引用登录充值等问题。
  • required:是否必填
  • showName:参数显示名
  • bWriteIntoManifest:是否写入到AndroidManifest.xml,0表示不写入,1表示写入,参数会以meta-data的方式写入AndroidManifest.xml,android:name为参数名(name),android:value为用户填写或选中的值。
注意:如果写入到AndroidManifest.xml就不在写入到developer.xml,即插件代码中获取不到该参数。
  • bUserOffer:是否由用户填写,目前默认为1(暂无用)
  • bWriteIntoClient:设置参数是否写到APK包里面,1表示写入,0表示不写入。有些参数为服务端需要,而客户端不需要时,不要写入客户端,否则会导致安全隐患。
  • placeholderText:当该参数没有值时显示内容。
  • desc:参数描述,在参数列表中通过tooltips显示,可以通过"\n"实现换行显示。配置和显示如下:
        <param
            name="HM_CheckUpdateNote"
            bUserOffer="1"
            bWriteIntoClient="1"
            bWriteIntoManifest="0"
            desc="检查更新失败需要的提示:\n  CHECKUPDATE_FAILED_SHOW_NONE 不会显示任何界面,相当于非强制更新;\n  CHECKUPDATE_FAILED_SHOW_SURE 只有确定按钮,相当于强制更新;\n  CHECKUPDATE_FAILED_SHOW_CANCLEANDSURE 有确定和取消按钮,相当于非强制更新"
            required="1"
            showName="检查更新提示" >

            <option value="CHECKUPDATE_FAILED_SHOW_NONE" />

            <option value="CHECKUPDATE_FAILED_SHOW_SURE" />

            <option value="CHECKUPDATE_FAILED_SHOW_CANCLEANDSURE" />
        </param>
插件SDK参数 decs.jpg

子节点:

<option>标签:当该参数为下拉选项时,而非自己填写时,可在其param添加子节点option。value:option值,非中文字,在插件中获取到的参数值。
<input>标签:当需要用户传入文件时使用(如移动基地,需要用户传入一张图片作为展示图),拷贝至哪个目录则由python脚本的特殊需求来完成。
  • type:文件类型,目前支持:file(文件)
  • data1:文件过滤器,用于搜索什么格式文件
  • data2:新文件名.(即用户传入后,将被拷贝至工具目录下做备份管理)
  • width:图片最大宽度
  • height:图片最大高度

注意:

  1. SDK必要参数必须提供参数配置,由用户填写。SDK可选参数中,常用设置项也必须提供参数由用户填写。
  2. 必要参数在前,可选参数在后。
  3. 游戏名、游戏版本号、调试模式、屏幕方向等通过框架PluginHelper类提供的函数来获取,不以参数的形式传入。
  • 游戏名:getApplicationName()
  • 游戏版本号:getApplicationVersion()
  • 调试模式:getDebugModeStatus(),返回值 true (调试模式)或者 false(非调试模式);
  • 屏幕方向:getApplicationOrientation(),返回字符串,Android 返回值是landscape或者portrait;iOS 返回值为字符串数组;

子节点pluginLs

插件拥有的模块,比如用户模块、支付模块等等。插件模块在此处的排列顺序决定了客户端打包插件时的顺序,也就是插件初始化时的顺序。要求先用户再支付。
以下为酷派插件中的配置,包括用户系统类名为UserCoolpad、支付系统类名为IAPOnlineCoolpad、统计系统类名为AnalyticsDataEye。
    <pluginLs>
         <plugin
            name="UserCoolpad"
            desc="酷派用户插件"
            typePlugin="0" />

        <plugin
            name="IAPOnlineCoolpad"
            desc="酷派微支付"
            typePlugin="2" />
       
       <plugin 
           name = "AnalyticsDataEye" 
           typePlugin = "5" 
           desc = "DataEye"/>
       
    </pluginLs>

说明:

  • name:模块的类名,根据该类名创建对象,所以类名如果和代码中的类名不一致,初始化时,无法创建对象。
  • typePlugin:模块的类型,对应关系如下:用户:0;广告:1;支付:2;分享:4;统计:5

子节点version

    <version
        code="1001"
        minClientVer="1.5"
        minFrameworkVer="1.5"
        showVersion="2.0.1_1.1.2" >
        <![CDATA[更新至1.1.2版本]]>
    </version>

说明:

  • Code:插件版本,最初为1000,被更新一个版本+1,是插件更新的版本号。
  • showVersion:插件版本号,与代码中的插件版本号一致,详见【名词解释:插件版本号】。
  • minClientVer:表示插件要求使用的客户端的最低版本;
  • minFrameworkVer:表示插件要求使用的框架的最低版本。
  • <![CDATA[更新的内容说明]]>:注释节点中是插件更新内容说明,包括SDK版本,更新的内容,插件所具有的功能,注意事项等。客户端参数配置页中,鼠标移动到SDK版本号上时显示,如下图。
Config配置 cdata.jpg

子节点SDKLs

子节点SDKLs是同一个插件里,用来划分相对独立的模块的,这些模块功能互不影响,有各自独立的参数,可以认为是子 SDK。一般用在分享插件中的shareSDK和广告类插件中。
已有米广告为例,其参数管理界面和子 SDK 配置文件内容分别如下:
子 SDK 参数配置截图.png
        <SDKLs>
		<SDK SDKName = "Banner" SDKType = "16" SDKShowName = "Banner">
			<param name = "youmiBannerPos" required = "1" desc = "Banner" showName = "Pos" bWriteIntoManifest = "0" bUserOffer = "1">
			    <option value = "center"/>
				<option value = "top-middle"/>
				<option value = "top-left"/>
				<option value = "top-right"/>
				<option value = "bottom-middle"/>
				<option value = "bottom-left"/>
				<option value = "bottom-right"/>
			</param>
		</SDK>
		<SDK SDKName = "FullScreen" SDKType = "16" SDKShowName = "插屏广告">
		</SDK>
		<SDK SDKName = "OfferWall" SDKType = "16" SDKShowName = "积分墙">
		</SDK>
	</SDKLs>

说明:SDKLs中一个SDK子节点表示一个子 SDK,其中:

  • SDKName:子 SDK 标志
  • SDKShowName:在客户端的显示名字
  • SDKType:预留字段,暂时无用。
  • 子节点param与在params中的用法和属性一致。

注意:所有参数不可重名,即param的name值不可以相同。

子节点operateLs

该节点为预留操作节点,大部分特殊操作已经移动到特殊脚本的OpenApi中

子节点sysFrameworksLs

iOS特有节点,配置 SDK 要求引入的系统库。
Xcode7后系统动态库后缀从.dylib改为.tbd,2.1.0版本以后已做兼容
<sysFrameworksLs>
	<sysFrameworks name = "AudioToolbox.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "AdSupport.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "CoreTelephony.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "CoreGraphics.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "CoreText.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "MessageUI.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "SystemConfiguration.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "Security.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "QuartzCore.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "UIKit.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "Foundation.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "Accelerate.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "AddressBook.framework" path = "xcodeFrameworks" required = "1"/>
	<sysFrameworks name = "AddressBookUI.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "MobileCoreServices.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "CFNetwork.framework" path = "xcodeFrameworks" required = "1"/>
        <sysFrameworks name = "libsqlite3.0.dylib" path = "xcodeUsrlib" required = "1"/>
        <sysFrameworks name = "libstdc++.6.dylib" path = "xcodeUsrlib" required = "1"/>
        <sysFrameworks name = "libxml2.2.dylib" path = "xcodeUsrlib" required = "1"/>
        <sysFrameworks name = "libz.dylib" path = "xcodeUsrlib" required = "1"/>
</sysFrameworksLs>

说明:

  • name :引入库的名字
  • path:路径,一般情况下,静态库为 "xcodeFrameworks",动态库为"xcodeUsrlib"。
  • required:"1"表示 Required;"0"表示Optional。

script.py特殊脚本与OpenApi详细说明

目前打包过程中的绝大多数渠道特殊处理脚本已经被抽象到OpenApi类中,插件也在逐步更新
旧写法脚本为script.pyc, 新写法脚本为script.py,如果有需要,可以在新写法脚本中加入自己的特殊操作方法。因为更新插件时会覆盖原脚本,所以如果修改请做好备份

Android

初始化:先实例化一个api_obj, 再使用api_obj.方法名调用方法。

from any_open_api import AnyOpenAPI
def script(SDK, decompile_dir, package_name, usrSDKConfig):
    api_obj = AnyOpenAPI(SDK, decompile_dir, package_name, usrSDKConfig)
  • get_param_value(param): 获取参数值方法
       @调用例子: api_obj.get_param_value("BDChannelId")
       @param param_name config.xml中对应的参数名
       @return 返回对应参数值,若是参数未找到,返回空字符串
  • get_file_absolute_path(file_name): 获取文件的绝对路径方法
       @调用例子: api_obj.get_param_value("res/values/strings.xml")
       @param file_name:APK中的相对文件路径
       @return 返回在打包临时文件夹中该文件的绝对路径地址,若是参数未找到,返回空字符串
  • get_start_activity(): 获取启动的Activity
       @调用例子: api_obj.get_start_activity()
       @return 返回找到的启动Activity名,若是失败返回None
  • modify_start_activity(new_activity_name): 修改Apk的启动Activity
       @调用例子: api_obj.modify_start_activity("com.anysdk.sample.MainActivity")
       @param new_activity_name:新的启动Activity名称,如:com.anysdk.sample.MainActivity
       @return:成功则返回原先的启动Activity以供其他位置修改,失败返回None
  • modify_application_or_inherit(application_name): 主要用于解决以下两种情况:
  1. 在母包未指定Application的时候,指定Application名称
  2. 在已有Application的时候,让其Application继承指定的Application
       @调用例子: api_obj.modify_application_or_inherit("com.anysdk.framework.DemoApplication")
       @param application_name:指定的Application
       @return:返回修改结果是否成功(True/False)
  • modify_strings_values(file_name, key_name, value, is_add_when_not_existed=False): 修改apk的中xml字符串资源
       @调用例子: api_obj.modify_strings_values("res/values/g_strings.xml", "g_class_name", origin_activity)
       @param file_name 指定的文件,如res/values/strings.xml
       @param key_name 要修改的string节点的name值
       @param value 要修改的string值
       @param is_add_when_not_existed: 如果该字段不存在是否添加进去,默认不添加
       @return 返回操作结果,成功与否(True/False)
  • set_application_attr(attr_key, attr_value): 修改AndroidManifest.xml中的application属性
       @调用例子: api_obj.set_application_attr("key", "value")
       @param attr_key 属性名
       @param attr_value 属性值
       @return 返回操作结果,成功与否(True/False)
  • get_application_attr(self, attr_key): 获取AndroidManifest.xml中的application属性
       @调用例子:api_obj.get_application_attr("key")
       @param attr_key 属性名
       @return 成功返回获取结果,若失败返回None
  • add_meta_data(key_name, key_value_param_name, front, back):添加Meta-data键值。一些需要脚本计算后才能填写的meta-data可用此方法
       @调用例子:api_obj.add_meta_data("ANQUSDK_PRIVATEKEY", anqu_value, "", "")
       @param key_name meta-data的android:name值
       @param key_value_param_name meta-data的android:value值(传入配置的参数名,由打包工具去解析获取真实值,若无此参数则取填写的字段)
       @param front meta-data的android:value值的前缀
       @param back meta-data的android:value值的后缀,最终的value值为 front+[param值]+back
       @return 返回操作结果,成功与否(True/False)
  • write_information_into_file(file_name, information):向指定文件覆盖写入信息
       @调用例子:api_obj.write_information_into_file("/assets/ztsdk_config.properties", "CONFIG_APPID = 1")
       @param file_name 相对文件路径
       @param information 要写入的信息
       @return 返回操作结果,成功与否(True/False)
  • copy_rfiles(target_dir):拷贝R文件,部分SDK会默认读取指定目录下的R文件,此函数用于根据当前资源生成R文件并拷贝至指定目录
       @调用例子:api_obj.copy_rfiles("mobi/zty/pay")
       @param target_dir:拷贝指定的目录
       @return:返回该操作是否成功(True/False)
  • copy_smali_file(file_name, origin_path, target_path):拷贝代码文件
       @调用例子:api_obj.copy_smali_file("WXPayEntryActivity", packageName + ".wxapi", "com/anysdk/framework/wxapi")
       @param file_name 要拷贝的代码文件名,不包含后缀
       @param origin_path 代码原所在位置
       @target_path 目标位置
       @return 返回操作结果,成功与否(True/False)
  • copy_smali_to_package_name(file_name, origin_path, target_path):拷贝代码文件到包名目录下
       @调用例子:api_obj.copy_smali_file("WXPayEntryActivity", packageName + ".wxapi", "framework/wxapi")
       @param file_name 要拷贝的代码文件名,不包含后缀
       @param origin_path 代码原所在位置
       @param target_path 包名目录下的指定路径
       @return 返回操作结果,成功与否(True/False)
  • copy_file(param_name, target_path):拷贝文件到指定目录下
       @调用例子:api_obj.copy_file("BPGameIcon", "res/drawable-xhdpi/bp_gameicon_64.png")
       @param param_name:源资源所在目录(传入参数名,由打包工具去解析获取真实路径,若是相对路径会去查找config/game/gameid/channel目录)
       @param target_path:拷贝到的指定位置(如Res/Drawable/game_icon.png)
       @return 返回操作结果,成功与否(True/False)

iOS

初始化:先实例化一个api_obj, 再使用api_obj.方法名调用方法。

from any_open_api import AnyOpenAPI
def script(SDK, work_dir, target_name, usrSDKConfig, SDKDestDir, project):
    api_obj = AnyiOSOpenAPI(SDK, work_dir, target_name, usrSDKConfig, SDKDestDir, project)
  • get_param_value(param): 获取参数值方法
       @调用例子: api_obj.get_param_value("BDChannelId")
       @param param_name config.xml中对应的参数名
       @return 返回对应参数值,若是参数未找到,返回空字符串
  • get_file_absolute_path(file_name):获取文件的绝对路径
       @调用例子:api_obj.get_file_absolute_path("AppController.mm")
       @param file_name 文件名,如"AppController.mm",必须为已经加入工程的文件名
       @return 返回绝对路径,如果工程中获取不到,则返回空值
  • get_bundle_id():获取BundleID
       @return:返回BundleID值
  • modify_archs(archs):修改Architectures指令集, 取原工程和所选指令集的交集
       @调用例子:api_obj.modify_archs("armv7 arm64")
       @param archs:修改后的指令集,以空格分隔
       @return: 返回操作结果,修改与否(True/False),若archs都已经存在则返回False
  • add_ldflags(flags):添加OtherLinkerFlags参数
       @调用例子:api_obj.add_ldflags("-ld  -all_load")
       @param flags 要添加的OtherLinkerFlags,以空格分隔
       @return: 返回操作结果,修改与否(True/False),若flags都已经存在则返回False
  • add_plist_url_schemes(schemes, name="", front="", back=""):添加UrlSchemes
       @调用例子:api_obj.add_plist_url_schemes("Bundle_ID", "weixin", "wx", "")
       @param schemes 要添加的schemes的参数名/值,若写为"Bundle_ID",则取bundle id值,若有对应的参数名则取参数值,否则取文本
       @param name 若需要填写Identifier填写在此
       @param front 填写在scheme前的文本
       @param back:填写在scheme后的文本,最终的schemes为front+schemes取值+back

Android 插件工程说明

工程目录

插件工程目录结构基于Eclipce创建的 Android 项目进行,建议使用 Eclipse 进行开发。
Android工程对比图.jpg

新增目录有:

  • config.xml:插件配置文件,详细说明见【 config.xml详细说明
  • DependProject:第三方sdk是以工程方式提供时,资源(res,assets,so等)都需要移到相应的For***目录里,另外:
如果工程中没有java代码,工程弃之不用(不需要把工程放入此目录);
若有代码,还需要将工程放在插件目录DependProject下,并引用工程(也可以编译成jar包,以jar包方式使用)。
  • ForAssets:把第三方SDK需要的assets资源文件全部拷贝到ForAssets文件夹下面.
  • ForLibs:如果第三方SDK需要引用额外的so文件,把so文件及文件夹全部拷贝到ForLibs文件夹下面。
  • ForManifest:添加第三方SDK会有要求在AndroidManifest.xml中添加sdk的activity、service、 receiver、权限等,详细说明见【 ForManifest
  • ForRes:如果第三方SDK提供res资源文件,则需要全部拷贝到ForRes文件夹下面.
  • ForRootDir:如果第三方SDK要求某些文件或资源必须放在Android安装包(.apk)的根目录下,则需要全部拷贝到ForRootDir文件夹下面。
  • ForSplash:如果第三方SDK有提供闪屏图片,则图片需要按照插件格式修改。详细说明参考文档:http://docs.anysdk.com/CustomSplash
  • icon:放置第三方SDK要求游戏图标需要加角标.需要尺寸为512*512的左上、左下、右上、右下四张图片,图片分别命名为left-top.png、left-bottom.png、right-top.png、right-bottom.png
  • sdk:放置第三方SDK的jar包,这些 jar 包需要进入项目才开使用。
注意:有些SDK的Jar包里含有资源,这是jar包里的资源(res,assets,so等)都需要移到相应的For***目录中
  • README.md:更新历史,详细说明见【 README.md

插件工程以相对路径的方式引入了插件开发过程中用到的框架jar 包——libPluginProtocol.jar,需要注意的是,这个 jar 包仅在编译时用到,不会编入最终发布的插件中。

ForManifest

第三方SDK会有要求在AndroidManifest.xml中添加sdk的activity、service、 receiver、权限等。我们把这些内容放入ForManifest.xml,供打包工具读取后添加到渠道包中,实现第三方SDK的要求。
ForManifest文件夹通常只有ForManifest.xml,如果第三方SDK需要分别对横竖屏进行添加时可分为ForManifestLandscape.xml和ForManifestPortrait.xml分别编写(也就是说 ForManifest 文件夹下有ForManifest.xml或者ForManifestLandscape.xml和ForManifestPortrait.xml)。客户端打包工具会根据的渠道参数中设置的横竖屏来判断使用ForManifestLandscape.xml还是ForManifestPortrait.xml。

我们以内容比较少的金立插件为例来说明,其ForManifest.xml全部内容如下:

<?xml version="1.0" encoding="utf-8"?>
<manifestConfig xmlns:android="http://schemas.android.com/apk/res/android" >

    <permissionCfg>

        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

        <uses-permission android:name="android.permission.GET_TASKS" />

        <uses-permission android:name="android.permission.INTERNET" />

        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

        <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />

        <uses-permission android:name="android.permission.READ_PHONE_STATE" />

        <uses-permission android:name="android.permission.INSTALL_PACKAGES" />

        <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

        <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    </permissionCfg>

    <applicationCfg keyword="com.gionee.gsp.floatingwindow.FloatingWindowService" >

        <!-- Amigo Play SDK组件声明开始 -->

        <service android:name="com.gionee.gsp.floatingwindow.FloatingWindowService" >

            <intent-filter>

                <action android:name="com.gionee.pay.ic.FloatingWindowService" />

                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </service>

        <activity
            android:name="com.gionee.gsp.service.activity.AssistActivity"
            android:configChanges="mcc|mnc|orientation|screenSize"
            android:theme="@android:style/Theme.Translucent.NoTitleBar" />
        <!-- Amigo Play SDK组件声明结束 -->

    </applicationCfg>

</manifestConfig>

说明:

ForManifest.xml文档的根节点是manifestConfig,子节点:
  • permissionCfg子节点:第三方SDK需要的所有应用权限配置;
  • applicationCfg子节点:第三方SDK需要的activity、service、 receiver等所有配置。applicationCfg中的keyword属性值为applicationCfg中第一个子节点的name 值,有防止客户端打包工具重复读取添加的作用。

编码注意事项

1、线程问题

在调用SDK 提供的可能涉及到页面的接口时,如登录、登出、支付、显示/隐藏悬浮窗、显示广告等,必须在主线程中调用,否则会导致崩溃或者界面不正常显示等错误。

通过调用框架提供的函数 PluginWrapper.runOnMainThread实现在主线程调用,百度游戏登出如下:

	public void logout() {
		PluginWrapper.runOnMainThread(new Runnable() {
			@Override
			public void run() {
				BDGameSDK.logout();     //登出
				BDYouxiWrapper.setLogined(false); //设置登录状态
				actionResult(UserWrapper.ACTION_RET_LOGOUT_SUCCESS, "logout success"); //回调
			}
		});
	}

2、主 Activity 的回调

SDK的很多接口要求在主 Activity 的回调中调用,如销毁等,在插件里通过调用框架提供的PluginWrapper.setActivityCallback函数将监听添加到框架的队列里,框架在主Activity回调时,调用相应的接口。一般情况下,一个插件中,只设置一次。

以百度游戏为例,其代码如下:

 		PluginWrapper.setActivityCallback(new IActivityCallback() {
 			
 			@Override
 			public void onStop() {
 				LogD("BDYouxiWrapper", "onStop");
 				mActivityAdPage.onStop();				
 			}
 			
 			@Override
 			public void onResume() {
 				LogD("BDYouxiWrapper", "onResume");
 				mActivityAnalytics.onResume();
 				mActivityAdPage.onResume();
 			}
 			
 			@Override
 			public void onRestart() {

 			}
 			
 			@Override
 			public void onPause() {
 				mActivityAnalytics.onPause();
 			}
 			
 			@Override
 			public void onNewIntent(Intent intent) {
 				
 			}
 			
 			@Override
 			public void onDestroy() {
 				BDGameSDK.destroy();
 			}
 			
 			@Override
 			public void onActivityResult(int requestCode, int resultCode, Intent data) {
 				
 			}
 		});

详细开发文档

用户系统开发文档
支付系统开发文档
推送系统开发文档
分享系统开发文档
统计系统开发文档
广告系统开发文档
一个插件中可以包含多个系统,如渠道类的一般都有用户系统和支付系统,酷派有用户系统、支付系统和统计系统,360有用户系统、支付系统、统计系统和推送系统。
当一个插件中有多个系统存在时,就会有些数据或方法是每个系统都需要用到的,如 SDK 版本号,插件 ID,主 Activity 回调,登录及登录验证等,我们把这些公共的数据和方法抽象到一个类中,命名为"插件名+Wrapper"。详细示例请参考已开源代码。

iOS 插件工程说明

iOS 插件工程是一个拥有特殊目录结构的静态库工程。

工程目录

自助开发工具生成的 iOS插件工程的目录结构如下(以模板为例,即插件名为 iOS_Template):
IOS 插件工程目录.png

说明:

  • config.xml:插件配置文件,详细说明见【 config.xml详细说明
  • ForSplash:如果第三方SDK有提供闪屏图片,则图片需要按照插件格式修改。详细说明参考文档:http://docs.anysdk.com/CustomSplash
  • Frameworks:放置第三方sdk提供的.framework和.a等文件。头文件需要引入到项目工程里,非头文件不需要引入工程。
  • icon:放置第三方SDK要求游戏图标需要加角标.需要尺寸为512*512的左上、左下、右上、右下四张图片,图片分别命名为left-top.png、left-bottom.png、right-top.png、right-bottom.png
  • README.md:更新历史,详细说明见【 README.md
  • Resources:放置第三方SDK提供的资源文件(.bundle等).不需要引入项目工程里。
  • Template:插件源码目录。
  • Template.xcodeproj:项目工程文件。

项目结构

可读性和可维护性,我们对项目结构制定了规则,我们以快用为例,其截图如下:
IOS 插件项目结构图.png

说明:

  • Frameworks:对应工程目录中的 Frameworks目录,只引入头文件及源码文件(如.m等)。
  • Projects:生成的静态库目录。
  • protocols:插件需要用到的框架支持文件,以相对路径方式引入头文件。
  • kuaiyong:对应插件目录中的源码目录,源码最终编译为静态库(即 Kuaiyong.framework)。

详细开发文档

用户系统开发文档
支付系统开发文档
广告系统开发文档
统计系统开发文档
推送系统开发文档
分享系统开发文档

一个插件中可以包含多个系统,如渠道类的一般都有用户系统和支付系统。有的渠道包含更多的系统。当一个插件中有多个系统存在时,就会有些数据或方法是每个系统都需要用到的,如 SDK 版本号,插件 ID,登录及登录验证等,我们把这些公共的数据和方法抽象到一个类中,命名为"插件名+Wrapper"。

README.md

每一个插件都对应一份README.md文件,记录插件的更新历史。
在自助开发工具中配置后,可自动生成,但开始开发时更新内容和注意实现可能不完整,所以记得开发完成后补充完整。
本文件的查看者有:
  1. 测试人员:根据更新内容和注意实现进行测试
  2. 开发人员:根据文件内容进展后续开发和维护
  3. 技术支持:根据注意实现补充SDK 注意事项等,提醒用户。
注意:插件更新时,在顶部增加内容,只允许增加内容,不允许删除已有内容;采用Markdown格式书写。

文件显示格式如下:

XXXX年XX月XX日 
客户端开发:email 
服务端开发:email 
插件版本:x.x.x_x.x.x.x
更新内容:
1、更新内容1
2、更新内容2
注意事项:
1、注意事项1
2、注意事项2

说明:

  • 时间:
  • 客户端开发:客户端开发联系人邮箱,方便提交过程中联系沟通。
  • 服务端开发:服务端开发联系人邮箱,方便提交过程中联系沟通。
  • 插件版本:config.xml中 version的showVersion和代码中的插件版本号保持一致。
  • 更新内容:本次更新的具体内容,如 SDK版本升级,使用的框架、客户端标准修改,插件的扩展接口增减等
  • 注意事项:SDK 的特殊情况,使用中的注意点等,如:登录取消、支付失败没有回调,未登录时不能显示悬浮窗,测试模式下所有商品只需支付0.1元等,不同的 SDK,注意事项有很大不同。
腾讯MSDK的注意事项比较多, 我们以它为例,其内容(Markdown格式书写)如下:
####2015年3月23日 
客户端开发:daincameng@anysdk.com  
服务端开发:xqp  
插件版本:2.0.0_2.5.3a   
更新内容:  
1、更新至2.5.3a版本  
注意事项:  
1、必须在onCreate()中加载插件,否则可能导致登录回调丢失  
2、如果游戏的Activity为Launch Activity, 则需要在游戏Activity声明中添加android:configChanges="orientation|screenSize|keyboardHidden", 否则可能造成没有登录没有回调。  
3、微信授权需要保证微信版本高于4.0  
4、拉起微信时候, 微信会检查应用程序的签名和微信后台配置的签名是否匹配(此签名在申请微信appId时提交过), 如果不匹配则无法唤起已经授权过的微信客户端.   
5、游戏点击注销按钮或者其余弹出登录框的逻辑中都必须要调用Logout来清空本地的登录信息。否则会导致授权失败等问题  
6、微信accessToken只有两个小时的有效期,不采用自动刷新模式,但用到accessToken时发现失效,自动完成重新登录流程,并回调登录消息  
7、微信refreshToken的有效期为30天,过期后回调登录失败消息,需要重新登录,一般不会出现这种情况。
8、jar包编译时注意,需要区分大小写。  
9、特别注意:测试模式下无法显示支付界面,必须收到安装支付的测试插件才可用。  
10、微信帐号和QQ帐号在支付时,服务端请求参数都要用手Q的APPID和APPKEY,否则会提示没有支付权限。

在10条注意事项中:

1、2是登录没有回调的可能原因;
3、4、5是微信授权失败的可能原因;
6是说明存在这样的情况:未调用微信登录授权却出现登录授权界面,原因是accessToken无效了。
7是自动登录失败的可能原因,概率比较小;
8是插件打包成 zip 的编译时,必需在区分大小写的磁盘上进行;
9是调试模式的特殊处理;
10是维护插件时的注意点。