woshidan's loose leaf

ぼんやり勉強しています

現在インストールされているアプリを取得する

AsyncTaskLoaderについて調べてたのだけど、実装例の現在インストールされているアプリの取得の仕方が面白かったので、貼る。

以下、実装を少し追って見る。

BroadCastReceiverのサブクラス

  1. BroadCastReceiverのサブクラスを作る
  2. 1のクラス内で、呼び出し元のLoaderクラスのContextにBroadcastReceiverのインスタンスを登録する。そのBroadcastReceiverのインスタンスにはIntent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_CHANGEDのACTIONとDataSchemeにpackageが追加されているintentフィルターを使って作る。
  3. 2.の処理で取得しきれないSDカードにインストールされた分を取得するため、呼び出し元のLoaderクラスのContextにもう1個BroadcastReceiverのインスタンスを登録する。そのBroadcastReceiverのインスタンスにはIntent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE, Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLEのACTION指定が追加されているintentフィルターを使って作る。
  4. 1.のクラスのonReceiveメソッドを実装する。実装内容はLoaderクラスのonContentChanged()メソッドの呼び出し。
/**
 * Helper class to look for interesting changes to the installed apps
 * so that the loader can be updated.
 */
public static class PackageIntentReceiver extends BroadcastReceiver {
    final AppListLoader mLoader;

    public PackageIntentReceiver(AppListLoader loader) {
        mLoader = loader;
        IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
        filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
        filter.addAction(Intent.ACTION_PACKAGE_CHANGED);
        filter.addDataScheme("package");
        mLoader.getContext().registerReceiver(this, filter);
        // Register for events related to sdcard installation.
        IntentFilter sdFilter = new IntentFilter();
        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE);
        sdFilter.addAction(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
        mLoader.getContext().registerReceiver(this, sdFilter);
    }

    @Override public void onReceive(Context context, Intent intent) {
        // Tell the loader about the change.
        mLoader.onContentChanged();
    }
}

AsyncTaskLoaderのサブクラス

  1. AsyncTaskLoaderのサブクラスを作る。コンストラクタで呼び出し元のコンテキストのパッケージマネージャを取得してメンバ変数として保存する。
  2. loadInBackground() の実装。PackageManager#getInstalledApplications(Options)List<ApplicationInfo>を取得。オプションで与える値はPackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_DISABLED_COMPONENTS。戻り値はList<ApplicationInfo>かnull。
  3. loadInBackground() の実装。2.の返り値がnullなら、要素が空のList<ApplicationInfo>を作成。
  4. loadInBackground() の実装。AsyncTaskLoaderの呼び出し元のContextを取得。
  5. loadInBackground() の実装。AppEntryのリストを作ります。4.で得たcontextにアプリのLabelを読み込ませます。そのあと、entryをリストに追加します。
  6. loadInBackground() の実装。5.で作成したリストを並べ替えます
  7. loadInBackground()の終了・
/**
 * This is where the bulk of our work is done.  This function is
 * called in a background thread and should generate a new set of
 * data to be published by the loader.
 */
@Override public List<AppEntry> loadInBackground() {
    // Retrieve all known applications.
    List<ApplicationInfo> apps = mPm.getInstalledApplications(
            PackageManager.GET_UNINSTALLED_PACKAGES |
            PackageManager.GET_DISABLED_COMPONENTS);
    if (apps == null) {
        apps = new ArrayList<ApplicationInfo>();
    }

    final Context context = getContext();

    // Create corresponding array of entries and load their labels.
    List<AppEntry> entries = new ArrayList<AppEntry>(apps.size());
    for (int i=0; i<apps.size(); i++) {
        AppEntry entry = new AppEntry(this, apps.get(i));
        entry.loadLabel(context);
        entries.add(entry);
    }

    // Sort the list.
    Collections.sort(entries, ALPHA_COMPARATOR);

    // Done!
    return entries;
}

Loader#onContentChanged()はLoaderクラスが持っているメソッドでサブクラスでは実装してません。
呼び出すと、Loader.ForceLoadContentObserverが変化を検出して、onChange()メソッドを呼ぶみたいですが、 これを呼び出されるとどうなるかは今は保留で...

ApplicationInfohttp://developer.android.com/reference/android/content/pm/ApplicationInfo.html 参照。