如何在android源码中找到PackageManager类的具体实现

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了如何在android源码中找到PackageManager类的具体实现相关的知识,希望对你有一定的参考价值。

我只在源码中找到了android.content.pm.PackageManager这个类的抽象类定义,在PackageManager.java这个文件里,我想知道它其中的方法是怎样具体实现的,应该去哪里找。
官网上它的子类android.test.mock.MockPackageManager的说明里说这个类也没有实现父类的方法功能。而且这个类的代码实现没有在源码中找到。
但是在settings这个模块中明明直接调用了PackageManager这个类的getInstalledApps这个方法,是这样实现获取所有已安装程序列表的呢?
望高手赐教,问题解决后在追加50分

frameworks\base\core\java\android\app\ApplicationPackageManager.java
这个文件定义ApplicationPackageManager类,继承并实现了PackageManager中接口;
ApplicationPackageManager使用mPM变量,这个变量是PackageManagerService的客户端(IBinder对象),调用mPM对象中的方法,实际调用到了frameworks\base\services\java\com\android\server\pm\PackageManagerService.java文件中
的方法。
这是Java层中Binder的使用方式。
通过 grep -rnsw "extends PackageManager" * 可以找到PackageManager的实现。经常要用Linux中grep命令来查找文件。追问

你好,按照你说的路径我已经在server\PackageManagerService.java中找到getInstalledApps这个方法具体实现了。还有一点不明白这个方法用到了一个hashmap结构变量mPackage,我想知道是从那个文件里读到的数据?我没有找到相应的read方法

追答

看PackageManagerService构造,
// Collect all system packages.
mSystemAppDir = new File(Environment.getRootDirectory(), "app");
mSystemInstallObserver = new AppDirObserver(
mSystemAppDir.getPath(), OBSERVER_EVENTS, true);
mSystemInstallObserver.startWatching();
scanDirLI(mSystemAppDir, PackageParser.PARSE_IS_SYSTEM
| PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);
scanDirLI()----->scanPackageLI(),scanPackageLI()这些方法有修改mPackage,你可以沿着这个线路看下去。应该是扫描目录,把应用的一些信息收集起来,并写入到ApplicationsProvider这个数据库。ApplicationsProvider在packages\providers\目录,可以用Sqlite3查询这个数据库记录。

追问

scanPackageLI()这个方法将获取的信息同步到mPackage中,在getInstalledApplications方法中也是通过读取mPackages的数据的到安装程序列表,没有找到对ApplicationsProvider这个类的调用,是在那里调用的这个类呢?ApplicationsProvider这个类中将得到数据写入到APPLICATIONS_TABLE 这个表中,我应该去哪里找到这个表呢?刚开始接触android,有许多不懂能否加一下我的qq:769285035,十分感谢。

追答

加你了

参考技术A 如果你在eclipse里 可以按住ctrl,鼠标移到相应的接口方法上,会弹出“打开定义”,和“打开实现”,如果有多个实现类的话也会在上面列出来。你选择打开实现 就可以找到你要的代码了。 参考技术B server\PackageManagerService.java中找
private PackageParser.Package scanPackageLI这个方法是用来扫描packages的,其中你会发现第一个参数就是扫描的文件,但是这个参数是从其他的传进来的,不是很好找了,只能跟踪到这里了
参考技术C 看ApplicationPackageManager.java的getInstalledApplications()。
最终还是通过IPC调用,调用了PackageManagerService中的getInstalledApplications()方法。
-----------------------------------------------------------------------------------------------------
安卓精英团为你解答
安卓精英团欢迎各位精英加入追问

您好,那PackageManagerService这个文件在源码的那个目录下呢?或是它完整的api路径是什么呢?

如何在android源码中添加自己的jni方法

参考技术A 1,、 项目实现了一个简单的四则运算,项目的目录层次如下:

AndroidManifest.xml Android.mk jni res src

资源文件简简单单,一个布局文件,稍后会有demo的下载地址

主要记录备忘的内容如下:

MainActivity.Java

[html] view plain copypublic native int add(int x, int y);

public native int substraction(int x, int y);

public native float multiplication(int x, int y);

public native float division(int x, int y);

static

System.loadLibrary("arithmetic");



2、生成lib的名称为libarithmetic.so.注意load的时候写"arithmetic"

jni 目录下有两个文件,一个是Android.mk,一个是c++源文件long.cpp

jni/Android.mk如下:

[html] view plain copyLOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_MODULE:= libarithmetic

LOCAL_SRC_FILES:= \

long.cpp

LOCAL_SHARED_LIBRARIES := \

libutils

LOCAL_STATIC_LIBRARIES :=

LOCAL_C_INCLUDES += \

$(JNI_H_INCLUDE)

LOCAL_CFLAGS +=

LOCAL_PRELINK_MODULE := false

include $(BUILD_SHARED_LIBRARY)

3、 注释:

LOCAL_PATH(必须定义,而且要第一个定义),宏函数‘my-dir’, 由编译系统提供,用于返回当前路径(即包含Android.mk
file文件的目录);

include $( CLEAR_VARS),

CLEAR_VARS由编译系统提供,指定让GNU MAKEFILE为你清除许多LOCAL_XXX变量(例如 LOCAL_MODULE,
LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES, 等等...),除LOCAL_PATH
。这是必要的,因为所有的编译控制文件都在同一个GNU MAKE执行环境中,所有的变量都是全局的;

LOCAL_MODULE_TAGS :=user eng tests optional

user: 指该模块只在user版本下才编译

eng: 指该模块只在eng版本下才编译

tests: 指该模块只在tests版本下才编译

optional:指该模块在所有版本下都编译

