English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Android6.0 Launcher2应用解析

Dans le passé, nous avons analysé Android6Le processus d'installation des applications lors du démarrage du système .0, après que ces applications soient installées, l'application Launcher est responsable de les afficher sur le bureau.

I. Démarrage de Launcher par AMS 

L'application Launcher est lancée directement par la méthode systemReady de AMS en appelant startHomeActivityLocked, voici le code pour démarrer Launcher par systemReady. 

startHomeActivityLocked(mCurrentUserId, "systemReady");Nous allons examiner cette fonction, elle appelle d'abord getHomeIntent pour obtenir l'Intent, puis appelle résoudreActivityInfo pour obtenir ActivityInfo de PKMS, ensuite, lorsque le processus n'est pas démarré, appelle la fonction startHomeActivity de ActivityStackSupervisor

   boolean startHomeActivityLocked(int userId, String reason) {
    if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
        && mTopAction == null) {
      // Nous fonctionnons en mode test de fabrication, mais nous ne parvenons pas à trouver
      // l'application de test de fabrication, donc restez simplement assis et affichez la
      // message d'erreur et ne tentez pas de démarrer quoi que ce soit.
      retourner faux;
    }
    Intent intent = getHomeIntent();//Obtenir intent
    ActivityInfo aInfo =
      résoudreActivityInfo(intent, STOCK_PM_FLAGS, userId);//Obtenir ActivityInfo
    if (aInfo != null) {
      intent.setComponent(new ComponentName(
          aInfo.applicationInfo.packageName, aInfo.name));
      // Ne faites pas cela si l'application d'accueil est actuellement en cours d'exécution
      // instrumented.
      aInfo = new ActivityInfo(aInfo);
      aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
      ProcessRecord app = getProcessRecordLocked(aInfo.processName,
          aInfo.applicationInfo.uid, true);
      if (app == null || app.instrumentationClass == null) {//Le processus n'a pas été démarré appel
        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);
      }
    }
    return true;
  } 

D'abord, examinons la fonction getHomeIntent.

Intent getHomeIntent() {
    Intent intent = new Intent(mTopAction, mTopData != null ? Uri.parse(mTopData) : null);
    intent.setComponent(mTopComponent);
    if (mFactoryTest != FactoryTest.FACTORY_TEST_LOW_LEVEL) {
      intent.addCategory(Intent.CATEGORY_HOME);
    }
    return intent;
  }

Ensuite, examinons la fonction startHomeActivity de ActivityStackSupervisor, qui appelle également startActivityLocked pour démarrer une Activity. Nous avons déjà analysé cette fonction dans nos précédents blogs et ne la présenterons pas ici.

   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) {
      // Si nous sommes déjà dans la section de redémarrage, l'activité home sera initialisée, mais pas
      // redémarré (pour éviter un redémarrage récursif) et restera ainsi jusqu'à ce que quelque chose le pousse
      // encore. Nous devons planifier un autre redémarrage.
      scheduleResumeTopActivities();
    }
  }

Deuxième partie : le lancement de Launcher 

Ensuite, regardons le fichier AndroidManifest.xml de Launcher, nous voyons que son Activity principale a une catégorie 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>
    ...... 

In the onCreate function of Launcher.java, the mModel.startLoader function is called

   protected void onCreate(Bundle savedInstanceState) {
    ......
    if (!mRestoring) {
      if (sPausedFromUserAction) {
        // If the user leaves launcher, then we should just load items asynchronously when
        // they return.
        mModel.startLoader(true, -1);
      }
        // We only load the page synchronously if the user rotates (or triggers a
        // configuration change) while launcher is in the foreground
        mModel.startLoader(true, mWorkspace.getCurrentPage());
      }
    }
    ...... 

