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

Analisi dell'applicazione Launcher2 Android 6.0

In precedenza abbiamo analizzato il processo di installazione delle applicazioni durante l'avvio del sistema Android 6.0, dopo che queste applicazioni sono state installate, l'applicazione Launcher è responsabile di mostrarle sul desktop.

1. Avvio di Launcher da parte di AMS 

L'applicazione Launcher viene avviata direttamente chiamando startHomeActivityLocked nel metodo systemReady di AMS, di seguito è il codice per avviare Launcher con systemReady. 

startHomeActivityLocked(mCurrentUserId, "systemReady"); Esaminiamo questa funzione, prima chiamiamo il metodo getHomeIntent per ottenere l'Intent, quindi chiamiamo anche la funzione resolveActivityInfo per ottenere ActivityInfo dal PKMS, infine, se il processo non è stato avviato, chiamiamo la funzione startHomeActivity di ActivityStackSupervisor

   boolean startHomeActivityLocked(int userId, String reason) {
    if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
        && mTopAction == null) {
      // Stiamo eseguendo in modalità di test della fabbrica, ma non riusciamo a trovare
      // l'applicazione di test della fabbrica, quindi solo sedersi e visualizzare il
      // messaggio di errore e non provare a avviare nulla.
      return false;
    }
    Intent intent = getHomeIntent(); // ottenere intent
    ActivityInfo aInfo =
      resolveActivityInfo(intent, STOCK_PM_FLAGS, userId); // ottenere ActivityInfo
    if (aInfo != null) {
      intent.setComponent(new ComponentName(
          aInfo.applicationInfo.packageName, aInfo.name));
      // Non fare questo se l'applicazione di casa è attualmente in esecuzione
      // strumentato.
      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) { // processo non avviato chiamata
        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;
  } 

Vediamo prima la funzione 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;
  }

Poi vediamo la funzione startHomeActivity di ActivityStackSupervisor, che chiama anche startActivityLocked per avviare l'Activity, questa funzione è stata analizzata in precedenti blog post e non verrà introdotta di nuovo qui.

   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) {
      // Se siamo già nella sezione di riavvio, l'activity home sarà inizializzata, ma non
      // riavviato (per evitare riavvio ricorsivo) e rimarrà così fino a che qualcosa non lo spinga
      // di nuovo. Dobbiamo pianificare un altro riavvio.
      scheduleResumeTopActivities();
    }
  }

Secondo, il lancio di Launcher 

Poi vediamo il file AndroidManifest.xml di Launcher, vediamo che la sua Activity principale ha una categoria 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());
      }
    }
    ...... 

startLoader function will post a Runnable message, let's take a look at its run method 

  public void startLoader(boolean isLaunching, int synchronousBindPage) {
    synchronized (mLock) {
      if (DEBUG_LOADERS) {
        Log.d(TAG, "startLoader isLaunching=" + isLaunching);
      }
      // Clear any deferred bind-runnables from the synchronized load process
      // Dobbiamo farlo prima di pianificare qualsiasi caricamento/binding sottostante.
      mDeferredBindRunnables.clear();
      // Non preoccuparti di avviare il thread se sappiamo che non farà nulla
      if (mCallbacks != null && mCallbacks.get() != null) {
        // Se c'è già uno in esecuzione, dicigli di fermarsi.
        // anche, non abbassare isLaunching se stiamo già eseguendo
        isLaunching = isLaunching || stopLoaderLocked();
        mLoaderTask = new LoaderTask(mApp, isLaunching);
        if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
          mLoaderTask.runBindSynchronousPage(synchronousBindPage);
        }
          sWorkerThread.setPriority(Thread.NORM_PRIORITY);
          sWorker.post(mLoaderTask);
        }
      }
    }
  }

 nel suo metodo run chiamerà la funzione loadAndBindAllApps, e nella funzione loadAndBindAllApps verrà chiamata la funzione loadAllAppsByBatch 

    public void run() {
      synchronized (mLock) {
        mIsLoaderTaskRunning = true;
      }
      final Callbacks cbk = mCallbacks.get();
      final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
      keep_running: {
        // Alza la priorità quando Home viene avviato per la prima volta per evitare
        // durante il boot. Guardare un home vuoto non è cool.
        synchronized (mLock) {
          if (DEBUG_LOADERS) Log.d(TAG, "Impostazione della priorità della thread a " +
              (mIsLaunching ? "DEFAULT" : "BACKGROUND"));
          Process.setThreadPriority(mIsLaunching
              ? Process.THREAD_PRIORITY_DEFAULT : Process.THREAD_PRIORITY_BACKGROUND);
        }
        // Primo passo. Carica prima l'area di lavoro, questo è necessario poiché l'aggiunta di applicazioni da
        // Il profilo gestito in tutte le applicazioni è differito fino a onResume. Vedi http://b/17336902.
        if (loadWorkspaceFirst) {
          if (DEBUG_LOADERS) Log.d(TAG, "passo 1: caricamento dell'area di lavoro");
          loadAndBindWorkspace();
        }
          Log.d(TAG, "passo 1: speciale: caricamento di tutte le applicazioni");
          loadAndBindAllApps();
        } 

Prima di tutto, analizziamo la funzione loadAndBindAllApps, che entra in un ciclo while, poi chiama la funzione getActivityList di LauncherApps e successivamente chiama bindAllApplications di callbacks

    private void loadAllAppsByBatch() {
      final long t = DEBUG_LOADERS ? SystemClock.uptimeMillis() : 0;
      ......
      mBgAllAppsList.clear();
      final int profileCount = profiles.size();
      per (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, "applicate " + added.size() + " app in "
                    + (SystemClock.uptimeMillis() - t) + "ms");
                }
              }
                Log.i(TAG, "nessuna app associata: nessuna attività Launcher");
              }
            }
          });
          ......

