Android13 PMS是如何启动的 _ config sdcardfs(1),HarmonyOS鸿蒙高级开发及实践课后题

(i, pm) -> new PackageInstallerService(
i.getContext(), pm, i::getScanningPackageParser),
(i, pm, cn) -> new InstantAppResolverConnection(
i.getContext(), cn, Intent.ACTION_RESOLVE_INSTANT_APP_PACKAGE),
(i, pm) -> new ModuleInfoProvider(i.getContext()),
(i, pm) -> LegacyPermissionManagerService.create(i.getContext()),
(i, pm) -> domainVerificationService,
(i, pm) -> {
HandlerThread thread = new ServiceThread(TAG,
Process.THREAD_PRIORITY_DEFAULT, true /allowIo/);
thread.start();
return new PackageHandler(thread.getLooper(), pm);
},
new DefaultSystemWrapper(),
LocalServices::getService,
context::getSystemService,
(i, pm) -> new BackgroundDexOptService(i.getContext(), i.getDexManager(), pm),
(i, pm) -> IBackupManager.Stub.asInterface(ServiceManager.getService(
Context.BACKUP_SERVICE)),
(i, pm) -> new SharedLibrariesImpl(pm, i));

if (Build.VERSION.SDK_INT <= 0) {
Slog.w(TAG, “**** ro.build.version.sdk not set!”);
}
//创建一个PackageManagerService实例
PackageManagerService m = new PackageManagerService(injector, onlyCore, factoryTest,
PackagePartitions.FINGERPRINT, Build.IS_ENG, Build.IS_USERDEBUG,
Build.VERSION.SDK_INT, Build.VERSION.INCREMENTAL);
t.traceEnd(); // “create package manager”

//根据用户类型安装或卸载系统应用安装包
m.installAllowlistedSystemPackages();
//创建IPackageManager并添加到ServiceManager中,客户端交互
IPackageManagerImpl iPackageManager = m.new IPackageManagerImpl();
ServiceManager.addService(“package”, iPackageManager);
//创建native的PackageManager,注册到ServiceManager中
final PackageManagerNative pmn = new PackageManagerNative(m);
ServiceManager.addService(“package_native”, pmn);
LocalManagerRegistry.addManager(PackageManagerLocal.class, m.new PackageManagerLocalImpl());
//返回PackageManagerService和IPackageManager
return Pair.create(m, iPackageManager);
}

在main方法中重要的就几件事

1.创建PackageManagerServiceInjector
2.创建PackageManagerService
3.创建iPackageManager添加到ServiceManager中
4.创建PackageManagerNative添加到ServiceManager中

PackageManagerServiceInjector就是很简单的做了一层封装,把所有相关的对象都初始化都保存了起来。接下来看PackageManagerService,这个初始化函数非常长,所以分几段来看,可以根据log分为BOOT_PROGRESS_PMS_START,BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,BOOT_PROGRESS_PMS_SCAN_END,BOOT_PROGRESS_PMS_READY,这几个阶段

BOOT_PROGRESS_PMS_START

public PackageManagerService(PackageManagerServiceInjector injector, boolean onlyCore,
boolean factoryTest, final String buildFingerprint, final boolean isEngBuild,
final boolean isUserDebugBuild, final int sdkVersion, final String incrementalVersion) {
mIsEngBuild = isEngBuild;
mIsUserDebugBuild = isUserDebugBuild;
mSdkVersion = sdkVersion;
mIncrementalVersion = incrementalVersion;
mInjector = injector;
mInjector.getSystemWrapper().disablePackageCaches();

final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + “Timing”,
Trace.TRACE_TAG_PACKAGE_MANAGER);
mPendingBroadcasts = new PendingPackageBroadcasts();
mInjector.bootstrap(this);
mLock = injector.getLock();
mPackageStateWriteLock = mLock;
mInstallLock = injector.getInstallLock();//重要的lock
LockGuard.installLock(mLock, LockGuard.INDEX_PACKAGES);
EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,
SystemClock.uptimeMillis());//BOOT_PROGRESS_PMS_START阶段

