Estoy interesado en saber si es posible instalar mediante programación un apk descargado dinámicamente desde una aplicación personalizada de Android.
Estoy interesado en saber si es posible instalar mediante programación un apk descargado dinámicamente desde una aplicación personalizada de Android.
Respuestas:
Puede iniciar fácilmente un enlace de Play Store o un mensaje de instalación:
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 puede instalar .apks sin el permiso explícito del usuario ; no a menos que el dispositivo y su programa estén rooteados.
/sdcard
, ya que está mal en Android 2.2+ y otros dispositivos. Usar en su Environment.getExternalStorageDirectory()
lugar.
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);
Tuve el mismo problema y después de varios intentos, funcionó para mí de esta manera. No sé por qué, pero configurar los datos y el tipo por separado arruinó mi intención.
setData()
causaría la eliminación del parámetro tipo. DEBE usar setDataAndType()
si desea dar valores para ambos. Aquí: developer.android.com/reference/android/content/…
Las soluciones proporcionadas a esta pregunta son aplicables a los targetSdkVersion
s de 23 y menos. Sin embargo, para Android N, es decir, el nivel de API 24 y superior, no funcionan y se bloquean con la siguiente excepción:
android.os.FileUriExposedException: file:///storage/emulated/0/... exposed beyond app through Intent.getData()
Esto se debe al hecho de que a partir de Android 24, la Uri
dirección del archivo descargado ha cambiado. Por ejemplo, un archivo de instalación denominado appName.apk
almacenado en el sistema de archivos externo primario de la aplicación con el nombre del paquete com.example.test
sería
file:///storage/emulated/0/Android/data/com.example.test/files/appName.apk
para API 23
y debajo, mientras que algo como
content://com.example.test.authorityStr/pathName/Android/data/com.example.test/files/appName.apk
para API 24
y encima.
Más detalles sobre esto se pueden encontrar aquí y no voy a pasar por ello.
Para responder a la pregunta targetSdkVersion
de 24
y más arriba, uno tiene que seguir estos pasos: Agregue lo siguiente 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. Agregue el siguiente paths.xml
archivo a la xml
carpeta res
en 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>
El pathName
es el que se muestra en el ejemplo de contenido de uri anterior y pathValue
es la ruta real en el sistema. Sería una buena idea poner un "." (sin comillas) para pathValue en lo anterior si no desea agregar ningún subdirectorio adicional.
Escriba el siguiente código 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();
Tampoco se necesita permiso al escribir en el directorio privado de su propia aplicación en el sistema de archivos externo.
He escrito una biblioteca de AutoUpdate aquí en la que he usado lo anterior.
.authorityStr
después, context.getPackageName()
entonces debería funcionar.
Bueno, profundicé y encontré fuentes de la aplicación PackageInstaller de Android Source.
https://github.com/android/platform_packages_apps_packageinstaller
De manifiesto encontré que requiere permiso:
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
Y el proceso real de instalación ocurre 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);
Solo quiero compartir el hecho de que mi archivo apk se guardó en el directorio "Datos" de mi aplicación y que necesitaba cambiar los permisos en el archivo apk para que fuera legible en todo el mundo para permitir que se instale de esa manera, de lo contrario, el sistema estaba lanzando "Error de análisis: hay un problema al analizar el paquete"; entonces usando la solución de @Horaceman que hace:
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);
¡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 superior, debe definir un proveedor en manifiesto como 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 esta ruta para el almacenamiento interno si desea cambiarla a otra cosa, ¡hay algunas maneras! Puedes ir a este enlace: 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>
Adelante: debe agregar este permiso en manifiesto:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
Permite que una aplicación solicite paquetes de instalación. Las aplicaciones dirigidas a API mayores de 25 deben tener este permiso para usar Intent.ACTION_INSTALL_PACKAGE.
¡Asegúrese de que las autoridades del proveedor sean las mismas!
Si es posible. Pero para eso necesita el teléfono para instalar fuentes no verificadas. Por ejemplo, slideMe hace eso. Creo que lo mejor que puede hacer es verificar si la aplicación está presente y enviar una intención para Android Market. deberías usar algo del esquema de URL para Android Market.
market://details?id=package.name
No sé exactamente cómo iniciar la actividad, pero si comienzas una actividad con ese tipo de URL. Debería abrir el Android Market y darle la opción de instalar las aplicaciones.
Vale la pena señalar que si utiliza el DownloadManager
para iniciar su 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-archivo no parece gustarle el content:
esquema utilizado con las descargas a una ubicación interna, pero sí file:
. (Intentar ajustar la ruta interna en un objeto File y luego obtener la ruta tampoco funciona, a pesar de que resulta en unfile:
URL, ya que la aplicación no analizará el apk; parece que debe ser externo).
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);
No olvides solicitar permisos:
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
android.Manifest.permission.READ_EXTERNAL_STORAGE
Agregue 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>
Crear proveedor de archivos 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>
Utilice el siguiente código de ejemplo:
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();
}
}
}
...
}
prueba esto
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);
Primero agregue la siguiente línea a AndroidManifest.xml:
<uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" />
Luego use el siguiente código 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);
UpdateNode proporciona una API para Android para instalar paquetes APK desde dentro de otra aplicación.
Simplemente puede definir su actualización en línea e integrar la API en su aplicación, eso es todo.
Actualmente, la API está en estado Beta, pero ya puede hacer algunas pruebas usted mismo.
Además de eso, UpdateNode también ofrece mostrar mensajes a través del sistema, bastante útil si desea contar algo importante para sus usuarios.
Soy parte del equipo de desarrollo del cliente y estoy usando al menos la funcionalidad de mensajes para mi propia aplicación de Android.
Prueba esto: escribe en el manifiesto:
uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions"
Escribe 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);