createPackageContext方法分析----------记一次context的getApplicationContext为空问题

本文深入探讨了在Android中使用createPackageContext方法获取其他应用的Context,并利用此Context加载和显示目标应用布局时遇到的问题。特别关注了加载的布局视图在获取宿主应用ApplicationContext时返回空值的原因,通过对源码的详细分析,揭示了问题根源在于LoadedApk对象的mApplication字段未被赋值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

通过createPackageContext方法获取其他应用context,lnflate这个应用的布局视图添加后,此视图context的getApplicationContext为空问题调查。

问题现象:

通过调用createPackageContext方法获取了某个应用的context:remoteContext,

然后通过remoteContext.getResources()获取对于的resource,该resource调用getIdentifier方法获取对于的layoutResId,

利用remoteContext.getSystemService(layout_inflater) 获取Inflater,来Inflater此layoutResId,获取View,添加宿主应用中。

 

这个地方其他View的代码中通过,context.getApplicationContext 获取为空,不太明白,因此系统看了下对于源码。

https://blog.youkuaiyun.com/ZHXLXH/article/details/90082381中,我们知道,View的context实际上是LayoutLayoutInflater.from()传递的context,因此我们这里的context就是createPackageContext获得的。

那么看一下createPackageContext方法的调用流程:

在ContextImpl.java中有一个createPackageContext方法可以根据包名来创建一个新的Context对象。下面来分析下这个方法的实现。

@Override
public Context createPackageContext(String packageName, int flags)
        throws NameNotFoundException {
    return createPackageContextAsUser(packageName, flags,
            mUser != null ? mUser : Process.myUserHandle());
}

在createPackageContext方法中实际上,是调用了createPackageContextAsUser方法。

@Override
public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)
        throws NameNotFoundException {
    if (packageName.equals("system") || packageName.equals("android")) {
        return new ContextImpl(this, mMainThread, mPackageInfo, mActivityToken,
                user, flags, mDisplay, null, Display.INVALID_DISPLAY);
    }

    LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
            flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
    if (pi != null) {
        ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
                user, flags, mDisplay, null, Display.INVALID_DISPLAY);
        if (c.mResources != null) {
            return c;
        }
    }

    // Should be a better exception.
    throw new PackageManager.NameNotFoundException(
            "Application package " + packageName + " not found");
}

在该方法中,先去检测包名是不是system 或者 android, 直接去创建一个ContextImpl对象。分析普通应用创建过程,从mMainThread.getPackageInfo中获取该包名的PackageInfo对象(LoadedApk).分析getPackageInfo的实现过程。
ActivityThread.java 中getPackageInfo 方法分析

public final LoadedApk getPackageInfo(String packageName, CompatibilityInfo compatInfo,
        int flags, int userId) {
    final boolean differentUser = (UserHandle.myUserId() != userId);
    synchronized (mResourcesManager) {
        WeakReference<LoadedApk> ref;
        if (differentUser) {
            // Caching not supported across users
            ref = null;
        } else if ((flags & Context.CONTEXT_INCLUDE_CODE) != 0) {
            ref = mPackages.get(packageName);
        } else {
            ref = mResourcePackages.get(packageName);
        }

        LoadedApk packageInfo = ref != null ? ref.get() : null;
        //Slog.i(TAG, "getPackageInfo " + packageName + ": " + packageInfo);
        //if (packageInfo != null) Slog.i(TAG, "isUptoDate " + packageInfo.mResDir
        //        + ": " + packageInfo.mResources.getAssets().isUpToDate());
        if (packageInfo != null && (packageInfo.mResources == null
                || packageInfo.mResources.getAssets().isUpToDate())) {
            if (packageInfo.isSecurityViolation()
                    && (flags&Context.CONTEXT_IGNORE_SECURITY) == 0) {
                throw new SecurityException(
                        "Requesting code from " + packageName
                        + " to be run in process "
                        + mBoundApplication.processName
                        + "/" + mBoundApplication.appInfo.uid);
            }
            return packageInfo;
        }
    }

    ApplicationInfo ai = null;
    try {
        ai = getPackageManager().getApplicationInfo(packageName,
                PackageManager.GET_SHARED_LIBRARY_FILES
                        | PackageManager.MATCH_DEBUG_TRIAGED_MISSING,
                userId);
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }

    if (ai != null) {
        return getPackageInfo(ai, compatInfo, flags);
    }

    return null;
}

