欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

android系统源码中添加app源码(源码部署移植)

程序员文章站 2024-02-26 23:19:52
...

转载android系统源码中添加app源码(源码部署移植)

涉及到系统定制,需要在系统中加入自己的apk工程,但是上网找了很多资料都是不够全面的,或者看了还是没搞懂,我自己也是一点点摸索过来的,花了不少的时间,也是踩了不少的坑,因此特开一文,帮助大家渡河。

申明,本文亲测有效,如果有疑问,欢迎在下方留言。人人为我,我为人人

如需要深入了解make文件的编写和build系统,可以先看看这篇文章《Android系统源码Build入门详解》,本篇文章不对make文件进行深入的解释,但是会在make文件上做相当部分的注释。如果你是刚接触系统源码移植,强烈建议按照以下阅读顺序一个一个Demo自己写,然后测试运行,在确保理解的前提下提高自己的编码水平和源码认知水平。

文章思路大致为:

最简单的android源码移植HelloWorld
含有jni本地代码的源码移植
含有第三方jar包的源码移植
往系统源码中添加自己的纯java源码并打包成jar
含有第三方so文件的源码移植
既有so又有第三方jar的源码移植
自有java源码包+so+三方jar+本地源码的综合项目
添加公用so共享库并打包进system.img
注意和总结

移植HelloWorld

相信我们接触Android第一个练手项目就是HelloWorld,不过那是在IDE上开发的,这次我们需要把这个HelloWorld工程移植到系统源码中,并编译成目标Apk。关于不带jni的项目也可以参考源码树中/development/samples/HelloActivity工程,可以说,google为开发者考虑的还是很周到的。

(一)修改mk文件。
1.在Android内核源码中选择一个目录来存放HelloWorld应用的源码,比如放到/packages/apps目录下。
2.在HelloWorld目录下新建Android.mk文件,示例如下:

    LOCAL_PATH:= $(call my-dir)
    #清理缓存变量
    include $(CLEAR_VARS)
    #表示目标模式
    LOCAL_MODULE_TAGS := samples
    #表示源文件编译路径 这个应用里面只有java源文件
    LOCAL_SRC_FILES := $(call all-java-files-under, src)
    #表示项目包名也就是模块名,在项目中唯一
    LOCAL_PACKAGE_NAME := HelloWorld
    #指定编译sdk版本为当前版本
    LOCAL_SDK_VERSION := current
    # 使用该指令编译目标Apk.
    include $(BUILD_PACKAGE)
    #搜索编译该源码目录下所有的mk文件,如果没有可以不写
    include $(call all-makefiles-under,$(LOCAL_PATH))

注:LOCAL_MODULE_TAGS的备选值有user,eng,tests,optional,本示例中使用的TAGS值为eng(工程模式,自带root),因此,仅当用户指定的编译选项为eng时才会编译该工程。否则会停止编译,默认值为optional。具体可以参考文章《Android系统源码Build入门详解》

(二).配置makefile,添加新的项目

注意,这是很关键的一步,如果忽略了你是没办法在编译成的system.img里面找到这个工程的。

选择 /device// /xxxx.mk,或者从 build/target/product/ 目录下选择一个被“引用”的.mk,比如我用的是build/target/product/core.mk ,如果是在/devices/下指定产品下修改添加,则只会在指定目标产品生效,如果你的工程对所有产品有效,建议添加在/build/target/product/core.mk中。

最重要的是在其中的PRODUCT_PACKAGES参数列表中添加本工程:如果不添加的话整编出来的竞相里面是没有添加到源码树的应用的。下面要提到的添加公用so文件也是需要在这个地方添加,添加的方式则是把mk文件中的标识名填上(LOCAL_PACKAGE_NAME或者LOCAL_MODULE)。

    PRODUCT_PACKAGES := \ 
    DeskClock \ 
    Calculator \ 
    Calendar \ 
    Camera2 \ 
    Email \ 
    HelloWorld//这个是我的工程

(三)编译
在HelloWorld目录下输入mm命令,或者切换到Android源码根目录下执行下面任意一条命令即可:

