JNI JAR和系统服务

 

目录

1         JNIJAR集成... 2

1.1        了解mk文件基本语法和关键字... 2

1.2        为项目集成新的模块... 3

1.3        集成包含外部架包应用... 4

1.4        集成带so库的应用... 4

2         系统级服务... 6

2.1        .学习系统服务基本知识... 6

系统服务的启动过程... 7

2.2        添加一个自定义系统服务,实现对外接口... 7

自定义系统服务实现... 7

3         遇到问题及解决... 11

 


 

第1章     JNIJAR集成

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++函数来处理一些任务,以提高应用的性能。

自写so库首先在android studio中新建项目时勾选Include C++Support选项,如下图:
JNI JAR和系统服务


 

 
 


就会自动生成一个cpp文件,编译项目,就会在生成各个架构下的so文件,如下图:

JNI JAR和系统服务

需要注意的是C/C++函数名,一定是Java_[任意包名]_[函数名],只有该包名下的Java函数才能调用该C/C++函数。

引入so库的配置与引入jar的配置类似,同意在模块源码的mk文件中配置,使用命令:

LOCAL_PREBUILT_LIBS :=[so库文件夹路径]

 

 

 

第2章     系统级服务

2     

2.1   .学习系统服务基本知识

系统服务分为两种:本地服务和Java层系统服务。

本地服务存在于librarise层,主要包括Audio FlingerSurface FlingerMedia PlayerServiceCamera ServiceAudio Policy Service等,除了Surface FlingerSystem Server进程启动之外,其他都由Media Server进程启动(由C/C++实现)。

java系统服务由SystemServer系统进程启动,它分为:核心平台服务和硬件服务。核心平台服务一般不会与应用程序进行交互,他们是Android Framework运行所必须的服务。硬件服务提供了控制底层硬件的api

一般的使用应用程序服务前,先调用strat Service()启动应用程序的服务,但是系统服务不需要,直接调用getSystemService()即可。因为Android在系统启动的过程中,init进程已经启动了这些系统服务。

android系统启动过程中,会启动SystemServer进程,SystemServer进程又通过调用ServiceManager类的addService()方法,将所有Java系统服务注册到Context Manager

                          系统服务的启动过程

 

JNI JAR和系统服务


 


 


 



 


        

2.2    添加一个自定义系统服务,实现对外接口

理一下思路:要添加自定义系统服务,首先要实现它:服务一般要通过IBinder与其他进程通讯,教程中的方法是使用继承了IBinderStub。实现了自定义的服务之后,要考虑怎样才能获取到它。上一节提到,系统服务都会在SystemServer进程中随着android系统启动而启动,然后就可以使用getSystemService()方法获取。因此,要先在SystemServer进程中将自定义服务添加到ServiceManager中管理。还有, getSystemService()方法(在ContextImpl中实现)会跳到SystemServiceRegistry中,注册并返回一个服务管理器实例类。

自定义系统服务实现

定义interface

JNI JAR和系统服务


 


 


 



 


 

    

 

 

具体步骤如下:

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函数中调用ServiceMangeraddServiceContext.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文件中声明的服务名不一样,大小写不一致。