月度归档:2011年07月

Android NDK r6发布

google更新android开发工具的速度还是真快呀,一不小心,NDK已经发布r6版本了。这次发布版本的最大变化是增加了x86的支持,看来是为android x86铺路。下面还是来看看具体有哪些变化吧:

  • 增加x86 ABI支持,可以产生运行于x86android设备的机器码。主要的特性有x86工具链、系统头文件、库和调试支持。

        缺省情况下,产生的代码是基于ARM设备的,但是您可以在Application.mk文件中APP_ABI定义中增加x86,以生成x86平台的代码。例如:

        APP_ABI := x86

  • 在调用make-standalone-toolchain.sh脚本时加上-toolchian=x86-4.4.3选项,您可以生成单独的x86工具链。
  • 新的ndk-stack工具可以翻译由原生代码产生的在logcat中显示的栈调用信息。该工具将指令地址翻译成可读格式,包含函数、源文件和行号等。
  • 从NDK r5开始arm-eabi-4.4.0标记为废弃,在本版中正式移除。

本次ndk升级中,个人认为比较有用的是ndk-stack工具。在ndk开发中,最令人头疼的是native代码崩溃,但是logcat只显示一些地址信息,无从查找代码崩溃的位置,有了ndk-stack工具,相信会给ndk开发带来很大的帮助。

使用内部(com.android.internal)和隐藏(@hide)API[第4部分,定制ADT]

本文翻译自http://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-4-customizing-adt/

在前面的文章中,我讲述了如何创建定制的original-android.jar和创建定制的android平台以使用original-android.jar。这仅仅能够使用隐藏API,还为内部API留有一个障碍: ADT。ADT定义了一个规则禁止使用来自com.android.internal的类。

有几种方法可以越过该限制规则:

1)完整的ADT源代码可以下载,可以移除或者修改代码,编译然后安装新的定制版本ADT。不好的地方是您必须配置一台64位linux系统,下载源码,编译之。这会花费一些时间,当新版本的ADT出来后,您又需要重新来过。

2)另外一种方式是修改ADT的字节码,只需替换"com/android/inter/**”字符串为其它的字符串,比如"com/android/internax”。

第二种方法可通过脚本自动化完成,也不需要访问源码,在windows下也能工作,这也是我在本文中说明第二种方式的原因。

修改ADT字节码

进入到您的eclipse的plugins文件夹,找出名为com.android.ide.eclipse.adt_*.jar的文件。做一个备份(以防修改错了),另外复制一份改文件到一个单独的"experimental”文件夹,在那里进行字节码修改。

修改*.jar为*.zip,解压文件到一个单独的文件夹,下面就是我所得到的:

现在进入到com/android/ide/eclipse/adt/internal/project子目录,找出AndroidClasspathContainerInitializer.class文件。

