我们可以从ContentProvider安装APK吗?
我正在开发一个库,以允许应用程序自行更新 ,以便在Android Market之外进行分发。
我原来的计划是包括将APK文件下载到内部存储的代码,然后通过ContentProvider
和content://
Uri
从那里安装。 但是,当我尝试的时候,安装程序系统倾倒LogCat的“跳过目录:”警告,并没有实际安装它。 一旦我切换到APK下载到外部存储,并使用ACTION_VIEW
安装程序Intent
使用file://
Uri
,它的工作。
“跳过目录:”消息似乎由PackageParser
的parsePackage()
logging,似乎认为它正在使用一个File
。 这表明我们不能使用content://
Uri
值。
有没有人成功地使用ACTION_VIEW
application/vnd.android.package-archive
Intent
与content://
application/vnd.android.package-archive
Uri
? 如果是这样的话,是否有一些特定的技巧来设置ContentProvider
使其工作?
谢谢!
我会认为这是不可能的,因为Java API似乎不允许它。 ContentProvider的openFile()
返回一个ParcelFileDescriptor
,从中你可以得到一个java.io.FileDescriptor
。 然后,您可以使用此FileDescriptor
打开FileInputStream
或FileOutputStream
。 不幸的是,你不能用它来打开一个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.parsePackage
find,它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);