Pregunta

Estoy interesado en saber si es posible instalar mediante programación una apk descargada dinámicamente desde una aplicación personalizada de Android.

¿Fue útil?

Solución

Puede iniciar fácilmente un enlace de juego tienda o una instalación rápida:

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

Sin embargo, no se puede instalar sin .apks permiso explícito del usuario ; no menos que el dispositivo y el programa tiene sus raíces.

Otros consejos

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

Yo tenía el mismo problema y después de varios intentos, funcionó para mí de esta manera. No sé por qué, pero datos de configuración y el tipo de la pata separado mi intención.

Las soluciones aportadas a esta pregunta son aplicables a targetSdkVersion s de 23 y por debajo. Para Android N, es decir, nivel de API 24, y por encima, sin embargo, no trabajan y choque con la siguiente excepción:

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

Esto es debido al hecho de que a partir del androide 24, el Uri para abordar el archivo descargado ha cambiado. Por ejemplo, un archivo de instalación llamado appName.apk almacenada en el sistema de archivos externo principal de la aplicación con el nombre del paquete com.example.test sería tan

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

para API 23 y por debajo, mientras que algo como

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

para API 24 y superior.

Más detalles sobre esto se puede encontrar aquí y yo no voy a ir a través de él.

Para responder a la pregunta de targetSdkVersion de 24 y por encima, uno tiene que seguir estos pasos: Añadir lo siguiente a la 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. Agregue el siguiente archivo a la carpeta paths.xml xml en res en src, principal:

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

El pathName es que se muestra en el ejemplo uri contenido ejemplar de arriba y pathValue es la ruta real en el sistema. Sería una buena idea poner un "" (Sin comillas) para valor_ruta en el anterior si no desea añadir cualquier subdirectorio adicional.

  1. Escribir el código siguiente para instalar el apk con el nombre appName.apk en el sistema de archivos externo primario:

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

No permission también es necesario cuando se escribe en el directorio privado de su propia aplicación en el sistema de archivos externo.

He escrito una biblioteca de actualización automática aquí en el que he utilizado lo anterior.

Bueno, cavó más profundo, y se encontró fuentes de aplicación desde Android PackageInstaller Fuente.

https://github.com/android/platform_packages_apps_packageinstaller

A partir de manifiesto encontré que requieren autorización:

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

Y el propio proceso de instalación se produce después de la confirmación

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

Sólo quiero compartir el hecho de que mi archivo apk se guarda en mi aplicación directorio "datos" y que tenía que cambiar los permisos en el archivo apk ser leer todo el mundo con el fin de que pueda ser instalado de esa manera, de lo contrario el sistema estaba lanzando "error de análisis: Hay un problema de análisis del paquete"; así que usar solución de @Horaceman que hace que:

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

Otra solución que no no requiere para codificar la aplicación de recepción y, por tanto, que es más seguro:

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

Sí, es posible. Pero para eso se necesita el teléfono para instalar fuentes no verificadas. Por ejemplo, SlideMe hace eso. Creo que lo mejor que puede hacer es comprobar si la aplicación está presente y enviar una intención para el Android Market. usted debe usar algo el esquema de URL para el mercado androide.

market://details?id=package.name

No sé exactamente cómo iniciar la actividad, pero si se inicia una actividad con ese tipo de URL. Se debe abrir el mercado androide y le dará la opción de instalar las aplicaciones.

Vale la pena señalar que si se utiliza el DownloadManager para dar comienzo a la descarga, asegúrese de guardarlo en una ubicación externa, por ejemplo, setDestinationInExternalFilesDir(c, null, "<your name here>).apk";. La intención con un tipo de paquete de archivado y no parece que como el esquema de content: utilizado con descargas a un lugar interno, pero sí le gusta file:. (Tratando de envolver la ruta interna en un objeto File y luego conseguir el camino no funciona bien, a pesar de que resulta en una url file:, ya que la aplicación no será analizar el apk; parece que deben ser externos.)

Ejemplo:

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

¡Esto puede ayudar mucho a otros!

Primero:

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

Segundo: Para Android 7 y superiores, debes definir un proveedor en el manifiesto como se muestra a continuación.

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

Tercero: ¡Defina path.xml en la carpeta res/xml como se muestra a continuación!estoy usando esto camino Para el almacenamiento interno, si desea cambiarlo por otra cosa, ¡hay varias formas!Puedes ir a este enlace:Proveedor de archivos

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

Adelante: Debes agregar este permiso en el manifiesto:

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

¡Asegúrese de que las autoridades del proveedor sean las mismas!


Sólo una extensión, si alguien necesita una biblioteca continuación, esta ayuda puede . Gracias a Raghav

Probar

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 proporciona una API para Android APK para instalar paquetes desde dentro de otra aplicación.
Usted puede simplemente definir su actualización en línea e integrar la API en su App - eso es todo.
Actualmente la API se encuentra en estado beta, pero ya puedes hacer algunas pruebas a ti mismo.
Además de eso, las ofertas UpdateNode también la visualización de mensajes a través del sistema - muy útil si usted quiere decirle algo importante a sus usuarios.
Soy parte del equipo de desarrollo del cliente y estoy usando al menos la funcionalidad de mensajes para mi propio Android App.

Vea aquí una descripción de cómo integrar la API

Primero se debe agregar la siguiente línea al AndroidManifest.xml:

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

A continuación, utilice el código siguiente para instalar 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);

No se olvide de pedir permisos:

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

Añadir en AndroidManifest.xml el proveedor y el permiso:

<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 proveedor de archivo 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>

Uso de abajo ejemplo de código:

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

        }

    }

    ...

}

Probar - Escribir en el Manifiesto:

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

Escribir el código:

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);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top