web-dev-qa-db-fra.com

Installer l'application par programme sur Android

Je suis intéressé à savoir s’il est possible d’installer par programme un apk téléchargé dynamiquement à partir d’une application Android personnalisée.

180
Alkersan

Vous pouvez facilement lancer un lien vers le marché ou une invite d'installation:

Intent promptInstall = new Intent(Intent.ACTION_VIEW)
    .setDataAndType(Uri.parse("file:///path/to/your.apk"), 
                    "application/vnd.Android.package-archive");
startActivity(promptInstall); 

la source

Intent goToMarket = new Intent(Intent.ACTION_VIEW)
    .setData(Uri.parse("market://details?id=com.package.name"));
startActivity(goToMarket);

la source

Cependant, vous ne pouvez pas installer .apks sans l'autorisation explicite de l'utilisateur; pas sauf si le périphérique et votre programme sont enracinés.

221
Lie Ryan
File file = new File(dir, "App.apk");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.Android.package-archive");
startActivity(intent);

J'ai eu le même problème et après plusieurs tentatives, cela a fonctionné pour moi de cette façon. Je ne sais pas pourquoi, mais la définition des données et le type séparément foiré mon intention. 

54
Horaceman

Les solutions apportées à cette question s’appliquent toutes à targetSdkVersions de 23 ans et moins. Pour Android N, c’est-à-dire les API de niveau 24 et supérieur, toutefois, ils ne fonctionnent pas et plantent avec l’exception suivante:

Android.os.FileUriExposedException: file:///storage/emulated/0/... exposed beyond app through Intent.getData()

Cela est dû au fait que, depuis Android 24, la Uri pour l’adressage du fichier téléchargé a été modifiée. Par exemple, un fichier d'installation nommé appName.apk stocké sur le système de fichiers externe principal de l'application avec le nom de package com.example.test serait le suivant:

file:///storage/emulated/0/Android/data/com.example.test/files/appName.apk

pour API 23 et ci-dessous, alors que quelque chose comme

content://com.example.test.authorityStr/pathName/Android/data/com.example.test/files/appName.apk

pour API 24 et ci-dessus.

Vous trouverez plus de détails à ce sujet ici et je ne vais pas le parcourir.

Pour répondre à la question pour targetSdkVersion de 24 et supérieur, vous devez suivre ces étapes: Ajoutez ce qui suit au fichier AndroidManifest.xml:

<application
        Android:allowBackup="true"
        Android:label="@string/app_name">
        <provider
            Android:name="Android.support.v4.content.FileProvider"
            Android:authorities="${applicationId}.authorityStr"
            Android:exported="false"
            Android:grantUriPermissions="true">
            <meta-data
                Android:name="Android.support.FILE_PROVIDER_PATHS"
                Android:resource="@xml/paths"/>
        </provider>
</application>

2. Ajoutez le fichier paths.xml suivant au dossier xml dans res dans src, main:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <external-path
        name="pathName"
        path="pathValue"/>
</paths>

pathName est celui indiqué dans l'exemple de contenu ci-dessus, uri exemple ci-dessus, et pathValue est le chemin d'accès réel sur le système. Ce serait une bonne idée de mettre un "." (sans guillemets) pour pathValue dans ce qui précède si vous ne souhaitez pas ajouter de sous-répertoire supplémentaire.

  1. Ecrivez le code suivant pour installer l'apk avec le nom appName.apk sur le système de fichiers externe principal:

    File directory = context.getExternalFilesDir(null);
    File file = new File(directory, fileName);
    Uri fileUri = Uri.fromFile(file);
    if (Build.VERSION.SDK_INT >= 24) {
        fileUri = FileProvider.getUriForFile(context, context.getPackageName(),
                file);
    }
    Intent intent = new Intent(Intent.ACTION_VIEW, fileUri);
    intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
    intent.setDataAndType(fileUri, "application/vnd.Android" + ".package-archive");
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    context.startActivity(intent);
    activity.finish();
    

Aucune autorisation n'est également nécessaire pour écrire dans le répertoire privé de votre propre application sur le système de fichiers externe.

J'ai écrit une bibliothèque AutoUpdate ici dans laquelle j'ai utilisé ce qui précède.

