Ich bin daran interessiert zu wissen, ob es möglich ist, eine dynamisch heruntergeladene apk programmgesteuert von einer benutzerdefinierten Android-Anwendung zu installieren.
Ich bin daran interessiert zu wissen, ob es möglich ist, eine dynamisch heruntergeladene apk programmgesteuert von einer benutzerdefinierten Android-Anwendung zu installieren.
Antworten:
Sie können ganz einfach einen Play Store-Link oder eine Installationsaufforderung starten:
Intent promptInstall = new Intent(Intent.ACTION_VIEW)
.setDataAndType(Uri.parse("content:///path/to/your.apk"),
"application/vnd.android.package-archive");
startActivity(promptInstall);
oder
Intent goToMarket = new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse("https://play.google.com/store/apps/details?id=com.package.name"));
startActivity(goToMarket);
Sie können .apks jedoch nicht ohne die ausdrückliche Erlaubnis des Benutzers installieren . Nicht, wenn das Gerät und Ihr Programm nicht gerootet sind.
/sdcard
, da dies auf Android 2.2+ und anderen Geräten falsch ist. Verwenden Sie Environment.getExternalStorageDirectory()
stattdessen.
File file = new File(dir, "App.apk");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
startActivity(intent);
Ich hatte das gleiche Problem und nach mehreren Versuchen hat es für mich so geklappt. Ich weiß nicht warum, aber das separate Einstellen von Daten und Typ hat meine Absicht durcheinander gebracht.
setData()
Würde dazu führen, dass der Typparameter entfernt wird. Sie MÜSSEN verwenden, setDataAndType()
wenn Sie Werte für beide angeben möchten. Hier: developer.android.com/reference/android/content/…
Die Lösungen für diese Frage gelten alle für targetSdkVersion
s von 23 und darunter. Für Android N, dh API-Level 24 und höher, funktionieren sie jedoch nicht und stürzen mit der folgenden Ausnahme ab:
android.os.FileUriExposedException: file:///storage/emulated/0/... exposed beyond app through Intent.getData()
Dies liegt daran, dass sich ab Android 24 die Uri
Adressierung der heruntergeladenen Datei geändert hat. Zum Beispiel wurde eine Installationsdatei appName.apk
auf dem primären externe Dateisystem des App mit Paketnamen gespeichert com.example.test
wären als
file:///storage/emulated/0/Android/data/com.example.test/files/appName.apk
für API 23
und unter, während so etwas wie
content://com.example.test.authorityStr/pathName/Android/data/com.example.test/files/appName.apk
für API 24
und darüber.
Weitere Details dazu finden Sie hier und ich werde es nicht durchgehen.
Zur Beantwortung der Frage nach targetSdkVersion
dem 24
und oben, muss man folgendermaßen vor: Fügen Sie folgende zu dem AndroidManifest.xml:
<application
android:allowBackup="true"
android:label="@string/app_name">
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.authorityStr"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths"/>
</provider>
</application>
2. Fügen Sie dem Ordner in src, main, die folgende paths.xml
Datei hinzu :xml
res
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="pathName"
path="pathValue"/>
</paths>
Das pathName
ist , dass oben in dem beispielhaften Inhalt uri Beispiel gezeigt und pathValue
ist der tatsächliche Pfad auf dem System. Es wäre eine gute Idee, ein "." (ohne Anführungszeichen) für pathValue oben, wenn Sie kein zusätzliches Unterverzeichnis hinzufügen möchten.
Schreiben Sie den folgenden Code, um die apk mit dem Namen appName.apk
auf dem primären externen Dateisystem zu installieren :
File directory = context.getExternalFilesDir(null);
File file = new File(directory, fileName);
Uri fileUri = Uri.fromFile(file);
if (Build.VERSION.SDK_INT >= 24) {
fileUri = FileProvider.getUriForFile(context, context.getPackageName(),
file);
}
Intent intent = new Intent(Intent.ACTION_VIEW, fileUri);
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
intent.setDataAndType(fileUri, "application/vnd.android" + ".package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
context.startActivity(intent);
activity.finish();
Es ist auch keine Berechtigung erforderlich, wenn Sie in das private Verzeichnis Ihrer eigenen App im externen Dateisystem schreiben.
Ich habe hier eine AutoUpdate-Bibliothek geschrieben , in der ich die oben genannten verwendet habe.
.authorityStr
nach dem context.getPackageName()
dann sollte es funktionieren.
Nun, ich habe tiefer gegraben und Quellen für die PackageInstaller-Anwendung von Android Source gefunden.
https://github.com/android/platform_packages_apps_packageinstaller
Aus dem Manifest habe ich herausgefunden, dass es eine Erlaubnis erfordert:
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
Der eigentliche Installationsvorgang erfolgt nach Bestätigung
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallAppProgress.class);
String installerPackageName = getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName);
}
startActivity(newIntent);
Ich möchte nur die Tatsache teilen, dass meine APK-Datei in meinem App-Verzeichnis "Daten" gespeichert wurde und dass ich die Berechtigungen für die APK-Datei so ändern musste, dass sie weltweit lesbar ist, damit sie auf diese Weise installiert werden kann, andernfalls auf dem System warf "Parse error: Es gibt ein Problem beim Parsen des Pakets"; Verwenden Sie also eine Lösung von @Horaceman, die Folgendes ergibt:
File file = new File(dir, "App.apk");
file.setReadable(true, false);
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
startActivity(intent);
Dies kann anderen sehr helfen!
Zuerst:
private static final String APP_DIR = Environment.getExternalStorageDirectory().getAbsolutePath() + "/MyAppFolderInStorage/";
private void install() {
File file = new File(APP_DIR + fileName);
if (file.exists()) {
Intent intent = new Intent(Intent.ACTION_VIEW);
String type = "application/vnd.android.package-archive";
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri downloadedApk = FileProvider.getUriForFile(getContext(), "ir.greencode", file);
intent.setDataAndType(downloadedApk, type);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
intent.setDataAndType(Uri.fromFile(file), type);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
getContext().startActivity(intent);
} else {
Toast.makeText(getContext(), "ّFile not found!", Toast.LENGTH_SHORT).show();
}
}
Zweitens: Für Android 7 und höher sollten Sie einen Anbieter im Manifest wie unten definieren!
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="ir.greencode"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/paths" />
</provider>
Drittens: Definieren Sie path.xml im Ordner res / xml wie unten! Ich verwende diesen Pfad für den internen Speicher, wenn Sie ihn in etwas anderes ändern möchten. Es gibt einige Möglichkeiten! Sie können zu diesem Link gehen: FileProvider
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="your_folder_name" path="MyAppFolderInStorage/"/>
</paths>
Viertens: Sie sollten diese Berechtigung im Manifest hinzufügen:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
Ermöglicht einer Anwendung, die Installation von Paketen anzufordern. Apps, die auf APIs mit mehr als 25 abzielen, müssen über diese Berechtigung verfügen, um Intent.ACTION_INSTALL_PACKAGE verwenden zu können.
Bitte stellen Sie sicher, dass die Anbieterbehörden gleich sind!
Ja es ist möglich. Dafür benötigen Sie jedoch das Telefon, um nicht überprüfte Quellen zu installieren. Zum Beispiel macht slideMe das. Ich denke, das Beste, was Sie tun können, ist zu überprüfen, ob die Anwendung vorhanden ist, und eine Absicht für den Android Market zu senden. Sie sollten etwas das URL-Schema für Android Market verwenden.
market://details?id=package.name
Ich weiß nicht genau, wie ich die Aktivität starten soll, aber wenn Sie eine Aktivität mit dieser Art von URL starten. Es sollte den Android-Markt öffnen und Ihnen die Wahl geben, die Apps zu installieren.
Es ist erwähnenswert, dass Sie den DownloadManager
Download an einem externen Ort speichern müssen, z setDestinationInExternalFilesDir(c, null, "<your name here>).apk";
. Die Absicht mit einem Paketarchivtyp scheint das content:
Schema, das beim Herunterladen an einen internen Speicherort verwendet wird, nicht zu mögen, gefällt aber file:
. (Der Versuch, den internen Pfad in ein Dateiobjekt zu verpacken und dann den Pfad file:
abzurufen, funktioniert ebenfalls nicht, obwohl dies zu einer URL führt, da die App die apk nicht analysiert. Sieht so aus, als müsste sie extern sein.)
Beispiel:
int uriIndex = cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI);
String downloadedPackageUriString = cursor.getString(uriIndex);
File mFile = new File(Uri.parse(downloadedPackageUriString).getPath());
Intent promptInstall = new Intent(Intent.ACTION_VIEW)
.setDataAndType(Uri.fromFile(mFile), "application/vnd.android.package-archive")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
appContext.startActivity(promptInstall);
Vergessen Sie nicht, Berechtigungen anzufordern:
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
android.Manifest.permission.READ_EXTERNAL_STORAGE
Fügen Sie in AndroidManifest.xml den Anbieter und die Berechtigung hinzu:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
...
<application>
...
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
Erstellen Sie den XML-Dateianbieter res / xml / provider_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>
Verwenden Sie den folgenden Beispielcode:
public class InstallManagerApk extends AppCompatActivity {
static final String NAME_APK_FILE = "some.apk";
public static final int REQUEST_INSTALL = 0;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// required permission:
// android.Manifest.permission.WRITE_EXTERNAL_STORAGE
// android.Manifest.permission.READ_EXTERNAL_STORAGE
installApk();
}
...
/**
* Install APK File
*/
private void installApk() {
try {
File filePath = Environment.getExternalStorageDirectory();// path to file apk
File file = new File(filePath, LoadManagerApkFile.NAME_APK_FILE);
Uri uri = getApkUri( file.getPath() ); // get Uri for each SDK Android
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData( uri );
intent.setFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK );
intent.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true);
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
intent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, getApplicationInfo().packageName);
if ( getPackageManager().queryIntentActivities(intent, 0 ) != null ) {// checked on start Activity
startActivityForResult(intent, REQUEST_INSTALL);
} else {
throw new Exception("don`t start Activity.");
}
} catch ( Exception e ) {
Log.i(TAG + ":InstallApk", "Failed installl APK file", e);
Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG)
.show();
}
}
/**
* Returns a Uri pointing to the APK to install.
*/
private Uri getApkUri(String path) {
// Before N, a MODE_WORLD_READABLE file could be passed via the ACTION_INSTALL_PACKAGE
// Intent. Since N, MODE_WORLD_READABLE files are forbidden, and a FileProvider is
// recommended.
boolean useFileProvider = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
String tempFilename = "tmp.apk";
byte[] buffer = new byte[16384];
int fileMode = useFileProvider ? Context.MODE_PRIVATE : Context.MODE_WORLD_READABLE;
try (InputStream is = new FileInputStream(new File(path));
FileOutputStream fout = openFileOutput(tempFilename, fileMode)) {
int n;
while ((n = is.read(buffer)) >= 0) {
fout.write(buffer, 0, n);
}
} catch (IOException e) {
Log.i(TAG + ":getApkUri", "Failed to write temporary APK file", e);
}
if (useFileProvider) {
File toInstall = new File(this.getFilesDir(), tempFilename);
return FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID, toInstall);
} else {
return Uri.fromFile(getFileStreamPath(tempFilename));
}
}
/**
* Listener event on installation APK file
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(requestCode == REQUEST_INSTALL) {
if (resultCode == Activity.RESULT_OK) {
Toast.makeText(this,"Install succeeded!", Toast.LENGTH_SHORT).show();
} else if (resultCode == Activity.RESULT_CANCELED) {
Toast.makeText(this,"Install canceled!", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this,"Install Failed!", Toast.LENGTH_SHORT).show();
}
}
}
...
}
Versuche dies
String filePath = cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_FILENAME));
String title = filePath.substring( filePath.lastIndexOf('/')+1, filePath.length() );
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(new File(filePath)), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // without this flag android returned a intent error!
MainActivity.this.startActivity(intent);
Fügen Sie zunächst die folgende Zeile zu AndroidManifest.xml hinzu:
<uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" />
Verwenden Sie dann den folgenden Code, um apk zu installieren:
File sdCard = Environment.getExternalStorageDirectory();
String fileStr = sdCard.getAbsolutePath() + "/MyApp";// + "app-release.apk";
File file = new File(fileStr, "TaghvimShamsi.apk");
Intent promptInstall = new Intent(Intent.ACTION_VIEW).setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
startActivity(promptInstall);
UpdateNode bietet eine API für Android, mit der APK-Pakete in einer anderen App installiert werden können.
Sie können Ihr Update einfach online definieren und die API in Ihre App integrieren - fertig.
Derzeit befindet sich die API im Beta-Status, Sie können jedoch bereits einige Tests selbst durchführen.
Darüber hinaus bietet UpdateNode auch die Anzeige von Nachrichten über das System an - sehr nützlich, wenn Sie Ihren Benutzern etwas Wichtiges mitteilen möchten.
Ich bin Teil des Client-Entwicklerteams und verwende mindestens die Nachrichtenfunktionalität für meine eigene Android-App.
Versuchen Sie dies - Schreiben Sie auf Manifest:
uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions"
Schreiben Sie den Code:
File sdCard = Environment.getExternalStorageDirectory();
String fileStr = sdCard.getAbsolutePath() + "/Download";// + "app-release.apk";
File file = new File(fileStr, "app-release.apk");
Intent promptInstall = new Intent(Intent.ACTION_VIEW).setDataAndType(Uri.fromFile(file),
"application/vnd.android.package-archive");
startActivity(promptInstall);