中文亚洲精品无码_熟女乱子伦免费_人人超碰人人爱国产_亚洲熟妇女综合网

當前位置: 首頁 > news >正文

金川做網(wǎng)站公司網(wǎng)絡推廣代運營公司

金川做網(wǎng)站公司,網(wǎng)絡推廣代運營公司,安徽省交通運輸廳施平,根河企業(yè)網(wǎng)站建設Android 系統(tǒng)源碼源碼-應用安裝過程 Android 中應用安裝的過程就是解析 AndroidManifest.xml 的過程,系統(tǒng)可以從 Manifest 中得到應用程序的相關信息,比如 Activity、Service、Broadcast Receiver 和 ContentProvider 等。這些工作都是由 PackageManage…

Android 系統(tǒng)源碼源碼-應用安裝過程

Android 中應用安裝的過程就是解析 AndroidManifest.xml 的過程,系統(tǒng)可以從 Manifest 中得到應用程序的相關信息,比如 Activity、Service、Broadcast Receiver 和 ContentProvider 等。這些工作都是由 PackageManageService 負責的,也就是所謂的 PMS. 它跟 AMS 一樣都是一種遠程的服務,并且都是在系統(tǒng)啟動 SystemServer 的時候啟動的。下面我們通過源代碼來分析下這個過程。

1、啟動 PMS 的過程

系統(tǒng)在啟動 SystemServer 的過程會啟動 PMS,系統(tǒng)的啟動過程可以參考下面這篇文章學習,

Android 系統(tǒng)源碼-1:Android 系統(tǒng)啟動流程源碼分析