La fonction startLoader envoie un message Runnable, voyons maintenant sa méthode run 

  public void startLoader(boolean isLaunching, int synchronousBindPage) {
    synchronized (mLock) {
      if (DEBUG_LOADERS) {
        Log.d(TAG, "startLoader isLaunching=" + isLaunching);
      }
      // Effacer toute liaison différée-runnable du processus de chargement synchronisé
      // Nous devons faire cela avant tout chargement/la liaison est planifiée ci-dessous.
      mDeferredBindRunnables.clear();
      // Ne vous préoccupez pas de démarrer le thread si nous savons qu'il ne va rien faire
      if (mCallbacks != null && mCallbacks.get() != null) {
        // Si une autre instance est déjà en cours d'exécution, demandez-lui d'arrêter.
        // également, ne pas abaisser isLaunching si nous sommes déjà en cours d'exécution
        isLaunching = isLaunching || stopLoaderLocked();
        mLoaderTask = new LoaderTask(mApp, isLaunching);
        if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
          mLoaderTask.runBindSynchronousPage(synchronousBindPage);
        }
          sWorkerThread.setPriority(Thread.NORM_PRIORITY);
          sWorker.post(mLoaderTask);
        }
      }
    }
  }

 Dans sa méthode run, elle appelle la fonction loadAndBindAllApps, et dans la fonction loadAndBindAllApps, elle appelle à son tour la fonction loadAllAppsByBatch 

    public void run() {
      synchronized (mLock) {
        mIsLoaderTaskRunning = true;
      }
      final Callbacks cbk = mCallbacks.get();
      final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
      keep_running: {
        // Elevate priority when Home launches for the first time to avoid
        // starving at boot time. Watching a blank home is not cool.
        synchronized (mLock) {
          if (DEBUG_LOADERS) Log.d(TAG, "Setting thread priority to " +
              (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
          Process.setThreadPriority(mIsLaunching
              ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
        }
        // Première étape. Chargez d'abord l'espace de travail, c'est nécessaire car l'ajout d'applications à partir de
        // profile géré dans toutes les applications est différé jusqu'à onResume. Voir http://b/17336902.
        if (loadWorkspaceFirst) {
          if (DEBUG_LOADERS) Log.d(TAG, "step 1: loading workspace");
          loadAndBindWorkspace();
        }
          Log.d(TAG, "step 1: special: loading all apps");
          loadAndBindAllApps();
        } 

Nous allons d'abord examiner la fonction loadAndBindAllApps, cette fonction entre d'abord dans une boucle while, puis appelle la fonction getActivityList de LauncherApps, puis appelle également la fonction bindAllApplications de callbacks

    private void loadAllAppsByBatch() {}}
      final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
      ......
      mBgAllAppsList.clear();
      final int profileCount = profiles.size();
      for (int 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, "lié " + added.size() + " apps in "
                    + (SystemClock.uptimeMillis() - t) + "ms");
                }
              }
                Log.i(TAG, "pas de liaison d'applications : aucune activité Launcher");
              }
            }
          });
          ......

Nous allons d'abord examiner la fonction getActivityList de LauncherApps, qui utilise la variable membre mService pour appeler la fonction getLauncherActivities pour obtenir une liste de type list<ResolveInfo>, puis l'emballer dans une 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("Échec de l'appel au 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) {
        // Désolé, impossible de trouver le paquet
      }
      LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user,
          firstInstallTime);
      if (DEBUG) {
        Log.v(TAG, "Retour de l'activité pour le profil " + user + " : "
            + lai.getComponentName());
      }
      lais.add(lai);
    }
    return lais;
  } 

Son service est class LauncherAppsImpl extends ILauncherApps.Stub, voici la fonction getLauncherActivities, qui est certainement utilisée pour obtenir les ResolveInfo des Activity via PKMS. 

    @Override
    public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
        throws RemoteException {
      ensureInUserProfiles(user, "Cannot retrieve activities for unrelated profile " + user);
      if (!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 */,
            return apps;
        finally {
      }
        Binder.restoreCallingIdentity(ident);
      }
    } 

