关闭

Android 源代码中增加新apk

228人阅读 评论(0) 收藏 举报
Android build system就是编译系统的意思 
在我们需要向自己编译的源代码中增加模块的时候,需要一些规则,当然这个规则都是类似的。 

Android.mk文件解析 


让我们来看一个 Android.mk 文件的样子 
Java代码
  1. LOCAL_PATH := $(call my-dir)

  2. include $(CLEAR_VARS)

  3. LOCAL_MODULE :=Hello

  4. LOCAL_SRC_FILES := hello.c

  5. include $(BUILD_SHARED_LIBRARY)
    复制代码
复制代码
① LOCAL_PATH :=$(call my-dir) 
固定写法, LOCAL_PATH 表示此时位于工程目录的根目录中, (call my-dir) 的功能由编译器提供,被用来返回当前目录的地址(包含 Android.mk 本身) 
② include $(CLEAR_VARS) 
固定写法, CLEAR_VARS 这个变量由编译系统提供,并且要执行一个 GNU makefile 文件,这个功能会清理掉所有以 LOCAL_ 开头的内容(比如 LOCAL_MODULE 、 LOCAL_SRC_FILES 等),除了 LOCAL_PATH 。这句话也是必须的,因为如果所有变量都是全局变量的话,所有的可控的编译文件都需要在一个单独的 GNU 中被解析并执行 
③ LOCAL_MODULE :=Hello 
LOCAL_MODLE 变量必须被定义,用来区分 Android.mk 中的每一个模块。文件名必须是唯一的,不能有空格。注意,编译器会为你自动加上一些前缀和后缀,来保证文件是一致的。比如:这里表明一个动态链接库模块被命名为“ Hello ”,但是最后会生成“ libHello.so ”文件。但是在 java 中装载这个库的时候还要使用“ Hello ”名称。 
④ LOCAL_SRC_FILES :=hello.c 
LOCAL_SRC_FILES 变量必须包含一个 C 和 C++ 源文件的列表,这些会被编译并聚合到一个模块中 
注意:这里并不需要列头文件和被包含的文件,因为编译系统会自动为你计算相关的属性,源代码的列表会直接传递给编译器 
⑤ include $(BUILD_SHARED_LIBRARY) 
BUILD_SHARED_LIBRARY 这个变量由系统提供,并且指定给 GNU makefile 的脚本,它可以收集所有你定义的”include $(CLEAR_VARS)” 中以 LOCAL_ 开头的变量,并且决定哪些要编译,哪些应该做的更加准确。我们同样可以使用 BUILD_STATIC_LIBRARY 来生成一个静态库,如果使用 BUILD_STATIC_LIBRARY 编译系统便会生成一个以“ lib$(LOCAL_MODULE).a ”为文件名的文件来提供动态库的调用 
⑥ TARGET_ARCH 
TARGET_ARCH 是指架构中 CPU 的名字已经被 Android 开源代码明确指出了,这里的 arm 包含了任何 ARM-独立结构的架构,以及每个独立的 CPU 版本 
⑦ TARGET_PLATFORM 
Android 平台的名字在 Android.mk 文件中被解析,比如 ”android-2.3” 
⑧ TARGET_ROOT_OUT :表示根文件系统 
用法: CAL_MODULE_PATH:=$(TARGET_ROOT_OUT) 
⑨ TARGET_OUT: 表示 system 文件系统 
⑩ TARGET_OUT_DATA: 表示 data 文件系统 
? TARGET_ABI 
TARGET_ABI 平台目标板和 abi 的链接,这里要定义 $(TARGET_PLATFORM)-$(TARGET_ARCH_ABI) ,它们都非常有用,特变是当你想测试一个具体的系统镜像在几个真实设备的时候 
下面是 GNU 编译出来的宏,并且它们都必须使用“ $(call <function>) ”才能返回文字化的信息。 
all-subdir-makefiles :返回一个 Android.mk 文件所在位置的列表,以及当前 my-dir 的路径。 
比如: include $(call all-subdir-makefiles) 
this-makefile :返回当前 makefile 的路径(就是哪个功能被调用了) 
parent-makefile :返回 makefile 的包含树,也就是包含 makefile 的当前文件 
Application.mk 


