我们可以从ContentProvider安装APK吗?

我正在开发一个库,以允许应用程序自行更新 ,以便在Android Market之外进行分发。

我原来的计划是包括将APK文件下载到内部存储的代码,然后通过ContentProvidercontent:// Uri从那里安装。 但是,当我尝试的时候,安装程序系统倾倒LogCat的“跳过目录:”警告,并没有实际安装它。 一旦我切换到APK下载到外部存储,并使用ACTION_VIEW安装程序Intent使用file:// Uri ,它的工作。

“跳过目录:”消息似乎由PackageParserparsePackage()logging,似乎认为它正在使用一个File 。 这表明我们不能使用content:// Uri值。

有没有人成功地使用ACTION_VIEW application/vnd.android.package-archive Intentcontent:// application/vnd.android.package-archive Uri ? 如果是这样的话,是否有一些特定的技巧来设置ContentProvider使其工作?

谢谢!

我会认为这是不可能的,因为Java API似乎不允许它。 ContentProvider的openFile()返回一个ParcelFileDescriptor ,从中你可以得到一个java.io.FileDescriptor 。 然后,您可以使用此FileDescriptor打开FileInputStreamFileOutputStream 。 不幸的是,你不能用它来打开一个RandomAccessFile (尽pipeRandomAccessFile在描述符上的作用与其他描述符相同,你需要的构造函数只是从API中缺less)。

由于APK文件是ZIP文件,必须按顺序读取(您必须到最后find文件目录),我假设安装的实现将需要一个RandomAccessFile ,所以它不可能支持你正试图实施的情况。

ACTION_INSTALL_PACKAGE的文档不正确。 它也只会接受文件。

因此,我唯一的build议就是在应用程序文件区创build一个文件副本,使其具有世界可读性,并在以后清理所有剩余的文件。

以前不正确的答案 :在4.0及以上版本中有一个ACTION_INSTALL_PACKAGE,它将接受一个content:// URI( JavaDoc ),但在此之前,你只能通过ACTION_VIEW进行安装,假设传递的URI是一个文件: / URI。

我同意朱尔斯的分析,我会join具体的精度:

PackageInstallerActivity ,在apk上通过ACTION_VIEW调用,在onCreate()方法中有:

 315 String apkPath = mPackageURI.getPath(); 316 File apkFile = new File(apkPath); 

在此之前,这个来自PackageUtil方法被调用:

 73 public static PackageParser.Package getPackageInfo(Uri packageURI) { 74 final String archiveFilePath = packageURI.getPath(); 75 PackageParser packageParser = new PackageParser(archiveFilePath); 76 File sourceFile = new File(archiveFilePath); 77 DisplayMetrics metrics = new DisplayMetrics(); 78 metrics.setToDefaults(); 79 return packageParser.parsePackage(sourceFile, archiveFilePath, metrics, 0); 80 } 

所有这一切往往确认PackageManager确实只需要File Uris。

你有的日志, Skipping dir:packageParser.parsePackagefind,它testingUri中给出​​的path是否是一个文件。

我在我的应用程序之一,允许我访问本地存储(用户偏好可选之前,你去我);)

 import java.io.*; import android.content.*; import android.database.*; import android.net.*; import android.os.*; import android.preference.PreferenceManager; import android.util.Log; public class LocalFileContentProvider extends ContentProvider { private static final String URI_PREFIX = "content://your.content.provider.as.per.manifest"; public static String constructUri(String url) { Uri uri = Uri.parse(url); return uri.isAbsolute() ? url : URI_PREFIX + url; } @Override public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException { SharedPreferences app_preferences = PreferenceManager.getDefaultSharedPreferences(getContext()); boolean allowLocal = app_preferences.getBoolean("allowLocalFiles", false); if (allowLocal) { try { File file = new File(uri.getPath()); if (file.isDirectory()) return null; ParcelFileDescriptor parcel = ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY); return parcel; } catch (Exception e) { return null; } } else { return null; } } @Override public boolean onCreate() { return true; } @Override public int delete(Uri uri, String s, String[] as) { throw new UnsupportedOperationException("Not supported by this provider"); } @Override public String getType(Uri uri) { throw new UnsupportedOperationException("Not supported by this provider"); } @Override public Uri insert(Uri uri, ContentValues contentvalues) { throw new UnsupportedOperationException("Not supported by this provider"); } @Override public Cursor query(Uri uri, String[] as, String s, String[] as1, String s1) { throw new UnsupportedOperationException("Not supported by this provider"); } @Override public int update(Uri uri, ContentValues contentvalues, String s, String[] as) { throw new UnsupportedOperationException("Not supported by this provider"); } } 

我的清单包含

 <provider android:name="it.automated.android.kiosk.se.LocalFileContentProvider" android:authorities="it.automated" /> 

然后开始安装(或动作)

 Intent viewIntent = new Intent(Intent.ACTION_VIEW); viewIntent.setDataAndType(Uri.parse(url), mimeType);