当前位置: 首页 > news >正文

Android 安装应用-浏览阶段

  应用安装的浏览阶段主要是由PackageManagerService类中的scanPackageNewLI()实现的,看一下它的代码:

    // TODO: scanPackageNewLI() and scanPackageOnly() should be merged. But, first, commiting// the results / removing app data needs to be moved up a level to the callers of this// method. Also, we need to solve the problem of potentially creating a new shared user// setting. That can probably be done later and patch things up after the fact.@GuardedBy({"mInstallLock", "mLock"})private ScanResult scanPackageNewLI(@NonNull ParsedPackage parsedPackage,final @ParseFlags int parseFlags, @ScanFlags int scanFlags, long currentTime,@Nullable UserHandle user, String cpuAbiOverride) throws PackageManagerException {final String renamedPkgName = mSettings.getRenamedPackageLPr(parsedPackage.getRealPackage());final String realPkgName = getRealPackageName(parsedPackage, renamedPkgName);if (realPkgName != null) {ensurePackageRenamed(parsedPackage, renamedPkgName);}final PackageSetting originalPkgSetting = getOriginalPackageLocked(parsedPackage,renamedPkgName);final PackageSetting pkgSetting = mSettings.getPackageLPr(parsedPackage.getPackageName());final PackageSetting disabledPkgSetting =mSettings.getDisabledSystemPkgLPr(parsedPackage.getPackageName());if (mTransferredPackages.contains(parsedPackage.getPackageName())) {Slog.w(TAG, "Package " + parsedPackage.getPackageName()+ " was transferred to another, but its .apk remains");}scanFlags = adjustScanFlags(scanFlags, pkgSetting, disabledPkgSetting, user, parsedPackage);synchronized (mLock) {boolean isUpdatedSystemApp;if (pkgSetting != null) {isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();} else {isUpdatedSystemApp = disabledPkgSetting != null;}applyPolicy(parsedPackage, parseFlags, scanFlags, mPlatformPackage, isUpdatedSystemApp);assertPackageIsValid(parsedPackage, parseFlags, scanFlags);SharedUserSetting sharedUserSetting = null;if (parsedPackage.getSharedUserId() != null) {// SIDE EFFECTS; may potentially allocate a new shared usersharedUserSetting = mSettings.getSharedUserLPw(parsedPackage.getSharedUserId(),0 /*pkgFlags*/, 0 /*pkgPrivateFlags*/, true /*create*/);if (DEBUG_PACKAGE_SCANNING) {if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {Log.d(TAG, "Shared UserID " + parsedPackage.getSharedUserId()+ " (uid=" + sharedUserSetting.userId + "):"+ " packages=" + sharedUserSetting.packages);}}}String platformPackageName = mPlatformPackage == null? null : mPlatformPackage.getPackageName();final ScanRequest request = new ScanRequest(parsedPackage, sharedUserSetting,pkgSetting == null ? null : pkgSetting.pkg, pkgSetting, disabledPkgSetting,originalPkgSetting, realPkgName, parseFlags, scanFlags,Objects.equals(parsedPackage.getPackageName(), platformPackageName), user,cpuAbiOverride);return scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime);}}

  参数parsedPackage是解析安装文件得到的解析包对象。parsedPackage.getRealPackage()是在解析安装文件时,配置文件存在application同级别original-package标签 的属性 "name"的值,并且它的值和包名不一致时,会设置该值。在这里是找是否存在更改包名的情况,包名的更改关系维护在mSettings中的mRenamedPackages中,所以如果存在包名更改,renamedPkgName就是改名之前的包名。
  getRealPackageName(parsedPackage, renamedPkgName)是来判断改名之前的名字,是否存在parsedPackage对象的OriginalPackages中。如果存在,就返回parsedPackage.getRealPackage()。
  如果realPkgName != null不为null,说明存在改名字的情况,所以调用ensurePackageRenamed(parsedPackage, renamedPkgName)来将解析包的包名改为之前的包名。
  getOriginalPackageLocked(parsedPackage, renamedPkgName)是得到它原来的PackageSetting对象,但是像上面改名之前的包名在parsedPackage对象的OriginalPackages中的,则会返回null。因为它的名字已经是现在解析包的包名了。如果renamedPkgName不在parsedPackage对象的OriginalPackages中,并且parsedPackage对象的OriginalPackages中包名存在PackageSetting对象,但是看它的条件得旧包是系统包,并且在mPackages中不存在对应的值的情况下,才能返回它的PackageSetting对象。所以在这里我们也知道,包名修改是为了系统应用的功能,普通APP没法使用。在这里originalPkgSetting大部分情况下,为null。
  pkgSetting则是维护在mSettings中根据包名得到的PackageSetting对象。disabledPkgSetting是禁止的系统包的PackageSetting对象。
  mTransferredPackages中保存的是改过应用的包名。
  接下来就是调用adjustScanFlags()方法调整浏览标识。
  接着通过判断pkgSetting里面的状态和disabledPkgSetting != null来设置变量isUpdatedSystemApp的值。
  applyPolicy()方法,是用来根据标识来设置解析包对象的一些状态。
  assertPackageIsValid(parsedPackage, parseFlags, scanFlags)是进行一些判断,如果有些状态不符合要求,会报PackageManagerException异常。
  sharedUserSetting是解析包中配置的“android:sharedUserId”标签的共享用户的SharedUserSetting对象。
  platformPackageName是平台包名,它是由mPlatformPackage得出的值。mPlatformPackage是应用包名为"android"的安装包对象,如果存在,它不为null;如果不存在,它为null。
  下面会将上面说的变量封装到ScanRequest对象request中去。最后就是调用scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime),并将它的结果返回。下面看一下它的代码:

scanPackageOnlyLI(request, mInjector, mFactoryTest, currentTime)的实现

