English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
في وقت سابق، قمنا بتحليل عملية تثبيت التطبيقات في نظام Android 6.0 عند بدء التشغيل، وبعد تثبيت هذه التطبيقات، يتحمل تطبيق Launcher المسؤولية عن عرضها على سطح المكتب.
أولاً، بدء Launcher بواسطة AMS
يتم تشغيل تطبيق Launcher من خلال startHomeActivityLocked مباشرة في واجهة AMS في نظام systemReady. إليك كود بدء Launcher في systemReady.
startHomeActivityLocked(mCurrentUserId, "systemReady"); دعنا نرى هذه الوظيفة، حيث يتم استدعاء getHomeIntent لتحديد Intent، ثم يتم استدعاء resolveActivityInfo من PKMS للحصول على ActivityInfo، ومن ثم عند عدم بدء عملية، يتم استدعاء startHomeActivity من ActivityStackSupervisor
boolean startHomeActivityLocked(int userId, String reason) { if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL && mTopAction == null) { // نحن نعمل في وضع الاختبار المصنع، لكننا لا نستطيع العثور على // الصفحة الاختبارية للمنشأة، لذا فقط استرخِ وأظهر العرض // رسالة خطأ ولا تحاول بدء أي شيء. إرجاع الحق; } Intent intent = getHomeIntent();// للحصول على intent ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);// للحصول على ActivityInfo إذا (aInfo != null) { intent.setComponent(new ComponentName( aInfo.applicationInfo.packageName, aInfo.name)); // لا تقم بذلك إذا كان تطبيق المنزل يتم حاليًا // تم تعديل هذا. aInfo = new ActivityInfo(aInfo); aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId); ProcessRecord app = getProcessRecordLocked(aInfo.processName, aInfo.applicationInfo.uid, true); إذا (app == null || app.instrumentationClass == null) { // لم يتم بدء عملية التطبيق EventLog.writeEvent(EventLogTags.AM_PROC_START,"AMS -> startHomeActivityLocked startHomeActivity then startActivityLock : "+ aInfo.processName); intent.setFlags(intent.getFlags() | Intent.FLAG_ACTIVITY_NEW_TASK); mStackSupervisor.startHomeActivity(intent, aInfo, reason); } } إرجاع الحق. }
نحن نبدأ بالنظر إلى دالة getHomeIntent هذه.
Intent getHomeIntent() { Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null); intent.setComponent(mTopComponent); إذا (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) { intent.addCategory(Intent.CATEGORY_HOME); } return intent; }
ثم نأتي إلى دالة startHomeActivity في ActivityStackSupervisor، التي تُستدعى أيضًا باستخدام startActivityLocked للبدء في نشاط Activity، تمت مناقشة هذه الدالة في المدونة السابقة ولا نود أن نتحدث عنها هنا.
void startHomeActivity(Intent intent, ActivityInfo aInfo, String reason) { moveHomeStackTaskToTop(HOME_ACTIVITY_TYPE, reason); startActivityLocked(null /* caller */, intent, null /* resolvedType */, aInfo, null /* voiceSession */, null /* voiceInteractor */, null /* resultTo */, null /* resultWho */, 0 /* requestCode */, 0 /* callingPid */, 0 /* callingUid */, null /* callingPackage */, 0 /* realCallingPid */, 0 /* realCallingUid */, 0 /* startFlags */, null /* options */, false /* ignoreTargetSecurity */, false /* componentSpecified */, null /* outActivity */, null /* container */, null /* inTask */); if (inResumeTopActivity) { // إذا كنا في قسم الإعادة التشغيل بالفعل، سيتم إعداد نشاط المنزل، ولكن لا // تم إعادة التشغيل (لمنع إعادة التشغيل التكرارية) وسيمضي في ذلك حتى يتم دفعه // مرة أخرى. نحتاج إلى جدولة استئناف آخر. scheduleResumeTopActivities(); } }
الجزء الثاني: بدء Launcher
نحن الآن سننظر إلى ملف AndroidManifest.xml الخاص بلوادر، ونرى أن Activity الرئيسية لديها فئة android.intent.category.HOME
<application android:name="com.android.launcher2.LauncherApplication" android:label="@string/application_name" android:icon="@mipmap/ic_launcher_home" android:hardwareAccelerated="true" android:largeHeap="@bool/config_largeHeap" android:supportsRtl="true"> <activity android:name="com.android.launcher2.Launcher" android:launchMode="singleTask" android:clearTaskOnLaunch="true" android:stateNotNeeded="true" android:resumeWhilePausing="true" android:theme="@style/Theme" android:windowSoftInputMode="adjustPan" android:screenOrientation="nosensor" <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.HOME" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.MONKEY"/> </intent-filter> </activity> ......
تم استدعاء وظيفة mModel.startLoader في وظيفة onCreate في Launcher.java
protected void onCreate(Bundle savedInstanceState) { ...... if (!mRestoring) { if (sPausedFromUserAction) { // إذا غادر المستخدم الم.launcher، فإنه يجب علينا تحميل العناصر بشكل غير متزامن عند // يعودون. mModel.startLoader(true, -1); } // نحمّل الصفحة بشكل متزامن فقط إذا قام المستخدم بتحويل الشاشة (أو إطلاق // تغيير التكوين) طالما يكون الم.launcher في الخلفية mModel.startLoader(true, mWorkspace.getCurrentPage()); } } ......
وظيفة startLoader ستقوم بتحميل رسالة Runnable، لنلق نظرة على طريقة run الخاصة بها
public void startLoader(boolean isLaunching, int synchronousBindPage) { متزامن (mLock) { if (DEBUG_LOADERS) { Log.d(TAG, "startLoader isLaunching=" + isLaunching); } // تنظيف أي bind-runnables معوقة من عملية التحميل المتزامن // يجب أن نفعل هذا قبل أي تحميل/ربط مخطط أدناه. mDeferredBindRunnables.clear(); // لا تحتاج إلى بدء الخيط إذا نعلم أنه لن يفعل أي شيء إذا (mCallbacks != null && mCallbacks.get() != null) { // إذا كان هناك واحد يعمل بالفعل، اخبره بأن يوقف. // أيضًا، لا تخفض مستوى isLaunching إذا كنا نعمل بالفعل isLaunching = isLaunching || stopLoaderLocked(); mLoaderTask = new LoaderTask(mApp, isLaunching); إذا (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) { mLoaderTask.runBindSynchronousPage(synchronousBindPage); } sWorkerThread.setPriority(Thread.NORM_PRIORITY); sWorker.post(mLoaderTask); } } } }
في طريقة run الخاصة بها، سيتم استدعاء وظيفة loadAndBindAllApps، وفي وظيفة loadAndBindAllApps سيتم استدعاء وظيفة loadAllAppsByBatch
public void run() { متزامن (mLock) { mIsLoaderTaskRunning = true; } final Callbacks cbk = mCallbacks.get(); final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true; keep_running: { // عند إطلاق المنزل للمرة الأولى، قم بزيادة الأولوية لتجنب // الجوع عند بدء التشغيل. النظر إلى منزل فارغ ليس ممتعًا. متزامن (mLock) { إذا (DEBUG_LOADERS) Log.d(TAG, "ضبط أولوية السطر إلى " + (mIsLaunching ? "DEFAULT" : "BACKGROUND")); Process.setThreadPriority(mIsLaunching ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND); } // الخطوة الأولى. قم بتشغيل مساحة العمل أولاً، هذا ضروري لأن إضافة التطبيقات من // ملف الشخصية المدارة في جميع التطبيقات يتم تأجيله حتى onResume. راجع http://b/17336902. إذا (loadWorkspaceFirst) { إذا (DEBUG_LOADERS) Log.d(TAG, "الخطوة 1: تحميل المساحة العمل"); loadAndBindWorkspace(); } Log.d(TAG, "الخطوة 1: خاص: تحميل جميع التطبيقات"); loadAndBindAllApps(); }
دعونا نبدأ بالنظر إلى دالة loadAndBindAllApps، هذه الدالة تدخل أولاً في حلقة while، ثم تستدعي دالة(getActivityList) من LauncherApps، وبعد ذلك تستدعي callbacks'bindAllApplications
private void loadAllAppsByBatch() { final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; ...... mBgAllAppsList.clear(); final int profileCount = profiles.size(); لـ (إنت p = 0; p < profileCount; p++) { ...... while (i < N && !mStopped) { if (i == 0) { final long qiaTime = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0; apps = mLauncherApps.getActivityList(null, user); ...... mHandler.post(new Runnable() { public void run() { final long t = SystemClock.uptimeMillis(); if (callbacks != null) { if (firstProfile) { callbacks.bindAllApplications(added); } callbacks.bindAppsAdded(added); } if (DEBUG_LOADERS) { Log.d(TAG, "bound " + added.size() + " apps in ") + (SystemClock.uptimeMillis() - t) + "ms"); } } Log.i(TAG, "not binding apps: no Launcher activity"); } } }); ......
我们先来看LauncherApps的getActivityList函数,它先用mService成员变量调用getLauncherActivities函数获取到list<ResolveInfo>,然后封装在ArrayList<LauncherActivityInfo> 中。
public List<LauncherActivityInfo> getActivityList(String packageName, UserHandle user) { List<ResolveInfo> activities = null; try { activities = mService.getLauncherActivities(packageName, user); } catch (RemoteException re) { throw new RuntimeException("Failed to call LauncherAppsService"); } if (activities == null) { return Collections.EMPTY_LIST; } ArrayList<LauncherActivityInfo> lais = new ArrayList<LauncherActivityInfo>(); final int count = activities.size(); for (int i = 0; i < count; i++) { ResolveInfo ri = activities.get(i); long firstInstallTime = 0; try { firstInstallTime = mPm.getPackageInfo(ri.activityInfo.packageName, PackageManager.GET_UNINSTALLED_PACKAGES).firstInstallTime; } catch (NameNotFoundException nnfe) { // عذراً، لا يمكن العثور على الحزمة } LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user, firstInstallTime); if (DEBUG) { Log.v(TAG, "Returning activity for profile " + user + " : " + lai.getComponentName()); } lais.add(lai); } return lais; }
خدمة service هي class LauncherAppsImpl extends ILauncherApps.Stub وهي تحتوي على دالة getLauncherActivities، والتي تعتمد أيضًا على PKMS للحصول على ResolveInfo للأنشطة.
@Override public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user) throws RemoteException { ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);}} إذا (!isUserEnabled(user)) { return new ArrayList<ResolveInfo>(); } final Intent mainIntent = new Intent(Intent.ACTION_MAIN, null); mainIntent.addCategory(Intent.CATEGORY_LAUNCHER); mainIntent.setPackage(packageName); long ident = Binder.clearCallingIdentity(); try { List<ResolveInfo> apps = mPm.queryIntentActivitiesAsUser(mainIntent, 0 /* flags */, user.getIdentifier()); return apps; } Binder.restoreCallingIdentity(ident); } }
في النهاية، يتم استدعاء دالة bindAllApplications في Launcher.java، وفي هذه الدالة يمكن عرض جميع التطبيقات في النظام على سطح المكتب.
public void bindAllApplications(final ArrayList<ApplicationInfo> apps) { Runnable setAllAppsRunnable = new Runnable() { public void run() { إذا (mAppsCustomizeContent != null) { mAppsCustomizeContent.setApps(apps); } } }; // إزالة بار التقدم بشكل كامل؛ يمكننا أيضًا جعله GONE // لكن من الأفضل إزالته لأننا نعلم أنه لن يستخدم عرض بار التقدم = mAppsCustomizeTabHost. findViewById(R.id.apps_customize_progress_bar); إذا (progressBar != null) { ((ViewGroup)progressBar.getParent()).removeView(progressBar); // We just post the call to setApps so the user sees the progress bar // disappear-- otherwise, it just looks like the progress bar froze // which doesn't look great mAppsCustomizeTabHost.post(setAllAppsRunnable); } // If we did not initialize the spinner in onCreate, then we can directly set the // list of applications without waiting for any progress bars views to be hidden. setAllAppsRunnable.run(); } }
ثالثًا، عرض أيقونات التطبيقات
دعونا نرى مرة أخرى دالة onClick في Launcher، عندما يتم استدعاء showWorkspace يمكن عرض جميع أيقونات التطبيقات.
public void onClick(View v) { // Make sure that rogue clicks don't get through while allapps is launching, or after the // view has detached (it's possible for this to happen if the view is removed mid touch). إذا (v.getWindowToken() == null) { return; } إذا (!mWorkspace.isFinishedSwitchingState()) { return; } Object tag = v.getTag(); إذا (tag instanceof ShortcutInfo) { // Open shortcut final Intent intent = ((ShortcutInfo) tag).intent; int[] pos = new int[2]; v.getLocationOnScreen(pos); intent.setSourceBounds(new Rect(pos[0], pos[1], pos[0] + v.getWidth(), pos[1] + v.getHeight())); boolean success = startActivitySafely(v, intent, tag); if (success && v instanceof BubbleTextView) { mWaitingForResume = (BubbleTextView) v; mWaitingForResume.setStayPressed(true); } } if (v instanceof FolderIcon) { FolderIcon fi = (FolderIcon) v; handleFolderClick(fi); } } if (isAllAppsVisible()) { showWorkspace(true); } onClickAllAppsButton(v); } } }
في showWorkspace سيتم عرض جميع الرسومات
void showWorkspace(boolean animated, Runnable onCompleteRunnable) { if (mState != State.WORKSPACE) { boolean wasInSpringLoadedMode = (mState == State.APPS_CUSTOMIZE_SPRING_LOADED); mWorkspace.setVisibility(View.VISIBLE); hideAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable); // اعرض شريط البحث (استخدم التأثير فقط إذا كنا نعرض شريط الهدف في الوضع المائي) // mode) إذا (mSearchDropTargetBar != null) { mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode); } // نحتاج إلى تعديل ميزة تقسيم الرف فقط إذا كنا ننتقل من وضع التحميل المائي showDockDivider(animated && wasInSpringLoadedMode); // ضع التركيز على زر التخصيص للتطبيقات إذا (mAllAppsButton != null) { mAllAppsButton.requestFocus(); } } mWorkspace.flashScrollingIndicator(animated); // قم بتغيير الحالة *بعد* أن قمنا باستدعاء جميع أكواد التحويل mState = State.WORKSPACE; // استئناف التقدم التلقائي للمستطيلات mUserPresent = true; updateRunning(); // إرسال حدث سهولة الوصول لتعريف تغيير السياق getWindow().getDecorView(); .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); }
بالنقر على أيقونة التطبيق، سيتم في النهاية استدعاء Launcher.java startActivitySafely لبدء التطبيق. هنا يتم استدعاء startActivity هو دالة startActivity الخاصة بActivity.
boolean startActivitySafely(View v, Intent intent, Object tag) { boolean success = false; try { success = startActivity(v, intent, tag); } Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show(); Log.e(TAG, "Unable to launch. tag=" + tag + " intent=" + intent, e); } return success; }
هذا هو محتوى المقال الكامل، نأمل أن يساعدكم هذا في تعلمكم، ونأمل أيضًا أن تشجعوا دائمًا تعليمات الصراخ.
البيان: محتوى هذا المقال تم جمعه من الإنترنت، ملكية حقوق الطبع والتأليف لأصحاب المشاريع، المحتوى تم تقديمه من قبل مستخدمي الإنترنت، لا يمتلك هذا الموقع حقوق الملكية، لم يتم تعديل المحتوى يدويًا، ولا يتحمل هذا الموقع أي مسؤولية قانونية. إذا رأيت محتوى يشتبه في انتهاك حقوق الطبع والتأليف، فالرجاء إرسال بريد إلكتروني إلى: notice#oldtoolbag.com (الرجاء استبدال # بـ @ عند إرسال البريد الإلكتروني) لتقديم الشكوى، مع تقديم الدليل المناسب، وإذا تم التحقق من ذلك، سيتم حذف المحتوى المزعوم فورًا.