在这个方法中其实相当于两个过程,一,从mPackages中取出之前已经存在的packageInfo, 二,本地mPackages中如果没有存储,则先去取出ApplicationInfo,然后再去创建一个LoadedApk, 分析getPackageInfo(ai, compatInfo, flags);

private LoadedApk getPackageInfo(ApplicationInfo aInfo, CompatibilityInfo compatInfo,
        ClassLoader baseLoader, boolean securityViolation, boolean includeCode,
        boolean registerPackage) {
    final boolean differentUser = (UserHandle.myUserId() != UserHandle.getUserId(aInfo.uid));
    synchronized (mResourcesManager) {
        WeakReference<LoadedApk> ref;
        if (differentUser) {
            // Caching not supported across users
            ref = null;
        } else if (includeCode) {
            ref = mPackages.get(aInfo.packageName);
        } else {
            ref = mResourcePackages.get(aInfo.packageName);
        }

        LoadedApk packageInfo = ref != null ? ref.get() : null;
        if (packageInfo == null || (packageInfo.mResources != null
                && !packageInfo.mResources.getAssets().isUpToDate())) {
            if (localLOGV) Slog.v(TAG, (includeCode ? "Loading code package "
                    : "Loading resource-only package ") + aInfo.packageName
                    + " (in " + (mBoundApplication != null
                            ? mBoundApplication.processName : null)
                    + ")");
            packageInfo =
                new LoadedApk(this, aInfo, compatInfo, baseLoader,
                        securityViolation, includeCode &&
                        (aInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0, registerPackage);

            if (mSystemThread && "android".equals(aInfo.packageName)) {
                packageInfo.installSystemApplicationInfo(aInfo,
                        getSystemContext().mPackageInfo.getClassLoader());
            }

            if (differentUser) {
                // Caching not supported across users
            } else if (includeCode) {
                mPackages.put(aInfo.packageName,
                        new WeakReference<LoadedApk>(packageInfo));
            } else {
                mResourcePackages.put(aInfo.packageName,
                        new WeakReference<LoadedApk>(packageInfo));
            }
        }
        return packageInfo;
    }
}

同样分析,先去从本地Map中取是否有已经存在的packageInfo, 否则去new 一个新的LoadedApk对象。此时比较关键时的两个参数,aInfo,baseLoader。

public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,
        CompatibilityInfo compatInfo, ClassLoader baseLoader,
        boolean securityViolation, boolean includeCode, boolean registerPackage) {

    mActivityThread = activityThread;
    setApplicationInfo(aInfo);
    mPackageName = aInfo.packageName;
    mBaseClassLoader = baseLoader;
    mSecurityViolation = securityViolation;
    mIncludeCode = includeCode;
    mRegisterPackage = registerPackage;
    mDisplayAdjustments.setCompatibilityInfo(compatInfo);
}

此时LoadedApk保存一些信息,如果ApplicationInfo, pacakageName等。
返回ContextImpl.java 的createPackageContextAsUser方法。

public Context createPackageContextAsUser(String packageName, int flags, UserHandle user)  {
    LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),
            flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());
    if (pi != null) {
        ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,
                user, flags, mDisplay, null, Display.INVALID_DISPLAY);
        if (c.mResources != null) {
            return c;
        }
    }
}

从getPackageInfo中拿到LoadedApk之后,开始创建ContextImpl对象。