30
Ali Nem

Eh bien, j'ai approfondi et trouvé les sources de l'application PackageInstaller à partir d'Android Source.

https://github.com/Android/platform_packages_apps_packageinstaller

De manifeste, j'ai trouvé qu'il nécessite une autorisation:

    <uses-permission Android:name="Android.permission.INSTALL_PACKAGES" />

Et le processus d'installation réel a lieu après confirmation

Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallAppProgress.class);
String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (installerPackageName != null) {
   newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);
}
startActivity(newIntent);
26
Alkersan

Je veux simplement partager le fait que mon fichier apk a été enregistré dans le répertoire "Données" de mon application et que je devais modifier les autorisations sur le fichier apk pour qu'il soit lisible par tout le monde, afin de permettre son installation de cette façon, sinon le système jetait "Erreur d'analyse: il y a un problème d'analyse du package"; en utilisant donc la solution de @Horaceman qui fait:

File file = new File(dir, "App.apk");
file.setReadable(true, false);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.Android.package-archive");
startActivity(intent);
20
user1604240

Suivez ces étapes:

1 - Ajoutez ce qui suit au AndroidManifest.xml:

<provider
        Android:name="Android.support.v4.content.FileProvider"
        Android:authorities="${applicationId}.provider"
        Android:exported="false"
        Android:grantUriPermissions="true">
        <meta-data
            Android:name="Android.support.FILE_PROVIDER_PATHS"
            Android:resource="@xml/paths"/>
    </provider>

2 - Ajoute le fichier paths.xml suivant au dossier xml (s'il n'existe pas, crée-le) à l'aide de res dans src, main

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:Android="http://schemas.Android.com/apk/res/Android">
<external-path
    name="external_file"
    path="."/>
</paths>

PathName est celui indiqué dans l'exemple de contenu exemple ci-dessus et pathValue est le chemin réel sur le système. Ce serait une bonne idée de mettre un "." pour pathValue dans ce qui précède si vous ne souhaitez pas ajouter de sous-répertoire supplémentaire. 

3 - Écrivez le code suivant dans Run Your Apk files:

File file = "path of yor apk file";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    Uri fileUri = FileProvider.getUriForFile(getBaseContext(), getApplicationContext().getPackageName() + ".provider", file);
    Intent intent = new Intent(Intent.ACTION_VIEW, fileUri);
    intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
    intent.setDataAndType(fileUri, "application/vnd.Android" + ".package-archive");
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    startActivity(intent);

} else {
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setDataAndType(Uri.fromFile(file), "application/vnd.Android.package-archive");
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    startActivity(intent);
}
13
Lila saad

Oui c'est possible. Mais pour cela, il faut que le téléphone installe des sources non vérifiées. Par exemple, slideMe fait cela. Je pense que la meilleure chose à faire est de vérifier si l’application est présente et d’envoyer une intention pour Android Market. vous devriez utiliser quelque chose comme le schéma d'URL pour Android Market.

market://details?id=package.name

Je ne sais pas exactement comment démarrer l'activité, mais si vous démarrez une activité avec ce type d'URL. Il devrait ouvrir le marché Android et vous donner le choix d'installer les applications.

4