make HelloWorld
mmm package/apps/HelloWorld

编译生成的apk会放到在out/target/product/《your product》/system/app/目录下。
含有第三方jar

Android开发中经常会用到一些开源库,使用Eclipse作为开发工具的时代,第三方库基本都是jar包的形式存在的。现在我们讨论一下怎么在源码中使用第三方库。
使用第三方库其实也是很简单,只需要修改mk文件,按照如下方式修改即可,有一些公用的库可能在你的系统远吗工程需要多次引用,你可以把这些库单独做一个Android.mk并对每一个要使用的库添加标识。

第三方库包括两个部分,一个是引用,一个是模块定义。正如上面所说,这两个是可以放在不同的mk文件中的,假设我们的apk程序名为SayhelloFromJar,在这个apk里面引用了一个叫testlib.jar的第三方库。我们这么写:
Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_STATIC_JAVA_LIBRARIES := testlib

# Only compile source java files in this apk.
LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_PACKAGE_NAME := SayhelloFromJar
LOCAL_PRIVILEGED_MODULE := true

#LOCAL_SDK_VERSION := current

include $(BUILD_PACKAGE)

#############################################
##上面这部分和前面的HelloWorld基本是没区别的,唯一的区别就是添加了一句
##LOCAL_STATIC_JAVA_LIBRARIES := testlib
##下面则是对于这个jar的定义,假设这个jar在libs/testlib.jar
##################### add third part library #######################
include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES :=testlib:/libs/testlib.jar
LOCAL_MODULE_TAGS := optional
include $(BUILD_MULTI_PREBUILT)

上面是合并的写法,前面说过,这个jar很可能要被多个apk引用,为了管理方便,我们可以分开来写:
Apk的Android.mk

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_STATIC_JAVA_LIBRARIES := testlib

# Only compile source java files in this apk.
LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_PACKAGE_NAME := SayhelloFromJar
LOCAL_PRIVILEGED_MODULE := true

#LOCAL_SDK_VERSION := current

include $(BUILD_PACKAGE)

那么,jar的mk文件怎么写呢?其实就是挪到了两个不同的make文件中了而已。

include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES :=testlib:/libs/testlib.jar
LOCAL_MODULE_TAGS := optional
#实际上就是一个copy的过程,把jar包放到了out/...obj/下
include $(BUILD_MULTI_PREBUILT)

错误提示:

错误1:
如果有引用第三方jar包但是没有进行以上配置,则会出现类似如下错误:

make: *** No rule to make target 'out/target/common/obj/JAVA_LIBRARIES/BaiduLBS_Android_intermediates/javalib.jar', needed by 'out/target/common/obj/APPS/Voice_intermediates/classes-full-debug.jar'.  Stop.
make: *** Waiting for unfinished jobs....

错误2:
还有一点需要注意,:=和+=是不一样的:=是赋值操作,也就是如果你要引用多个jar包,就不能都用:=了,有两种方式:
第一种就是不同的jar的定义用 \ 隔开,比如这样:

LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := android-support-v4:/libs/android-support-v4.jar \
BaiduLBS_Android:/libs/BaiduLBS_Android.jar \
bugly_crash_release:/libs/bugly_crash_release.jar

或者这样写:

LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := android-support-v4:/libs/android-support-v4.jar
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES += BaiduLBS_Android:/libs/BaiduLBS_Android.jar
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES += bugly_crash_release:/libs/bugly_crash_release.jar

虽然是很细微的区别,但是很容易在这里踩坑,切记切记;
添加自己的源码包依赖

不知道怎么形容,所以就暂时称之为源码包依赖把,所谓源码包依赖,就是还没有打包成jar包的java源码,也就是源代码,Android系统远吗中是没有jar包的,都是使用源码的方式,这充分体现了Android系统开源的本质。这个源码包就是可以用来编译成jar文件的源码文件夹。我们需要把这个源码文件夹在系统源码中通过编写make文件的方式编译成所需要的jar,从而被apk使用。

