Domanda

Sono interessato a sapere se è possibile installare a livello di codice un apk scaricato dinamicamente da un'applicazione Android personalizzata.

È stato utile?

Soluzione

Si può facilmente lanciare un link Play Store o un prompt di installazione:

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

o

Intent goToMarket = new Intent(Intent.ACTION_VIEW)
    .setData(Uri.parse("https://play.google.com/store/apps/details?id=com.package.name"));
startActivity(goToMarket);

Tuttavia, non è possibile installare .apks senza l'esplicita autorizzazione dell'utente ; Non meno che il dispositivo e il programma è radicata.

Altri suggerimenti

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);

Ho avuto lo stesso problema e dopo vari tentativi, ha funzionato per me in questo modo. Non so perché, ma di impostazione dati e il tipo avvitato a parte il mio intento.

Le soluzioni fornite a questa domanda sono tutti applicabili a targetSdkVersion s di 23 e al di sotto. Per Android N, vale a dire il livello di API 24, e sopra, tuttavia, non lavorano e crash con la seguente eccezione:

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

Ciò è dovuto al fatto che a partire da Android 24, il Uri per affrontare il file scaricato è cambiato. Per esempio, un file di installazione di nome appName.apk memorizzato sul filesystem esterno principale dell'app con nome del pacchetto com.example.test sarebbe come

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

per API 23 e al di sotto, mentre qualcosa come

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

per API 24 e al di sopra.

Maggiori dettagli su questo possono essere trovati qui e io non ho intenzione di passare attraverso di essa.

Per rispondere alla domanda per targetSdkVersion di 24 e, soprattutto, si deve procedere come segue: Aggiungere la seguente al 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. Aggiungere il seguente file paths.xml nella cartella xml su res in src, principale:

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

Il pathName è quello mostrato nell'esempio uri contenuto esemplificativa sopra e pathValue è il percorso effettivo sul sistema. Sarebbe una buona idea mettere un "" (Senza virgolette) per pathValue in quanto sopra, se non si desidera aggiungere qualsiasi sottodirectory supplementare.

  1. Scrivi il codice seguente per installare l'apk con il nome appName.apk sul filesystem esterno principale:

    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();
    

Nessun permesso è necessario anche quando si scrive alla directory privata del proprio app sul filesystem esterno.

Ho scritto una libreria AutoUpdate qui in cui ho usato il sopra.

Bene, ho scavato più a fondo, e ha trovato le fonti di applicazione PackageInstaller da source Android.

https://github.com/android/platform_packages_apps_packageinstaller

Da manifesta ho scoperto che richiedono il permesso:

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

E l'effettivo processo di installazione si verifica dopo la conferma

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);

Voglio solo condividere il fatto che il mio file apk è stato salvato nella directory "Data" della mia app e che avevo bisogno di modificare le autorizzazioni sul file apk in modo che fosse leggibile da tutti per consentirne l'installazione in questo modo, altrimenti il ​​sistema lanciava "Errore di analisi:C'è un problema nell'analisi del pacchetto";quindi utilizzando la soluzione di @Horaceman che rende:

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);

Un'altra soluzione che non non richiede a hard-code l'applicazione di ricezione e che è quindi più sicuro:

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

Sì, è possibile. Ma per questo è necessario il telefono per installare fonti non verificate. Ad esempio, SlideME fa. Penso che la cosa migliore che puoi fare è quello di verificare se l'applicazione è presente e inviare una intenti per l'Android Market. si dovrebbe usare qualcosa lo schema URL per il mercato Android.

market://details?id=package.name

Non so esattamente come avviare l'attività, ma se si avvia un'attività con quel tipo di URL. Si dovrebbe aprire il mercato di Android e vi darà la possibilità di installare le applicazioni.

vale la pena di notare che se si utilizza il DownloadManager per dare il via il download, assicurarsi di salvarlo in una posizione esterna per esempio setDestinationInExternalFilesDir(c, null, "<your name here>).apk";. L'intento con un tipo di pacchetto-archivio non sembra come lo schema content: utilizzato con download in una posizione interna, ma fa come file:. (Cercando di avvolgere il percorso interno in un oggetto file e quindi ottenere il percorso non funziona nemmeno, anche se si traduce in un URL file:, come l'applicazione non sarà analizzare l'apk, sembra che deve essere esterno.)

Esempio:

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);

Questo può aiutare gli altri un sacco!

Primo:

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();
    }
}

In secondo luogo: per Android 7 e soprattutto è necessario definire un fornitore in palese come qui di seguito

    <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>

Terzo: Definisci path.xml in res / xml cartella come qui di seguito! Sto usando questo path per l'archiviazione interna, se si desidera cambiarlo in qualcosa d'altro c'è un modo pochi! Si può andare a questo link: 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: Si dovrebbe aggiungere questa autorizzazione a manifestare:

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

Si prega di assicurarsi che le autorità del provider sono uguali!


solo una proroga, se qualcuno bisogno di una libreria, allora questo aiuto potrebbe . Grazie a Raghav

Prova questo

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);

UpdateNode fornisce un'API per Android per installare i pacchetti APK da dentro un'altra applicazione.
Si può solo definire il vostro aggiornamento on-line e di integrare le API nella vostra App - il gioco è fatto.
Attualmente l'API è in stato di beta, ma si può già fare alcuni test da soli.
Accanto a questo, offre UpdateNode anche la visualizzazione di messaggi se il sistema - molto utile se si vuole raccontare qualcosa di importante per gli utenti.
Io sono parte del team cliente dev e sto usando almeno la funzionalità messaggio per il mio App Android.

Vedi qui una descrizione di come integrare l'API

In primo luogo aggiungere la seguente riga alla AndroidManifest.xml:

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

Quindi utilizzare il seguente codice per installare 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);

Non dimenticare di richiedere le autorizzazioni:

android.Manifest.permission.WRITE_EXTERNAL_STORAGE 
android.Manifest.permission.READ_EXTERNAL_STORAGE

Aggiungere nel AndroidManifest.xml il provider e l'autorizzazione:

<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>

Crea provider di file 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>

Usa sotto esempio di codice:

   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();
            }

        }

    }

    ...

}

Prova questo - Scrivere sul manifesto:

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

scrivere il codice:

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);
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top