JNI JAR和系统服务
目录
第1章 JNI和JAR集成
1.1 了解mk文件基本语法和关键字
mk文件的作用是类似于Android studio中的build.gradle文件,是编译应用时的配置文件,从单个应用或模块到整个系统的配置,都是在mk文件中进行设置的,不进行配置或者配置错误都会导致系统编译失败,因此,mk文件的学习是基础而重要的。要将android系统定制化,就必须先学习了解mk文件的基本语法和关键字。
当清楚了mk文件的作用是对应用或系统进行配置之后,思考编译自己的应用需要什么配置,然后查看其他模块的mk文件,学习其他模块是如何进行相关设置的配置的,不懂的地方在网上查阅资料,实在不懂的询问导师。
通过此次实践,学习到了编译自己应用进android系统时要进行的简单设置,了解mk文件所要做的事情:1.清楚旧变量2.设置新变量3.调用编译函数。主要的配置在第二步中,要设置源文件路径,依赖库以及依赖库的路径。以下是我的mk文件:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS) #调用函数清除旧变量
LOCAL_MODULE_TAGS := optional #设置新变量:
#设置源文件:
LOCAL_SRC_FILES := $(call all-java-files-under, src)
#设置模块名:
LOCAL_PACKAGE_NAME := CSCS
#设置使用平台签名:
LOCAL_CERTIFICATE := platform
#设置编译时依赖库
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v7-appcompat
LOCAL_STATIC_JAVA_LIBRARIES += android-support-v4
#设置编译时依赖资源目录
LOCAL_RESOURCE_DIR += $(LOCAL_PATH)/res
LOCAL_RESOURCE_DIR += frameworks/support/v7/appcompat/res
#设置打包后去系统中寻找资源,不用在将资源打包进APK
LOCAL_AAPT_FLAGS := --auto-add-overlay \
--extra-packagesandroid.support.v7.appcompat
include $(BUILD_PACKAGE)
1.2 为项目集成新的模块
为项目集成新的模块是之后工作中的重要部分之一,同时作为一个合格的android软件工程师,这也是必备技能之一。学会了为系统集成新模块之后,学习查看其他模块的源码也将事半功倍。
在上一节的学习中,我了解到了android系统编译的配置就是在mk文件中,因此,要集成新的模块进系统,也要在mk文件中配置。
在项目源码中,一个项目对应一个机型,但同一机型也会有不同的系统版本。在项目根目录下的build/target中的core.mk文件是配置项目对应机型的所有系统版本进行配置,在此添加模块名,使用语句:
PRODUCT_PACKAGES += [源码mk文件中设置的模块名]
1.3 集成包含外部架包应用
Jar即将Java函数源码编译压缩,以供其他应用使用。在实际项目中,经常会有使用第三方jar包或者将自己的代码打包成jar包的情况。因此,学会集成包含外部jar包的应用是很有必要的。
Jar的配置同样是在模块源码的mk文件中。需要使用命令:
LOCAL_STATIC_JAVA_LIBRARIES += [jar相对路径(包含名字)]
同时将jar文件放在源码中对应路径下。
自写jar,需要在对应模块的build.gradle文件中添加任务:
task [任务名](type: Copy){
from('build/intermediates/bundles/release')//源文件
into('build/libs/') //生成路径
include('classes.jar')
rename('classes.jar','JarOfCCL.jar')//重命名
}
[任务名].dependsOn(build)
然后在命令行窗口中切换到android studio项目根目录,使用命令:
./gradlew [任务名]
然后就会在生成路径中生成jar文件。
1.4 集成带so库的应用
so库是将C/C++函数源码编译压缩,以供其他应用使用。在实际项目中,处理一些任务时,比如对图像的处理,C/C++的处理效率和效果要比Java好的多,因此,可能会在Java项目中使用C/C++函数来处理一些任务,以提高应用的性能。
|
需要注意的是C/C++函数名,一定是Java_[任意包名]_[函数名],只有该包名下的Java函数才能调用该C/C++函数。
引入so库的配置与引入jar的配置类似,同意在模块源码的mk文件中配置,使用命令:
LOCAL_PREBUILT_LIBS :=[so库文件夹路径]
第2章 系统级服务
2.1 .学习系统服务基本知识
系统服务分为两种:本地服务和Java层系统服务。
本地服务存在于librarise层,主要包括Audio Flinger,Surface Flinger,Media PlayerService,Camera Service,Audio Policy Service等,除了Surface Flinger由System Server进程启动之外,其他都由Media Server进程启动(由C/C++实现)。
java系统服务由SystemServer系统进程启动,它分为:核心平台服务和硬件服务。核心平台服务一般不会与应用程序进行交互,他们是Android Framework运行所必须的服务。硬件服务提供了控制底层硬件的api。
一般的使用应用程序服务前,先调用strat Service()启动应用程序的服务,但是系统服务不需要,直接调用getSystemService()即可。因为Android在系统启动的过程中,init进程已经启动了这些系统服务。
在android系统启动过程中,会启动SystemServer进程,SystemServer进程又通过调用ServiceManager类的addService()方法,将所有Java系统服务注册到Context Manager。
系统服务的启动过程
|
|
|
2.2 添加一个自定义系统服务,实现对外接口
理一下思路:要添加自定义系统服务,首先要实现它:服务一般要通过IBinder与其他进程通讯,教程中的方法是使用继承了IBinder的Stub。实现了自定义的服务之后,要考虑怎样才能获取到它。上一节提到,系统服务都会在SystemServer进程中随着android系统启动而启动,然后就可以使用getSystemService()方法获取。因此,要先在SystemServer进程中将自定义服务添加到ServiceManager中管理。还有, getSystemService()方法(在ContextImpl中实现)会跳到SystemServiceRegistry中,注册并返回一个服务管理器实例类。
自定义系统服务实现
定义interface
|
|
|
|
|
具体步骤如下:
1.在/frameworks/base/core/java/android/os/目录下新建XXX.aidl文件(android interfacedefinition language),将服务声明为interface。
package android.os;
interface ICCLManager{
intmyInterfaceToCount(String para);
}
在frameworks/base/Android.mk文件的LOCAL_SRC_FILES中添加aidl文件的路径。然后使用单编frameworks/base模块,就会在out/target/common/obj/JAVA_LIBRARIES/android_system_stubs_current_intermediates/src/android/os下生成IXXX.java文件,为对应的Stub接口,然后一般把它放在frameworks/base/services/core/java/com/android/server目录下。
2.frameworks/base/core/java/android/os下新建XXX.Java文件继承IXXX.Stub,并实现接口。
packagecom.android.server;
importandroid.os.ICCLManager;
importandroid.os.RemoteException;
public classCCLManagerImpl extends ICCLManager.Stub {
public CCLManagerImpl(){}
@Override
public int myInterfaceToCount(String para)throws RemoteException {
return para.length();
}
}
3.frameworks/base/core/java/android/os下新建XXXManager.Java文件,内含IXXX对象,给出对外接口函数,函数内可调用IXXX的方法。
packageandroid.os;
importandroid.content.Context;
public finalclass CCLManagerService {
private static final String TAG ="CCLManagerService";
private ICCLManager mService;
Context mContext;
public CCLManagerService(Context context,ICCLManager service) {
mContext = context;
mService = service;
}
public int count(String para){
int i=0;
try {
i =mService.myInterfaceToCount(para);
}catch (Exception e){
e.printStackTrace();
}
return i;
}}
4. 在frameworks/base/core/java/android/content/Context.java中定义常量字符串XXX_SERVICE:
public static final String CCL_SERVICE="ccl";
在frameworks/base/services/java/com/android/server/SystemServer.java中的run函数中调用ServiceManger的addService(Context.XXX_SERVICE, new XXX())将自定义服务添加到ServiceManager中。
try{
Slog.i(TAG, "CCL Service");
ServiceManager.addService(Context.CCL_SERVICE,new CCLManagerImpl());
}catch(Throwable e) {
reportWtf("addCCLService", e);
}
5. 在frameworks/base/core/java/android/app/SystemServiceRegistry.java的静态代码块中使用registerService()返回XXXManager实例。
registerService(Context.CCL_SERVICE,CCLManagerService.class, new CachedServiceFetcher<CCLManagerService>() {
@Override
public CCLManagerServicecreateService(ContextImpl ctx) {
IBinder iBinder =ServiceManager.getService(Context.CCL_SERVICE);
ICCLManager service =ICCLManager.Stub.asInterface(iBinder);
return newCCLManagerService(ctx,service);
}
});
6.在system/sepoily/service.te文件中定义服务类型(定义服务权限):
typeccl_service,system_api_service,system_server_service,service_manager_type;
更新service_contexts文件(参照其他服务):
ccl u:object_r:ccl_service:s0
此常量——“ccl”要与Context中定义的字符串常量一致。
参考教程:http://blog.****.net/mockingbirds/article/details/54382072
第3章 遇到问题及解决
1.遇到问题及解决
1. 错误:打包jar时使用命令./gradlew [任务名]出错。
原因:第一次执行该命令时系统需联网下载一些文件,而电脑不能访问外网,连接外网更新即可。
2.错误:使用so库的文件时提示为找到对应函数的实现。
原因:so库源码对应的函数名没有按规范命名,引用so库的函数的包名一定要与so库源码中函数名中的包名一致。
3. 错误:ServiceManager中获取不到service对象。
原因:Context中定义的字符串常量与service_contexts文件中声明的服务名不一样,大小写不一致。