在啟動 SystemServer 的時候會調用 startBootstrapServices() 方法啟動引導服務。PMS 就是在這個方法中啟動的,

    private void startBootstrapServices() {// ...mPackageManagerService = PackageManagerService.main(mSystemContext, installer,mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);mFirstBoot = mPackageManagerService.isFirstBoot();mPackageManager = mSystemContext.getPackageManager();// ...}

可以看出,系統(tǒng)是通過調用 PMS 的 main 方法來將其啟動起來的。其 main 方法會先實例化一個 PMS 對象,然后調用 ServiceManager 的靜態(tài)方法將其注冊到 ServiceManager 中進行管理。

    public static PackageManagerService main(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {PackageManagerServiceCompilerMapping.checkProperties();PackageManagerService m = new PackageManagerService(context, installer,factoryTest, onlyCore);m.enableSystemUserPackages();ServiceManager.addService("package", m);final PackageManagerNative pmn = m.new PackageManagerNative();ServiceManager.addService("package_native", pmn);return m;}

當我們需要使用 PMS 解析 APK 的時候就會從 ServiceManager 中獲取。

在 PMS 的構造方法中有許多工作要完成。一個 APK 安裝的主要分成下面幾個步驟,

  1. 拷貝文件到指定的目錄:默認情況下,用戶安裝的 APK 首先會被拷貝到 /data/app 目錄下,/data/app 目錄是用戶有權限訪問的目錄,在安裝 APK 的時候會自動選擇該目錄存放用戶安裝的文件,而系統(tǒng)的 APK 文件則被放到了 /system 分區(qū)下,包括 /system/app/system/vendor/app,以及 /system/priv-app 等等,該分區(qū)只有 ROOT 權限的用戶才能訪問,這也就是為什么在沒有 Root 手機之前,我們沒法刪除系統(tǒng)出場的 APP 的原因了。
  2. 解壓縮 APK,拷貝文件,創(chuàng)建應用的數(shù)據(jù)目錄:為了加快 APP 的啟動速度,APK 在安裝的時候,會首先將 APP 的可執(zhí)行文件 dex 拷貝到 /data/dalvik-cache 目錄,緩存起來。然后,在 /data/data/ 目錄下創(chuàng)建應用程序的數(shù)據(jù)目錄 (以應用的包名命名),存放在應用的相關數(shù)據(jù),如數(shù)據(jù)庫、XML 文件、Cache、二進制的 so 動態(tài)庫等。
  3. 解析 APK 的 AndroidManifest.xml 文件。
    public PackageManagerService(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {// ....synchronized (mInstallLock) {synchronized (mPackages) {// Expose private service for system components to use.LocalServices.addService(PackageManagerInternal.class, new PackageManagerInternalImpl());sUserManager = new UserManagerService(context, this,new UserDataPreparer(mInsstaller, mInstallLock, mContext, mOnlyCore), mPackages);mPermissionManager = PermissionManagerService.create(context,new DefaultPermissionGrantedCallback() {@Overridepublic void onDefaultRuntimePermissionsGranted(int userId) {synchronized(mPackages) {mSettings.onDefaultRuntimePermissionsGrantedLPr(userId);}}}, mPackages /*externalLock*/);mDefaultPermissionPolicy = mPermissionManager.getDefaultPermissionGrantPolicy();mSettings = new Settings(mPermissionManager.getPermissionSettings(), mPackages);}}// ...mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context, "*dexopt*");DexManager.Listener dexManagerListener = DexLogger.getListener(this, installer, mInstallLock);mDexManager = new DexManager(mContext, this, mPackageDexOptimizer, installer, mInstallLock, dexManagerListener);mArtManagerService = new ArtManagerService(mContext, this, installer, mInstallLock);// ...synchronized (mInstallLock) {synchronized (mPackages) {// 創(chuàng)建消息mHandlerThread = new ServiceThread(TAG,Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);mHandlerThread.start();mHandler = new PackageHandler(mHandlerThread.getLooper());// ...// 掃描各個目錄獲取 APK 文件:VENDOR_OVERLAY_DIR           // framework 文件夾:frameworkDir// 系統(tǒng)文件夾:privilegedAppDir systemAppDir// 供應商的包:Environment.getVendorDirectory()// 原始設備制造商的包 :Environment.getOdmDirectory()// 原始設計商的包:Environment.getOdmDirectory()// 原始產品的包:// ....mInstallerService = new PackageInstallerService(context, this);final Pair<ComponentName, String> instantAppResolverComponent = getInstantAppResolverLPr();if (instantAppResolverComponent != null) {mInstantAppResolverConnection = new InstantAppResolverConnection(mContext, instantAppResolverComponent.first,instantAppResolverComponent.second);mInstantAppResolverSettingsComponent =getInstantAppResolverSettingsLPr(instantAppResolverComponent.first);} else {mInstantAppResolverConnection = null;mInstantAppResolverSettingsComponent = null;}updateInstantAppInstallerLocked(null);final Map<Integer, List<PackageInfo>> userPackages = new HashMap<>();final int[] currentUserIds = UserManagerService.getInstance().getUserIds();for (int userId : currentUserIds) {userPackages.put(userId, getInstalledPackages(/*flags*/ 0, userId).getList());}mDexManager.load(userPackages);} // synchronized (mPackages)} // synchronized (mInstallLock)// ....}

在構造方法中會掃描多個目錄來獲取 APK 文件,上述注釋中我們已經(jīng)給出了這些目錄,及其獲取的方式。當掃描一個路徑的時候會使用 scanDirLI() 方法來完成掃描工作。

    private void scanDirLI(File scanDir, int parseFlags, int scanFlags, long currentTime) {final File[] files = scanDir.listFiles();if (ArrayUtils.isEmpty(files)) {return;}try (ParallelPackageParser parallelPackageParser = new ParallelPackageParser(mSeparateProcesses, mOnlyCore, mMetrics, mCacheDir, mParallelPackageParserCallback)) {int fileCount = 0;for (File file : files) {final boolean isPackage = (isApkFile(file) || file.isDirectory())&& !PackageInstallerService.isStageName(file.getName());if (!isPackage) {continue;}// 提交文件用來解析parallelPackageParser.submit(file, parseFlags);fileCount++;}for (; fileCount > 0; fileCount--) {// 獲取解析的結果,即從隊列阻塞隊列中獲取解析的結果ParallelPackageParser.ParseResult parseResult = parallelPackageParser.take();// ...if (throwable == null) {// TODO(toddke): move lower in the scan chain// Static shared libraries have synthetic package namesif (parseResult.pkg.applicationInfo.isStaticSharedLibrary()) {renameStaticSharedLibraryPackage(parseResult.pkg);}try {if (errorCode == PackageManager.INSTALL_SUCCEEDED) {scanPackageChildLI(parseResult.pkg, parseFlags, scanFlags, currentTime, null);}} catch (PackageManagerException e) {errorCode = e.error;}}// 。。。}}}

從上面的代碼中可以看出,提交文件來解析以及獲取解析都是通過 ParallelPackageParser 來完成的。它使用 submit() 方法來提交文件用來解析,使用 take() 方法獲取解析的結果。這兩個方法的定義如下,

    public void submit(File scanFile, int parseFlags) {mService.submit(() -> {ParseResult pr = new ParseResult();try {PackageParser pp = new PackageParser();pp.setSeparateProcesses(mSeparateProcesses);pp.setOnlyCoreApps(mOnlyCore);pp.setDisplayMetrics(mMetrics);pp.setCacheDir(mCacheDir);pp.setCallback(mPackageParserCallback);pr.scanFile = scanFile;pr.pkg = parsePackage(pp, scanFile, parseFlags);} catch (Throwable e) {pr.throwable = e;}try {mQueue.put(pr);} catch (InterruptedException e) {Thread.currentThread().interrupt();mInterruptedInThread = Thread.currentThread().getName();}});}public ParseResult take() {try {if (mInterruptedInThread != null) {throw new InterruptedException("Interrupted in " + mInterruptedInThread);}return mQueue.take();} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new IllegalStateException(e);}}

submit() 方法使用一個線程池來執(zhí)行任務,也就是上面的 mService。它會將要解析的信息封裝成 PackageParser 對象,然后把解析的結果信息封裝成 ParseResult 放進一個阻塞隊列中。當調用 take() 方法的時候會從該阻塞隊列中獲取解析的結果。

包信息的解析最終是通過 PackageParser 的 parsePackage() 方法來完成的。其定義如下,

    public Package parsePackage(File packageFile, int flags, boolean useCaches)throws PackageParserException {Package parsed = useCaches ? getCachedResult(packageFile, flags) : null;if (parsed != null) {return parsed;}long parseTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;if (packageFile.isDirectory()) {parsed = parseClusterPackage(packageFile, flags);} else {// 是文件,所以走這條路線parsed = parseMonolithicPackage(packageFile, flags);}long cacheTime = LOG_PARSE_TIMINGS ? SystemClock.uptimeMillis() : 0;cacheResult(packageFile, flags, parsed);return parsed;}

我們會在這方法中進入到 parseMonolithicPackage() 來對文件進行解析。

    public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {final PackageLite lite = parseMonolithicPackageLite(apkFile, flags);final SplitAssetLoader assetLoader = new DefaultSplitAssetLoader(lite, flags);try {// 解析final Package pkg = parseBaseApk(apkFile, assetLoader.getBaseAssetManager(), flags);pkg.setCodePath(apkFile.getCanonicalPath());pkg.setUse32bitAbi(lite.use32bitAbi);return pkg;} catch (IOException e) {throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION);} finally {IoUtils.closeQuietly(assetLoader);}}

在這個方法中會使用 parseBaseApk() 來對 APK 文件進行解析,

    private Package parseBaseApk(File apkFile, AssetManager assets, int flags)throws PackageParserException {final String apkPath = apkFile.getAbsolutePath();String volumeUuid = null;if (apkPath.startsWith(MNT_EXPAND)) {final int end = apkPath.indexOf('/', MNT_EXPAND.length());volumeUuid = apkPath.substring(MNT_EXPAND.length(), end);}mParseError = PackageManager.INSTALL_SUCCEEDED;mArchiveSourcePath = apkFile.getAbsolutePath();XmlResourceParser parser = null;try {final int cookie = assets.findCookieForPath(apkPath);// 讀取 AndroidManifest.xmlparser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);final Resources res = new Resources(assets, mMetrics, null);final String[] outError = new String[1];// 在這里進一步解析 Manifest 的各種信息final Package pkg = parseBaseApk(apkPath, res, parser, flags, outError);pkg.setVolumeUuid(volumeUuid);pkg.setApplicationVolumeUuid(volumeUuid);pkg.setBaseCodePath(apkPath);pkg.setSigningDetails(SigningDetails.UNKNOWN);return pkg;} catch (PackageParserException e) {throw e;} catch (Exception e) {throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION);} finally {IoUtils.closeQuietly(parser);}}

這里的 ANDROID_MANIFEST_FILENAME 是一個字符串,這個字符串的定義是 AndroidManifest.xml,所以,我們找到了解析 Manifest 的地方。

然后方法會進入到 parseBaseApk() 方法中進一步對 Manifest 進行解析。其讀取操作就是基本的 XML 解析的過程。它會使用內部定義的字符串常量從 Manifest 中獲取應用的版本還有四大組件等信息。

解析完了 APK 之后會一路經(jīng)過 return 語句返回到 scanDirLI() 方法中,當從阻塞隊列中取出 Package 之后將會調用 scanPackageChildLI() 在該方法中會將解析的出的 APK 信息緩存到 PMS 中。

這樣,在系統(tǒng)啟動之后 PMS 就解析了全部的 APK 文件,并將其緩存到了 PMS 中。這樣這些應用程序還無法展示給用戶,所以需要 Launcher 桌面程序從 PMS 中獲取安裝包信息并展示到桌面上。

2、應用安裝的過程

雖然 PMS 用來負責應用的安裝和卸載,但是真實的工作卻是交給 installd 來實現(xiàn)的。 installd 是在系統(tǒng)啟動的時候,由 init 進程解析 init.rc 文件創(chuàng)建的。在早期版本的 Android 中,它使用 Socket 與 Java 層的 Installer 進行通信。在 9.0 的代碼中,它使用 Binder 與 Java 層的 Installer 進行通信。當啟動 Installd 的時候,將會調用其 main 方法,

int main(const int argc, char *argv[]) {return android::installd::installd_main(argc, argv);
}static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) {int ret;int selinux_enabled = (is_selinux_enabled() > 0);setenv("ANDROID_LOG_TAGS", "*:v", 1);android::base::InitLogging(argv);SLOGI("installd firing up");union selinux_callback cb;cb.func_log = log_callback;selinux_set_callback(SELINUX_CB_LOG, cb);// 初始化全局信息if (!initialize_globals()) {exit(1);}// 初始化相關目錄if (initialize_directories() < 0) {exit(1);}if (selinux_enabled && selinux_status_open(true) < 0) {exit(1);}if ((ret = InstalldNativeService::start()) != android::OK) {exit(1);}// 加入到 Binder 線程池當中IPCThreadState::self()->joinThreadPool();LOG(INFO) << "installd shutting down";return 0;
}

在啟動 Installd 的時候會初始化各種相關的目錄,這部分內容就不展開了。然后,它會調用 IPCThreadState::self()->joinThreadPool() 一行來將當前線程池加入到 Binder 線程池當中等待通信。

當 Java 層的 Installer 需要與之通信的時候,會調用 connect() 方法與之建立聯(lián)系。其源碼如下,這里會通過 ServiceManager 獲取 installd 服務,然后將其轉換成本地的服務進行 IPC 的調用。

    private void connect() {// 獲取遠程服務 IBinder binder = ServiceManager.getService("installd");if (binder != null) {try {binder.linkToDeath(new DeathRecipient() {@Overridepublic void binderDied() {connect();}}, 0);} catch (RemoteException e) {binder = null;}}if (binder != null) {// 轉成本地服務進行 IPC 調用mInstalld = IInstalld.Stub.asInterface(binder);try {invalidateMounts();} catch (InstallerException ignored) {}} else {// 重連BackgroundThread.getHandler().postDelayed(() -> {connect();}, DateUtils.SECOND_IN_MILLIS);}}

Installer 與 PMC 類似,也是一種系統(tǒng)服務,它的啟動的時刻與 PMS 基本一致,位于同一個方法中,并且其啟動時刻位于 PMS 之前。

2、從 ADB 安裝的過程

另外
有什么技術問題歡迎加我交流 qilebeaf
本人10多年大廠軟件開發(fā)經(jīng)驗,精通Android,Java,Python,前端等開發(fā),空余時間承接軟件開發(fā)設計、課程設計指導、解決疑難bug、AI大模型搭建,AI繪圖應用等。
歡迎砸單

http://www.risenshineclean.com/news/41338.html

相關文章:

  • 免費建站自己的網(wǎng)址域名注冊查詢官網(wǎng)
  • 網(wǎng)站建設好以后怎么管理百度關鍵詞搜索量統(tǒng)計
  • 數(shù)據(jù)庫網(wǎng)站制作北京seo推廣系統(tǒng)
  • 搜索引擎優(yōu)化大致包含哪些內容或環(huán)節(jié)河北seo推廣公司
  • 建一個素材網(wǎng)站多少錢杭州網(wǎng)站制作排名
  • 廣州市荔灣區(qū)疫情最新消息重慶可靠的關鍵詞優(yōu)化研發(fā)
  • 可以做婚禮鮮花布置的網(wǎng)站外貿推廣平臺哪個好
  • 工業(yè)企業(yè)網(wǎng)站建設費教育培訓網(wǎng)站官網(wǎng)
  • 寶安網(wǎng)站制作公司合肥網(wǎng)絡seo推廣服務
  • wordpress圖片站點網(wǎng)站制作網(wǎng)站推廣
  • it培訓網(wǎng)站模板seo小白入門教學
  • 做棋牌網(wǎng)站建設哪家便宜免費數(shù)據(jù)查詢網(wǎng)站
  • 免費網(wǎng)站制作案例汽車宣傳軟文
  • 微擎如何做網(wǎng)站百度我的訂單app
  • 網(wǎng)站開發(fā)用python嗎推廣策劃方案范文
  • 做網(wǎng)站要錢嗎sem優(yōu)化公司
  • 北京新冠最新情況最新消息百度seo2022新算法更新
  • 亞馬遜網(wǎng)是b2b還是b2c廈門網(wǎng)站seo外包
  • 江門網(wǎng)站制作培訓網(wǎng)頁seo優(yōu)化
  • 品牌建設網(wǎng)站規(guī)劃網(wǎng)店營銷與推廣策劃方案
  • 做網(wǎng)站需要多少人中國十大電商公司排名
  • 網(wǎng)站商城定制網(wǎng)站建設哪個平臺可以免費發(fā)廣告
  • 簡潔企業(yè)網(wǎng)站源碼專注于seo顧問
  • 揭陽cms建站模板數(shù)據(jù)分析師35歲以后怎么辦
  • wordpress調用js函數(shù)魔方優(yōu)化大師官網(wǎng)下載
  • 網(wǎng)絡規(guī)劃設計 網(wǎng)站建設seo視頻教程百度網(wǎng)盤
  • wap網(wǎng)站建設公司新聞頭條今天最新消息
  • 帝國網(wǎng)站管理系統(tǒng)入門教程百度排名點擊軟件
  • 企業(yè)做網(wǎng)站有用嗎簡述如何對網(wǎng)站進行推廣
  • 做槍版電影網(wǎng)站賺錢免費二級域名平臺