Özel bir Android uygulamasından dinamik olarak indirilen bir apk'yi programlı olarak kurmanın mümkün olup olmadığını bilmekle ilgileniyorum.
Özel bir Android uygulamasından dinamik olarak indirilen bir apk'yi programlı olarak kurmanın mümkün olup olmadığını bilmekle ilgileniyorum.
Yanıtlar:
Bir play store bağlantısını veya bir yükleme istemini kolayca başlatabilirsiniz:
Intent promptInstall = new Intent(Intent.ACTION_VIEW)
.setDataAndType(Uri.parse("content:///path/to/your.apk"),
"application/vnd.android.package-archive");
startActivity(promptInstall);
veya
Intent goToMarket = new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse("https://play.google.com/store/apps/details?id=com.package.name"));
startActivity(goToMarket);
Ancak, .apks'i kullanıcının açık izni olmadan yükleyemezsiniz ; cihaz ve program köklü değilse.
/sdcard, çünkü bu Android 2.2 ve diğer cihazlarda yanlıştır. Environment.getExternalStorageDirectory()Bunun yerine kullanın .
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);
Aynı sorunu yaşadım ve birkaç denemeden sonra benim için bu şekilde çalıştı. Nedenini bilmiyorum, ancak veri ve türü ayrı ayrı ayarlamak niyetimi bozdu.
setData()type parametresinin kaldırılmasına neden olur. Her setDataAndType()ikisi için de değer vermek istiyorsanız KULLANMALISINIZ . Burada: developer.android.com/reference/android/content/…
Bu soruya sunulan çözümlerin tümü targetSdkVersion23 ve altı için geçerlidir. Bununla birlikte, Android N, yani API düzey 24 ve üstü için, aşağıdaki İstisna ile çalışmaz ve çökmezler:
android.os.FileUriExposedException: file:///storage/emulated/0/... exposed beyond app through Intent.getData()
Bu, Android 24'ten başlayarak Uri, indirilen dosyanın adreslenmesi için değişmiş olmasından kaynaklanmaktadır . Örneğin appName.apk, uygulamanın paket adıyla birincil harici dosya sisteminde depolanan bir yükleme dosyası com.example.testşu şekilde olur:
file:///storage/emulated/0/Android/data/com.example.test/files/appName.apk
için API 23ve aşağıda gibi bir şey oysa
content://com.example.test.authorityStr/pathName/Android/data/com.example.test/files/appName.apk
için API 24 ve yukarıda.
Bununla ilgili daha fazla ayrıntıyı burada bulabilirsinizBununla ve bunun üzerinden geçmeyeceğim.
İçin soruya cevap vermek için targetSdkVersionbir 24ve üzeri, tek adımları takip etmelidir: AndroidManifest.xml için aşağıdaki ekleyin:
<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. src, main paths.xmliçindeki xmlklasöre aşağıdaki dosyayı ekleyin 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>
pathNameYukarıdaki örnek içerik uri örnekte gösterildiği olduğu vepathValue sistem üzerinde gerçek yol. Bir "." Koymak iyi bir fikir olabilir. (tırnak işaretleri olmadan) yukarıdaki herhangi bir ek alt dizin eklemek istemiyorsanız pathValue için.
appName.apkBirincil harici dosya sistemine apk ile adı yüklemek için aşağıdaki kodu yazın :
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();Harici dosya sistemindeki kendi uygulamanızın özel dizinine yazarken de izin gerekmez.
Burada yukarıda kullandığım bir AutoUpdate kütüphanesi yazdım .
.authorityStrsonra eklemelisiniz context.getPackageName().
Daha derinlere inip Android Source'dan PackageInstaller uygulaması kaynakları buldum.
https://github.com/android/platform_packages_apps_packageinstaller
Manifest'ten izin gerektirdiğini gördüm:
<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
Ve gerçek kurulum işlemi onaylandıktan sonra gerçekleşir
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);
Ben sadece benim apk dosya benim app "Veri" dizinine kaydedildi ve bu şekilde yüklenmesini sağlamak için apk dosyasında dünya okunabilir olması için izinleri değiştirmek gerektiğini gerçeği paylaşmak istiyorum, aksi takdirde sistem "Ayrıştırma hatası: Paketi Ayrıştırmada Bir Sorun Var"; yani @Horaceman'ın çözümünü kullanarak:
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);
Bu başkalarına çok yardımcı olabilir!
İlk:
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();
}
}
İkinci: android 7 ve üstü için aşağıdaki gibi manifest bir sağlayıcı tanımlamanız gerekir!
<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>
Üçüncü olarak: res / xml klasöründe path.xml dosyasını aşağıdaki gibi tanımlayın! İç depolama için bu yolu başka bir şeye değiştirmek istiyorsanız birkaç yol var! Bu bağlantıya gidebilirsiniz: 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>
Dördüncü: Bu izni bildirimde eklemelisiniz:
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
Uygulamaya, paketlerin yüklenmesini isteme izni verir. 25'ten büyük hedefleme API'ları, Intent.ACTION_INSTALL_PACKAGE kullanmak için bu izne sahip olmalıdır.
Lütfen sağlayıcı yetkililerin aynı olduğundan emin olun!
Evet mümkün. Ancak bunun için doğrulanmamış kaynakları kurmak için telefona ihtiyacınız var. Örneğin, slideMe bunu yapar. Yapabileceğiniz en iyi şey, uygulamanın mevcut olup olmadığını kontrol etmek ve Android Market için bir niyet göndermek. android Market için url şemasını bir şey kullanmalısınız.
market://details?id=package.name
Etkinliğe nasıl başlayacağımı tam olarak bilmiyorum, ancak bu tür bir URL ile bir etkinliğe başlarsanız. Android pazarını açmalı ve uygulamaları yükleme seçeneği sunmalıdır.
İndirmenizi DownloadManagerbaşlatmak için kullanıyorsanız, harici bir konuma kaydettiğinizden emin olun setDestinationInExternalFilesDir(c, null, "<your name here>).apk";. Paket arşiv türüne sahip niyet, content:dahili bir konuma indirmelerle kullanılan şema gibi görünmüyor , ancak beğeniyor file:. (Dahili yolu bir File nesnesine sarmaya çalıştıktan sonra, bir yolla sonuçlanmasına rağmen yolu almak da işe yaramaz.file: , uygulama apk'yi ayrıştırmayacağı için url ile ; harici olması gerektiği gibi görünüyor.)
Misal:
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);
İzin istemeyi unutmayın:
android.Manifest.permission.WRITE_EXTERNAL_STORAGE
android.Manifest.permission.READ_EXTERNAL_STORAGE
AndroidManifest.xml sağlayıcısını ve iznini ekleyin:
<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>
XML dosya sağlayıcısı oluşturma res / xml / sağlayıcı_yolları.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>
Aşağıdaki örnek kodu kullanın:
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();
}
}
}
...
}
bunu dene
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);
Önce AndroidManifest.xml dosyasına aşağıdaki satırı ekleyin:
<uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" />
Sonra apk yüklemek için aşağıdaki kodu kullanın:
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 , Android için APK paketlerini başka bir Uygulama içinden yüklemek için bir API sağlar.
Güncellemenizi çevrimiçi olarak tanımlayabilir ve API'yı Uygulamanıza entegre edebilirsiniz - hepsi bu kadar.
Şu anda API Beta durumundadır, ancak zaten bazı testleri kendiniz yapabilirsiniz.
Bunun yanı sıra, UpdateNode, sistem üzerinden mesajlar da sunar - kullanıcılarınız için önemli bir şey söylemek istiyorsanız oldukça yararlıdır.
Client dev ekibinin bir parçasıyım ve kendi Android Uygulamam için en azından mesaj işlevselliğini kullanıyorum.
Bunu deneyin - Manifest üzerine yazın:
uses-permission android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions"
Kodu yazın:
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);