我们可以从ContentProvider安装APK吗?

时间:2012-03-09 16:34:03

标签: android

我正在a library to allow apps to self-update处理正在Android Market以外发布的内容。

我的原始计划是包含将APK文件下载到内部存储的代码,然后通过ContentProvidercontent:// 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是否有一些特定的技巧可以使它工作?

谢谢!

4 个答案:

答案 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打开FileOutputStreamRandomAccessFile。遗憾的是,您无法使用它来打开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);