要讲 C\C++ 编译为 so 文件,光有 Android.mk 文件是不行的,还需要 Application.mk 文件。 
Application.mk 文件存放的位置是 NDK 工程的根目录, Application.mk 文件就是用来描述应用程序中所需要的原生的模块(也就是静态库和动态库),每一个 Application.mk 文件都必须放在应用目录下的子目录,例如$NDK/apps/HelloNdk/Application.mk ,作为 GNU makefile 的一部分, Application.mk 文件必须要定义以下部分 
1、 APP_MODULES 
APP_MODULES 变量是强制性的,并且会列出所有你所需要的模块(通过 Android.mk ) 
2、 APP_PROJECT_PATH 
APP_PROJECT_PATH 变量也是强制性的,并且会给出应用程序工程的根目录一个绝对路径。这是用来复制或者安装一个没有任何版本限制的 JNI 库,从而给 APK 生成工具一个详细的路径。 
例如: \HelloNDK\Application.mk 
APP_PROJECT_PATH := $(call my-dir)/project 
APP_MODULES := HelloNdk 
这里定义了工程路径为 $(call my-dir)/project ,而要编译的模块则是 HelloNdk ,这样编译系统才会找到我们要编译的库和原文件 
3、 APP_CFLAGS 则是当要编译模块中有任何 C 文件的时候, C 编译器的信号就会被发出 
4、 APP_OPTIM 
这个变量是可选的,可以定义为发布版或者测试版 
------------------------------------------------------------------------------------------------------------------------ 
在Android.mk中: 
include $(BUILD_EXECUTABLE)表示生成二进制可执行文件 
include $(BUILD_STATIC_LIBRARY)表示生成静态库.a文件,名字叫做lib<工程名>.a 
include $(BUILD_SHARED_LIBRARY)表示生成动态库.so文件,名字叫做lib<工程名>.so 

另外需要注意的是,生成的文件需要放在手机的data/local目录下,才可以有执行的权限(未root),如果root了,则会有些目录是可以执行,但是某些目录依然不能执行,当然可以用umount命令解决。SD卡是没有执行权限的,所以当你生成的比如二进制可执行文件放到sdcard中时,是无法运行的。 

可以这样测试一下哪些是有执行权限,哪些是没有的: 
*将手机插入电脑,并打开USB调试 
*在终端输入adb shell进入 
*su(root了的手机) 
*mount:可以看到一大堆的列表,如果对应的目录的信息中有noexec,说明这个目录就没有执行权限,剩下的都可以执行二进制等文件。 

Android.mk文件的具体语法参见我的博客:http://hualang.iteye.com/blog/1140414 

向Android源代码中增加模块主要有如下几种: 

1、增加可执行文件 
增加可执行文件,这些可执行文件一般都是由C/C++生成,下面简单的介绍一些如何向源代码中生成可执行文件 
假设我的源代码在~/mydroid目录下 
在external/loulijun/test目录下,创建两个文件,一个C,一个Android.mk 
hello.c 
C代码
  1. #include <stdio.h>

  2. int main(void)

  3. {

  4. printf("Hello world!\n");

  5. return 0;

  6. }
    复制代码
复制代码
Android.mk 
Java代码
  1. LOCAL_PATH := $(call my-dir)

  2. include $(CLEAR_VARS)



  3. LOCAL_SRC_FILES :=hello.c

  4. LOCAL_MODULE_TAGS :=optional

  5. LOCAL_MODULE :=test



  6. include $(BUILD_EXECUTABLE)
    复制代码
复制代码
首先退出到mydroid目录下,执行 
Java代码
  1. . build/envsetup.sh

  2. 或者

  3. source build/envsetup.sh
    复制代码
复制代码
进行环境变量的配置 
然后进入到test目录下,执行“mm”(mm表示编译当前项目),如果想重新执行,可以"mm -B" 

这样,会在out/target/product/generic/obj/EXECUTABLES/test_intermediates/LINKED/目录下生成可执行文件test 

