سؤال

أنا مهتم بمعرفة ما إذا كان من الممكن تثبيت APK تم تنزيله بشكل ديناميكي من تطبيق Android المخصص.

هل كانت مفيدة؟

المحلول

يمكنك بسهولة تشغيل رابط متجر تشغيل أو موجه تثبيت:

Intent promptInstall = new Intent(Intent.ACTION_VIEW)
    .setDataAndType(Uri.parse("content:///path/to/your.apk"), 
                    "application/vnd.android.package-archive");
startActivity(promptInstall); 

أو

Intent goToMarket = new Intent(Intent.ACTION_VIEW)
    .setData(Uri.parse("https://play.google.com/store/apps/details?id=com.package.name"));
startActivity(goToMarket);

ومع ذلك ، لا يمكنك تثبيت. إذن صريح; ؛ ليس ما لم يكن الجهاز وبرنامجك متجذرًا.

نصائح أخرى

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);

واجهت نفس المشكلة وبعد عدة محاولات ، نجحت بالنسبة لي بهذه الطريقة. لا أعرف لماذا ، لكن إعداد البيانات واكتب بشكل منفصل عن نيتي.

الحلول المقدمة لهذا السؤال تنطبق جميعها targetSdkVersion S من 23 وما يلي. بالنسبة إلى Android N ، IE API المستوى 24 ، وما فوق ، فإنهم لا يعملون ويتعطلون مع الاستثناء التالي:

android.os.FileUriExposedException: file:///storage/emulated/0/... exposed beyond app through Intent.getData()

ويرجع ذلك إلى حقيقة أن بدءًا من Android 24 ، Uri لمعالجة الملف الذي تم تنزيله قد تغير. على سبيل المثال ، تم تسمية ملف التثبيت appName.apk مخزنة على نظام الملفات الخارجي الأساسي للتطبيق باسم الحزمة com.example.test سيكون كما

file:///storage/emulated/0/Android/data/com.example.test/files/appName.apk

ل API 23 وأسفل ، في حين مثل شيء مثل

content://com.example.test.authorityStr/pathName/Android/data/com.example.test/files/appName.apk

ل API 24 و ما فوق.

يمكن العثور على مزيد من التفاصيل حول هذا هنا ولن أذهب من خلاله.

للإجابة على السؤال targetSdkVersion من 24 وما فوق ، يجب على المرء أن يتبع هذه الخطوات: أضف ما يلي إلى 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. أضف ما يلي paths.xml ملف إلى xml مجلد على res في SRC ، الرئيسي:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="pathName"
        path="pathValue"/>
</paths>

ال pathName هل يظهر في المحتوى المثالي مثال URI أعلاه و pathValue هو المسار الفعلي على النظام. سيكون من الجيد وضع ". (بدون اقتباسات) للمسار في ما سبق إذا كنت لا ترغب في إضافة أي دليل فرعي إضافي.

  1. اكتب الرمز التالي لتثبيت APK بالاسم appName.apk على نظام الملفات الخارجي الأساسي:

    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();
    

لا يوجد إذن ضروري أيضًا عند الكتابة إلى الدليل الخاص للتطبيق الخاص على نظام الملفات الخارجي.

لقد كتبت مكتبة autoupdate هنا التي استخدمت ما سبق.

حسنًا ، قمت بحفر أعمق ، ووجدت مصادر لتطبيق PackageInstaller من مصدر Android.

https://github.com/android/platform_packages_apps_packageinstaller

من البيان ، وجدت أنه يتطلب الإذن:

    <uses-permission android:name="android.permission.INSTALL_PACKAGES" />

وتحدث العملية الفعلية للتثبيت بعد التأكيد

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);

أريد فقط أن أشارك حقيقة أن ملف APK الخاص بي قد تم حفظه في دليل "البيانات" الخاص بي وأنني بحاجة إلى تغيير الأذونات على ملف APK ليكون قابلاً للقراءة للعالم من أجل السماح بتثبيته بهذه الطريقة ، وإلا فإن النظام كان يرمي "خطأ في التحليل: هناك مشكلة في تحليل الحزمة" ؛ لذا فإن استخدام الحل من @Horaceman يصنع:

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);

