U8SDK——多包名下生成R.java(海外SDK接入普遍遇到的坑)
在接入海外SDK,比如Google、Facebook以及国内的一些SDK,比如迅雷等,会遇到一个普遍的问题, 就是这些SDK中,对于资源的引用,是直接通过R..调用的。
R.java里面存储了Android工程中各种资源ID的索引,一般在编译的时候,R.java会自动生成在gen目录下。
这些SDK直接引用R的时候, 多半这些R所在的包名,是这些SDK自己的包名。 我们反编译下迅雷的SDK的jar包,以迅雷SDK中的一个UI资源引用为例:
这里他直接引用了com.xunlei.thundergamesdk包名下的R类, 代码中,直接通过R.stytleable.*来获取资源ID。
而U8SDK打包工具在反编译资源动态整合的时候,会在当前游戏包名(比如,我们的游戏包名是:com.u8.sdk.demo)路径下,重新生成一个R.java,
那么不做特殊处理,程序运行的时候, 迅雷SDK中这些直接引用R的地方,会出现资源找不到的问题。
为了处理这种情况, 打包工具在重新生成R.java的时候, 我们将这个R.java同时也编译到com.xunlei.thundergamesdk包名下。 这样, 程序运行的时候, 这个R文件在这个包名下,就是存在的。
有了这个思路之后, 我们将需要生成R文件的包名,放在配置中。 同时,同一个SDK,可能需要在多个包名下生成R文件, 所以,我们的配置,需要支持配置多个包名。
我们在渠道配置文件中(打包工具/games/当前游戏/config.xml)的每个渠道参数配置这里,增加一条配置
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 |
<channel> <param name="id" value="68" /> <param name="name" value="xunlei" /> <param name="sdk" value="xunlei" /> <param name="desc" value="迅雷" /> <param name="suffix" value="com.xunlei.thundergame" /> <param name="splash" value="0" /> <param name="splash_copy_to_unity" value="0" /> <param name="icon" value="rb" /> <param name="extraR" value="com.xunlei.thundergamesdk" /> <sdk-params> <param name="XL_GAME_ID" value="44" desc="Game ID"/> <param name="XL_APPKEY" value="444" desc="App Key"/> <param name="XL_ACCESS_KEY" value="4444" desc="Access Key"/> <param name="XL_SECRET_KEY" value="444" desc="Secret Key"/> <param name="XL_REPORT_ID" value="444" desc="Report ID"/> <param name="XL_REPORT_KEY" value="4444" desc="Report Key"/> </sdk-params> <sdk-version> <versionCode>1</versionCode> <versionName>1.0.6_2</versionName> </sdk-version> </channel> |
然后,我们在打包工具/scripts/apk_utils.py中,我们增加一个方法, 将生成的R.java文件, 拷贝到上面配置的多个包名路径下。
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 79 80 81 82 83 84 85 86 87 88 89 90 91 |
def copyExtraR(decompileDir, channel, newPackageName): """ copy the new generated R.java to sdk extra package first:add a new param in sdk channel config <param name="extraR" value="com.facebook" /> for those sdk which used R.*.* directly in code. """ if "extraR" not in channel: log_utils.debug("the sdk %s has no extraR config. don't need to generate extra R.java", channel['sdk']) return 0 log_utils.debug("sdk %s need to generate extra R.java. package names:%s", channel['sdk'], channel['extraR']) newPackageNames = channel['extraR'].split(",") ret = 0 decompileDir = file_utils.getFullPath(decompileDir) sdkPath = os.path.dirname(decompileDir) + "/sdk" resPath = os.path.join(sdkPath, channel['sdk']+"/res") tempPath = os.path.dirname(decompileDir) tempPath = tempPath + "/temp" genPath = os.path.join(tempPath, "gen") rPath = newPackageName.replace('.', '/') rPath = os.path.join(genPath, rPath) rPath = os.path.join(rPath, "R.java") if not os.path.exists(rPath): log_utils.error("copy extra R failed. the R.java is not exists:%s", rPath) return 1 for k in range(len(newPackageNames)): packageName = newPackageNames[k] tempPath = os.path.join(sdkPath, 'extraTemp'+str(k)) log_utils.debug("generate sdk R: the temp path is %s", tempPath) if os.path.exists(tempPath): file_utils.del_file_folder(tempPath) if not os.path.exists(tempPath): os.makedirs(tempPath) targetResPath = os.path.join(tempPath, "res") file_utils.copy_files(resPath, targetResPath) genPath = os.path.join(tempPath, "gen") if not os.path.exists(genPath): os.makedirs(genPath) trPath = packageName.replace('.', '/') trPath = os.path.join(genPath, trPath) if not os.path.exists(trPath): os.makedirs(trPath) targetRPath = os.path.join(trPath, "R.java") file_utils.copy_file(rPath, targetRPath) file_utils.modifyFileContent(targetRPath, newPackageName, packageName) cmd = '"%sjavac" -source 1.7 -target 1.7 -encoding UTF-8 "%s"' % (file_utils.getJavaBinDir(), targetRPath) ret = file_utils.execFormatCmd(cmd) if ret: return 1 dexToolPath = file_utils.getFullToolPath("/lib/dx.jar") heapSize = config_utils.getJDKHeapSize() targetDexPath = os.path.join(tempPath, "classes.dex") cmd = file_utils.getJavaCMD() + ' -jar -Xmx%sm -Xms%sm "%s" --dex --output="%s" "%s"' % (heapSize, heapSize, dexToolPath, targetDexPath, genPath) ret = file_utils.execFormatCmd(cmd) if ret: return 1 smaliPath = os.path.join(decompileDir, "smali") ret = dex2smali(targetDexPath, smaliPath, "baksmali.jar") return 0 |
上面这个方法,就是将游戏包名下生成的R.java,编译到配置的多个包名路径下。 然后我们在generateNewRFile方法的最后,调用一下上面这个方法:
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 |
def generateNewRFile(newPackageName, decompileDir, channel): """ Use all new resources to generate the new R.java, and compile it ,then copy it to the target smali dir """ ret = checkValueResources(decompileDir) if ret: return 1 # ret = generateSdkRFile(decompileDir, channel) # if ret: # return 1 decompileDir = file_utils.getFullPath(decompileDir) tempPath = os.path.dirname(decompileDir) tempPath = tempPath + "/temp" log_utils.debug("generate R:the temp path is %s", tempPath) if os.path.exists(tempPath): file_utils.del_file_folder(tempPath) if not os.path.exists(tempPath): os.makedirs(tempPath) resPath = os.path.join(decompileDir, "res") targetResPath = os.path.join(tempPath, "res") file_utils.copy_files(resPath, targetResPath) genPath = os.path.join(tempPath, "gen") if not os.path.exists(genPath): os.makedirs(genPath) manifestPath = os.path.join(decompileDir, "AndroidManifest.xml") targetDexPath = os.path.join(tempPath, "classes.dex") ret = doGenerateR(decompileDir, targetResPath, manifestPath, genPath, targetDexPath, newPackageName) if ret: return 1 return copyExtraR(decompileDir, channel, newPackageName) |
好了,这样,对于google,facebook,迅雷等这些直接引用R文件的SDK,这种方式就可以解决了。
本文出自 U8SDK技术博客,转载时请注明出处及相应链接。
本文永久链接: http://www.uustory.com/?p=2114