mContext = injector.getContext();
mFactoryTest = factoryTest;
mOnlyCore = onlyCore;//是否只启动核心业务
mMetrics = injector.getDisplayMetrics();
mInstaller = injector.getInstaller();//获取installer
mEnableFreeCacheV2 = SystemProperties.getBoolean(“fw.free_cache_v2”, true);
// 将当前PackageManagerInternal注册到了LocalServices中
LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());
LocalServices.addService(TestUtilityService.class, this);
mTestUtilityService = LocalServices.getService(TestUtilityService.class);
//…中间都是获取PackageManagerServiceInjector的一些成员变量
// 创建了Settings类了,mSettings管理着app的安装信息,这里添加了6个app的包名到共享用户列表,同时设置权限为系统级别的权限uid
mSettings = injector.getSettings();
mSettings.addSharedUserLPw(“android.uid.system”, Process.SYSTEM_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw(“android.uid.phone”, RADIO_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw(“android.uid.log”, LOG_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw(“android.uid.nfc”, NFC_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw(“android.uid.bluetooth”, BLUETOOTH_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw(“android.uid.shell”, SHELL_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw(“android.uid.se”, SE_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw(“android.uid.networkstack”, NETWORKSTACK_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
mSettings.addSharedUserLPw(“android.uid.uwb”, UWB_UID,
ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
//dex到odex/oat的转换
mPackageDexOptimizer = injector.getPackageDexOptimizer();
mDexManager = injector.getDexManager();
mBackgroundDexOptService = injector.getBackgroundDexOptService();
//ART虚拟机管理服务
mArtManagerService = injector.getArtManagerService();
mMoveCallbacks = new MovePackageHelper.MoveCallbacks(FgThread.get().getLooper());
mViewCompiler = injector.getViewCompiler();
//共享库
mSharedLibraries = mInjector.getSharedLibrariesImpl();
//获取分辨率
mContext.getSystemService(DisplayManager.class)
.getDisplay(Display.DEFAULT_DISPLAY).getMetrics(mMetrics);

t.traceBegin(“get system config”);
//获取系统配置
SystemConfig systemConfig = injector.getSystemConfig();
mAvailableFeatures = systemConfig.getAvailableFeatures();
t.traceEnd();
//限制几个系统app的访问,如launcher
mProtectedPackages = new ProtectedPackages(mContext);
mApexManager = injector.getApexManager();
mAppsFilter = mInjector.getAppsFilter();

mInstantAppRegistry = new InstantAppRegistry(mContext, mPermissionManager,
mInjector.getUserManagerInternal(), new DeletePackageHelper(this));
//复制监听包变化的事件,安装卸载等
mChangedPackagesTracker = new ChangedPackagesTracker();
//非系统app的路径
mAppInstallDir = new File(Environment.getDataDirectory(), “app”);
//…中间初始化了各种操作类
// CHECKSTYLE:OFF IndentationCheck
synchronized (mInstallLock) {
// writer
synchronized (mLock) {
//获取安装线程的handler,同样是injector中创建的
mHandler = injector.getHandler();
mProcessLoggingHandler = new ProcessLoggingHandler();
Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
//获取系统的共享库
ArrayMap<String, SystemConfig.SharedLibraryEntry> libConfig
= systemConfig.getSharedLibraries();
final int builtInLibCount = libConfig.size();
for (int i = 0; i < builtInLibCount; i++) {
mSharedLibraries.addBuiltInSharedLibraryLPw(libConfig.valueAt(i));
}

// Now that we have added all the libraries, iterate again to add dependency
// information IFF their dependencies are added.
//添加库的依赖
long undefinedVersion = SharedLibraryInfo.VERSION_UNDEFINED;
for (int i = 0; i < builtInLibCount; i++) {
String name = libConfig.keyAt(i);
SystemConfig.SharedLibraryEntry entry = libConfig.valueAt(i);
final int dependencyCount = entry.dependencies.length;
for (int j = 0; j < dependencyCount; j++) {
final SharedLibraryInfo dependency =
computer.getSharedLibraryInfo(entry.dependencies[j], undefinedVersion);
if (dependency != null) {
computer.getSharedLibraryInfo(name, undefinedVersion)
.addDependency(dependency);
}
}
}

SELinuxMMAC.readInstallPolicy();

t.traceBegin(“loadFallbacks”);
FallbackCategoryProvider.loadFallbacks();
t.traceEnd();

t.traceBegin(“read user settings”);
//根据/data/system下的XML判断是不是第一次启动
mFirstBoot = !mSettings.readLPw(computer,
mInjector.getUserManagerInternal().getUsers(
/* excludePartial= / true,
/
excludeDying= / false,
/
excludePreCreated= */ false));
t.traceEnd();

if (mFirstBoot) {
t.traceBegin("setFirstBoot: ");
try {
mInstaller.setFirstBoot();
} catch (InstallerException e) {
Slog.w(TAG, "Could not set First Boot: ", e);
}
t.traceEnd();
}
//获取系统中定义的权限保存起来
mPermissionManager.readLegacyPermissionsTEMP(mSettings.mPermissions);
mPermissionManager.readLegacyPermissionStateTEMP();

if (!mOnlyCore && mFirstBoot) {
//如果都不是就复制dex
DexOptHelper.requestCopyPreoptedFiles();
}

这里做的还都是准备工作,获取了需要的一系列对象和路径,比较重要的点就是创建了Settings。

//frameworks/base/services/core/java/com/android/server/pm/Settings.java
Settings(File dataDir, RuntimePermissionsPersistence runtimePermissionsPersistence,
LegacyPermissionDataProvider permissionDataProvider,
@NonNull DomainVerificationManagerInternal domainVerificationManager,
@NonNull Handler handler,
@NonNull PackageManagerTracedLock lock) {

mSystemDir = new File(dataDir, “system”);
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
mSettingsFilename = new File(mSystemDir, “packages.xml”);
mBackupSettingsFilename = new File(mSystemDir, “packages-backup.xml”);
mPackageListFilename = new File(mSystemDir, “packages.list”);
FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);

final File kernelDir = new File(“/config/sdcardfs”);
mKernelMappingFilename = kernelDir.exists() ? kernelDir : null;

// Deprecated: Needed for migration
mStoppedPackagesFilename = new File(mSystemDir, “packages-stopped.xml”);
mBackupStoppedPackagesFilename = new File(mSystemDir, “packages-stopped-backup.xml”);

}

Settings中保存了安装包信息,packages-stopped.xml标记为废弃不关注,主要文件就是/data/system/下的packages.xml,packages-backup.xml,packages.list,packages-backup作为备份文件,其他两个文件的作用是

  • packages.xml: PKMS 扫描完目标文件夹后会创建该文件。当系统进行程序安装、卸载和更新等操作时,均会更新该文件。该文件保存了系统中与 package 相关的一些信息。
  • packages.list:描述系统中存在的所有非系统自带的 APK 的信息。当这些程序有变动时,PKMS 就会更新该文件。

//frameworks/base/services/core/java/com/android/server/pm/Settings.java
boolean readLPw(@NonNull Computer computer, @NonNull List users) {
FileInputStream str = null;
final ArrayMap<String, Long> originalFirstInstallTimes = new ArrayMap<>();

try {
if (str == null) {
if (!mSettingsFilename.exists()) {//如果不存在就返回false,也就是第一次创建
mReadMessages.append(“No settings file found\n”);
PackageManagerService.reportSettingsProblem(Log.INFO,
“No settings file; creating initial state”);
// It’s enough to just touch version details to create them
// with default values
findOrCreateVersion(StorageManager.UUID_PRIVATE_INTERNAL).forceCurrent();
findOrCreateVersion(StorageManager.UUID_PRIMARY_PHYSICAL).forceCurrent();
return false;
}
str = new FileInputStream(mSettingsFilename);
}
final TypedXmlPullParser parser = Xml.resolvePullParser(str);
//…如果存在这些xml文件就通过TypedXmlPullParser解析这些xml文件,保存在Settings中


return true;
}

BOOT_PROGRESS_PMS_SYSTEM_SCAN_START

EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,
startTime);
//获取bootClassPath和systemServerClassPath下的环境变量
final String bootClassPath = System.getenv(“BOOTCLASSPATH”);
final String systemServerClassPath = System.getenv(“SYSTEMSERVERCLASSPATH”);

final VersionInfo ver = mSettings.getInternalVersion();
//判断是否升级了
mIsUpgrade =
!buildFingerprint.equals(ver.fingerprint);
if (mIsUpgrade) {
PackageManagerServiceUtils.logCriticalInfo(Log.INFO, "Upgrading from "

  • ver.fingerprint + " to " + PackagePartitions.FINGERPRINT);
    }
    //初始化app的类
    mInitAppsHelper = new InitAppsHelper(this, mApexManager, mInstallPackageHelper,
    mInjector.getSystemPartitions());

    //如果是android M升级上来的需要修改为运行时权限
    mPromoteSystemApps =
    mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;

    // 对于Android N之前版本升级上来的情况,需像首次启动一样处理package
    mIsPreNUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N;
    mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1;
    mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q;

    final WatchedArrayMap<String, PackageSetting> packageSettings =
    mSettings.getPackagesLocked();

    //扫描之前就存在的系统package的名称,这样就可以确定哪些更新的
    if (isDeviceUpgrading()) {
    mExistingPackages = new ArraySet<>(packageSettings.size());
    for (PackageSetting ps : packageSettings.values()) {
    mExistingPackages.add(ps.getPackageName());
    }
    }
    //获取缓存目录
    mCacheDir = PackageManagerServiceUtils.preparePackageParserCache(
    mIsEngBuild, mIsUserDebugBuild, mIncrementalVersion);

    final int[] userIds = mUserManager.getUserIds();
    //获取PackageParser2
    PackageParser2 packageParser = mInjector.getScanningCachingPackageParser();
    //扫描系统app
    mOverlayConfig = mInitAppsHelper.initSystemApps(packageParser, packageSettings, userIds,startTime);
    //系统app以外的app
    mInitAppsHelper.initNonSystemApps(packageParser, userIds, startTime);
    packageParser.close();

    // Resolve the storage manager.
    mStorageManagerPackage = getStorageManagerPackageName(computer);

    // Resolve protected action filters. Only the setup wizard is allowed to
    // have a high priority filter for these actions.
    //只允许安装向导这个应用拥有高级别的action
    mSetupWizardPackage = getSetupWizardPackageNameImpl(computer);
    mComponentResolver.fixProtectedFilterPriorities(mSetupWizardPackage);
    //获取了几个系统默认应用的包名
    mDefaultTextClassifierPackage = ensureSystemPackageName(computer,
    mContext.getString(R.string.config_servicesExtensionPackage));
    mSystemTextClassifierPackageName = ensureSystemPackageName(computer,
    mContext.getString(R.string.config_defaultTextClassifierPackage));
    mConfiguratorPackage = ensureSystemPackageName(computer,
    mContext.getString(R.string.config_deviceConfiguratorPackageName));
    mAppPredictionServicePackage = ensureSystemPackageName(computer,
    getPackageFromComponentString(R.string.config_defaultAppPredictionService));
    mIncidentReportApproverPackage = ensureSystemPackageName(computer,
    mContext.getString(R.string.config_incidentReportApproverPackage));
    mRetailDemoPackage = getRetailDemoPackageName();
    mOverlayConfigSignaturePackage = ensureSystemPackageName(computer,
    mInjector.getSystemConfig().getOverlayConfigSignaturePackage());
    mRecentsPackage = ensureSystemPackageName(computer,
    getPackageFromComponentString(R.string.config_recentsComponentName));
    mAmbientContextDetectionPackage = ensureSystemPackageName(computer,
    getPackageFromComponentString(
    R.string.config_defaultAmbientContextDetectionService));

    // Now that we know all of the shared libraries, update all clients to have
    // the correct library paths.
    // 更新客户端以确保持有正确的共享库路径
    mSharedLibraries.updateAllSharedLibrariesLPw(
    null, null, Collections.unmodifiableMap(mPackages));

    for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {
    // NOTE: We ignore potential failures here during a system scan (like
    // the rest of the commands above) because there’s precious little we
    // can do about it. A settings error is reported, though.
    //如果升级导致abi发生了变化,那么就卸载原来api的代码
    final List changedAbiCodePath =
    ScanPackageUtils.applyAdjustedAbiToSharedUser(
    setting, null /scannedPackage/,
    mInjector.getAbiHelper().getAdjustedAbiForSharedUser(
    setting.getPackageStates(), null /scannedPackage/));
    if (changedAbiCodePath != null && changedAbiCodePath.size() > 0) {
    for (int i = changedAbiCodePath.size() - 1; i >= 0; --i) {
    final String codePathString = changedAbiCodePath.get(i);
    try {
    mInstaller.rmdex(codePathString,
    getDexCodeInstructionSet(getPreferredInstructionSet()));
    } catch (InstallerException ignored) {
    }
    }
    }
    // Adjust seInfo to ensure apps which share a sharedUserId are placed in the same
    // SELinux domain.
    setting.fixSeInfoLocked();
    setting.updateProcesses();
    }

    // Now that we know all the packages we are keeping,
    // read and update their last usage times.
    // 读取并更新保留的package的上次使用时间
    mPackageUsage.read(packageSettings);
    mCompilerStats.read();

这里最重要的就是通过mInitAppsHelper获取了系统apk和其他的apk的信息。

//frameworks/base/services/core/java/com/android/server/pm/InitAppsHelper.java
public OverlayConfig initSystemApps(PackageParser2 packageParser,
WatchedArrayMap<String, PackageSetting> packageSettings,
int[] userIds, long startTime) {

scanSystemDirs(packageParser, mExecutorService);
// Parse overlay configuration files to set default enable state, mutability, and
// priority of system overlays.
final ArrayMap<String, File> apkInApexPreInstalledPaths = new ArrayMap<>();
for (ApexManager.ActiveApexInfo apexInfo : mApexManager.getActiveApexInfos()) {
for (String packageName : mApexManager.getApksInApex(apexInfo.apexModuleName)) {
apkInApexPreInstalledPaths.put(packageName, apexInfo.preInstalledApexPath);
}
}

return overlayConfig;
}


private void scanSystemDirs(PackageParser2 packageParser, ExecutorService executorService) {
///system/framework路径下
File frameworkDir = new File(Environment.getRootDirectory(), “framework”);

scanDirTracedLI(frameworkDir, null,
mSystemParseFlags,
mSystemScanFlags | SCAN_NO_DEX | SCAN_AS_PRIVILEGED,
packageParser, executorService);
//…其他系统app路径也一样就不展示了
}

scanSystemDirs中就是通过scanDirTracedLI扫描各种路径下的apk。

private void scanDirTracedLI(File scanDir, List frameworkSplits,
int parseFlags, int scanFlags,
PackageParser2 packageParser, ExecutorService executorService) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, “scanDir [” + scanDir.getAbsolutePath() + “]”);
try {
if ((scanFlags & SCAN_AS_APK_IN_APEX) != 0) {
// when scanning apk in apexes, we want to check the maxSdkVersion
parseFlags |= PARSE_APK_IN_APEX;
}
mInstallPackageHelper.installPackagesFromDir(scanDir, frameworkSplits, parseFlags,
scanFlags, packageParser, executorService);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}

scanDirTracedLI调用了InstallPackageHelper.installPackagesFromDir来安装

//frameworks/base/services/core/java/com/android/server/pm/InstallPackageHelper.java
public void installPackagesFromDir(File scanDir, List frameworkSplits, int parseFlags,
int scanFlags, PackageParser2 packageParser,
ExecutorService executorService) {
final File[] files = scanDir.listFiles();

ParallelPackageParser parallelPackageParser =
new ParallelPackageParser(packageParser, executorService, frameworkSplits);

// Submit files for parsing in parallel
int fileCount = 0;
for (File file : files) {
final boolean isPackage = (isApkFile(file) || file.isDirectory())
&& !PackageInstallerService.isStageName(file.getName());
if (!isPackage) {
// Ignore entries which are not packages
continue;
}
if ((scanFlags & SCAN_DROP_CACHE) != 0) {
final PackageCacher cacher = new PackageCacher(mPm.getCacheDir());
Log.w(TAG, "Dropping cache of " + file.getAbsolutePath());
cacher.cleanCachedResult(file);
}
parallelPackageParser.submit(file, parseFlags);
fileCount++;
}

// Process results one by one
for (; fileCount > 0; fileCount–) {
ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();
Throwable throwable = parseResult.throwable;
int errorCode = PackageManager.INSTALL_SUCCEEDED;
String errorMsg = null;

if (throwable == null) {
// TODO(b/194319951): move lower in the scan chain
// Static shared libraries have synthetic package names
if (parseResult.parsedPackage.isStaticSharedLibrary()) {
PackageManagerService.renameStaticSharedLibraryPackage(
parseResult.parsedPackage);
}
try {
addForInitLI(parseResult.parsedPackage, parseFlags, scanFlags,
null);
} catch (PackageManagerException e) {
errorCode = e.error;
errorMsg = "Failed to scan " + parseResult.scanFile + ": " + e.getMessage();
Slog.w(TAG, errorMsg);
}
} else if (throwable instanceof PackageManagerException) {
PackageManagerException e = (PackageManagerException) throwable;
errorCode = e.error;
errorMsg = "Failed to parse " + parseResult.scanFile + ": " + e.getMessage();
Slog.w(TAG, errorMsg);
} else {
throw new IllegalStateException("Unexpected exception occurred while parsing "

  • parseResult.scanFile, throwable);
    }

}
}

通过ParallelPackageParser.submit提交安装任务,ParallelPackageParser.submit.take获取任务结果。

//frameworks/base/services/core/java/com/android/server/pm/ParallelPackageParser.java
public void submit(File scanFile, int parseFlags) {
mExecutorService.submit(() -> {
ParseResult pr = new ParseResult();
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, “parallel parsePackage [” + scanFile + “]”);
try {
pr.scanFile = scanFile;
pr.parsedPackage = parsePackage(scanFile, parseFlags);
} catch (Throwable e) {
pr.throwable = e;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

,而且极易碰到天花板技术停滞不前!**

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-hKgvzHJg-1712646323388)]
[外链图片转存中…(img-lLh3jurW-1712646323388)]
[外链图片转存中…(img-lpHOd4Q6-1712646323389)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
[外链图片转存中…(img-7ATWCtWj-1712646323389)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 21
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值