LOCAL_MODULE(必须定义),标识你在Android.mk文件中描述的每个模块。名称必须是唯一的,而且不包含任何空格。Note:编译系统会自动产生合适的前缀和后缀,例如:arithmetic编译成功后将生成libarithmetic.so库文件

LOCAL_SRC_FILES 变量必须包含将要编译打包进模块中源代码文件。不用在这里列出头文件和包含文件。

LOCAL_SHARED_LIBRARIES中加入所需要链接的动态库(*.so)的名称

LOCAL_STATIC_LIBRARIES加入所需要链接的静态库(*.a)的名称

LOCAL_CFLAG可选的编译器选项,用法之一是定义宏,例如LOCAL_CFLAGS := -Werror作用是编译警告也作为错误信息

LOCAL_PRELINK_MODULE:=false,不作prelink处理,默认是要prelink操作的,有可能造成地址空间冲突(这地方目前还不明白)
long.cpp源代码如下:

[html] view plain copy#define LOG_TAG "LongTest2 long.cpp"

#include

#include

#include "jni.h"

jint add(JNIEnv *env, jobject thiz, jint x, jint y)

return x + y;



jint substraction(JNIEnv *env, jobject thiz, jint x, jint y)

return x - y;



jfloat multiplication(JNIEnv *env, jobject thiz, jint x, jint y)

return (float)x * (float)y;



jfloat division(JNIEnv *env, jobject thiz, jint x, jint y)

return (float)x/(float)y;



static const char *classPathName = "com/inspur/test2/MainActivity";

static JNINativeMethod methods[]=

"add", "(II)I", (void*)add,

"substraction", "(II)I", (void*)substraction,

"multiplication", "(II)F", (void*)multiplication,

"division", "(II)F", (void*)division,

;

typedef union

JNIEnv* env;

void* venv;

UnionJNIEnvToVoid;

static int registerNativeMethods(JNIEnv* env, const char* className,

JNINativeMethod* gMethods, int numMethods)

jclass clazz;

clazz = env->FindClass(className);

if (clazz == NULL)

return JNI_FALSE;

if (env->RegisterNatives(clazz, gMethods, numMethods)<0)

return JNI_FALSE;

return JNI_TRUE;



static int registerNatives(JNIEnv *env)

if (!registerNativeMethods(env, classPathName,

methods, sizeof(methods)/sizeof(methods[0])))



return JNI_FALSE;



return JNI_TRUE;



jint JNI_OnLoad(JavaVM* vm, void* reserved)

UnionJNIEnvToVoid uenv;

uenv.venv = NULL;

jint result = -1;

JNIEnv *env = NULL;

if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK)

goto bail;



env = uenv.env;

env = uenv.env;

if (registerNatives(env) != JNI_TRUE)

goto bail;



result = JNI_VERSION_1_4;

bail:

return result;



除了利用 编写native JAVA类,通过javah生成.h文件,根据.h文件编写.c/cpp文件
方法外(名字像老太太的裹脚步,又臭又长,而且不灵活),Android还可以通过引用JNI_Onload方式实现。jint JNI_onLoad(JavaVM*
vm, void* reverced),改方法在so文件被加载时调用。

JNI_OnLoad()有两个重要的作用:

指定JNI版本:告诉VM该组件使用那一个JNI版本(若未提供JNI_OnLoad()函数,VM会默认该使用最老的JNI
1.1版),如果要使用新版本的JNI,例如JNI
1.4版,则必须由JNI_OnLoad()函数返回常量JNI_VERSION_1_4(该常量定义在jni.h中) 来告知VM。

初始化设定,当VM执行到System.loadLibrary()函数时,会立即先呼叫JNI_OnLoad()方法,因此在该方法中进行各种资源的初始化操作最为恰当。

JNI_OnUnload()的作用与JNI_OnLoad()对应,当VM释放JNI组件时会呼叫它,因此在该方法中进行善后清理,资源释放的动作最为合适。

4、 项目根目录下Android.mk文件:

[html] view plain copyLOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

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

LOCAL_JNI_SHARED_LIBRARIES := libarithmetic

LOCAL_PACKAGE_NAME := LongTest

LOCAL_SHARED_LIBRARIES := \

libutils\

liblog

include $(BUILD_PACKAGE)

include $(LOCAL_PATH)/jni/Android.mk

# Also build all of the sub-targets under this one: the shared library.

include $(call all-makefiles-under,$(LOCAL_PATH))

LOCAL_PACKAGE_NAME:项目名称,即最终生成apk的名字

LOCAL_JNI_SHARED_LIBRARIES := libxxx就是把so文件放到apk文件里的libs/armeabi里

执行BUILD_PACKAGE。它的定义也是在config.mk中定义如下:BUILD_PACKAGE:=
$(BUILD_SYSTEM)/package.mk

$(call all-java-files-under, src)编译的源代码文件列表添加src目录下所有的java 源文件

$(call all-makefiles-under, $(LOCAL_PATH))编译器会在编译完当前目录下的文件后再深入子目录编译

如果make过android源码,可以在项目根目录下执行mm命令进行编译。前提是执行过source
androidSRC/build/envsetup.sh

或者直接把source androidSRC/build/envsetup.sh添加到~/.bashrc中,会更加方便
参考技术B 为啥要在源码添加自己的方法,你自己的方法,写来自己用不就行了

以上是关于如何在android源码中找到PackageManager类的具体实现的主要内容,如果未能解决你的问题,请参考以下文章

如何发布 Android .aar 源码让 Android Studio 自动找到它们?

Android - 请求查看用户位置的权限

androidkillsamli2_class未找到apk源码

如何从Eclipse导入github上的项目源码

看看Android源码中View是如何实现LongPress的

看看Android源码中View是如何实现LongPress的