加载中…
个人资料
Steven天天Helloworld
Steven天天Helloworld
  • 博客等级:
  • 博客积分:0
  • 博客访问:4,491
  • 关注人气:4
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
相关博文
推荐博文
正文 字体大小:

android平台编译自己编写的framework级service server实录

(2012-08-16 09:30:50)
分类: Android驱动学习笔记
首先,我在网上并没有找到如何把android的JNI层以下的代码导入eclipse开发环境的好方法,如果谁找到,希望能告诉我。
所以,上篇博文的代码最好的办法是用vi编辑,然后把代码放到代码数的指定目录下,通过写Android.mk文件,指定依赖关系。然后用make编译。
首先是代码的具体路径:
在/frameworks/base/include路径下建立一个helloworld文件夹,把所有服务接口,服务和服务代理的头文件放到这个目录下:BnHelloWorldService.h,BpHelloWorldService.h,HelloWorldService.h,IHelloWorldService.h。
在/frameworks/base/libs路径下建立一个helloworld文件夹,把上述头文件对应的cpp文件放到这里:
BnHelloWorldService.cpp,BpHelloWorldService.cpp,HelloWorldService.cpp,IHelloWorldService.cpp。
上面的文件编译好了,会生成一个libhelloworld.so的函数库。
然后在/frameworks/base/cmds路径下建立一个helloworld文件夹,把启动程序放到这里:
helloworldservice.cpp,helloworldclient.cpp
上面的代码编译后会生成一个两个可执行文件,helloworldservice和helloworldclient。

下面要编写Android.mk文件来指定make的参数。
首先编写这个libhelloworld的Android.mk
LOCAL_PATH:=$(call my-dir)--------------<1>
include $(CLEAR_VARS)--------------------<2>

LOCAL_SRC_FILES:= \-------------------<3>
    IHelloWorldService.cpp \
    BnHelloWorldService.cpp \
    BpHelloWorldService.cpp \
    HelloWorldService.cpp

LOCAL_SHARED_LIBRARIES:= \------------------<4>
    libcutils \
    libutils \
    libbinder \

LOCAL_PRELINK_MODULE := false----------------------<5>

LOCAL_MODULE := libhelloworld-----------------------<6>

LOCAL_MODULE_TAGS := optional---------------------------<7>

include $(BUILD_SHARED_LIBRARY)----------------------------<8>
<1>这一行一般出现在模块定义的最上面,用来指定目标源代码的路径,后面的my-dir的意思是当前路径。
<2>这一句也基本出现在前面,而且基本每个本地模块的定义都会含有这一行,表示清空原来的各种编译的环境变量。
<3>这里指定了合成目标模块需要编译的源代码文件。
<4>这里指所编译的源代码所要依赖的函数库,当然,如果函数库更新,会出发该模块的更新。PS:但是,貌似不一定,GNU的工作,不能用直觉决定,我就在这里走了老大的弯路。
<5>指预链接模块,其实,我不是很懂这一行的意思,但是,大多数模块都加了这一行,而且,网上也解释要加,虽然看不懂为什么。
<6>这里指定你要编译的目标模块的名称。
<7>这一行我最开始写Android.mk是没有的,是在写完后,发现编译不通过,编译器建议加上这么一行,(我第一见编译错误弹出文本知道修改的,GNU,拿什么说喜欢你)貌似是指定模块的编译类型,反正不是特别清楚。
<8>最后加入build所需要的函数库。
最后把这个Android.mk 放到 /framework/base/libs/helloworld 下。

然后编写启动程序的Android.mk文件:
LOCAL_PATH:=$(call my-dir)
include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
    helloworldservice.cpp

LOCAL_SHARED_LIBRARIES := \
    libutils \
    libbinder \
    libhelloworld
base :=$(LOCAL_PATH)/../..

LOCAL_MODULE:= helloworldservice
LOCAL_MODULE_TAGS:=optional

include $(BUILD_EXECUTABLE)

#================================================

include $(CLEAR_VARS)

LOCAL_SRC_FILES:= \
    helloworldclient.cpp

LOCAL_SHARED_LIBRARIES:= \
    libutils \
    libbinder \
    libhelloworld

base:= $(LOCAL_PATH)/../..

LOCAL_MODULE:= helloworldclient
LOCAL_MODULE_TAGS:=optional

include $(BUILD_EXECUTABLE)

这里包含了两个模块的编译make文件。和上面的Android.mk的文件差不多
把这个文件放到/framework/base/cmds/helloworld路径下。
这里编译成功后,会在/system/bin文件下,生成可执行文件,helloworldservice和helloworldclient。