Il est à noter que si vous utilisez la DownloadManager pour lancer votre téléchargement, veillez à le sauvegarder dans un emplacement externe, par exemple, par exemple. setDestinationInExternalFilesDir(c, null, "<your name here>).apk";. L'intention avec un type d'archive de paquet ne semble pas ressembler au schéma content: utilisé avec les téléchargements vers un emplacement interne, mais ressemble à file:. (Essayer d'encapsuler le chemin interne dans un objet File, puis obtenir le chemin ne fonctionne pas non plus, même s'il en résulte une URL file:, car l'application n'analysera pas l'apk; il semble que ce soit externe.)

Exemple:

int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
String downloadedPackageUriString = cursor.getString(uriIndex);
File mFile = new File(Uri.parse(downloadedPackageUriString).getPath());
Intent promptInstall = new Intent(Intent.ACTION_VIEW)
        .setDataAndType(Uri.fromFile(mFile), "application/vnd.Android.package-archive")
        .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
appContext.startActivity(promptInstall);
4
qix

Une autre solution qui n’a pas besoin de coder en dur l’application de réception et qui est donc plus sûre:

Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData( Uri.fromFile(new File(pathToApk)) );
startActivity(intent);
4
Hartok

Juste une extension, si quelqu'un a besoin d'une bibliothèque, alors ceci pourrait vous aider. Merci à Raghav

2
IronBlossom

essaye ça

String filePath = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
String title = filePath.substring( filePath.lastIndexOf('/')+1, filePath.length() );
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(filePath)), "application/vnd.Android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // without this flag Android returned a intent error!
MainActivity.this.startActivity(intent);
2
Joolah

Commencez par ajouter la ligne suivante à AndroidManifest.xml:

<uses-permission Android:name="Android.permission.INSTALL_PACKAGES"
    tools:ignore="ProtectedPermissions" />

Ensuite, utilisez le code suivant pour installer apk:

File sdCard = Environment.getExternalStorageDirectory();
            String fileStr = sdCard.getAbsolutePath() + "/MyApp";// + "app-release.apk";
            File file = new File(fileStr, "TaghvimShamsi.apk");
            Intent promptInstall = new Intent(Intent.ACTION_VIEW).setDataAndType(Uri.fromFile(file),
                    "application/vnd.Android.package-archive");
            startActivity(promptInstall);
1
Abbas.moosavi

N'oubliez pas de demander des autorisations:

Android.Manifest.permission.WRITE_EXTERNAL_STORAGE 
Android.Manifest.permission.READ_EXTERNAL_STORAGE

Ajoutez dans AndroidManifest.xml le fournisseur et l'autorisation:

<uses-permission Android:name="Android.permission.REQUEST_INSTALL_PACKAGES"/>
...
<application>
    ...
    <provider
        Android:name="Android.support.v4.content.FileProvider"
        Android:authorities="${applicationId}"
        Android:exported="false"
        Android:grantUriPermissions="true">
        <meta-data
            Android:name="Android.support.FILE_PROVIDER_PATHS"
            Android:resource="@xml/provider_paths"/>
    </provider>
</application>

Créer un fournisseur de fichier XML res/xml/provider_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <external-path
        name="external"
        path="." />
    <external-files-path
        name="external_files"
        path="." />
    <cache-path
        name="cache"
        path="." />
    <external-cache-path
        name="external_cache"
        path="." />
    <files-path
        name="files"
        path="." />
</paths>

Utilisez l'exemple de code ci-dessous:

   public class InstallManagerApk extends AppCompatActivity {

    static final String NAME_APK_FILE = "some.apk";
    public static final int REQUEST_INSTALL = 0;

     @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // required permission:
        // Android.Manifest.permission.WRITE_EXTERNAL_STORAGE 
        // Android.Manifest.permission.READ_EXTERNAL_STORAGE

        installApk();

    }

    ...

    /**
     * Install APK File
     */
    private void installApk() {

        try {

            File filePath = Environment.getExternalStorageDirectory();// path to file apk
            File file = new File(filePath, LoadManagerApkFile.NAME_APK_FILE);

            Uri uri = getApkUri( file.getPath() ); // get Uri for  each SDK Android

            Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
            intent.setData( uri );
            intent.setFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK );
            intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
            intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
            intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, getApplicationInfo().packageName);

            if ( getPackageManager().queryIntentActivities(intent, 0 ) != null ) {// checked on start Activity

                startActivityForResult(intent, REQUEST_INSTALL);

            } else {
                throw new Exception("don`t start Activity.");
            }

        } catch ( Exception e ) {

            Log.i(TAG + ":InstallApk", "Failed installl APK file", e);
            Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG)
                .show();

        }

    }

    /**
     * Returns a Uri pointing to the APK to install.
     */
    private Uri getApkUri(String path) {

        // Before N, a MODE_WORLD_READABLE file could be passed via the ACTION_INSTALL_PACKAGE
        // Intent. Since N, MODE_WORLD_READABLE files are forbidden, and a FileProvider is
        // recommended.
        boolean useFileProvider = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;

        String tempFilename = "tmp.apk";
        byte[] buffer = new byte[16384];
        int fileMode = useFileProvider ? Context.MODE_PRIVATE : Context.MODE_WORLD_READABLE;
        try (InputStream is = new FileInputStream(new File(path));
             FileOutputStream fout = openFileOutput(tempFilename, fileMode)) {

            int n;
            while ((n = is.read(buffer)) >= 0) {
                fout.write(buffer, 0, n);
            }

        } catch (IOException e) {
            Log.i(TAG + ":getApkUri", "Failed to write temporary APK file", e);
        }

        if (useFileProvider) {

            File toInstall = new File(this.getFilesDir(), tempFilename);
            return FileProvider.getUriForFile(this,  BuildConfig.APPLICATION_ID, toInstall);

        } else {

            return Uri.fromFile(getFileStreamPath(tempFilename));

        }

    }

    /**
     * Listener event on installation APK file
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if(requestCode == REQUEST_INSTALL) {

            if (resultCode == Activity.RESULT_OK) {
                Toast.makeText(this,"Install succeeded!", Toast.LENGTH_SHORT).show();
            } else if (resultCode == Activity.RESULT_CANCELED) {
                Toast.makeText(this,"Install canceled!", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this,"Install Failed!", Toast.LENGTH_SHORT).show();
            }

        }

    }

    ...

}
1
amiron

UpdateNode fournit une API pour Android permettant d'installer des packages APK depuis une autre application .

Vous pouvez simplement définir votre mise à jour en ligne et intégrer l’API dans votre application - c’est tout .
Actuellement, l’API est à l’état bêta, mais vous pouvez déjà effectuer certains tests vous-même.

À côté de cela, UpdateNode propose également l’affichage de messages via le système - très utile si vous voulez dire quelque chose d’important à vos utilisateurs .

Je fais partie de l'équipe de développement du client et j'utilise au moins la fonctionnalité de message pour ma propre application Android.

Voir ici une description de l'intégration de l'API

1
sarahara

Essayez ceci - Écrivez sur manifeste:

uses-permission Android:name="Android.permission.INSTALL_PACKAGES"
        tools:ignore="ProtectedPermissions"

Écrivez le code:

File sdCard = Environment.getExternalStorageDirectory();
String fileStr = sdCard.getAbsolutePath() + "/Download";// + "app-release.apk";
File file = new File(fileStr, "app-release.apk");
Intent promptInstall = new Intent(Intent.ACTION_VIEW).setDataAndType(Uri.fromFile(file),
                        "application/vnd.Android.package-archive");

startActivity(promptInstall);
0
Phuoc-Loc Truong

Cela peut beaucoup aider les autres!

Premier:

private static final String APP_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyAppFolderInStorage/";

private void install() {
    File file = new File(APP_DIR + fileName);

    if (file.exists()) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        String type = "application/vnd.Android.package-archive";

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            Uri downloadedApk = FileProvider.getUriForFile(getContext(), "ir.greencode", file);
            intent.setDataAndType(downloadedApk, type);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        } else {
            intent.setDataAndType(Uri.fromFile(file), type);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        }

        getContext().startActivity(intent);
    } else {
        Toast.makeText(getContext(), "ّFile not found!", Toast.LENGTH_SHORT).show();
    }
}

Second: Pour Android 7 et les versions ultérieures, vous devez définir un fournisseur dans manifest, comme ci-dessous!

    <provider
        Android:name="Android.support.v4.content.FileProvider"
        Android:authorities="ir.greencode"
        Android:exported="false"
        Android:grantUriPermissions="true">
        <meta-data
            Android:name="Android.support.FILE_PROVIDER_PATHS"
            Android:resource="@xml/paths" />
    </provider>

Troisième: Définissez path.xml dans le dossier res/xml comme ci-dessous! J'utilise ce chemin pour le stockage interne si vous souhaitez le remplacer par un autre moyen. Vous pouvez aller à ce lien: FileProvider

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:Android="http://schemas.Android.com/apk/res/Android">
    <external-path name="your_folder_name" path="MyAppFolderInStorage/"/>
</paths>

Forth: Vous devez ajouter cette permission dans le manifeste:

<uses-permission Android:name="Android.permission.REQUEST_INSTALL_PACKAGES"/>

Assurez-vous que les autorités du fournisseur sont les mêmes!


0
Hadi Note