该文件包含了字符串"com/android/internal/**”,下一步就是替换该字符串为其它的字符串,如"com/android/internax/**”。改变字符串的长度可能没什么问题,但最好只替换一个字母,保持长度相同。

我是用notepad++进行替换的,因为它支持非打印字符,而且在编辑打印字符时不会修改非打印字符。

修改完后,保存文件,zip压缩文件夹,文件名和原始版本一样。以我的为例:com.android.ide.eclipse.adt_8.0.1.v201012062107-82219.zip,然后重命名为*.jar。

注意:请确保您正确的压缩了文件,可以比较一下修改的zip和原始的zip的内部目录结构。

现在删除eclipse plugins文件夹下的原始ADT*.jar文件,复制经过修改的版本,重启eclipse。

如果没有问题,则会如下图所示:

步骤总结:

  1. 停止eclipse
  2. 从eclipse的plugins文件夹取得adt插件的jar文件。
  3. 重命名.jar为.zip,然后解压到一个单独的目录。
  4. 找到com/android/ide/eclipse/adt/internal/project/AndroidClasspathContainerInitializer.class
  5. 将字符串"com/android/internal/**”替换为"com/android/internax/**”
  6. zip压缩所有文件
  7. 重命名.zip为.jar
  8. 用修改版本替换eclipse plugins文件夹下的原始adt jar文件
  9. 启动eclipse。

使用内部(com.android.internal)和隐藏(@hide)API[第3部分,定制android平台]

本文翻译自https://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-3-custom-android-platform/

在前一篇文章中我展示了如何创建定制的original-android.jar,包含内部API和隐藏API中的所有类。

接下的步骤很明显,就是修改现有的android平台(SDK_DIR/platforms/platform-X/android.jar)。您可以简单的用第2部分创建的original-android.jar替换android.jar,但这样您的所有工程都能够无限制的使用内部API和隐藏API。这不是很合适,因为在大多数工程中,您可能并不允许这样。而且,您基本上希望禁止使用这些API(这是ADT/android.jar的缺省行为),只是在少数工程中要用到内部和隐藏API。

为了达到这种灵活性,需要创建一个新的定制的android平台。当没有必要使用内部和隐藏API时,您使用原始的android平台。当需要访问内部和隐藏API时,您使用定制的android平台。

Android SDK目录树

让我们看看android SDK是如何组织的:

我们需要"platforms”目录,进去瞧瞧:

这里有一个支持的Android平台列表。

现在让我们瞧瞧这是如何与Eclipse设置关联起来的。选择一个Android工程,右键点击->Properties -> Android。您可以看到一个支持平台的列表(它反映的是…/platforms/文件夹)。下面是截图:

创建新的平台

要创建一个定制的平台,需要复制android-9文件夹,命名为android-9-internals,然后做一些修改:

  1. 从android-9-internals文件夹删除android.jar。
  2. 复制original-android.jar,重命名为android.jar。
  3. 修改build.prop文件:

    …  

    ro.build.version.sdk=9 –> ro.build.version.sdk=-9

    …

    ro.build.version.release=2.3 –> ro.build.version.release=2.3.extended

重启eclipse,确认可以看到该新的平台,下面是我所看到的:

为什么我会选择API Level -9? 那是因为必须为一个数字,而且不能是9(或其它已经存在的API Level),否则您的定制平台将不会使用(它会在列表中显示,但不会工作,编译时会取原始的相同数字的平台)。

下面是Libraries视图的截图(当定制平台选中时):

总结

在前一篇文章中,我说明了如何创建一个android.jar的未经裁减版本original-android.jar。本篇文章中,我演示了如何创建使用该original-android.jar的定制android平台。这对于使用隐藏API足够了,但是对于使用内部API还需要更进一步。这是因为ADT仍然禁止使用com.android.internals包中的类(请看上面的截图)。在下一篇文章中,我将告诉您如何定制ADT,允许使用内部API。

使用内部(com.android.internal)和隐藏(@hide)API[第2部分,定制android.jar]

本文翻译自https://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-2-hacking-around/

在前一篇文章中,我解释了为什么不通过反射机制,不太容易使用com.android.internal包的内部API和标记为@hide的隐藏API,这是由于android.jar文件没有包含内部API和隐藏API的类,这样在编译时刻就没有办法引用这些类了。

本文将说明如何恢复原始的android.jar,这样就可以使用内部API和隐藏API了,就如同使用公开API一样。

如何获得原始(未裁减)的android.jar?

我们需要修改android.jar,让它包含所有原始的类文件(包括内部和隐藏API)。有两种方法:

1)Android是一个开源项目。我们可以下载源代码,定制构建系统,不从android.jar中移除内部和隐藏类。这种方法比较难。

2)每个模拟器或者真实设备都有一个与android.jar等价的包用于运行时。我们可以取得此jar,解出原始的.class文件,然后复制到android.jar。

我倾向于第二种方法,它比较容易,不需要linux机器(如果您是在windows下工作),也不需要编译所有的源码、定制构建系统,等等。

从设备获取framework.jar

您可以用命令行(adb pull)或者DDMS(从eclipse,或者android sdk单独启动)从设备或者模拟器中下载文件。

(注:模拟器总是在.dex文件中包含代码,而真实设备通常将代码包含在优化版的dex-odex文件。使用odex文件通常比较难,所以本文推荐用模拟器)

运行时刻和Android SDK中android.jar等价的文件是framework.jar. 该文件位于/system/framework/framework.jar。

adb pull /system/framework/framework.jar

当framework.jar下载后,重命名为framework.zip,然后unzip到一个单独的目录,您应该可以得到如下所示的内容:

 

文件classes.dex就是我们要的。

创建framework-classes.zip

首先我们需要将.dex文件转化为.jar文件格式。这可以用一个小工具dex2jar做到,您仅需要运行:

dex2jar classes.dex

当转化完成后,会得到classes.dex.dex2jar.jar文件,将其命名为framework-class.zip。使用zip文件查看器,进到framework-class.zip/com/android/internal:

哇,我们得到了内部API和隐藏API的.class文件(虽然截图只确认了内部API)。

创建original-android.jar

Android SDK中的android.jar位于ANDROID_SDK/platforms/android-X/android.jar(这里X代表API Level,比如X==9)。

复制android.jar为custom-android.zip。unzip到custom-android文件夹。从framework-class.zip中复制所有.class文件到custom-android文件夹(您需要替换所有已有的.class文件)。

然后zip文件夹custom-android为original-android.zip,重命名为original-android.jar。

步骤总结

  1. 选择目标平台X(我使用API Leve 9的平台,所以X == 9)
  2. 创建平台X的模拟器
  3. 启动模拟器,从中下载/system/framework/framework.jar文件
  4. 重命名framework.jar为framework.zip
  5. 从framework.zip解压出classes.dex
  6. 使用dex2jar将classes.dex转化为classes.jar
  7. 重命名classes.jar为framework-classes.zip
  8. ANDROID_SDK/platforms/android-X/复制android.jar,重命名为custom-android.zip
  9. 解压custom-android.zip到custom-android目录
  10. 从framework-classes.zip复制所有文件到custom-android文件夹(替换已有文件)
  11. zip压缩custom-android文件夹为original-android.zip
  12. 重命名original-android.zip为original-android.jar

完成。

结论

我们恢复了原始的android.jar,包含内部API和隐藏API的.class文件。这只是第一步,下一步是创建定制的平台,使用未裁减版本的android.jar,然后加入到Android SDK platforms目录。

使用内部(com.android.internal)和隐藏(@hide)API[第1部分,介绍]

本文翻译自http://devmaze.wordpress.com/2011/01/18/using-com-android-internal-part-1-introduction/#more-79

Android有两类API在SDK中不能使用。

第一类就是位于包com.android.internal的API,我将这些API称为内部API。第二类API就是用@hide标记的类和函数,虽然严格说这不是一个API而是一系列隐藏API的集合,我仍然假定这是一个API,称为隐藏API。

隐藏API的例子

你阅读android的源码,就会发现有些常量、函数和类标记为@hide.

这里有一个隐藏常量的例子,来自WifiManager(source code of API Level 10).

另外一个例子是隐藏函数setWifiAppEnabled,来自WifiManager(source code of API Level 10).

所以您只要看到@hide属性,这就是一个隐藏API。

内部API和隐藏API的不同

隐藏API隐藏是为了防止开发人员使用SDK中未完成或者未稳定(接口和架构方面看)的部分。比如,Bluetooth API在API Level 5(android 2.0)之前就存在,但在API Level 3和4(android 1.5和1.6)中使用@hide隐藏起来了。当该API稳定下来,google的开发人员移除@hide属性,在API Level 5中就有Bluetooth API了。还有很多东西在Level 4和5之间发生了变化。如果程序依赖于某些隐藏API,可能会在新版本的Android OS上运行出现问题。

而内部API则不计划对外开放。这是android的内部餐厅,开发人员可以视为黑盒子。这里面的东西同样可能发生改变。同样的,如果您的程序依赖于内部API,在新的Android发布后,可能遇到麻烦。

下面总结它们之间的不同:

隐藏API = 正在开发中;

内部API = 黑盒

内部和隐藏API的编译时和运行时对比

当您使用Android SDK进行开发时,会引用一个非常重要的jar文件android.jar。它位于Android SDK的平台目录SDK_DIR/platforms/platform-X/android.jar(其中X为API Level,可以是5或者10或其它的数字)。在android.jar中,com.android.internal中所有的类移除了,同样的,所有标记为@hide的类、枚举、字段、方法也移除了。

但是当您在设备中运行应用程序时,加载的是framework.jar(大约等价于android.jar),它没有被裁减,包含所有的内部类和隐藏API。所以您可以使用反射机制来访问隐藏API和内部API(当然,这种方法使用起来不太方便,下面我将介绍不使用反射机制访问这些API的方法)。

关于内部API还有一些特别。Eclipse的ADT插件增加了一条额外规则,禁止使用来自com.android.internal包的任何东西。所以,即使您使用了原始(未裁减)的android.jar,也不容易在eclipse中使用内部API。

您可以自己检查一下。在eclipse中创建一个新的Android工程(或使用现有的),查看它的引用库(右键点击工程,Properties –> Java Build Path –> Libraries)。

重要总结:在SDK中内部API和隐藏API处理方式基本上相同(都从android.jar中移除),但内部API在Eclipse ADT插件中显示禁止了。

不通过反射机制使用内部API和隐藏API

本系列文章的终极目标是给程序员不用反射而使用内部API和隐藏API的方法。如果您完成了在后面文章中的所有步骤,您将可以向象使用官方API那样使用内部API和隐藏API,没有必要使用反射。

但是如果您使用这些非公开的API,必须意识到存在一个巨大的风险。首先不能保证这些API在Android OS升级后不会变化,其次也不能保证在不同厂家的不同设备上有一致的行为。这完全取决您自己。

有以下三种场景:

  1. 开启内部API和隐藏API(场景A)
  2. 仅开启隐藏API(场景B)
  3. 仅开启内部API(场景C)

场景A是场景B和C的综合。场景B是最简单的(不需要修改eclipse ADT插件)。

场景A:阅读1,2,3,4,5

场景B:阅读1,2,3,5

场景C:阅读1,2,3,4,5