假如我们要使用的源码的模块名定义为testlib,那么Android.mk文件可以这样写:
jar的Android.mk

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
#jar标识,被其他模块引用的时候就是直接饮用这个标识
LOCAL_MODULE := testlib
#可以写可以不写,这里写8就是说这个代码班一起的版本号为8
LOCAL_SDK_VERSION := 8
#源码所在目录,这里表示包含了所有当前目录下所有的java文件
LOCAL_SRC_FILES := $(call all-java-files-under, src/java)
#把这个jar包编译成java静态代码库的方式
include $(BUILD_STATIC_JAVA_LIBRARY)

含有jni本地代码(不包含So文件)

对于含有jni的项目但是不含有so文件或者.a或者其他目标文件,则需要修改上述新建的Android.mk,如下(也可以参照源码树中的SimpleJNI工程,位于development/samples/SimpleJNI):

 TOP_LOCAL_PATH:= $(call my-dir)
    # 定义LOCAL_PATH,这两句的意思相当于LOCAL_PATH:= $(call my-dir)
    LOCAL_PATH:= $(TOP_LOCAL_PATH)
    #清理缓存变量
    include $(CLEAR_VARS)
    #表示目标模式,sample表示是示例代码,我们根据实际需要修改
    LOCAL_MODULE_TAGS := samples
    #编译java文件
    LOCAL_SRC_FILES := $(call all-subdir-java-files)
    #模块名称
    LOCAL_PACKAGE_NAME := SimpleJNI
    #编译成的so文件名称,so文件会安装到输出目录的system/lib下
    LOCAL_JNI_SHARED_LIBRARIES := libmynative
    #去掉代码混淆,默认是混淆的
    LOCAL_PROGUARD_ENABLED := disabled
    #使用当前sdk版本编译
    LOCAL_SDK_VERSION := current
    #编译目标apk
    include $(BUILD_PACKAGE)

    # ============================================================
    # 如果该目录下还有其他的mk文件也进行编译,因为这个项目里面jni相关的文件放在了jni目录下编译
    # 推荐大家熟练以后也这么做,看起来好看很多,当然你也可以把mk文件合并为一个。
    include $(call all-makefiles-under,$(LOCAL_PATH))

jni目录下的Android.mk如下(假设jni目录下有inc和src目录,inc下):

 LOCAL_PATH := $(call my-dir)
    include (CLEAR_VARS)  
    # 目标模式,可以不写
    LOCAL_MODULE_TAGS := samples
    # 编译成的莫表模块名称,引用的时候就是引用这个
    LOCAL_MODULE := libmynative  
    # 原文件所在路径,这里只有一个c文件
    LOCAL_SRC_FILES := src/mynative.c  
    # jni里面链接到的共享库,没有可以不写
    LOCAL_SHARED_LIBRARIES := libutils liblog
    # 引用的静态哭,没有可以不写或者像下面这样
    LOCAL_STATIC_LIBRARIES :=
    # jni头文件通过这种方式包含进来
    LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
    # 同上
    LOCAL_C_INCLUDES += $(LOCAL_PATH)/inc
    # 编译共享库,生成的so文件呗安装到输出目录的system/lib下
    include $(BUILD_SHARED_LIBRARY)

注意:所有jni相关的代码产生的目标文件和的使用和jar包不一样,因为它是在system目录下,因此如果不把so文件刷到设备,设备是没有这个本地库的。如果没有则需要PRODUCT_PACKAGES添加
含有so文件不含C文件

这种情况在引用第三方sdk的时候经常用到,比如百度地图高德地图,比如腾讯bugly,为了叙述简单,这里只提添加so文件,后面会有一个整合的例子,大家明白就好。这里我就把自己的项目中使用到的代码贴出来了。
这个里面涉及到三处需要注意,一个是目标apk的Android.mk文件的修改,这里是引用so文件的主体工程,引用的方式则是通过指定LOCAL_SHARED_LIBRARIES,第二处是so文件对应的Android.mk文件的编写,第三处是PRODUCT_PACKAGES添加so的标识名。