然后将test文件用adb push test /data/local 到data/local目录下。 

下面开始执行,你可以在手机中用terminal emulator来执行,也可以以adb shell后,执行 
Java代码
  1. ./test

  2. 显示:Hello world!
    复制代码
复制代码
2、增加静态库(.a) 
Android.mk文件 
Java代码
  1. LOCAL_PATH :=$(call my-dir)

  2. include $(CLEAR_VARS)



  3. LOCAL_SRC_FILES := \

  4. hello.c

  5. LOCAL_MODULE :=test



  6. include $(BUILD_STATIC_LIBRARY)
    复制代码
复制代码
编译: 
mydroid#. build/envsetup.sh 
test#mm 
生成的结果在out/target/product/generic/obj/STATIC_LIBRARY 
目标文件夹{XXX}_static_intermediates下,XXX为你定义的模块名称test 

假如这个静态库是由hello.c生成的,但是生成的静态库是不能直接使用的,而是由动态库调用它 

3、增加动态库(.so) 
编译动态库其实可以用NDK的,那样生成非常方便,但是有时候还是需要掌握其他的方法的 
Android.mk 
Java代码
  1. LOCAL_PATH :=$(call my-dir)

  2. include $(CLEAR_VARS)



  3. LOCAL_SRC_FILES := \

  4. hello.c

  5. LOCAL_MODULE :=test



  6. include $(BUILD_SHARED_LIBRARY)
    复制代码
复制代码
编译的放法都差不多,只不过Android.mk不同的是最后一句,如果比较一下就会发现那句话决定了生成的是什么 

不过要想生成动态库,绝非是这么简单的,有时候只需要Android.mk和源文件即可,但是有时候还需要Application.mk文件。Application.mk文件的具体语法很快会在博客中更新 




下面是使用 NDK 来生成 .so 文件的,环境是在 ubuntu11.04 下面 
1、 下载 Android-NDK-r6 ,将其解压到 ~/android/android-ndk-r6 目录下 
2、 配置 .bash_profile 文件,加入 
NDK=~/android/android-ndk-r6 
export NDK 
3、 cd $NDK 后,进入 ndk 的目录,我以它自带的项目为例,进入 samples/hello-jni 
在终端输入 $NDK/ndk-build 
系统会自动编译这个文件,并将生成的 libhello-jni.so 文件存放在当前目录的 libs/armeabi 目录下 
4、 我们可以将这个生成的 libhello-jni.so 放在 Android 源代码的适当的位置即可使用了 
下面是相应的文件 
hello-jni.c 
Java代码
  1. #include <string.h>

  2. #include <jni.h>

  3. jstring

  4. Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env,

  5. jobject thiz )

  6. {

  7. return (*env)->NewStringUTF(env, "Hello from JNI !");

  8. }
    复制代码
复制代码
Android.mk 文件 
Java代码
  1. LOCAL_PATH := $(call my-dir)



  2. include $(CLEAR_VARS)



  3. LOCAL_MODULE := hello-jni

  4. LOCAL_SRC_FILES := hello-jni.c



  5. include $(BUILD_SHARED_LIBRARY)
    复制代码
复制代码
由于这里只是使用了一个简单的 C 文件,所以没用用到 Application.mk 文件 
然后将

hello-jni 导入到 eclipse 中,运行模拟器,即可显示

[local]1[/local]
下面再看一个例子 native-activity
1、 进入目录后执行 $NDK/ndk-build
2、 生成 libnative_activity.so 并存于当前目录的 lib/armeabi 目录下,另外由源代码生成的还有静态库,存放于obj/local/armeabi/ libandroid_native_app_glue.a
这里,由于 main.c 比较大,就不贴上了
Android.mk
Java代码

  1. LOCAL_PATH := $(call my-dir)



  2. include $(CLEAR_VARS)



  3. LOCAL_MODULE := native-activity

  4. LOCAL_SRC_FILES := main.c

  5. LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM

  6. LOCAL_STATIC_LIBRARIES := android_native_app_glue



  7. include $(BUILD_SHARED_LIBRARY)



  8. $(call import-module,android/native_app_glue)
    复制代码
