Можем ли мы установить APK из ContentProvider?
Я работаю над библиотекой, позволяющей пользователям самообновлять, для тех, которые распространяются за пределами Android Market.
Мой первоначальный план состоял в том, чтобы включить код, который будет загружать файл APK во внутреннюю память, а затем установить его там через ContentProvider
и content://
Uri
. Однако, когда я это пробовал, система установки сбрасывала предупреждение "Пропустить dir:" в LogCat и не смогла его фактически установить. Как только я переключился на загрузку APK во внешнее хранилище и с помощью file://
Uri
с установщиком ACTION_VIEW
Intent
, он сработал.
Сообщение "Пропуск dir:", по-видимому, регистрируется parsePackage()
в PackageParser
, что, по-видимому, предполагает, что оно работает с File
. Это означает, что мы не можем использовать значения content://
Uri
.
Кто-нибудь успешно использовал ACTION_VIEW
на application/vnd.android.package-archive
Intent
с content://
Uri
? Если да, был ли какой-то конкретный трюк в настройке ContentProvider
, который заставил его работать?
Спасибо!
Ответы
Ответ 1
Я бы предположил, что это невозможно, поскольку API Java, похоже, не позволяет этого. ContentProvider openFile()
возвращает a ParcelFileDescriptor
, из которого вы можете получить java.io.FileDescriptor
. Затем вы можете использовать этот FileDescriptor
, чтобы открыть либо FileInputStream
, либо FileOutputStream
. К сожалению, вы не можете использовать его для открытия RandomAccessFile
(несмотря на то, что RandomAccessFile
работает над дескрипторами, точно так же, как и другие, конструктор, который вам нужен, просто отсутствует в API).
Поскольку файлы APK являются ZIP файлами, которые должны быть считаны неработоспособными (вам нужно искать до конца, чтобы найти каталог файлов), я предполагаю, что для реализации установки потребуется RandomAccessFile
, поэтому у него не было бы было возможно поддержать случай, который вы пытаетесь реализовать.
Ответ 2
Неверная документация для ACTION_INSTALL_PACKAGE. Он также будет принимать файлы только.
Моим единственным предложением было бы создать копию файла в области файлов приложений, сделать его доступным для чтения и очистить все оставшиеся файлы позже.
Предыдущий неверный ответ. В версии 4.0 и выше есть ACTION_INSTALL_PACKAGE, который примет контент://URI (JavaDoc), но до этого вы ограничены установкой через ACTION_VIEW, который предполагает, что переданный URI является файлом://URI.
Ответ 3
Я согласен с анализом Жюля, и я бы добавил конкретные уточнения:
В PackageInstallerActivity
, который вызывается ACTION_VIEW на apk, это происходит в методе 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, файлом или нет.
Ответ 4
У меня это в одном из моих приложений, которое позволяет мне получить доступ к локальному хранилищу (пользовательский выбор можно выбрать до того, как у вас будет доступ;))
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);