суббота, 1 октября 2011 г.

Работа с пакетами в Android

Мне время от времени приходится решать задачи, связанные с работой с пакетами в Android. В этой статье я приведу несколько сниппетов для решения этих задач.

AndroidManifest

В любом приложении под Android есть файл AndroidManifect.xml. В нём публикуется вся необходимая системе информация о приложении и его компонентах. Кроме того, при необходимости можно обратиться к системе и получить эти данные. Для этого в SDK существует класс PackageManager.

Объектная модель

Объектная модель манифеста содержится в пакете android.content.pm. Обозначу несколько классов:

PackageInfo
Содержит данные о пакете приложения. Здесь: список активностей, сервисов, разрешений, имя и версия пакета и прочая информация, опубликованная в манифесте.
ComponentInfo
Базовый класс для хранения информации о различных компонентах приложения: активности (ActivityInfo), сервиса (ServiceInfo), контент-провайдера (ProviderInfo)
ResolveInfo
Класс для хранения информации об интентах, соответствующих компонентам приложения

PackageManager

Получить экземпляр PackageManager можно с помощью метода getPackageManager контекста. Обозначу методы этого класса, которые будут использоваться в статье:

PackageInfo getPackageInfo(String packageName, int flags)
Возвращает данные об установленном приложении с заданным packageName. Если приложение не установлено, будет выброшено исключение.
List<ResolveInfo> queryIntentActivities(Intent intent, int flags)
Возвращает список активностей, соответствующих заданному Intent-у.

Проверка существования приложения в системе

Это наиболее часто встречающаяся задача. Нужно узнать, установлено ли в системе приложение с заданным package name.

boolean isPackageInstalled(Context context, String packageName) {
    PackageManager pm = context.getPackageManager();
    boolean available = false;
    try {
        pm.getPackageInfo(packageName, 0);
        available = true;
    } catch (NameNotFoundException e) {
    }
        
    return available;
}

Не самое изящное решение, но единственно возможное.

Запуск приложения

С помощью этой копипасты можно запустить приложение с заданным package name:

boolean startApplication(Context context, String packageName) {
    Intent intent = new Intent()
        .setPackage(packageName)
        .addCategory(Intent.CATEGORY_LAUNCHER)
        .setAction(Intent.ACTION_MAIN)
        .addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
            
    PackageManager pm = activity.getPackageManager();
    List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
            
    if (list != null) {
        for (ResolveInfo ri : list) {
            intent.setClassName(ri.activityInfo.packageName, ri.activityInfo.name);
            break;
        }
    } else {
        Log.e(TAG, String.format("Cannot resolve application '%s' activities", packageName));
    }
            
    try {
        startActivity(intent);
    } catch (ActivityNotFoundException ex) {
        Log.e(TAG, e);
    }
}

Тут дело такое. Мы запускаем приложение при помощи метода startActivity, и потому обязательно должны указать имя класса активности, который должен быть запущен. Но внутренняя структура приложения может быть нам незнакома. Поэтому мы сначала формируем Intent, указав ему имя пакета, категорию CATEGORY_LAUNCHER и экшен ACTION_MAIN (необходимые значения для главной активности приложения), а потом получаем список всех возможных активностей с помощью вызова метода queryIntentActivities. В результате либо ничего не будет, либо будет всего одна активность. Имя этой активности мы и зададим как className нашему intent-у. А после этого его и запускать можно.

Проверка наличия Android Market

Бывает, что хочется узнать, установлен ли в системе маркет. Тут возможно два решения:

  1. По предложенной выше схеме проверить пакет com.google.process.gapps. Однако, не могу утверждать, что такое решение будет работать на всех устройствах.
  2. Решение, предложенное на Stackoverflow:
    boolean isMarketAvailable(Context context) {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        intent.setData(Uri.parse("market://search?q=foo"));
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> list = pm.queryIntentActivities(intent, 0);
    
        boolean installed = !(list == null || list.isEmpty());
    
        return installed;
    }
    
    Здесь мы пытаемся получить список приложений, обрабатывающих запрос на маркет. Если в этом списке хоть что-то есть — значит, маркет на месте.

Заключение

Мы кратко разобрали наиболее часто встречающиеся задачи, связанные с работой менеджера пакетов. Исходников не прилагаю, ибо вся полезная копипаста и так в статье.

2 комментария:

alaf комментирует...
Этот комментарий был удален автором.
alaf комментирует...

>> boolean isPackageInstalled(Context context, String packageName) { ...
>> Не самое изящное решение, но единственно возможное.


На самом деле можно реализовать поиск пакетов с помощью методов
getInstalledPackages (getInstalledpplications) или воспользоваться перебором List'ов, полученных при помощи
queryIntentActivities, queryBroadcastReceivers, queryContentProvider и т.д. Но так, как у Вас короче :)

>>boolean startApplication(Context context, String packageName) { ...

А для запуска приложений, имхо, лучше бы использовать getLaunchIntentForPackage.

P.S. Это я так, просто поумничать:) Удачи!