在Android上编程安装应用程序
我很想知道是否有可能以编程方式安装从自定义Android应用程序dynamic下载的apk。
您可以轻松启动市场链接或安装提示:
Intent promptInstall = new Intent(Intent.ACTION_VIEW) .setDataAndType(Uri.parse("file:///path/to/your.apk"), "application/vnd.android.package-archive"); startActivity(promptInstall);
资源
Intent goToMarket = new Intent(Intent.ACTION_VIEW) .setData(Uri.parse("market://details?id=com.package.name")); startActivity(goToMarket);
资源
但是,您不能在没有用户明确权限的情况下安装.apks。 除非设备和程序是根植的。
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);
我遇到了同样的问题,经过几次尝试,我就这样解决了这个问题。 我不知道为什么,但分别设置数据和types搞砸了我的意图。
那么,我深入挖掘,findAndroid Source的PackageInstaller应用程序的来源。
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);
提供给这个问题的解决scheme都适用于23和以下的targetSdkVersion
。 对于Android N,即API级别24以及更高版本,但是,它们不工作并因以下exception而崩溃:
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
及以上。
更多的细节可以在这里find,我不会去通过它。
要回答24
和以上的targetSdkVersion
问题,必须按照以下步骤操作:将以下内容添加到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
文件添加到src,main中res
的xml
文件夹中:
<?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
是上面示例性内容uri示例中显示的path, pathValue
是系统上的实际path。 放一个“。”是个好主意。 (不带引号)为上面的pathValue,如果你不想添加任何额外的子目录。
-
编写以下代码以在主外部文件系统上安装名称为
appName.apk
的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();
在外部文件系统上写入您自己的应用程序的专用目录时,也不需要权限。
我写了一个自动更新库,在这里我已经使用了上面的内容。
我只是想分享我的apk文件被保存到我的应用程序“数据”目录,我需要更改apk文件的权限是世界可读的,以便允许它以这种方式安装,否则系统抛出“parsing错误:parsing包有问题”; 所以使用来自@Horaceman的解决scheme:
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);
是的,这是可能的。 但为此,您需要手机来安装未经validation的来源。 例如,slideMe可以做到这一点。 我认为您可以做的最好的事情是检查应用程序是否存在,并发送Android Market的意图。 你应该使用Android Market的urlscheme。
market://details?id=package.name
我不知道如何开始活动,但如果你开始一个这样的url的活动。 它应该打开安卓市场,并给你select安装应用程序。
值得注意的是,如果您使用DownloadManager
启动下载,请务必将其保存到外部位置,例如setDestinationInExternalFilesDir(c, null, "<your name here>).apk";
。 包 – 档案types的意图似乎不喜欢用于下载到内部位置的content:
scheme,但是像file:
。 (尝试将内部path封装到File对象中,然后得到path也不起作用,即使它导致了file:
url,因为应用程序不会parsingapk;看起来它必须是外部的。
例:
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);
只是一个扩展,如果任何人需要一个库,那么这可能会有所帮助 感谢拉格哈夫
另一个解决scheme不需要硬编码接收应用程序,因此更安全:
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.setData( Uri.fromFile(new File(pathToApk)) ); startActivity(intent);
UpdateNode提供了一个Android的API来从另一个应用程序中安装APK包。
您可以在线定义更新,并将API集成到您的应用程序 – 就是这样。
目前API处于Beta状态,但您已经可以自己做一些testing。
除此之外,UpdateNode还提供了通过系统显示消息 – 如果你想告诉你的用户重要的东西非常有用。
我是客户端开发团队的一员,至less使用我自己的Android应用程序的消息function。
在这里看到如何整合API的描述
尝试这个
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);
试试这个 – 在Manifest上写:
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);
遵循这些步骤:1 – 将以下内容添加到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 – 将以下paths.xml文件添加到src,main中的res文件夹(如果不存在,创build它)
<?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是上面示例性内容uri示例中显示的path,pathValue是系统上的实际path。 放一个“。”是个好主意。 在上面的pathValue如果你不想添加任何额外的子目录。
3 – 编写下面的代码来运行你的Apk文件:
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); }
并感谢阿里Nemati Hayati成为第一个写解决schemehttps://stackoverflow.com/users/4514796/ali-nemati-hayati