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

Android6.0ランチャー2アプリケーション解析

以前にAndroidについて分析しました6.0システムが起動時にアプリケーションをインストールするプロセス、これらのアプリケーションがインストールされ次第、Launcherアプリがそれらをデスクトップに表示する責任を持ちます。

一、AMSがLauncherを起動する方法 

LauncherアプリはAMSのsystemReadyメソッドで直接startHomeActivityLockedを呼び出して起動されます。以下はsystemReadyがLauncherを起動するコードです。 

startHomeActivityLocked(mCurrentUserId, "systemReady");この関数を見てみましょう、まずgetHomeIntentメソッドを呼び出してIntentを取得し、次にresolveActivityInfo関数を呼び出してPKMSからActivityInfoを取得し、プロセスが起動していない場合、ActivityStackSupervisorのstartHomeActivity関数を呼び出します。

   boolean startHomeActivityLocked(int userId, String reason) {
    if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
        && mTopAction == null) {
      // ファクトリーテストモードで動作していますが、以下を検索できませんでした
      // ファクトリーテストアプリのために、ただ待機して表示を行う
      // エラーメッセージを表示し、何かを起動しようとしないでください。
      return false;
    }
    Intent intent = getHomeIntent();//intentを取得
    ActivityInfo aInfo =
      resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);//ActivityInfoを取得
    if (aInfo != null) {
      intent.setComponent(new ComponentName(
          aInfo.applicationInfo.packageName, aInfo.name));
      // ホームアプリが現在動作中であればこの操作を行わないでください。
      // 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) {//プロセスが起動していない呼び出し
        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;
  } 

まず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;
  }

それでは、ActivityStackSupervisorのstartHomeActivity関数を見てみましょう。この関数も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) {
      // すでに再起動セクションにいる場合、ホームアクティビティは初期化されますが、
      // resumed(再帰的な再起動を避けるために)となり、何かがそれを刺すまでそのままになります。
      // もう一度resumedになります。再起動をスケジュールする必要があります。
      scheduleResumeTopActivities();
    }
  }

二、Launcherの起動 

次に、LauncherのAndroidManifest.xmlを見てみましょう。主Activityにはandroid.intent.category.HOMEというcategoryがあります。 

  <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>
    ...... 

在Launcher.java的onCreate函数中调用了mModel.startLoader函数

   protected void onCreate(Bundle savedInstanceState) {
    ......
    if (!mRestoring) {
      if (sPausedFromUserAction) {
        // 如果用户离开启动器,那么我们应该在异步加载项目时
        // 它们返回。
        mModel.startLoader(true, -1);
      }
        // 如果用户旋转(或触发)
        // configuration change) 当启动器在前台时
        mModel.startLoader(true, mWorkspace.getCurrentPage());
      }
    }
    ...... 

startLoader関数はRunnableメッセージをpostし、そのrunメソッドを見てみましょう。 

  public void startLoader(boolean isLaunching, int synchronousBindPage) {
    synchronized (mLock) {
      if (DEBUG_LOADERS) {
        Log.d(TAG, "startLoader isLaunching=" + isLaunching);
      }
      // 遅延バインドをクリアします-同期読み込みプロセスからのrunnables
      // これを行う前に読み込みを行う必要があります。/バインディングは以下にスケジュールされています。
      mDeferredBindRunnables.clear();
      // それが何もしないとわかっている場合、スレッドを開始する必要はありません。
      if (mCallbacks != null && mCallbacks.get() != null) {
        // 既に実行中であれば、それを停止させるように指示します。
        // また、既に実行中であればisLaunchingをダウングレードしないでください。
        isLaunching = isLaunching || stopLoaderLocked();
        mLoaderTask = new LoaderTask(mApp, isLaunching);
        if (synchronousBindPage > -1 && mAllAppsLoaded && mWorkspaceLoaded) {
          mLoaderTask.runBindSynchronousPage(synchronousBindPage);
        }
          sWorkerThread.setPriority(Thread.NORM_PRIORITY);
          sWorker.post(mLoaderTask);
        }
      }
    }
  }

 そのrunメソッドではloadAndBindAllApps関数を呼び出し、loadAndBindAllApps関数ではさらにloadAllAppsByBatch関数を呼び出します。 

    public void run() {
      synchronized (mLock) {
        mIsLoaderTaskRunning = true;
      }
      final Callbacks cbk = mCallbacks.get();
      final boolean loadWorkspaceFirst = cbk != null ? (!cbk.isAllAppsVisible()) : true;
      keep_running: {
        // Homeが初めて起動したときに優先度を高め、
        // 起動時のスターカーローディング。ブランクなホーム画面を見るのは面白くありません。
        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);
        }
        // 最初のステップ。まずworkspaceをロードします。これは、アプリの追加が
        // すべてのアプリ内の管理プロファイルはonResumeまで遅延されます。詳しくはこちらを参照してください: http://b/17336902.
        if (loadWorkspaceFirst) {
          if (DEBUG_LOADERS) Log.d(TAG, "step" 1: workspaceをロード中);
          loadAndBindWorkspace();
        }
          Log.d(TAG, "step" 1: special: 全てのアプリをロード中);
          loadAndBindAllApps();
        } 

