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