private ContextImpl(ContextImpl container, ActivityThread mainThread,
        LoadedApk packageInfo, IBinder activityToken, UserHandle user, int flags,
        Display display, Configuration overrideConfiguration, int createDisplayWithId) {
    mOuterContext = this;

    // If creator didn't specify which storage to use, use the default
    // location for application.
    if ((flags & (Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE
            | Context.CONTEXT_DEVICE_PROTECTED_STORAGE)) == 0) {
        final File dataDir = packageInfo.getDataDirFile();
        if (Objects.equals(dataDir, packageInfo.getCredentialProtectedDataDirFile())) {
            flags |= Context.CONTEXT_CREDENTIAL_PROTECTED_STORAGE;
        } else if (Objects.equals(dataDir, packageInfo.getDeviceProtectedDataDirFile())) {
            flags |= Context.CONTEXT_DEVICE_PROTECTED_STORAGE;
        }
    }

    mMainThread = mainThread;
    mActivityToken = activityToken;
    mFlags = flags;

    if (user == null) {
        user = Process.myUserHandle();
    }
    mUser = user;

    mPackageInfo = packageInfo;
    mResourcesManager = ResourcesManager.getInstance();

    final int displayId = (createDisplayWithId != Display.INVALID_DISPLAY)
            ? createDisplayWithId
            : (display != null) ? display.getDisplayId() : Display.DEFAULT_DISPLAY;

    CompatibilityInfo compatInfo = null;
    if (container != null) {
        compatInfo = container.getDisplayAdjustments(displayId).getCompatibilityInfo();
    }
    if (compatInfo == null) {
        compatInfo = (displayId == Display.DEFAULT_DISPLAY)
                ? packageInfo.getCompatibilityInfo()
                : CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO;
    }

    Resources resources = packageInfo.getResources(mainThread);
    if (resources != null) {
        if (displayId != Display.DEFAULT_DISPLAY
                || overrideConfiguration != null
                || (compatInfo != null && compatInfo.applicationScale
                        != resources.getCompatibilityInfo().applicationScale)) {

            if (container != null) {
                // This is a nested Context, so it can't be a base Activity context.
                // Just create a regular Resources object associated with the Activity.
                resources = mResourcesManager.getResources(
                        activityToken,
                        packageInfo.getResDir(),
                        packageInfo.getSplitResDirs(),
                        packageInfo.getOverlayDirs(),
                        packageInfo.getApplicationInfo().sharedLibraryFiles,
                        displayId,
                        overrideConfiguration,
                        compatInfo,
                        packageInfo.getClassLoader());
            } else {
                // This is not a nested Context, so it must be the root Activity context.
                // All other nested Contexts will inherit the configuration set here.
                resources = mResourcesManager.createBaseActivityResources(
                        activityToken,
                        packageInfo.getResDir(),
                        packageInfo.getSplitResDirs(),
                        packageInfo.getOverlayDirs(),
                        packageInfo.getApplicationInfo().sharedLibraryFiles,
                        displayId,
                        overrideConfiguration,
                        compatInfo,
                        packageInfo.getClassLoader());
            }
        }
    }
    mResources = resources;

    mDisplay = (createDisplayWithId == Display.INVALID_DISPLAY) ? display
            : mResourcesManager.getAdjustedDisplay(displayId, mResources.getDisplayAdjustments());

    if (container != null) {
        mBasePackageName = container.mBasePackageName;
        mOpPackageName = container.mOpPackageName;
    } else {
        mBasePackageName = packageInfo.mPackageName;
        ApplicationInfo ainfo = packageInfo.getApplicationInfo();
        if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) {
            // Special case: system components allow themselves to be loaded in to other
            // processes.  For purposes of app ops, we must then consider the context as
            // belonging to the package of this process, not the system itself, otherwise
            // the package+uid verifications in app ops will fail.
            mOpPackageName = ActivityThread.currentPackageName();
        } else {
            mOpPackageName = mBasePackageName;
        }
    }

    mContentResolver = new ApplicationContentResolver(this, mainThread, user);
}

这个对象里主要就是保存了相关的对象信息,我们平时能从Context中拿到包名,ApplicationInfo,路径等信息,都在此处保存。

这个LoadedApk里,由于没有对mApplication变量赋值,因此它的getApplicatonContext为空,我们的问题原因也就知道了。
 

 