Prima di tutto, analizziamo la funzione getActivityList di LauncherApps, che utilizza il membro mService per chiamare la funzione getLauncherActivities e ottenere la lista di type List<ResolveInfo>, poi la impacchetta in 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("Impossibile chiamare 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) {
        // Spiacenti, pacchetto non trovato
      }
      LauncherActivityInfo lai = new LauncherActivityInfo(mContext, ri, user,
          firstInstallTime);
      if (DEBUG) {
        Log.v(TAG, "Ritornando l'activity per il profilo " + user + " : "
            + lai.getComponentName());
      }
      lais.add(lai);
    }
    return lais;
  } 

Il suo servizio è class LauncherAppsImpl extends ILauncherApps.Stub e di seguito è la funzione getLauncherActivities, che probabilmente ottiene la ResolveInfo dell'Activity relativa tramite PKMS. 

    @Override
    public List<ResolveInfo> getLauncherActivities(String packageName, UserHandle user)
        lancia un'eccezione RemoteException {
      ensureInUserProfiles(user, "Impossibile recuperare le attività per il profilo non correlato " + 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 */,
            user.getIdentifier());
        return apps;
      }
        Binder.restoreCallingIdentity(ident);
      }
    } 

Infine, richiama la funzione bindAllApplications di Launcher.java, infine in questa funzione è possibile visualizzare tutti gli applicativi del sistema sul desktop.

  public void bindAllApplications(final ArrayList<ApplicationInfo> apps) {
    Runnable setAllAppsRunnable = new Runnable() {
      public void run() {
        if (mAppsCustomizeContent != null) {
          mAppsCustomizeContent.setApps(apps);
        }
      }
    };
    // Rimuovi completamente la barra di progresso; potremmo anche renderla GONE
    // meglio rimuoverlo poiché sappiamo che non verrà utilizzato
    View progressBar = mAppsCustomizeTabHost.
      findViewById(R.id.apps_customize_progress_bar);
    if (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();
    }
  }

Three, display application icons 

Let's take a look at the onClick function of Launcher, when calling showWorkspace you can display all the application icons.

   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).
    if (v.getWindowToken() == null) {
      return;
    }
    if (!mWorkspace.isFinishedSwitchingState()) {
      return;
    }
    Object tag = v.getTag();
    if (tag instanceof ShortcutInfo) {
      // Apri scorciatoia
      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);
      }
    }
  } 

In showWorkspace verranno visualizzati tutti gli icone

   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);
      // Mostra la barra di ricerca (animare solo se stavamo mostrando la barra di destinazione del drop in modalità di caricamento rapido)
      // modalità caricamento rapido)
      if (mSearchDropTargetBar != null) {
        mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode);
      }
      // Abbiamo bisogno di animare il divisore del dock solo se stiamo passando dal modalità di caricamento rapido
      showDockDivider(animated && wasInSpringLoadedMode);
      // Imposta il focus sul pulsante AppsCustomize
      if (mAllAppsButton != null) {
        mAllAppsButton.requestFocus();
      }
    }
    mWorkspace.flashScrollingIndicator(animated);
    // Cambia lo stato *dopo* aver chiamato tutti i codici di transizione
    mState = State.WORKSPACE;
    // Riprendi l'avanzamento automatico degli widget
    mUserPresent = true;
    updateRunning();
    // Invia un evento di accessibilità per annunciare il cambiamento di contesto
    getWindow().getDecorView()
        .sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
  } 

Cliccando sull'icona dell'applicazione, alla fine verrà chiamato Launcher.java di startActivitySafely per avviare l'applicazione. La startActivity chiamata qui è la funzione startActivity dell'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(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。

Ti potrebbe interessare