在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中resxml文件夹中:

 <?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,如果你不想添加任何额外的子目录。

  1. 编写以下代码以在主外部文件系统上安装名称为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