43 votes

Peut-on installer un APK à partir d'un fournisseur de contenu ?

Je travaille sur une bibliothèque permettant aux applications de s'auto-mettre à jour pour ceux qui sont distribués en dehors de l'Android Market.

Mon plan initial était d'inclure un code qui téléchargerait le fichier APK sur le stockage interne, puis l'installerait à partir de là via un fichier de type ContentProvider et un content:// Uri . Cependant, lorsque j'ai essayé, le système d'installation a envoyé un avertissement "Skipping dir : " à LogCat et n'a pas réussi à l'installer. Une fois que j'ai téléchargé l'APK sur un support de stockage externe et que j'ai utilisé un fichier d'installation de l file:// Uri avec le ACTION_VIEW installateur Intent ça a marché.

Le message "Skipping dir :" semble être enregistré par parsePackage() en PackageParser qui semble partir du principe qu'il travaille avec une File . Cela suggère que nous ne pouvons pas utiliser content:// Uri valeurs.

Quelqu'un a-t-il utilisé avec succès ACTION_VIEW sur un application/vnd.android.package-archive Intent avec un content:// Uri ? Si c'est le cas, y a-t-il une astuce spécifique pour paramétrer la ContentProvider qui l'ont fait fonctionner ?

Gracias.

27voto

Al Sutton Points 3025

La documentation relative à ACTION_INSTALL_PACKAGE est incorrecte. Elle aussi n'accepte que les fichiers.

Ma seule suggestion serait donc de créer une copie du fichier dans la zone des fichiers d'applications, de la rendre lisible dans le monde entier et de nettoyer les fichiers restants à une date ultérieure.

Réponse incorrecte précédente : Dans les versions 4.0 et supérieures, il existe un ACTION_INSTALL_PACKAGE qui accepte un URI content://. ( JavaDoc ), mais, avant cela, vous êtes limité à une installation via ACTION_VIEW, qui suppose que l'URI transmis est un URI de type file://.

14voto

Jules Points 5015

Je suppose que ce n'est pas possible, car l'API Java ne semble pas le permettre. L'élément ContentProvider openFile() renvoie un ParcelFileDescriptor à partir duquel vous pouvez obtenir un java.io.FileDescriptor . Vous pouvez alors utiliser ce FileDescriptor pour ouvrir soit un FileInputStream o un FileOutputStream . Malheureusement, vous ne pouvez pas l'utiliser pour ouvrir une RandomAccessFile (malgré le fait que RandomAccessFile fonctionne sur les descripteurs de la même manière que les autres, le constructeur dont vous auriez besoin est simplement absent de l'API).

Comme les fichiers APK sont des fichiers ZIP, qui doivent être lus dans le désordre (il faut chercher jusqu'à la fin pour trouver le répertoire du fichier), je suppose que la mise en œuvre de l'installation nécessitera une RandomAccessFile Il n'aurait donc pas été possible de soutenir le cas que vous essayez de mettre en œuvre.

6voto

njzk2 Points 17085

Je suis d'accord avec l'analyse de Jules, et j'ajouterais des précisions concrètes :

Sur PackageInstallerActivity qui est appelé par ACTION_VIEW sur un apk, il y a ceci dans le fichier onCreate() méthode :

315 String apkPath = mPackageURI.getPath();
316 File apkFile = new File(apkPath);

Et avant cela, cette méthode de PackageUtil s'appelle :

73 public static  PackageParser.Package getPackageInfo(Uri packageURI) {
74     final String archiveFilePath = packageURI.getPath();
75     PackageParser packageParser = new PackageParser(archiveFilePath);
76     File sourceFile = new File(archiveFilePath);
77     DisplayMetrics metrics = new DisplayMetrics();
78     metrics.setToDefaults();
79     return packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0);
80 }

Tout ceci tend à confirmer que le PackageManager n'attend que des Uris de fichier.

Le journal que vous avez, Skipping dir: se trouve dans packageParser.parsePackage qui teste si le chemin donné dans l'Uri est un fichier ou non.

1voto

Fuzzy Points 422

J'ai ceci dans une de mes applications qui me permet d'accéder au stockage local (les préférences de l'utilisateur peuvent être sélectionnées avant que vous ne vous en preniez à moi ;) ).

import java.io.*;
import android.content.*;
import android.database.*;
import android.net.*;
import android.os.*;
import android.preference.PreferenceManager;
import android.util.Log;

public class LocalFileContentProvider extends ContentProvider {
   private static final String URI_PREFIX = "content://your.content.provider.as.per.manifest";

   public static String constructUri(String url) {
       Uri uri = Uri.parse(url);
       return uri.isAbsolute() ? url : URI_PREFIX + url;
   }

   @Override
   public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {

       SharedPreferences app_preferences = PreferenceManager.getDefaultSharedPreferences(getContext());

       boolean allowLocal = app_preferences.getBoolean("allowLocalFiles", false);

        if (allowLocal) {    
            try {
                File file = new File(uri.getPath());

                if (file.isDirectory())
                    return null;

                ParcelFileDescriptor parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
                return parcel;
            } catch (Exception e) {
                return null;
            }
        } else {
            return null;
        }

   }

   @Override
   public boolean onCreate() {
       return true;
   }

   @Override
   public int delete(Uri uri, String s, String[] as) {
       throw new UnsupportedOperationException("Not supported by this provider");
   }

   @Override
   public String getType(Uri uri) {
       throw new UnsupportedOperationException("Not supported by this provider");
   }

   @Override
   public Uri insert(Uri uri, ContentValues contentvalues) {
       throw new UnsupportedOperationException("Not supported by this provider");
   }

   @Override
   public Cursor query(Uri uri, String[] as, String s, String[] as1, String s1) {
       throw new UnsupportedOperationException("Not supported by this provider");
   }

   @Override
   public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {
       throw new UnsupportedOperationException("Not supported by this provider");
   }

}

Mon manifeste contient

<provider android:name="it.automated.android.kiosk.se.LocalFileContentProvider"
      android:authorities="it.automated" />

et ensuite pour lancer l'installation (ou l'action)

Intent viewIntent = new Intent(Intent.ACTION_VIEW);
viewIntent.setDataAndType(Uri.parse(url), mimeType);

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X