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

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

金川做網站公司吸引客流的25個技巧

金川做網站公司,吸引客流的25個技巧,上海網站開發(fā)有限公司,企業(yè)建站平臺哪個好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ù)據目錄:為了加快 APP 的啟動速度,APK 在安裝的時候,會首先將 APP 的可執(zhí)行文件 dex 拷貝到 /data/dalvik-cache 目錄,緩存起來。然后,在 /data/data/ 目錄下創(chuàng)建應用程序的數(shù)據目錄 (以應用的包名命名),存放在應用的相關數(shù)據,如數(shù)據庫、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 文件,上述注釋中我們已經給出了這些目錄,及其獲取的方式。當掃描一個路徑的時候會使用 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 之后會一路經過 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ā)經驗,精通Android,Java,Python,前端等開發(fā),空余時間承接軟件開發(fā)設計、課程設計指導、解決疑難bug、AI大模型搭建,AI繪圖應用等。
歡迎砸單

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

相關文章:

  • 營山網站建設seo扣費系統(tǒng)源碼
  • 廣州市網絡seo推廣seo秘籍優(yōu)化課程
  • 物理機安裝虛擬機做網站想建立自己的網站
  • ui作品集 網站怎么做搜素引擎優(yōu)化
  • wordpress 前臺刪除評論安徽網絡推廣和優(yōu)化
  • 做網站的圖片是怎么做的騰訊企點是干嘛的
  • 單頁網站設計最近一個月的熱點事件
  • 廣州網站建設360元陜西seo優(yōu)化
  • 公司做網站費用會計分錄谷歌是如何運營的
  • 本地做網站貴seo搜索引擎優(yōu)化推廣
  • 網站開發(fā)的前端語言是哪些品牌營銷戰(zhàn)略
  • 找網站建設公司百度seo插件
  • 鄭州做網站推廣價格廣東網站se0優(yōu)化公司
  • 昆明做網站建設的公司全國疫情最新消息今天新增
  • 花都網站開發(fā)公司百度新聞發(fā)布
  • h5制作開發(fā)地點企業(yè)關鍵詞優(yōu)化價格
  • 網站索引量暴增什么叫軟文推廣
  • wordpress表單上傳多個文件濰坊關鍵詞優(yōu)化軟件
  • 做ui的圖從哪個網站找制作公司官網多少錢
  • 廣東東莞智通人才招聘網榆林市網站seo
  • 沈陽做網站的地方百度推廣怎么賺錢
  • 深圳網站建設公司的英文名是網站seo分析工具
  • 做資源網站怎么不封今日新聞內容
  • 利用對象儲存做網站友情鏈接免費發(fā)布平臺
  • 網站展示型推廣北京網絡推廣有哪些公司
  • 給網站做壓力測試全國新冠疫苗接種率
  • 建站公司見客戶沒話說b2b商務平臺
  • 衡水建網站百度搜索風云排行榜
  • 網站商城如何獲取流量成都網絡營銷推廣
  • 做行業(yè)分析的網站百度指數(shù)網址是什么