現在インストールされているアプリを取得する
AsyncTaskLoaderについて調べてたのだけど、実装例の現在インストールされているアプリの取得の仕方が面白かったので、貼る。
以下、実装を少し追って見る。
BroadCastReceiverのサブクラス
- BroadCastReceiverのサブクラスを作る
- 1のクラス内で、呼び出し元のLoaderクラスのContextにBroadcastReceiverのインスタンスを登録する。そのBroadcastReceiverのインスタンスには
Intent.ACTION_PACKAGE_ADDED
,Intent.ACTION_PACKAGE_REMOVED
,Intent.ACTION_PACKAGE_CHANGED
のACTIONとDataSchemeにpackage
が追加されているintentフィルターを使って作る。 - 2.の処理で取得しきれないSDカードにインストールされた分を取得するため、呼び出し元のLoaderクラスのContextにもう1個BroadcastReceiverのインスタンスを登録する。そのBroadcastReceiverのインスタンスには
Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE
,Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE
のACTION指定が追加されているintentフィルターを使って作る。 - 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のサブクラス
- AsyncTaskLoaderのサブクラスを作る。コンストラクタで呼び出し元のコンテキストのパッケージマネージャを取得してメンバ変数として保存する。
loadInBackground()
の実装。PackageManager#getInstalledApplications(Options)
でList<ApplicationInfo>
を取得。オプションで与える値はPackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_DISABLED_COMPONENTS
。戻り値はList<ApplicationInfo>
かnull。loadInBackground()
の実装。2.の返り値がnullなら、要素が空のList<ApplicationInfo>
を作成。loadInBackground()
の実装。AsyncTaskLoaderの呼び出し元のContextを取得。loadInBackground()
の実装。AppEntry
のリストを作ります。4.で得たcontextにアプリのLabelを読み込ませます。そのあと、entryをリストに追加します。loadInBackground()
の実装。5.で作成したリストを並べ替えます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()
メソッドを呼ぶみたいですが、
これを呼び出されるとどうなるかは今は保留で...
ApplicationInfo
は http://developer.android.com/reference/android/content/pm/ApplicationInfo.html 参照。