<think>好的,用户现在问的是关于“vmmem”的问题。我需要先理解这个词的含义。根据我之前的知识库,“vmmem”通常与虚拟机相关,特别是在Windows系统中。得Windows的Hyper-V或者WSL2会使用这个进程。 首先,用户可能遇到了系统资源占用过高的问题,比如任务管理器里看到vmmem进程占用了大量内存或CPU。这时候他们想知道这是什么进程,是否安全,以及如何优化。 接下来,我需要确认vmmem的具体作用。它是虚拟机管理程序的一部分,负责分配和管理虚拟机使用的物理资源。如果是WSL2用户,或者使用Hyper-V运行虚拟机,这个进程出现是正常的。 然后,用户可能的深层需求是解决资源占用问题。他们可能发现电脑变慢,或者风扇狂转,想找到解决办法。需要提供一些优化方法,比如调整内存分配、关闭不必要的虚拟机服务,或者检查是否有虚拟机没有正确释放资源。 另外,用户可能对突然出现的vmmem进程感到困惑,担心是恶意软件。需要解释它的合法性,并建议通过任务管理器查看其路径,确认是否位于系统目录,以排除病毒的可能性。 还要考虑不同的使用场景,比如开发人员使用WSL2进行编程,或者普通用户使用虚拟机软件。针对不同情况,给出相应的建议。例如,对于WSL2用户,可以指导如何修改.wslconfig文件来限制内存使用。 最后,确保回答结构清晰,分点说明问题原因和解决方案,并给出预防措施,帮助用户全面理解和解决问题。</think>以下是关于`vmmem`进程的详细解析: --- ### **一、进程定义** `vmmem`是Windows系统中与**虚拟机内存管理**直接相关的核心进程,其名称全称为: $$Virtual\ Machine\ Memory\ Management\ Service$$ 主要负责: 1. 为Hyper-V虚拟机/WSL2子系统分配物理内存 2. 管理虚拟机与宿主机之间的内存共享 3. 实现内存动态分配(Ballooning机制) --- ### **二、高资源占用原因** 当任务管理器显示`vmmem`占用过高(特别是内存)时,常见于: $$内存占用率 \propto \frac{运行的虚拟机数量 \times 分配内存}{宿主机物理内存}$$ 具体诱因: 1. **WSL2配置不当** - 默认无内存限制(可能占用50%物理内存) - 可通过创建`%UserProfile%\.wslconfig`设置: ```ini [wsl2] memory=4GB # 限制最大内存 processors=2 # CPU核心数 ``` 2. **Hyper-V虚拟机未释放资源** - 虚拟机暂停未关闭时仍占用内存 - 快照文件积累(.avhd文件膨胀) 3. **内存泄漏** - 旧版Windows 10(1903之前)存在已知内存泄漏BUG - 虚拟机驱动不兼容 --- ### **三、解决方案** #### **步骤1:基础排查** ```powershell # 查看WSL实例状态 wsl --list --verbose # 终止所有WSL实例 wsl --shutdown # 重启LxssManager服务 Get-Service LxssManager | Restart-Service ``` #### **步骤2:内存限制(WSL2专用)** 1. 创建配置文件: ```shell notepad %UserProfile%\.wslconfig ``` 2. 输入配置: ```ini [wsl2] memory=6GB # 根据物理内存调整(建议≤50%) swap=0 # 禁用交换间 localhostForwarding=true ``` #### **步骤3:Hyper-V优化** ```powershell # 查看虚拟机状态 Get-VM | Format-Table Name, State # 完全关闭闲置虚拟机 Stop-VM -Name "VM_NAME" -Force # 调整动态内存配置 Set-VMMemory -VMName "VM_NAME" -DynamicMemoryEnabled $true ``` --- ### **四、进阶调试** 1. **内存转储分析** 使用`procdump -ma vmmem`生成dump文件,通过WinDbg分析: ```bash !address -summary # 查看内存分布 !vm # 显示虚拟内存信息 ``` 2. **性能计数器监控** 添加下列计数器: - `Hyper-V Dynamic Memory Baloon` - `Hyper-V Virtual Machine Bus\Write Bytes/sec` - `Memory\Available MBytes` --- ### **五、预防措施** 1. 定期清理虚拟机快照 2. 为WSL2设置资源上限 3. 升级到Windows 10 2004+或Windows 11(已修复内存泄漏) 4. 检查虚拟机集成服务版本: ```powershell Get-VMIntegrationService -VMName "VM_NAME" | Where-Object {$_.Name -eq "Guest Service Interface"} ``` --- **注意**:若无需使用虚拟机功能,可通过`控制面板 > 程序 > 启用或关闭Windows功能`中禁用**Hyper-V**和**Windows Hypervisor Platform**来彻底
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值