Enfin, appeler la fonction bindAllApplications de Launcher.java, et enfin, dans cette fonction, il est possible de montrer tous les applications du système sur le bureau.

  public void bindAllApplications(final ArrayList<ApplicationInfo> apps) {
    Runnable setAllAppsRunnable = new Runnable() {
      public void run() {
        if (mAppsCustomizeContent != null) {
          mAppsCustomizeContent.setApps(apps);
        }
      }
    ;
    // Retirer complètement la barre de progression ; nous pourrions également la rendre invisible
    // mais il est préférable de le supprimer car nous savons qu'il ne sera pas utilisé
    View progressBar = mAppsCustomizeTabHost.
      findViewById(R.id.apps_customize_progress_bar);
    if (progressBar != null) {
      ((ViewGroup)progressBar.getParent()).removeView(progressBar);
      // Nous postons simplement l'appel à setApps afin que l'utilisateur voit la barre de progression
      // disparaître-- sinon, cela ne semble que la barre de progression soit gelée
      // ce qui ne donne pas une bonne impression
      mAppsCustomizeTabHost.post(setAllAppsRunnable);
    }
      // Si nous n'avons pas initialisé le spinner dans onCreate, alors nous pouvons directement définir le
      // liste des applications sans attendre que les vues des barres de progression soient masquées.
      setAllAppsRunnable.run();
    }
  }

Troisième partie : Affichage des icônes des applications 

Revenons à la fonction onClick de Launcher, lorsque showWorkspace est appelé, il peut afficher les icônes de toutes les applications.

   public void onClick(View v) {
    // Assurez-vous que les clics indésirables ne passent pas pendant le lancement de tous les apps, ou après le
    // la vue a été détachée (cela peut se produire si la vue est supprimée au milieu du toucher).
    if (v.getWindowToken() == null) {
      return;
    }
    if (!mWorkspace.isFinishedSwitchingState()) {
      return;
    }
    Object tag = v.getTag();
    if (tag instanceof ShortcutInfo) {
      // Ouvrir le raccourci
      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);
      }
    }
  } 

Dans la fonction showWorkspace, toutes les icônes seront affichées

   void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
    if (mState != State.WORKSPACE) {
      boolean wasInSpringLoadedMode = (mState == State.APPS_CUSTOMIZE_SPRING_LOADED);
      mWorkspace.setVisibility(View.VISIBLE);
      cacherAppsCustomizeHelper(State.WORKSPACE, animated, false, onCompleteRunnable);
      // Afficher la barre de recherche (n'animé que si nous avons montré la barre de cible de glisser-déposer en mode spring)
      // mode)
      si (mSearchDropTargetBar != null) {
        mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode);
      }
      // Nous avons besoin d'animé le séparateur de dock uniquement si nous venons du mode spring loaded
      Afficher le séparateur de dock (n'animé que si nous venons du mode spring loaded)
      // Définir le focus sur le bouton AppsCustomize
      si (mAllAppsButton != null) {
        mAllAppsButton.requestFocus();
      }
    }
    mWorkspace.flashScrollingIndicator(animated);
    // Changer l'état *après* Nous avons appelé tous les codes de transition
    mState = State.WORKSPACE;
    // Reprendre l'auto-Avancement des widgets
    mUserPresent = true;
    miseÀ JourRunning();
    // Envoyer un événement d'accessibilité pour annoncer le changement de contexte
    getWindow().getDecorView()
        .envoyer un événement d'accessibilité de type ÉVÉNEMENT_ETAT_FENETRE_MODIFIÉ;
  } 

Cependant, en cliquant sur l'icône de l'application, cela appelle finalement startActivitySafely de Launcher.java pour démarrer l'application. L'appel startActivity ici est la fonction startActivity de l'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, "Impossible de lancer. tag=" + tag + " intent=" + intent, e);
    }
    return success;
  }

Voici la totalité du contenu de cet article, j'espère qu'il vous sera utile dans vos études, et que vous soutiendrez également le tutoriel d'entraînement.

Déclaration : le contenu de cet article est issu d'Internet, propriété des auteurs respectifs, mis en ligne par les utilisateurs d'Internet, ce site n'est pas propriétaire des droits, n'a pas été édité par l'homme, ni assume la responsabilité des responsabilités juridiques. Si vous trouvez du contenu présumé de violation de droits d'auteur, vous êtes invité à envoyer un e-mail à : notice#oldtoolbag.com (veuillez remplacer # par @ lors de l'envoi d'un e-mail pour signaler un abus, et fournir des preuves pertinentes. Une fois vérifié, le site supprimera immédiatement le contenu présumé de violation de droits d'auteur.)