حل آخر لا يتطلب أن يرمز إلى تطبيق الاستقبال ، وبالتالي يكون أكثر أمانًا:

Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData( Uri.fromFile(new File(pathToApk)) );
startActivity(intent);

انه من الممكن. ولكن لذلك تحتاج إلى تثبيت الهاتف لمصادر لم يتم التحقق منها. على سبيل المثال ، Slideme يفعل ذلك. أعتقد أن أفضل شيء يمكنك القيام به هو التحقق مما إذا كان التطبيق موجودًا وإرسال نية لسوق Android. يجب عليك استخدام شيء مخطط عنوان URL لسوق Android.

market://details?id=package.name

لا أعرف بالضبط كيفية بدء النشاط ولكن إذا بدأت نشاطًا بهذا النوع من عنوان URL. يجب أن يفتح سوق Android ويمنحك خيار تثبيت التطبيقات.

تجدر الإشارة إلى أنه إذا كنت تستخدم DownloadManager لبدء التنزيل الخاص بك ، تأكد من حفظه في موقع خارجي على سبيل المثال setDestinationInExternalFilesDir(c, null, "<your name here>).apk";. لا يبدو أن القصد من نوع حزمة محرك مثل content: مخطط يستخدم مع التنزيلات إلى موقع داخلي ، لكنه يعجبني file:. (محاولة لف المسار الداخلي في كائن ملف ثم الحصول على المسار لا يعمل أيضًا ، على الرغم من أنه ينتج عنه file: عنوان URL ، لأن التطبيق لن يحلل APK ؛ يبدو أنه يجب أن يكون خارجيًا.)

مثال:

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);

هذا يمكن أن يساعد الآخرين كثيرا!

أولاً:

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();
    }
}

ثانية: لنظام Android 7 وما فوق ، يجب أن تحدد مزودًا في الظهور أدناه!

    <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>

ثالث: تعريف path.xml في مجلد RES/XML مثل أدناه! أنا أستخدم هذا طريق للتخزين الداخلي إذا كنت تريد تغييره إلى شيء آخر ، فهناك طريقة قليلة! يمكنك الذهاب إلى هذا الرابط: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>

فصلا: يجب عليك إضافة هذا الإذن في الظهور:

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

يرجى التأكد من أن سلطات المزود هي نفسها!


مجرد امتداد ، إذا كان أي شخص بحاجة إلى مكتبة بعد ذلك هذه قد تساعد. شكرا ل راغاف

جرب هذا

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);

updatenode يوفر API لنظام Android لتثبيت حزم APK من داخل تطبيق آخر.

يمكنك فقط تحديد التحديث الخاص بك عبر الإنترنت ودمج واجهة برمجة التطبيقات في التطبيق الخاص بك - هذا كل شيء.
حاليا واجهة برمجة التطبيقات في حالة تجريبية ، ولكن يمكنك بالفعل إجراء بعض الاختبارات بنفسك.

بجانب ذلك ، يوفر updatenode أيضًا عرض الرسائل على الرغم من أن النظام - مفيد جدًا إذا كنت ترغب في إخبار شيء مهم للمستخدمين.

أنا جزء من فريق Dev Client وأستخدم على الأقل وظيفة الرسالة لتطبيق Android الخاص بي.

انظر هنا وصف كيفية دمج واجهة برمجة التطبيقات

أضف أولاً السطر التالي إلى AndroidManifest.xml:

<uses-permission android:name="android.permission.INSTALL_PACKAGES"
    tools:ignore="ProtectedPermissions" />

ثم استخدم الكود التالي لتثبيت APK:

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);

لا تنس طلب الأذونات:

android.Manifest.permission.WRITE_EXTERNAL_STORAGE 
android.Manifest.permission.READ_EXTERNAL_STORAGE

أضف في AndroidManifest.xml الموفر والإذن:

<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 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>

استخدم أدناه رمز المثال:

   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();
            }

        }

    }

    ...

}

جرب هذا - اكتب على البيان:

uses-permission android:name="android.permission.INSTALL_PACKAGES"
        tools:ignore="ProtectedPermissions"

اكتب الرمز:

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);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top