这里所有要配置的基本配置完毕,下面执行make。
和编译android代码一样,首先执行
#source build/envsetup.sh
#lunch full-eng
然后因为如果整体make效率比较没有效率,幸而Make可以就其中的某个模块个别编译,我们刚才编写的Android.mk文件可见,我们新生成的模块名称是:libhelloworld(函数库),helloworldservice,helloworldclient。三个模块。
则分别执行
#make libhelloworld
#make helloworldservice
#make helloworldclient
这里如果可以成功执行,这里就会生成新的libhelloworld.so文件,和新的helloworldservice,helloworldclient可执行文件。然后,system.img也会更新。
这时重启emulator。通过adb shell进入android 控制台,在/system/bin路径下可以找到可执行文件helloworldservice 和 helloworldclient。
则首先后台运行helloworldservice:
#./helloworldservice &
然后执行client端启动:
#./helloworldclient
可以看到输出:
get the sm seccess.
get the sm.
get the IBinder
get sHelloWorldService.
getting the setvice.
get the service success.
interface cast
hello,world
可以见到hello,world输出。

TIP:
1.首先,自己编写的程序可能出错,用make慢慢调,直到编译成功。其实如果没有拼写错误,应该是可以编译成功的,如果是报错:大致意思是无法转换为基类等的意思时,看一下自己的继承关系是否输入正确。
2.当编译好的程序,无法正常运行的时候,就需要用Print调试,因为在emulator下没有很好的gdb办法(听说有,就是很慢,慢到一个指令要几分钟),这里,print 也许是最有效率的debug方法。
3.print调试经常可以碰到的问题是,无法更新目标文件,其实,如果只是在cmds的启动程序插入Print代码,其实是比较容易在目标文件上更新的,也就是更新代码以后,用make name_module。如果碰到的信息是:
nothing to do with .....
表示没有更新到目标文件。如果有更新,会显示:
Install: 路径名/name_module
可是如果在lib中间更新代码,貌似就不是那么容易在顶层的cmds里的文件中更新了,我曾经看到多次,都明明在Make之后,显示.so有更新,而且,可执行文件也有更新,可是执行结果就是不如直觉判断,不像我们想象的那样。

于是,抱着不信任GNU工具链的思路,使用了下面方案去大规模改动target目录下文件,以让make多做点工作,希望能成功把改动扩散到所有依赖的文件:
方案1:干掉.so文件,强迫make重新生成.so文件。
结果:失败
分析:生成的.so文件,理论分析应该是和最新的源代码对应的,否则.so的生成依据是什么。

方案2:干掉lib整个目录,强迫make重新生成所有.so文件。
结果:失败
分析:
首先,我在编译指定模块后,在整体make的时候,我发现,其实system.img没有更新,也就是,在out/.../genenic下面的system文件目录结构与system.img并不同步,这时,我使用的方法,是用adb push把可执行文件helloworldservice和helloworldclient到/data/下,然后在/data/目录下直接执行可执行文件,结果,可执行文件可以执行,但是,.so上的改动无法同步到这些可执行文件上。
比如这里,我改动了lib中的helloworld.cpp加入了一行printf语句。可是在更新helloworldservice可执行文件后,然后重新adb push到emulator中,结果,没有相应的printf结果。
分析一下,我发现其实helloworldservice的大小,不随着你改变代码而改变,而且大小和helloworldclient一样大,都是5.3k,可见,这个文件很有可能是一个链接文件。可是链接文件用push后为什么可以执行?
分析可能是在system中有一真正的执行文件,只不过没有在/system/bin中有链接而已,但是push进去的文件却可以指向那个文件,虽然,我不知道真正的文件被放在了哪里。
接着,就是这次把Lib文件夹整体删除,导致了system.img文件镜像重新生成,而且是真正的重新生成!(因为之前尝试把system.img删除强迫make重生成,最后生成的文件还是老版本的system.img)。但是,出现了新的问题,没有了libhelloworld.so文件。
所以依此推测,make可能执行的操作是,重新编译了lib库,但是对于system的其他文件只是重新打包,没有重新编译,导致没有程序依赖libhelloworld.so库,因为只有helloworldservice和helloworldclient依赖此库,所以该库没有编译。

方案3:干掉lib文件夹,先生成libhelloworld.so,再全部生成Lib库
结果:成功
分析:在system/bin中的符号链接,链接到的真正的可执行文件依赖的库在system,img中有镜像,也就是,程序链接的.so库是在system.img中的,也就是说无论你如何在out目录下的target中更新库,system.img库不更新,这个.so就是老版本。写到这里插一句---------C++不到家真的伤不起。.so库是动态链接库,链接依赖于本地的镜像,而emulator和host本来就是两个隔断的系统,不更新system,img当然链接的是老版本,在host上再怎么更新也是host的,system.img是不知道的。(妈的,这个错误调了老子一天)结果,事实上,就是这么简单的问题。
最后成功执行的结果就如上所示了。

0

阅读 评论 收藏 转载 喜欢 打印举报/Report
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 电话:4000520066 提示音后按1键(按当地市话标准计费) 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有