(1) 在主apk工程的mk文件里面加上 LOCAL_SHARED_LIBRARIES,多个库引用之间空格隔开。假设我们引用的so库文件有三个,分别为libBaiduMapBase libBaiduMapSDK libBaiduLocation,位置则是在AppPath/jni/下。

   LOCAL_SHARED_LIBRARIES := libBaiduMapBase libBaiduMapSDK libBaiduLocation

如果你的百度地图的库放在你的工程目录下,需要你编译制定的mk文件则可以在主apk工程的mk文件后面加上:

#假设so对应的mk文件在jni目录下
    include $(LOCAL_PATH)/jni/Android.mk
    #或者
    include $(call all-makefiles-under,$(LOCAL_PATH))
    #其中,这个调用当前目录所有的make文件则会自动搜索不需要指定目录

(2) So文件对应的Android.mk文件

LOCAL_PATH := $(call my-dir)

    ##################### add libBaiduMapSDK_map_v4_5_0 library #######################
    include $(CLEAR_VARS)
    LOCAL_MODULE:=libBaiduMapSDK_map_v4_5_0
    LOCAL_SRC_FILES:=libBaiduMapSDK_map_v4_5_0.so
    LOCAL_MODULE_TAGS:=optional
    LOCAL_MODULE_CLASS:=SHARED_LIBRARIES
    LOCAL_MODULE_SUFFIX:=.so
    include $(BUILD_PREBUILT)

    ##################### add liblocSDK7a library #######################
    include $(CLEAR_VARS)
    LOCAL_MODULE:=liblocSDK7a
    LOCAL_SRC_FILES:=liblocSDK7a.so
    LOCAL_MODULE_TAGS:=optional
    LOCAL_MODULE_CLASS:=SHARED_LIBRARIES
    LOCAL_MODULE_SUFFIX:=.so
    include $(BUILD_PREBUILT)

    ##################### libBaiduMapSDK_base_v4_5_0 #######################
    include $(CLEAR_VARS)
    LOCAL_MODULE:=libBaiduMapSDK_base_v4_5_0
    LOCAL_SRC_FILES:=libBaiduMapSDK_base_v4_5_0.so
    LOCAL_MODULE_TAGS:=optional
    LOCAL_MODULE_CLASS:=SHARED_LIBRARIES
    LOCAL_MODULE_SUFFIX:=.so
    include $(BUILD_PREBUILT)

注意,不建议你修改LOCAL_MODULE:=libBaiduMapSDK_base_v4_5_0
因为生成so文件的时候就是按照这个来生成的,可能java调用jni的时候需要对应上so文件名,这个我没验证,但是为了保险起见建议不要修改,不过也说不定,因为这个LOCAL_MODULE只是用来在build系统中标识的,改了也应该没关系,有兴趣的朋友可以试试。

网上还有一种写法,这里也写出来供大家参考,实现的效果是一样的。

##################### add third part library #######################
    #include $(CLEAR_VARS)
    #LOCAL_MODULE := libBaiduMapSDK_base_v4_5_0
    #LOCAL_SRC_FILES := libBaiduMapSDK_base_v4_5_0.so
    #include $(PREBUILT_SHARED_LIBRARY)

既有so又有第三方jar

这种场景也很常见,很多厂商的sdk不仅带有jar,jar里面还有本地代码的引用,有些是为了加密防止核心代码泄露,有些使用到了自定义的底层库,不管怎么样,我们来搞一搞;
因为前面有介绍单独关于so文件的移植和第三方jar的移植,这个例子只是对这两个做一个合并,其实合并起来也是非常简单,在哪个里面引用到了就在哪个里面添加引用的代码,可能有些朋友被前面的东西搞晕了,因此我还是写出来,帮助大家梳理一下;

这是一个既有so引用又有jar引用的apk的Android.mk,以百度地图为例

LOCAL_PATH:= $(call my-dir)  
include $(CLEAR_VARS)  

LOCAL_MODULE_TAGS := optional  

LOCAL_STATIC_JAVA_LIBRARIES := libbaidumapapi  

LOCAL_SRC_FILES := $(call all-subdir-java-files)  

LOCAL_PACKAGE_NAME := MyMaps  

include $(BUILD_PACKAGE)  

