前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂,风趣幽默",感觉非常有意思,忍不住分享一下给大家。
一、PackageManagerService简称PMS:PackageManagerService是Android系统中核心的
服务之一,负责应用程序的查询,卸载和应用信息查询,相当于应用程序的大管家。
bash
try {
//调用系统的方法,获取应用程序信息
context.getPackageManager().getPackageInfo(null, 0);
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
1、首先是在抽象类PackageManager中定义了抽象方法getPackageInfo()
bash
public abstract PackageInfo getPackageInfo(@NonNull String packageName,
@PackageInfoFlags int flags)
throws NameNotFoundException;
2、类ApplicationPackageManager继承PackageManager类,重写了getPackageInfo()方法
bash
@Override
public PackageInfo getPackageInfo(String packageName, int flags)
throws NameNotFoundException {
return getPackageInfoAsUser(packageName, flags, mContext.getUserId());
}
实际调用了getPackageInfoAsUser()方法
bash
@Override
public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
throws NameNotFoundException {
try {
PackageInfo pi = mPM.getPackageInfo(packageName, flags, userId);
if (pi != null) {
return pi;
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
throw new NameNotFoundException(packageName);
}
mPM是系统中写的AIDL文件,通过mPM.getPackageInfo(),调用到代理对象Proxy类中重写的getPackageInfo()方法,代理对象又调用到PackageManagerService中的getPackageInfo()方法。
二、APK安装原理
1.APK安装的两种方式
1.静默安装,又叫无界面的安装,从各大手机厂商应用商店下载的APK,便是无界面的安装。
2.有界面的安装,从三方托管平台下载一个APK包,需要一步一步来操作的。
下面分析有界面的安装
1、点击安装后,会跳转到系统提供的PackageInstallerActivity和其对应的布局
install_start.xml.
2、之后通过PackageUtil类获取APK包里面的信息
bash
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
3、如果用户点击安装按钮进行安装,或者取消,两种情况。
bash
public void onClick(View v) {
if (v == mOk) { //点击安装
if (mOkCanInstall || mScrollView == null) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
clearCachedApkIfNeededAndFinish();
} else {
startInstall(); //跳转到一个新的页面,显示正在安装
}
} else {
mScrollView.pageScroll(View.FOCUS_DOWN);
}
} else if (v == mCancel) { //点击取消
// Cancel and finish
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
clearCachedApkIfNeededAndFinish();
}
}
4、正在安装中的Activity类名为:InstallAppProgress 继承Activity。
以上是有界面的安装方式的简单分析。
下面分析无界面的安装方式:
1、没有界面的安装方式,最开始是从C代码开始执行的。
首先通过adb install 输入包名后,一敲回车,会执行到commandline.c文件下的,adb_commandline()方法。
2.最终会调用到install_app()方法
bash
char* apk_file = argv[last_apk];
char apk_dest[PATH_MAX]; //APK安装路径
snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */);//把APK,push到手机存储里面。
if (err) {
goto cleanup_apk;
} else {
argv[last_apk] = apk_dest; /* destination name, not source location */
}
pm_command(transport, serial, argc, argv); //这个方法是为了执行shell:pm以命名的方式去安装
3、pm_command()方法如下
bash
static int pm_command(transport_type transport, char* serial,
int argc, char** argv)
{
char buf[4096];
snprintf(buf, sizeof(buf), "shell:pm"); //借助pm的脚本文件,实现安装,通过pm命名的方式实现安装操作
while(argc-- > 0) {
char *quoted = escape_arg(*argv++);
strncat(buf, " ", sizeof(buf) - 1);
strncat(buf, quoted, sizeof(buf) - 1);
free(quoted);
}
send_shellcommand(transport, serial, buf);
return 0;
}
4、pm脚本文件
bash
# Script to start "pm" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/pm.jar //重点是找到pm.jar
exec app_process $base/bin com.android.commands.pm.Pm "$@"
5、Android源码frameworks/base/cmds/pm/src/com/android/commands/pm.java 源码中有一个main方法如下
bash
public static void main(String[] args) {
int exitCode = 1;
try {
exitCode = new Pm().run(args); //重点关注run()方法
} catch (Exception e) {
Log.e(TAG, "Error", e);
System.err.println("Error: " + e);
if (e instanceof RemoteException) {
System.err.println(PM_NOT_RUNNING_ERR);
}
}
System.exit(exitCode);
}
6、run方法代码如下
bash
public int run(String[] args) throws IOException, RemoteException {
boolean validCommand = false;
if (args.length < 1) {
return showUsage();
}
mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
if (mPm == null) {
System.err.println(PM_NOT_RUNNING_ERR);
return 1;
}
mInstaller = mPm.getPackageInstaller();
mArgs = args;
String op = args[0];
mNextArg = 1;
if ("list".equals(op)) {
return runList();
}
if ("path".equals(op)) {
return runPath();
}
if ("dump".equals(op)) {
return runDump();
}
if ("install".equals(op)) { //安装
return runInstall();
}
if ("install-create".equals(op)) {
return runInstallCreate();
}
if ("install-write".equals(op)) {
return runInstallWrite();
}
if ("install-commit".equals(op)) {
return runInstallCommit();
}
if ("install-abandon".equals(op) || "install-destroy".equals(op)) {
return runInstallAbandon();
}
if ("set-installer".equals(op)) {
return runSetInstaller();
}
if ("uninstall".equals(op)) { //卸载
return runUninstall();
}
if ("clear".equals(op)) {
return runClear();
}
if ("enable".equals(op)) {
return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
}
if ("disable".equals(op)) {
return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
}
if ("disable-user".equals(op)) {
return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
}
if ("disable-until-used".equals(op)) {
return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
}
if ("hide".equals(op)) {
return runSetHiddenSetting(true);
}
if ("unhide".equals(op)) {
return runSetHiddenSetting(false);
}
if ("grant".equals(op)) {
return runGrantRevokePermission(true);
}
if ("revoke".equals(op)) {
return runGrantRevokePermission(false);
}
if ("set-permission-enforced".equals(op)) {
return runSetPermissionEnforced();
}
if ("set-install-location".equals(op)) {
return runSetInstallLocation();
}
if ("get-install-location".equals(op)) {
return runGetInstallLocation();
}
if ("trim-caches".equals(op)) {
return runTrimCaches();
}
if ("create-user".equals(op)) {
return runCreateUser();
}
if ("remove-user".equals(op)) {
return runRemoveUser();
}
if ("get-max-users".equals(op)) {
return runGetMaxUsers();
}
if ("force-dex-opt".equals(op)) {
return runForceDexOpt();
}
try {
if (args.length == 1) {
if (args[0].equalsIgnoreCase("-l")) {
validCommand = true;
return runListPackages(false);
} else if (args[0].equalsIgnoreCase("-lf")){
validCommand = true;
return runListPackages(true);
}
} else if (args.length == 2) {
if (args[0].equalsIgnoreCase("-p")) {
validCommand = true;
return displayPackageFilePath(args[1]);
}
}
return 1;
} finally {
if (validCommand == false) {
if (op != null) {
System.err.println("Error: unknown command '" + op + "'");
}
showUsage();
}
}
}
7、在runInstall()方法中,主要是调用了该方法
bash
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
//实际调用的是PackageManagerService中的installPackageAsUser()方法
mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
installerPackageName, verificationParams, abi, userId);
8、实际调用的是PackageManagerService中的installPackageAsUser()方法
bash
/**
* 1、originPath:代表应用程序文件的路径。这是一个字符串类型的参数,表示应用程序安装包的位置。
*
* 2、observer:代表应用程序安装的观察者。它是IPackageInstallObserver2接口的一个实例,通过观察者模式来监听安装过程的状态和结果。
*
* 3、installFlags:代表安装标志。这是一个整型参数,用于指定安装时的特定选项。例如,可以通过设置INSTALL_REPLACE_EXISTING标志来替换已存在的应用。
*
* 4、installerPackageName:代表安装程序的名称。这是一个字符串类型的参数,表示执行安装的应用程序的包名。
*
* 5、userId:代表要安装应用的用户ID。这是一个整型参数,用于指定要安装应用的用户。在多用户系统中,每个用户拥有自己的应用安装目录。
*/
@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, int userId)
9.最后通过Handler发送消息,最后执行到startCopy()方法
bash
final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
if (++mRetries > MAX_RETRIES) { //安装次数大于4次,安装失败
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
handleReturnCode();
return res;
}
9、最后执行安装操作的方法handleReturnCode()
bash
@Override
void handleReturnCode() {
if (mObserver != null) {
try {
mObserver.onGetStatsCompleted(mStats, mSuccess);
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
}
}
APK无界面安装流程图
APK安装原理如下: