我正在a library to allow apps to self-update处理正在Android Market以外发布的内容。
我的原始计划是包含将APK文件下载到内部存储的代码,然后通过ContentProvider
和content://
Uri
从那里安装。但是,当我尝试这样做时,安装程序系统向LogCat转储了一个“Skipping dir:”警告,但未能实际安装它。一旦我切换到将APK下载到外部存储并使用file://
Uri
ACTION_VIEW
安装程序Intent
,就可以了。
parsePackage()
中的PackageParser
似乎记录了“跳过目录:”消息,这似乎假设它正在使用File
。这表明我们无法使用content://
Uri
值。
是否有人在ACTION_VIEW
application/vnd.android.package-archive
Intent
content://
成功使用了Uri
?如果是这样,设置ContentProvider
是否有一些特定的技巧可以使它工作?
谢谢!
答案 0 :(得分:27)
ACTION_INSTALL_PACKAGE的文档不正确。它也只接受文件。
因此,我唯一的建议是在应用程序文件区域中创建该文件的副本,使其具有全局可读性,并在以后清理任何遗留文件。
以前的错误答案:在4.0及以上版本中有一个 ACTION_INSTALL_PACKAGE将接受content:// URI (JavaDoc),但在此之前,您只能安装via 假定传递的URI的ACTION_VIEW是file:// URI。
答案 1 :(得分:14)
我认为这是不可能的,因为Java API似乎不允许它。 ContentProvider的openFile()
返回ParcelFileDescriptor
,您可以从java.io.FileDescriptor
获取FileDescriptor
。然后,您可以使用此FileInputStream
打开FileOutputStream
或RandomAccessFile
。遗憾的是,您无法使用它来打开RandomAccessFile
(尽管RandomAccessFile
对描述符的工作方式与其他描述符相同,但您需要的构造函数在API中缺失)。
由于APK文件是ZIP文件,必须不按顺序读取(你必须寻找到最后才能找到文件目录),我认为安装的实现需要{{1}},所以它会不可能支持你试图实施的案例。
答案 2 :(得分:6)
我同意Jules的分析,并且我会添加具体的精确度:
在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
,用于测试Uri中给出的路径是否为文件。
答案 3 :(得分:1)
我在我的一个应用程序中有这个,它允许我访问本地存储(在你去我之前可以选择用户首选项;)
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);