まずloadAndBindAllApps関数を見てみましょう。この関数はwhileループに進み、LauncherAppsのgetActivityList関数を呼び出し、その後callbacksのbindAllApplicationsを呼び出します。

    private void loadAllAppsByBatch() {}}
      final long t = DEBUG_LOADERS &63; 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 &63; 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) {
      RuntimeException("Failed to call LauncherAppsService")をthrowします;
    }
    if (activities == null) {
      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);
    }
    laisを返します;
  } 

そのserviceはclass LauncherAppsImpl extends ILauncherApps.Stubで、以下はgetLauncherActivities関数であり、関連するActivityのResolveInfoを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);
      }
    } 

最後にLauncher.javaのbindAllApplications関数をコールバックし、最終的にこの関数でデスクトップにシステムのすべてのアプリケーションを表示できます。

  public void bindAllApplications(final ArrayList<ApplicationInfo> apps) {
    Runnable setAllAppsRunnable = new Runnable() {
      public void run() {
        if (mAppsCustomizeContent != null) {
          mAppsCustomizeContent.setApps(apps);
        }
      }
    ;
    // プログレスバーを完全に削除します;それともGONEに設定することもできます
    // しかし、使用しないと分かっているので、それを削除する方が良いでしょう。
    View progressBar = mAppsCustomizeTabHost.
      findViewById(R.id.apps_customize_progress_bar);
    if (progressBar != null) {
      ((ViewGroup)progressBar.getParent()).removeView(progressBar);
      // ユーザーが進行バーを見るためにsetAppsの呼び出しをポストします。
      // 消える-- そうでないと、ただ進行バーが凍結しているように見えます
      // 見た目が良くありません
      mAppsCustomizeTabHost.post(setAllAppsRunnable);
    }
      // onCreateでスピナーを初期化していなければ、直接設定することができます。
      // 進行バーのビューが非表示になるのを待つことなく、アプリケーションのリストを設定します。
      setAllAppsRunnable.run();
    }
  }

3. アプリアイコンの表示 

また、LauncherのonClick関数を見てみましょう。showWorkspaceが呼び出されると、すべてのアプリのアイコンが表示されます。

   public void onClick(View v) {
    // すべてのアプリが起動中またはその後、不正なクリックが通過しないようにしてください。
    // ビューがタッチ中に削除された場合に発生する可能性があるため、ビューが分離されていることを確認してください。
    if (v.getWindowToken() == null) {
      return;
    }
    if (!mWorkspace.isFinishedSwitchingState()) {
      return;
    }
    Object tag = v.getTag();
    if (tag instanceof ShortcutInfo) {
      // ショートカットを開く
      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);
      // 検索バーを表示します(スプリングロードモードでドロップターゲットバーを表示していた場合にのみアニメーションを追加します)。
      // ロードモード)
      if (mSearchDropTargetBar != null) {
        mSearchDropTargetBar.showSearchBar(wasInSpringLoadedMode);
      }
      // スプリングロードモードから来た場合にのみドックディビジョナーにアニメーションを追加します。
      ドックディビジョナーにアニメーションを追加して表示します。
      // AppsCustomizeボタンのフォーカスを設定します。
      if (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はActivityのstartActivityメソッドです。 

  boolean startActivitySafely(View v, Intent intent, Object tag) {
    boolean success = false;
    try {
      success = startActivity(v, intent, tag);
    } catch (ActivityNotFoundException e) {
      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(メールを送信する際には、#を@に置き換えてください。報告を行い、関連する証拠を提供してください。一旦確認が取れましたら、本サイトは即座に侵害を疑われるコンテンツを削除します。)

おすすめ