分段一

  它的代码也很长,所以分段阅读,第一段:

    @GuardedBy("mInstallLock")@VisibleForTesting@NonNullstatic ScanResult scanPackageOnlyLI(@NonNull ScanRequest request,Injector injector,boolean isUnderFactoryTest, long currentTime)throws PackageManagerException {final PackageAbiHelper packageAbiHelper = injector.getAbiHelper();final UserManagerInternal userManager = injector.getUserManagerInternal();ParsedPackage parsedPackage = request.parsedPackage;PackageSetting pkgSetting = request.pkgSetting;final PackageSetting disabledPkgSetting = request.disabledPkgSetting;final PackageSetting originalPkgSetting = request.originalPkgSetting;final @ParseFlags int parseFlags = request.parseFlags;final @ScanFlags int scanFlags = request.scanFlags;final String realPkgName = request.realPkgName;final SharedUserSetting sharedUserSetting = request.sharedUserSetting;final UserHandle user = request.user;final boolean isPlatformPackage = request.isPlatformPackage;List<String> changedAbiCodePath = null;if (DEBUG_PACKAGE_SCANNING) {if ((parseFlags & ParsingPackageUtils.PARSE_CHATTY) != 0) {Log.d(TAG, "Scanning package " + parsedPackage.getPackageName());}}// Initialize package source and resource directoriesfinal File destCodeFile = new File(parsedPackage.getPath());// We keep references to the derived CPU Abis from settings in oder to reuse// them in the case where we're not upgrading or booting for the first time.String primaryCpuAbiFromSettings = null;String secondaryCpuAbiFromSettings = null;boolean needToDeriveAbi = (scanFlags & SCAN_FIRST_BOOT_OR_UPGRADE) != 0;if (!needToDeriveAbi) {if (pkgSetting != null) {// TODO(b/154610922): if it is not first boot or upgrade, we should directly use// API info from existing package setting. However, stub packages currently do not// preserve ABI info, thus the special condition check here. Remove the special// check after we fix the stub generation.if (pkgSetting.pkg != null && pkgSetting.pkg.isStub()) {needToDeriveAbi = true;} else {primaryCpuAbiFromSettings = pkgSetting.primaryCpuAbiString;secondaryCpuAbiFromSettings = pkgSetting.secondaryCpuAbiString;}} else {// Re-scanning a system package after uninstalling updates; need to derive ABIneedToDeriveAbi = true;}}

  开始是给局部变量赋值。packageAbiHelper实际是PackageAbiHelperImpl类型。parsedPackage是解析的包实例,pkgSetting是旧包的PackageSetting对象。disabledPkgSetting是禁止的系统包的PackageSetting对象(包名相同)、originalPkgSetting是更改包名之前的原来包名对应的PackageSetting对象、realPkgName是如果存在更改包名现在的包名、user目前用户,像例子中也就是System用户(userId为0)。
  parsedPackage.getPath()是解析包的路径。解析Apk文件时,分为解析的是Apk还是目录。如果解析的直接是Apk,则parsedPackage.getPath()为Apk的完整路径名;如果是目录,则parsedPackage.getPath()为Apk文件的目录。
  primaryCpuAbiFromSettings是主要的cpu架构的abi,secondaryCpuAbiFromSettings是次要的cpu架构的abi。在升级系统或第一次启动的情况下,abi是需要从解析包里面取的。所以scanFlags 里面有SCAN_FIRST_BOOT_OR_UPGRADE标识时,会将变量needToDeriveAbi 设置为true,代表abi需要从解析包parsedPackage取得。
  所以needToDeriveAbi为false情况,如果pkgSetting 不为null,则直接从pkgSetting 取得abi。但是如果包设置pkgSetting对应的解析包pkg.isStub()的情况,那stub包是没有保留ABI信息的,所以也需要从当前解析的包信息里面取得。如果pkgSetting 为null,那也只能从解析包信息里面取得ABI。

分段二

  继续看scanPackageOnlyLI()的第二段代码:

        if (pkgSetting != null && pkgSetting.sharedUser != sharedUserSetting) {PackageManagerService.reportSettingsProblem(Log.WARN,"Package " + parsedPackage.getPackageName() + " shared user changed from "+ (pkgSetting.sharedUser != null? pkgSetting.sharedUser.name : "<nothing>")+ " to "+ (sharedUserSetting != null ? sharedUserSetting.name : "<nothing>")+ "; replacing with new");pkgSetting = null;}String[] usesStaticLibraries = null;if (!parsedPackage.getUsesStaticLibraries().isEmpty()) {usesStaticLibraries = new String[parsedPackage.getUsesStaticLibraries().size()];parsedPackage.getUsesStaticLibraries().toArray(usesStaticLibraries);}final UUID newDomainSetId = injector.getDomainVerificationManagerInternal().generateNewId();// TODO(b/135203078): Remove appInfoFlag usage in favor of individually assigned booleans//  to avoid adding something that's unsupported due to lack of state, since it's called//  with null.final boolean createNewPackage = (pkgSetting == null);if (createNewPackage) {final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;final boolean virtualPreload = (scanFlags & SCAN_AS_VIRTUAL_PRELOAD) != 0;// Flags contain system values stored in the server variant of AndroidPackage,// and so the server-side PackageInfoUtils is still called, even without a// PackageSetting to pass in.int pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, null);int pkgPrivateFlags = PackageInfoUtils.appInfoPrivateFlags(parsedPackage, null);// REMOVE SharedUserSetting from method; update in a separate callpkgSetting = Settings.createNewSetting(parsedPackage.getPackageName(),originalPkgSetting, disabledPkgSetting, realPkgName, sharedUserSetting,destCodeFile, parsedPackage.getNativeLibraryRootDir(),AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage),AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage),parsedPackage.getVersionCode(), pkgFlags, pkgPrivateFlags, user,true /*allowInstall*/, instantApp, virtualPreload,UserManagerService.getInstance(), usesStaticLibraries,parsedPackage.getUsesStaticLibrariesVersions(), parsedPackage.getMimeGroups(),newDomainSetId);} else {// make a deep copy to avoid modifying any existing system state.pkgSetting = new PackageSetting(pkgSetting);pkgSetting.pkg = parsedPackage;// REMOVE SharedUserSetting from method; update in a separate call.//// TODO(narayan): This update is bogus. nativeLibraryDir & primaryCpuAbi,// secondaryCpuAbi are not known at this point so we always update them// to null here, only to reset them at a later point.Settings.updatePackageSetting(pkgSetting, disabledPkgSetting, sharedUserSetting,destCodeFile, parsedPackage.getNativeLibraryDir(),AndroidPackageUtils.getPrimaryCpuAbi(parsedPackage, pkgSetting),AndroidPackageUtils.getSecondaryCpuAbi(parsedPackage, pkgSetting),PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting),PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting),UserManagerService.getInstance(),usesStaticLibraries, parsedPackage.getUsesStaticLibrariesVersions(),parsedPackage.getMimeGroups(), newDomainSetId);}

  如果pkgSetting存在共享用户,但是和sharedUserSetting不同,则会将pkgSetting = null。sharedUserSetting是来自解析包,如果不同,认为共享用户发生了变化,这个时候,将pkgSetting置为null,后面将建一个新的PackageSetting对象。
  parsedPackage.getUsesStaticLibraries()来自解析Apk文件AndroidManifest.xml时,"application"标签包裹内"uses-static-library"标签的属性值。这里将它们取出来放到usesStaticLibraries变量内。
  injector.getDomainVerificationManagerInternal()得到的是DomainVerificationService对象,调用它的generateNewId()生成一个UUID值newDomainSetId。
  如果pkgSetting == null,将createNewPackage置为true,代表需要新建一个PackageSetting对象。scanFlags如果存在SCAN_AS_INSTANT_APP或SCAN_AS_VIRTUAL_PRELOAD标识,会将变量instantApp和virtualPreload设置为true。这俩属性在创建新PackageSetting对象时,使用。然后从解析包对象parsedPackage中得到标识pkgFlags、私有标识pkgPrivateFlags。再接着就是调用Settings.createNewSetting()创建一个新的PackageSetting对象。
  如果它不为null,则会通过深copy新建一个PackageSetting对象。并且将解析包指定给它。接着又调用Settings.updatePackageSetting()来更新pkgSetting的对应值。

分段三

    继续看scanPackageOnlyLI()的第三段代码:

        if (createNewPackage && originalPkgSetting != null) {// This is the initial transition from the original package, so,// fix up the new package's name now. We must do this after looking// up the package under its new name, so getPackageLP takes care of// fiddling things correctly.parsedPackage.setPackageName(originalPkgSetting.name);// File a report about this.String msg = "New package " + pkgSetting.realName+ " renamed to replace old package " + pkgSetting.name;reportSettingsProblem(Log.WARN, msg);}final int userId = (user == null ? UserHandle.USER_SYSTEM : user.getIdentifier());// for existing packages, change the install state; but, only if it's explicitly specifiedif (!createNewPackage) {final boolean instantApp = (scanFlags & SCAN_AS_INSTANT_APP) != 0;final boolean fullApp = (scanFlags & SCAN_AS_FULL_APP) != 0;setInstantAppForUser(injector, pkgSetting, userId, instantApp, fullApp);}// TODO(patb): see if we can do away with disabled check here.if (disabledPkgSetting != null|| (0 != (scanFlags & SCAN_NEW_INSTALL)&& pkgSetting != null && pkgSetting.isSystem())) {pkgSetting.getPkgState().setUpdatedSystemApp(true);}parsedPackage.setSeInfo(SELinuxMMAC.getSeInfo(parsedPackage, sharedUserSetting,injector.getCompatibility())).setSeInfoUser(SELinuxUtil.assignSeinfoUser(pkgSetting.readUserState(userId == UserHandle.USER_ALL ? UserHandle.USER_SYSTEM : userId)));if (parsedPackage.isSystem()) {configurePackageComponents(parsedPackage);}

  如果是新创建包,并且originalPkgSetting不为null,则将解析包的包名改为originalPkgSetting的包名。这可能发生在改包名的首次安装时。
  得到userId,默认为UserHandle.USER_SYSTEM。
  如果不是新创建包,会根据SCAN_AS_INSTANT_APP和SCAN_AS_FULL_APP标识,设置pkgSetting里面对应用户的状态。
  如果参数disabledPkgSetting不为null,或者scanFlags 存在SCAN_NEW_INSTALL标识,并且pkgSetting是系统包的情况下,会将pkgSetting的成员变量PackageStateUnserialized对象 pkgState的updatedSystemApp设置为true。什么时候会设置SCAN_NEW_INSTALL标识呢?它是在用户手动安装一个Apk的时候,会设置该标识。
  如果解析包parsedPackage是系统的,调用configurePackageComponents()来设置解析包里组件的enabled属性。

分段四

继续看scanPackageOnlyLI()的第四段代码:

        final String cpuAbiOverride = deriveAbiOverride(request.cpuAbiOverride);final boolean isUpdatedSystemApp = pkgSetting.getPkgState().isUpdatedSystemApp();final File appLib32InstallDir = getAppLib32InstallDir();if ((scanFlags & SCAN_NEW_INSTALL) == 0) {if (needToDeriveAbi) {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "derivePackageAbi");final Pair<PackageAbiHelper.Abis, PackageAbiHelper.NativeLibraryPaths> derivedAbi =packageAbiHelper.derivePackageAbi(parsedPackage, isUpdatedSystemApp,cpuAbiOverride, appLib32InstallDir);derivedAbi.first.applyTo(parsedPackage);derivedAbi.second.applyTo(parsedPackage);Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);// Some system apps still use directory structure for native libraries// in which case we might end up not detecting abi solely based on apk// structure. Try to detect abi based on directory structure.String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);if (parsedPackage.isSystem() && !isUpdatedSystemApp&& pkgRawPrimaryCpuAbi == null) {final PackageAbiHelper.Abis abis = packageAbiHelper.getBundledAppAbis(parsedPackage);abis.applyTo(parsedPackage);abis.applyTo(pkgSetting);final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,isUpdatedSystemApp, appLib32InstallDir);nativeLibraryPaths.applyTo(parsedPackage);}} else {// This is not a first boot or an upgrade, don't bother deriving the// ABI during the scan. Instead, trust the value that was stored in the// package setting.parsedPackage.setPrimaryCpuAbi(primaryCpuAbiFromSettings).setSecondaryCpuAbi(secondaryCpuAbiFromSettings);final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =packageAbiHelper.deriveNativeLibraryPaths(parsedPackage,isUpdatedSystemApp, appLib32InstallDir);nativeLibraryPaths.applyTo(parsedPackage);if (DEBUG_ABI_SELECTION) {Slog.i(TAG, "Using ABIS and native lib paths from settings : " +parsedPackage.getPackageName() + " " +AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage)+ ", "+ AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage));}}} else {if ((scanFlags & SCAN_MOVE) != 0) {// We haven't run dex-opt for this move (since we've moved the compiled output too)// but we already have this packages package info in the PackageSetting. We just// use that and derive the native library path based on the new codepath.parsedPackage.setPrimaryCpuAbi(pkgSetting.primaryCpuAbiString).setSecondaryCpuAbi(pkgSetting.secondaryCpuAbiString);}// Set native library paths again. For moves, the path will be updated based on the// ABIs we've determined above. For non-moves, the path will be updated based on the// ABIs we determined during compilation, but the path will depend on the final// package path (after the rename away from the stage path).final PackageAbiHelper.NativeLibraryPaths nativeLibraryPaths =packageAbiHelper.deriveNativeLibraryPaths(parsedPackage, isUpdatedSystemApp,appLib32InstallDir);nativeLibraryPaths.applyTo(parsedPackage);}

  cpuAbiOverride是从request.cpuAbiOverride里面得到的Abi,isUpdatedSystemApp则是代表是系统升级App。
  文件appLib32InstallDir的路径为"/data/app-lib"。
  下面要从scanFlags存不存在SCAN_NEW_INSTALL标识两种情况去讨论,SCAN_NEW_INSTALL是在用户手动安装APP的时候,会设置。
  1、scanFlags不存在SCAN_NEW_INSTALL标识,并且需要从解析包里得到Abi值
  会调用packageAbiHelper.derivePackageAbi()函数得到Pair对象值。packageAbiHelper.derivePackageAbi()其实主要是将本地库文件提取出来,并且确定解析包的主要、次要CPU ABI。可以进入这篇文件里面了解一下,Android 提取出Apk的本地库。
  然后将Pair对象值应用到解析包。Pair的first是Abis对象,Pair的second是PackageAbiHelper.NativeLibraryPaths。看一下它俩的applyTo()方法:

Abis
public void applyTo(ParsedPackage pkg) {pkg.setPrimaryCpuAbi(primary).setSecondaryCpuAbi(secondary);}NativeLibraryPathspublic void applyTo(ParsedPackage pkg) {pkg.setNativeLibraryRootDir(nativeLibraryRootDir).setNativeLibraryRootRequiresIsa(nativeLibraryRootRequiresIsa).setNativeLibraryDir(nativeLibraryDir).setSecondaryNativeLibraryDir(secondaryNativeLibraryDir);}        

  可以看到,解析包对象主要设置了它的primaryCpuAbi、secondaryCpuAbi、nativeLibraryRootDir、nativeLibraryRootRequiresIsa、nativeLibraryDir、secondaryNativeLibraryDir。
  如果设置完之后,parsedPackage的主CpuAbi依然为null,这个时候,会调用packageAbiHelper.getBundledAppAbis()通过Apk文件所在的目录结构结合系统支持的ABI来得到Abi。得到结果之后,应用到解析包和包设置对象中。最后packageAbiHelper.deriveNativeLibraryPaths()(该方法也在这篇文章有介绍)得到本地库的路径然后设置到解析包中。
  2、scanFlags不存在SCAN_NEW_INSTALL标识,并且不需要从解析包里得到Abi值。
  在这种情况下,解析包直接取primaryCpuAbiFromSettings、secondaryCpuAbiFromSettings的值,从前面我们知道,它俩的值是从包设置对象里面取得的。接着调用packageAbiHelper.deriveNativeLibraryPaths()得到本地库的路径然后设置到解析包中。
  3、scanFlags存在SCAN_NEW_INSTALL标识
  代表着有可能用户手动安装APP的。SCAN_MOVE代表是要移动一个安装包,这个时候,将parsedPackage的Abi设置成包设置里的。接着也是调用packageAbiHelper.deriveNativeLibraryPaths()得到本地库的路径然后设置到解析包中。 Android 安装应用-准备阶段 里面已经执行过这些步骤了,为什么在这里又执行了一遍呢?Android 安装应用-准备阶段 里面执行过之后,紧接着就进行了更改文件目录名的操作,所以这些个路径其实已经发生改变了,在这里又执行了一遍,就将路径名改对了。

分段五

继续看scanPackageOnlyLI()的第五段代码:

        // This is a special case for the "system" package, where the ABI is// dictated by the zygote configuration (and init.rc). We should keep track// of this ABI so that we can deal with "normal" applications that run under// the same UID correctly.if (isPlatformPackage) {parsedPackage.setPrimaryCpuAbi(VMRuntime.getRuntime().is64Bit() ?Build.SUPPORTED_64_BIT_ABIS[0] : Build.SUPPORTED_32_BIT_ABIS[0]);}// If there's a mismatch between the abi-override in the package setting// and the abiOverride specified for the install. Warn about this because we// would've already compiled the app without taking the package setting into// account.if ((scanFlags & SCAN_NO_DEX) == 0 && (scanFlags & SCAN_NEW_INSTALL) != 0) {if (cpuAbiOverride == null) {Slog.w(TAG, "Ignoring persisted ABI override " + cpuAbiOverride +" for package " + parsedPackage.getPackageName());}}pkgSetting.primaryCpuAbiString = AndroidPackageUtils.getRawPrimaryCpuAbi(parsedPackage);pkgSetting.secondaryCpuAbiString = AndroidPackageUtils.getRawSecondaryCpuAbi(parsedPackage);pkgSetting.cpuAbiOverrideString = cpuAbiOverride;if (DEBUG_ABI_SELECTION) {Slog.d(TAG, "Resolved nativeLibraryRoot for " + parsedPackage.getPackageName()+ " to root=" + parsedPackage.getNativeLibraryRootDir()+ ", to dir=" + parsedPackage.getNativeLibraryDir()+ ", isa=" + parsedPackage.isNativeLibraryRootRequiresIsa());}// Push the derived path down into PackageSettings so we know what to// clean up at uninstall time.pkgSetting.legacyNativeLibraryPathString = parsedPackage.getNativeLibraryRootDir();if (DEBUG_ABI_SELECTION) {Log.d(TAG, "Abis for package[" + parsedPackage.getPackageName() + "] are"+ " primary=" + pkgSetting.primaryCpuAbiString+ " secondary=" + pkgSetting.primaryCpuAbiString+ " abiOverride=" + pkgSetting.cpuAbiOverrideString);}if ((scanFlags & SCAN_BOOTING) == 0 && pkgSetting.sharedUser != null) {// We don't do this here during boot because we can do it all// at once after scanning all existing packages.//// We also do this *before* we perform dexopt on this package, so that// we can avoid redundant dexopts, and also to make sure we've got the// code and package path correct.changedAbiCodePath = applyAdjustedAbiToSharedUser(pkgSetting.sharedUser, parsedPackage,packageAbiHelper.getAdjustedAbiForSharedUser(pkgSetting.sharedUser.packages, parsedPackage));}parsedPackage.setFactoryTest(isUnderFactoryTest && parsedPackage.getRequestedPermissions().contains(android.Manifest.permission.FACTORY_TEST));if (parsedPackage.isSystem()) {pkgSetting.setIsOrphaned(true);}

  如果是平台包,根据虚拟机是不是64位,将平台包的主Cpu ABI设置为系统支持的64位或32位的数组的第一序列的值。
  设置SCAN_NO_DEX是为了跳过dexopt。
  接着设置了包设置对象pkgSetting的主要、次要ABI为解析包里的主要次要ABI。并将pkgSetting.cpuAbiOverrideString = cpuAbiOverride。
  接着会将解析包的本地库文件的根目录,设置到包设置对象的legacyNativeLibraryPathString属性中。
  在不是启动的过程(SCAN_BOOTING代表系统启动)中,并且包设置对象pkgSetting的共享用户不为null的情况下,先调用packageAbiHelper.getAdjustedAbiForSharedUser(pkgSetting.sharedUser.packages, parsedPackage) 得到共享用户下包需要调整的ABI,接着调用applyAdjustedAbiToSharedUser()会找到共享用户下的包们主要CPU ABI为null的包,将包的getPath()放入changedAbiCodePath中。
  接着设置解析包的FACTORY_TEST标识。它是由系统处于的工程测试模式打开 和解析包中权限包含android.Manifest.permission.FACTORY_TEST共同确定的。
  如果解析包是系统的,会设置包设置对象setIsOrphaned(true)。

得到共享用户下包需要调整的ABI

  packageAbiHelper是PackageAbiHelperImpl实例,先看一下PackageAbiHelperImpl的getAdjustedAbiForSharedUser():

    /*** Adjusts ABIs for a set of packages belonging to a shared user so that they all match.* i.e, so that all packages can be run inside a single process if required.** Optionally, callers can pass in a parsed package via {@code newPackage} in which case* this function will either try and make the ABI for all packages in* {@code packagesForUser} match {@code scannedPackage} or will update the ABI of* {@code scannedPackage} to match the ABI selected for {@code packagesForUser}. This* variant is used when installing or updating a package that belongs to a shared user.** NOTE: We currently only match for the primary CPU abi string. Matching the secondary* adds unnecessary complexity.*/@Override@Nullablepublic String getAdjustedAbiForSharedUser(Set<PackageSetting> packagesForUser, AndroidPackage scannedPackage) {String requiredInstructionSet = null;if (scannedPackage != null) {String pkgRawPrimaryCpuAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(scannedPackage);if (pkgRawPrimaryCpuAbi != null) {requiredInstructionSet = VMRuntime.getInstructionSet(pkgRawPrimaryCpuAbi);}}PackageSetting requirer = null;for (PackageSetting ps : packagesForUser) {// If packagesForUser contains scannedPackage, we skip it. This will happen// when scannedPackage is an update of an existing package. Without this check,// we will never be able to change the ABI of any package belonging to a shared// user, even if it's compatible with other packages.if (scannedPackage != null && scannedPackage.getPackageName().equals(ps.name)) {continue;}if (ps.primaryCpuAbiString == null) {continue;}final String instructionSet =VMRuntime.getInstructionSet(ps.primaryCpuAbiString);if (requiredInstructionSet != null && !requiredInstructionSet.equals(instructionSet)) {// We have a mismatch between instruction sets (say arm vs arm64) warn about// this but there's not much we can do.String errorMessage = "Instruction set mismatch, "+ ((requirer == null) ? "[caller]" : requirer)+ " requires " + requiredInstructionSet + " whereas " + ps+ " requires " + instructionSet;Slog.w(PackageManagerService.TAG, errorMessage);}if (requiredInstructionSet == null) {requiredInstructionSet = instructionSet;requirer = ps;}}if (requiredInstructionSet == null) {return null;}final String adjustedAbi;if (requirer != null) {// requirer != null implies that either scannedPackage was null or that// scannedPackage did not require an ABI, in which case we have to adjust// scannedPackage to match the ABI of the set (which is the same as// requirer's ABI)adjustedAbi = requirer.primaryCpuAbiString;} else {// requirer == null implies that we're updating all ABIs in the set to// match scannedPackage.adjustedAbi = AndroidPackageUtils.getRawPrimaryCpuAbi(scannedPackage);}return adjustedAbi;}

  先取到解析包的主要ABI对应的指令集requiredInstructionSet。
  接着循环包设置对象集合packagesForUser,如果解析包不为null,并且包名和当前循环的包名相等,就直接跳出,进行下次循环。
  如果循环的包设置的主要ABI为null,就进行下次循环。
  接下来,如果解析包的指令集为null,找到第一个主要ABI不为null的包设置对象,在解析包的主要指令为null的情况下,将解析包的指令设置给requiredInstructionSet变量,将包对象设置为requirer。
  接着判断requirer不为null,其实就是取值为循环包设置集合里面第一个ABI不为null的包设置对象的ABI。
  如果requirer为null,取值就是解析包的主要ABI。
  其实它这个方法,主要就是解析包的主要ABI存在,就取解析包的主要ABI;如果不存在,就去包设置集合里面寻找第一个主要ABI不为null的包设置的主要ABI。

得到共享用户中包需要修改ABI的包的路径

    /*** Applies the adjusted ABI calculated by* {@link PackageAbiHelper#getAdjustedAbiForSharedUser(Set, AndroidPackage)} to all* relevant packages and settings.* @param sharedUserSetting The {@code SharedUserSetting} to adjust* @param scannedPackage the package being scanned or null* @param adjustedAbi the adjusted ABI calculated by {@link PackageAbiHelper}* @return the list of code paths that belong to packages that had their ABIs adjusted.*/private static List<String> applyAdjustedAbiToSharedUser(SharedUserSetting sharedUserSetting,ParsedPackage scannedPackage, String adjustedAbi) {if (scannedPackage != null)  {scannedPackage.setPrimaryCpuAbi(adjustedAbi);}List<String> changedAbiCodePath = null;for (PackageSetting ps : sharedUserSetting.packages) {if (scannedPackage == null || !scannedPackage.getPackageName().equals(ps.name)) {if (ps.primaryCpuAbiString != null) {continue;}ps.primaryCpuAbiString = adjustedAbi;if (ps.pkg != null) {if (!TextUtils.equals(adjustedAbi,AndroidPackageUtils.getRawPrimaryCpuAbi(ps.pkg))) {if (DEBUG_ABI_SELECTION) {Slog.i(TAG,"Adjusting ABI for " + ps.name + " to " + adjustedAbi+ " (scannedPackage="+ (scannedPackage != null ? scannedPackage : "null")+ ")");}if (changedAbiCodePath == null) {changedAbiCodePath = new ArrayList<>();}changedAbiCodePath.add(ps.getPathString());}}}}return changedAbiCodePath;}

  解析包不为null,直接设置解析包的主要ABI。
  接着就是在循环中寻找,包名和解析包的包名不同的情况下,包设置的主要ABI为null的包设置对象,并且会将包设置对象的主要ABI设置为调整的ABI adjustedAbi。然后检查包设置对象的解析包对象的ABI和调整的ABI不同,就会将包设置对象的getPathString()添加到结果集合中。

分段六

  继续看scanPackageOnlyLI()的最后一段代码:

        // Take care of first install / last update times.final long scanFileTime = getLastModifiedTime(parsedPackage);if (currentTime != 0) {if (pkgSetting.firstInstallTime == 0) {pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = currentTime;} else if ((scanFlags & SCAN_UPDATE_TIME) != 0) {pkgSetting.lastUpdateTime = currentTime;}} else if (pkgSetting.firstInstallTime == 0) {// We need *something*.  Take time time stamp of the file.pkgSetting.firstInstallTime = pkgSetting.lastUpdateTime = scanFileTime;} else if ((parseFlags & ParsingPackageUtils.PARSE_IS_SYSTEM_DIR) != 0) {if (scanFileTime != pkgSetting.timeStamp) {// A package on the system image has changed; consider this// to be an update.pkgSetting.lastUpdateTime = scanFileTime;}}pkgSetting.setTimeStamp(scanFileTime);// TODO(b/135203078): Remove, move to constructorpkgSetting.pkg = parsedPackage;pkgSetting.pkgFlags = PackageInfoUtils.appInfoFlags(parsedPackage, pkgSetting);pkgSetting.pkgPrivateFlags =PackageInfoUtils.appInfoPrivateFlags(parsedPackage, pkgSetting);if (parsedPackage.getLongVersionCode() != pkgSetting.versionCode) {pkgSetting.versionCode = parsedPackage.getLongVersionCode();}// Update volume if neededfinal String volumeUuid = parsedPackage.getVolumeUuid();if (!Objects.equals(volumeUuid, pkgSetting.volumeUuid)) {Slog.i(PackageManagerService.TAG,"Update" + (pkgSetting.isSystem() ? " system" : "")+ " package " + parsedPackage.getPackageName()+ " volume from " + pkgSetting.volumeUuid+ " to " + volumeUuid);pkgSetting.volumeUuid = volumeUuid;}SharedLibraryInfo staticSharedLibraryInfo = null;if (!TextUtils.isEmpty(parsedPackage.getStaticSharedLibName())) {staticSharedLibraryInfo =AndroidPackageUtils.createSharedLibraryForStatic(parsedPackage);}List<SharedLibraryInfo> dynamicSharedLibraryInfos = null;if (!ArrayUtils.isEmpty(parsedPackage.getLibraryNames())) {dynamicSharedLibraryInfos = new ArrayList<>(parsedPackage.getLibraryNames().size());for (String name : parsedPackage.getLibraryNames()) {dynamicSharedLibraryInfos.add(AndroidPackageUtils.createSharedLibraryForDynamic(parsedPackage, name));}}return new ScanResult(request, true, pkgSetting, changedAbiCodePath,!createNewPackage /* existingSettingCopied */, staticSharedLibraryInfo,dynamicSharedLibraryInfos);}

  处理包设置对象的firstInstallTime、lastUpdateTime。
  这里注意这一点,在这里将包设置对象的pkg设置为解析包对象parsedPackage。
  将包设置对象和解析包对象的Flags合并赋值给包设置对象的pkgFlags。
  将解析包对象的PrivateFlags合并赋值给包设置对象的pkgPrivateFlags。
  如果解析包对象的版本和包设置对象的版本不同,将包设置对象的版本改为解析包对象的版本。
  如果解析包对象的版本和包设置对象的volumeUuid不同,将pkgSetting.volumeUuid = volumeUuid。
  如果解析包对象里面如果配置了静态库,则生成静态库对象staticSharedLibraryInfo。
  如果解析包对象里面如果配置了动态库,则生成动态库对象添加到dynamicSharedLibraryInfos。
  最后将相关信息封装到ScanResult对象返回。封装的内容包括生成的ScanRequest对象,成功结果true、新生成的PackageSetting对象pkgSetting、共享用户中其他修改了ABI的文件路径、是否是深拷贝存在的PackageSetting对象、静态分享库信息、动态分享库信息。

总结

  根据标识调整浏览标识,设置解析包的状态,也会检查一些状态。
  将相关内容封装到ScanRequest对象中,包括解析包对象、共享用户对象、旧安装包对象、现在使用PackageSetting对象、禁止的系统PackageSetting对象、旧PackageSetting对象(改包名之前)、真包名、解析标识、浏览标识、是否是系统平台包、用户、CPU ABI参数。
  创建了一个新的PackageSetting对象(可能是直接new,也可能深copy现在使用的PackageSetting对象)。
  如果是系统第一次启动或者系统更新之后第一次启动时,需要去安装包里得到主要CPU ABI和次要CPU ABI,本地库解析的路径,提取本地库文件(能否提取需要判断)。如果不是,它的主要CPU ABI和次要CPU ABI取值是在生成PackageSetting对象时定的,但它会重新确定本地库解析的路径。
  最后将生成的ScanRequest对象,成功结果true、新生成的PackageSetting对象pkgSetting、共享用户中其他修改了ABI的文件路径、是否是深拷贝存在的PackageSetting对象、静态分享库信息、动态分享库信息,封装成ScanResult对象返回。

相关文章:

Android 安装应用-浏览阶段

应用安装的浏览阶段主要是由PackageManagerService类中的scanPackageNewLI()实现的&#xff0c;看一下它的代码&#xff1a; // TODO: scanPackageNewLI() and scanPackageOnly() should be merged. But, first, commiting// the results / removing app data needs to be move…...

JavaEE 初阶(10)——多线程8之“单例模式”

目录 一. 设计模式 二. 单例模式 2.1 饿汉模式 2.2 懒汉模式 a. 加锁synchronized b. 双重if判定 c. volatile关键字&#xff08;双重检查锁定&#xff09; 一. 设计模式 设计模式是在软件工程中解决常见问题的经典解决方案。针对一些特定场景给出的一些比较好的解决…...

Javascript常见设计模式

JS设计模式学习【待吸收】-CSDN博客 JavaScript 中的设计模式是用来解决常见问题的最佳实践方案。这些模式有助于创建可重用、易于理解和维护的代码。下面列出了一些常见的 JavaScript 设计模式及其代码示例。 1. 单例模式&#xff08;Singleton&#xff09; 单例模式确保一…...

JavaFX布局-SplitPane

JavaFX布局-SplitPane 常用属性orientationpaddingdividerPositionsdisable 实现方式Java实现fxml实现 一个拆分至少两个区域的容器支持水平、垂直布局可以拖动区域的大小初始化大小通过比例设置[0,1] 常用属性 orientation 排列方式&#xff0c;Orientation.VERTICAL、Orien…...

2.MySQL库的操作

创建数据库 创建数据库的代码&#xff1a; CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [,create_specification] ...];​create_specification:[DEFAULT] CHARACTER SET charset_name[DEFAULT] COLLATE collation_name 说明&#xff1a; 大写的表示关键…...

如何学习计算机

不要只盯着计算机语言学习&#xff0c;你现在已经学习了C语言和Java&#xff0c;暑假又规划学习Python&#xff0c;最后你掌握的就是计算机语言包而已。 2. 建议你找一门想要深挖的语言&#xff0c;沿着这个方向继续往后学习知识就行。计算机语言是学不完的&#xff0c;而未来就…...

Spring MVC 快速入门指南及实战演示

1、SpringMVC简介 1.1 背景 Servlet属于web层开发技术&#xff0c;技术特点&#xff1a; 1. 每个请求都需要创建一个Servlet进行处理 2. 创建Servlet存在重复操作 3. 代码灵活性低&#xff0c;开发效率低 是否有技术方案可以解决以上问题&#xff1f; 1.2 SpringMVC概述 Sp…...

在线测评系统(未完结)

文章目录 注意&#xff01;&#xff01;&#xff01;1、多模块开发&#xff08;后端&#xff09;(1).Maven依赖(2)swagger配置 2、判题机开发&#xff08;1&#xff09;docker 前言&#xff1a;大二刚开始接手了本学院的oj&#xff0c;并管理了一段时间&#xff0c;后来老师给我…...

Python 爬虫项目实战(一):破解网易云 VIP 免费下载付费歌曲

前言 网络爬虫&#xff08;Web Crawler&#xff09;&#xff0c;也称为网页蜘蛛&#xff08;Web Spider&#xff09;或网页机器人&#xff08;Web Bot&#xff09;&#xff0c;是一种按照既定规则自动浏览网络并提取信息的程序。爬虫的主要用途包括数据采集、网络索引、内容抓…...

PTA 6-7 统计某类完全平方数

6-7 统计某类完全平方数&#xff08;20分&#xff09; 本题要求实现一个函数&#xff0c;判断任一给定整数N是否满足条件&#xff1a;它是完全平方数&#xff0c;又至少有两位数字相同&#xff0c;如144、676等。 函数接口定义&#xff1a; int IsTheNumber ( const int N );…...

PyFilesystem2 - Python 操作文件系统

文章目录 一、关于 PyFilesystem2二、安装三、快速使用四、指南为什么要使用 PyFilesystem &#xff1f;打开文件系统树打印关闭目录信息子目录处理文件遍历 WalkingGlobbing移动和复制 五、概念路径系统路径沙盒错误 六、资源信息信息对象命名空间基本命名空间细节命名空间访问…...

Bug小记:关于servlet后端渲染界面时出现的问题小记1P

问题1&#xff1a; 问题描述&#xff1a; int delete(Integer Sno);后端在该方法调用时传入参数 req.getParameter("Sno")报错参数应该为Integer类型问题分析&#xff1a;后端通过请求获取到的前端数据都是字符串类型&#xff0c;需要手动转换参数类型 解决方法&a…...

智慧水务项目(二)django(drf)+angular 18 创建通用model,并对orm常用字段进行说明

一、说明 上一篇文章建立一个最简单的项目&#xff0c;现在我们建立一个公共模型&#xff0c;抽取公共字段&#xff0c;以便于后续模块继承&#xff0c;过程之中会对orm常用字段进行说明&#xff0c;用到的介绍一下 二、创建一个db.py 目录如下图 1、代码 from importlib im…...

<数据集>人员摔倒识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;8605张 标注数量(xml文件个数)&#xff1a;8605 标注数量(txt文件个数)&#xff1a;8605 标注类别数&#xff1a;1 标注类别名称&#xff1a;[fall] 序号类别名称图片数框数1fall860512275 使用标注工具&#xf…...

npm install 报错 ‘proxy‘ config is set properly. See: ‘npm help config‘

解决 参考链接&#xff1a;npm install 报错 ‘proxy‘ config is set properly. See: ‘npm help config‘-阿里云开发者社区 (aliyun.com)...

爬虫问题---ChromeDriver的安装和使用

一、安装 1.查看chrome的版本 在浏览器里面输入 chrome://version/ 回车查看浏览器版本 Chrome的版本要和ChromeDriver的版本对应&#xff0c;否则会出现版本问题。 2.ChromeDriver的版本选择 114之前的版本&#xff1a;https://chromedriver.storage.googleapis.com/index.ht…...

Spring的配置类分为Full和Lite两种模式

Spring的配置类分为Full和Lite两种模式 首先查看 Configuration 注解的源码, 如下所示: Target({ElementType.TYPE}) Retention(RetentionPolicy.RUNTIME) Documented Component public interface Configuration {AliasFor(annotation Component.class)String value() defau…...

探索Perl的代码生成艺术:利用编译器后端释放潜能

探索Perl的代码生成艺术&#xff1a;利用编译器后端释放潜能 Perl&#xff0c;作为一种解释型语言&#xff0c;通常不通过编译器后端直接生成机器代码。然而&#xff0c;通过一些高级技术&#xff0c;Perl 程序员可以利用编译器后端来生成代码&#xff0c;从而提高性能或实现特…...

21 B端产品经理之技术常识(1)

产品经理需要掌握一些基本的技术知识。 了解公司前端与后端 前端 前端开发&#xff1a;创建WEB页面或APP等前端界面呈现给用户的过程&#xff0c;即前端负责用户界面交互。 前端技能&#xff1a; HTML&#xff1a;一种标记语言&#xff0c;能够实现Web页面并在浏览器中显示。…...

数据结构基础详解(C语言):单链表_定义_初始化_插入_删除_查找_建立操作_纯c语言代码注释讲解

单链表理论知识详解 文章目录 单链表理论知识详解1.单链表的定义2.单链表的初始化3.单链表的插入和删除3.1 单链表的插入3.1.1 按位序插入3.1.2 在指定结点的前后插入一.后插操作二.前插操作 4.单链表的删除4.1 按位序删除4.2 指定结点的删除 5.单链表的查找5.1 按位序查找5.2 …...

【智能时代的创新工具】LangChain快速入门指南:轻松掌握语言模型的集成与运用

一、LangChain&#xff1a;连接语言模型与现实世界的桥梁 1.1 LangChain的定义与重要性 LangChain是一个开源的Python库&#xff0c;它旨在为开发人员提供一种简便的方式来集成和运用语言模型。它不仅仅是一个简单的API调用工具&#xff0c;而是一个具有丰富功能的框架&#x…...

文献阅读:细胞分辨率全脑图谱的交互式框架

文献介绍 文献题目&#xff1a; An interactive framework for whole-brain maps at cellular resolution 研究团队&#xff1a; Daniel Frth&#xff08;瑞典卡罗林斯卡学院&#xff09;、Konstantinos Meletis&#xff08;瑞典卡罗林斯卡学院&#xff09; 发表时间&#xff…...

YAML基础语言深度解析

引言 YAML&#xff08;YAML Aint Markup Language&#xff0c;即YAML不是一种标记语言&#xff09;是一种直观、易于阅读的数据序列化格式&#xff0c;常用于配置文件、数据交换和程序间的通信。其设计目标是易于人类阅读和编写&#xff0c;同时也便于机器解析和生成。在本文中…...

xcode使用

1. 界面 1.1. Build Settings,Build Phases和Build Rules三个设置项 Build Settings(编译设置): 每个选项由标题(Title)和定义(Definition)组成。这里主要定义了Xcode在编译项目时的一些具体配置 Build Phases(编译资源):用于指定编译过程中项目所链接的原文件,依赖对象,库…...

OV2640引脚的定义(OV2640 FPC模组规格书(接口线序))

OV2640是一款由Omni Vision公司生产的1/4寸CMOS UXGA&#xff08;1632x1222&#xff09;图像传感器。这款传感器以其小巧的体积、低工作电压和强大的功能而著称&#xff0c;它集成了单片UXGA摄像头和影像处理器&#xff0c;能够通过SCCB总线控制输出各种分辨率的8/10位影像数据…...

CTFSHOW 萌新 web10 解题思路和方法(passthru执行命令)

点击题目链接&#xff0c;分析页面代码。发现代码中过滤了system、exec 函数&#xff0c;这意味着我们不能通过system(cmd命令)、exec&#xff08;cmd命令&#xff09;的方式运行命令。 在命令执行中&#xff0c;常用的命令执行函数有&#xff1a; system(cmd_code);exec(cmd_…...

深入Java数据库连接和JDBC

引言 Java数据库连接(JDBC)是Java语言中用于执行SQL语句的标准API。通过JDBC,开发者可以方便地与关系型数据库进行交互。然而,直接使用JDBC API面临着数据库连接管理复杂、性能瓶颈等问题。数据库连接池作为一种解决方案,可以有效地管理数据库连接,提高应用程序的性能。…...

灰狼优化算法(GWO)与长短期记忆网络(LSTM)结合的预测模型(GWO-LSTM)及其Python和MATLAB实现

#### 一、背景 在现代数据科学和人工智能领域&#xff0c;预测模型的准确性和效率是研究者和工程师不断追求的目标&#xff0c;尤其是在时间序列预测、金融市场分析、气象预测等领域。长短期记忆&#xff08;LSTM&#xff09;网络是一种解决传统递归神经网络&#xff08;RNN&a…...

电路板热仿真覆铜率,功率,结温,热阻率信息计算获取方法总结

🏡《电子元器件学习目录》 目录 1,概述2,覆铜率3,功率4,器件尺寸5,结温6,热阻1,概述 电路板热仿真操作是一个复杂且细致的过程,旨在评估和优化电路板内部的热分布及温度变化,以确保电子元件的可靠性和性能。本文简述在进行电路板的热仿真时,元器件热信息的计算方法…...

C#中多线程编程中的同步、异步、串行、并行及并发及死锁

在C#中&#xff0c;多线程编程是一个强大的功能&#xff0c;它允许程序同时执行多个任务。然而&#xff0c;这也带来了复杂性&#xff0c;特别是在处理同步、异步、串行、并行、并发以及死锁等问题时。下面我将详细解释这些概念&#xff0c;并给出一些C#中的示例和注意事项。 …...

做公司网站的服务费入什么费用/网站优化比较好的公司

作者&#xff1a;深邃暗黑范特西 链接&#xff1a;https://www.zhihu.com/question/272156840/answer/367180861 来源&#xff1a;知乎 董可人的那个功夫&#xff0c;低时延的核心在于易经筋模块。进程间通过共享内存(mmap)通信。共享内存上面的数据结构比较简洁&#xff0c;就…...

手机怎么做弹幕小视频网站/站外推广渠道

UIView 继承于UIResponder 所遵守的协议有 NSCoding 、UIAppearance、 UIAppearanceContainer UIDynamicItem、 NSObject 从继承的类我们就可以看出 UIView 这个类可以响应手势 那么我们就从它的属性开始这一旅程吧 UIView 之属性篇 Pro…...

住房和城乡建设部标准定额司网站/引流平台有哪些

Picasso.with(MyApp.getContext()).load("http://avatar.csdn.net/9/3/9/1_mp624183768.jpg").into( viewHolder.ivMingxiImg);依赖 compile com.squareup.picasso:picasso:2.5.2...

宝塔做网站安全吗/网站建设定制

点击链接PTA-Python-AC全解汇总 题目&#xff1a; 本题要求编写程序&#xff0c;计算序列 2/13/25/38/5… 的前N项之和。注意该序列从第2项起&#xff0c;每一项的分子是前一项分子与分母的和&#xff0c;分母是前一项的分子。 输入格式: 输入在一行中给出一个正整数N。 输出…...

做社交网站要注册哪类商标/百度商家入驻

转自&#xff1a;http://www.cnblogs.com/ubosm/p/5444919.html 使用vs2015编译ffmpeg的一个小项时&#xff0c;出现了__imp__fprintf和__imp____iob_func 的错误&#xff0c;google了一下&#xff0c;有的人 建议下载SDL源码重新编译一下&#xff0c;当然这个方案非常不科学。…...

专业做pc+手机网站/短视频营销的优势

内容整理自花利忠教授的课件 文章目录组件对象模型COM基本概念使用接口定义类查询接口(QueryInterface)COM总结组件对象模型COM 基本概念 对象(Object)&#xff1a; 系统中用来描述客观事物的一个实体&#xff0c;构成系统的一个基本单位。由类实例化产生 类(Class)&#xf…...