Мне интересно знать, возможно ли программно установить динамически загруженный apk из пользовательского приложения для Android.
Мне интересно знать, возможно ли программно установить динамически загруженный apk из пользовательского приложения для Android.
Ответы:
Вы можете легко запустить ссылку на магазин Play Play или приглашение к установке:
Intent promptInstall = new Intent(Intent.ACTION_VIEW)
.setDataAndType(Uri.parse("content:///path/to/your.apk"),
"application/vnd.android.package-archive");
startActivity(promptInstall);
или
Intent goToMarket = new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse("https://play.google.com/store/apps/details?id=com.package.name"));
startActivity(goToMarket);
Однако вы не можете установить .apks без явного разрешения пользователя ; нет, если устройство и ваша программа не рутированы.
/sdcard
жесткий код, поскольку это неправильно на Android 2.2+ и других устройствах. Используйте Environment.getExternalStorageDirectory()
вместо этого.
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);
У меня была такая же проблема, и после нескольких попыток у меня все получилось. Я не знаю почему, но установка данных и типа по отдельности провалила мои намерения.
setData()
, вызывает удаление параметра типа. Вы ДОЛЖНЫ использовать, setDataAndType()
если хотите дать значения для обоих. Здесь: developer.android.com/reference/android/content/…
Решения, предоставленные на этот вопрос, все применимы к targetSdkVersion
с 23 и ниже. Однако для Android N, то есть уровня API 24 и выше, они не работают и аварийно завершают работу со следующим исключением:
android.os.FileUriExposedException: file:///storage/emulated/0/... exposed beyond app through Intent.getData()
Это связано с тем, что, начиная с Android 24, Uri
изменился адрес для загруженного файла. Например, установочный файл с именем, appName.apk
хранящимся в основной внешней файловой системе приложения с именем пакета, com.example.test
будет иметь вид
file:///storage/emulated/0/Android/data/com.example.test/files/appName.apk
для API 23
и ниже, тогда как что-то вроде
content://com.example.test.authorityStr/pathName/Android/data/com.example.test/files/appName.apk
за API 24
и выше.
Более подробную информацию об этом можно найти здесь, и я не собираюсь проходить через это.
Чтобы ответить на вопрос targetSdkVersion
о 24
и выше, необходимо выполнить следующие шаги: Добавьте следующее в 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. Добавьте следующий paths.xml
файл в xml
папку res
в 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
то, что показано в приведенном выше примере содержимого и pathValue
является фактическим путем в системе. Было бы неплохо поставить "." (без кавычек) для pathValue в приведенном выше, если вы не хотите добавлять какие-либо дополнительные подкаталоги.
Напишите следующий код для установки apk с именем appName.apk
в основной внешней файловой системе:
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();
Разрешение также не требуется при записи в личный каталог вашего приложения во внешней файловой системе.
Я написал здесь библиотеку автообновления, в которой я использовал вышеизложенное.
.authorityStr
после, context.getPackageName()
это должно работать.
Ну, я покопался глубже и нашел источники приложения PackageInstaller из Android Source.
https://github.com/android/platform_packages_apps_packageinstaller
Из декларации я обнаружил, что для этого требуется разрешение:
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
И фактический процесс установки происходит после подтверждения
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);
Я просто хочу поделиться тем фактом, что мой apk-файл был сохранен в директории «Data» моего приложения и что мне нужно было изменить разрешения для apk-файла, чтобы они были доступны для чтения всем пользователям, чтобы разрешить его установку таким образом, в противном случае система выдает «Ошибка синтаксического анализа: существует проблема с анализом пакета»; так что используя решение @Horaceman, которое делает:
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);
Это может сильно помочь другим!
Первый:
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();
}
}
Второе: для Android 7 и выше вы должны определить провайдера в манифесте, как показано ниже!
<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>
Третье: определите path.xml в папке res / xml, как показано ниже! Я использую этот путь для внутреннего хранения, если вы хотите изменить его на что-то еще, есть несколько способов! Вы можете перейти по этой ссылке: 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: Вы должны добавить это разрешение в манифест:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
Позволяет приложению запрашивать установку пакетов. Приложения с таргетингом на API более 25 должны иметь это разрешение для использования Intent.ACTION_INSTALL_PACKAGE.
Пожалуйста, убедитесь, что полномочия провайдера одинаковы!
Да, это возможно. Но для этого нужен телефон для установки непроверенных источников. Например, slideMe делает это. Я думаю, что лучшее, что вы можете сделать, это проверить, присутствует ли приложение, и отправить намерение в Android Market. Вы должны использовать что-то схему URL для Android Market.
market://details?id=package.name
Я не знаю точно, как начать задание, но если вы начнете занятие с такого рода URL. Это должно открыть Android Market и дать вам выбор для установки приложений.
Стоит отметить, что если вы используете DownloadManager
для запуска загрузки, обязательно сохраните ее во внешнем местоположении, например setDestinationInExternalFilesDir(c, null, "<your name here>).apk";
. Намерение с типом пакета-архива, похоже, не похоже на content:
схему, используемую при загрузке во внутреннее местоположение, но ему нравится file:
. (Попытка обернуть внутренний путь в объект File и затем получить путь также не работает, даже если это приводит к file:
URL-адресу, поскольку приложение не анализирует apk; похоже, оно должно быть внешним.)
Пример:
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);
Не забудьте запросить разрешения:
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
android.Manifest.permission.READ_EXTERNAL_STORAGE
Добавьте в AndroidManifest.xml провайдера и разрешение:
<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>
Создать провайдера 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>
Используйте приведенный ниже пример кода:
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();
}
}
}
...
}
попробуй это
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);
Сначала добавьте следующую строку в AndroidManifest.xml:
<uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" />
Затем используйте следующий код для установки 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 предоставляет API для Android для установки пакетов APK из другого приложения.
Вы можете просто определить свое обновление онлайн и интегрировать API в свое приложение - и все.
В настоящее время API находится в бета-состоянии, но вы уже можете выполнить некоторые тесты самостоятельно.
Кроме того, UpdateNode предлагает также отображение сообщений через систему - довольно полезно, если вы хотите рассказать что-то важное для ваших пользователей.
Я являюсь частью команды разработчиков клиента и использую по крайней мере функциональность сообщений для моего собственного приложения для Android.
Попробуйте это - напишите на Манифесте:
uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions"
Напишите код:
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);
В Android Oreo и выше версии мы должны подходить к различным методам, чтобы установить apk программно.
private void installApkProgramatically() {
try {
File path = activity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);
File file = new File(path, filename);
Uri uri;
if (file.exists()) {
Intent unKnownSourceIntent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(Uri.parse(String.format("package:%s", activity.getPackageName())));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
if (!activity.getPackageManager().canRequestPackageInstalls()) {
startActivityForResult(unKnownSourceIntent, Constant.UNKNOWN_RESOURCE_INTENT_REQUEST_CODE);
} else {
Uri fileUri = FileProvider.getUriForFile(activity.getBaseContext(), activity.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);
alertDialog.dismiss();
}
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Intent intent1 = new Intent(Intent.ACTION_INSTALL_PACKAGE);
uri = FileProvider.getUriForFile(activity.getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", file);
activity.grantUriPermission("com.abcd.xyz", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.grantUriPermission("com.abcd.xyz", uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent1.setDataAndType(uri,
"application/*");
intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent1.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivity(intent1);
} else {
Intent intent = new Intent(Intent.ACTION_VIEW);
uri = Uri.fromFile(file);
intent.setDataAndType(uri,
"application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
} else {
Log.i(TAG, " file " + file.getPath() + " does not exist");
}
} catch (Exception e) {
Log.i(TAG, "" + e.getMessage());
}
}
В версии Oreo и выше нам нужно разрешение на установку неизвестного ресурса. так что в результате деятельности вы должны проверить результат на разрешение
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case Constant.UNKNOWN_RESOURCE_INTENT_REQUEST_CODE:
switch (resultCode) {
case Activity.RESULT_OK:
installApkProgramatically();
break;
case Activity.RESULT_CANCELED:
//unknown resouce installation cancelled
break;
}
break;
}
}