###########有人说可以这样写,我没试过,有兴趣可以试试#############  
#include $(CLEAR_VARS)  
#LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES :=libbaidumapapi:libs/baidumapapi.jar  
#LOCAL_PREBUILT_LIBS :=libBMapApiEngine_v1_3_1:libs/armeabi/libBMapApiEngine_v1_3_1.so  
#LOCAL_MODULE_TAGS := optional  
#include $(BUILD_MULTI_PREBUILT)  
###########################################################

###########我自己是这么写的###################################
#添加so文件依赖(So的写法参照《含有so文件不含C文件》)
LOCAL_SHARED_LIBRARIES := libBMapApiEngine_v1_3_1
include $(CLEAR_VARS)
LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES :=libbaidumapapi:libs:/libs/baidumapapi.jar
LOCAL_MODULE_TAGS := optional
include $(BUILD_MULTI_PREBUILT)

######## Use the following include to make our testapk.  
include $(callall-makefiles-under,$(LOCAL_PATH))  

含有jni文件

如果公司在做一套软件硬件结合的产品,通常会遇到需要在应用层打包So库的情况,当然,即使应用层不需要关心怎么打包so库,懂这个方法总比不懂强;毕竟现在安卓工程师越来越不好找工作,懂点高阶的知识是和新手拉开差距的必要手段;
以一个HelloJni的工程为例,代码的实现请移步Idea开发Jni程序(HelloJni)这里直说Android.mk怎么实现,以及在源码树打包我们需要的so文件并作为apk工程的一部分;

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

在应用中使用这个so库则只要使用:

static {
        System.loadLibrary("hello-jni");
    }

注意:
hello-jni和mk中的LOCAL_MODULE 一一对应,写错了就找不到的;
如果你写的是cpp而不是c记得在Android.mk中添加这行,否则会报错误fatal error: iostream: No such file or directory:

LOCAL_CPP_EXTENSION := .cxx .cpp .cc

其实HelloJni在安卓源码树的development/ndk/samples/hello-jni/下也有提供示例代码,在示例代码中我们发现不只有Android.mk,同级目录下还有一个Application.mk,这个文件是可选的用来详细描述你的项目,尽管你开始的话不一定需要它,但是它允许你使用更多的CPU或者覆盖编译器/链接器的标记;
当然,如果你只是针对某一个产品编译的话,不需要关心这个;
含有So文件且含有jni文件

这里我就不写了,因为我自己没有这样实现,但是我相信这个思路是正确的。Android在make的时候,根据mk文件来编译的,mk中可以制定编译成的共享库或者java库或者apk或者其他的,但是不管怎么样,没有mk生成的目标文件都是有指定module名称的,要引用这个module或者库,只需要指定引用的名称和方式即可,所以,建议给具体的库单独写一个mk文件以方便其他应用使用。
注意和总结

前面提醒过的一天我这里在强调一下,因为很多朋友看文章的时候不仔细,常常漏掉在core.mk中配置apk或者so库,从而导致刷如手机后没有这个应用或者应用打开后崩溃说没有满足的库或者本地方法。

因此不仅仅是编写项目的mk文件就行了,一定要在/build/target/product/core.mk下的PRODUCT_PACKAGES添加自己的模块,或者在devices下添加(表示只在编译指定target的时候生效,我这里是6735的代码,因此目录是device/lentek/lentk6735_35gc_l1/device.mk)

一定不要忘记把PRODUCT_PACKAGES加进去。

注意2
如果只是在源码上局部编译,且只需要使用编译成功的apk(而不是把整个系统重新刷写),则需要注意的是,apk中有使用so文件,则需要把这个so文件推送到手机的对应目录中:

#进入到生成so文件的目录(所有生成的so都在这里,而不是在apk中,并安装到system.img)
cd <source_root>\out\target\product\<productname>\system\lib
#如果手机没有root
adb root
#不执行这一步则push会被拒绝
adb remount
#把文件推送到设备
adb push xxx.so /system/lib/xxx.so
#可能需要重启,非系统应用则不需要
adb reboot
相关标签: Android.mk 编译