复制代码
Application.mk 
Java代码
  1. APP_PLATFORM := android-10<span style="white-space: normal;"> </span>
    复制代码
复制代码
运行结果:颜色会逐渐变浅,然后再次从这个颜色变浅 

[local]2[/local] 

4、增加apk文件(有源代码) 


如果将 android 程序的源代码加入到 build system 中呢 
(1) 在 eclipse 开发环境中创建你的 android 工程,比如叫做 Success 
(2) 将工程拷贝到源代码的 package/apps 目录下 
(3) 进入 Success 目录下,创建一个 Android.mk 文件,内容如下 
Java代码
  1. LOCAL_PATH :=$(call my-dir)

  2. include $(CLEAR_VARS)



  3. LOCAL_MODULE_TAGS :=optional

  4. LOCAL_SRC_FILES :=$(call all-java-files-under, src)



  5. LOCAL_PACKAGE_NAME :=(工程名字)

  6. LOCAL_CERTIFICATE :=platform

  7. include $(BUILD_PACKAGE)
    复制代码
复制代码
(4) 退回到 android 源代码目录下,执行 
#. build/envsetup.sh 
#mmm packages/apps/Success 
编译成功之后,会在 out/target/product/generic/system/app/Success.apk 
(5) 如果要在真机上测试, system 的目录是在 out/target/product/crespo 目录下,编译的时候需要设置一些参数。为了测试,将 crespo 中的 system 记其他内核等文件放入到一个叫做 samsung 的文件夹中,再将Success.apk 放到 system/app 中 
(6) 用 #zip –r update.zip . 命令将其打包为 zip ,也可以用 zip 直接压缩 
(7) 用 #java –jar testsign.jar Samsung/update.zip update.zip 将 zip 包签名 
(8) 打开手机的 usb 调试,连接到电脑上,在终端输入 #adb push update.zip /sdcard/update.zip ,将 zip 包上传到设备的 sdcard 目录下 
(9) 输入 #adb reboot bootloader 进入 bootloader 界面 
(10) 输入 #fastboot flash recovery recovery.img 刷 recovery, 我刷的是 Recovery 3.0 
(11) 进入 Recovery 选项,刷机,重启后就可以见到 Success.apk 程序了 
注意:修改 AndroidManifest.xml ,在 manifest 标签中加入 android:sharedUserId=”media” ,当然这个 media只是个 id ,它的名字随便一般类似包名。我们知道,在不同的 apk 包中默认是不能相互访问数据的,但是如果在 AndroidManifest.xml 中设置 sharedUserId 并且相同的话,那么这两个包就可以相互访问数据。由于我写的只是个测试程序,所以没有加入这条 

5、增加apk文件(无源代码) 


(1) 这种方式最简单,就是将 ***.apk 文件拷贝到编译 android 源代码时候生成的out/target/product/crespo/system/app 中,执行 make snod 后就可以把 apk 添加到 system.img 中了,然后将system 目录及其他的几个文件打包成 zip 并签名后即可,刷机后可以看到这个内置的系统程序。 
(2) 上一种方式在 make clean 之后,再次 make 以后才能执行上述的操作,比较麻烦 
① 新建一个目录,在 packages/apps 下面,专门用来存放 apk 文件 
#mkdir packages/apps/apks 
#cd packages/apps/apks 
在这个目录中新建一个 Android.mk 文件 
LOCAL_PATH := $(call my-dir) 
include $(CLEAR_VARS) 
LOCAL_POST_PROCESS_COMMAND := (shell cp –r $(LOCAL_PATH)/*.apk $(TARGET_OUT)/app/) 
保存退出 
② 把需要编译的 apk 拷贝到 apks 目录中,执行 make ,在 apks 中的 apk 就会被拷贝到out/target/product/generic/system/app 中 
③ 执行 make snod 即可 
这样,在执行 make clean 之后,再次 make ,只需要 make snod 即可了
0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:238次
    • 积分:4
    • 等级:
    • 排名:千里之外
    • 原创:0篇
    • 转载:1篇
    • 译文:0篇
    • 评论:0条
    文章存档