prosource

Android에 프로그래밍 방식으로 응용 프로그램 설치

probook 2023. 9. 25. 22:49
반응형

Android에 프로그래밍 방식으로 응용 프로그램 설치

사용자 정의 Android 애플리케이션에서 동적으로 다운로드 받은 apk를 프로그래밍 방식으로 설치할 수 있습니다.

마켓 링크나 설치 프롬프트를 쉽게 실행할 수 있습니다.

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

원천

Intent goToMarket = new Intent(Intent.ACTION_VIEW)
    .setData(Uri.parse("market://details?id=com.package.name"));
startActivity(goToMarket);

원천

그러나 사용자의 명시적인 권한 없이는 .apks를 설치할 수 없으며, 장치와 프로그램이 루팅되지 않는 한 설치할 수 없습니다.

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 24 충돌합니다그러나 Android N, 즉 API 레벨 24 이상의 경우에는 작동하지 않으며 다음 예외와 함께 충돌합니다.

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

이는 , 부터해서 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그 이상.

이것에 대한 더 자세한 사항은 여기에서 확인할 수 있고 저는 그것을 검토하지 않을 것입니다.

답을 하기 는 다음과 같습니다.targetSdkVersion24그리고 위에서 다음 단계를 따라야 합니다.Android Manifest.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.xmle에 철하다xml:nressrc, main:

<?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는 시스템의 실제 경로입니다.추가 하위 디렉토리를 추가하지 않으려면 위에서 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 라이브러리를 여기에 작성했습니다.

더 깊이 파고들어 안드로이드 소스에서 패키지 설치 프로그램의 소스를 찾았습니다.

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 파일을 그런 방식으로 설치하기 위해 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);

이것은 다른 사람들에게 많은 도움을 줄 수 있습니다!

첫번째:

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

둘째: 안드로이드 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>

셋째: 아래와 같이 res/xml 폴더에 path.xml을 정의합니다!경로를 내부 저장용으로 사용하고 있습니다. 다른 방법으로 변경하려면 몇 가지 방법이 있습니다.다음 링크로 이동할 수 있습니다.파일 공급자

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

Forth: 매니페스트에 이 권한을 추가해야 합니다.

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

응용프로그램이 패키지 설치를 요청할 수 있습니다.인텐트를 사용하려면 25개 이상의 API를 대상으로 하는 앱에서 이 권한을 보유해야 합니다.ACTION_INSTALL_PACKAGE.

공급자 권한이 동일한지 확인하십시오!


안드로이드 오레오 이상 버전에서는 apk를 프로그램적으로 설치하기 위해 다른 방법으로 접근해야 합니다.

 private void installApkProgramatically() {


    try {
        File path = activity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS);

        File file = new File(path, filename);

        Uri uri;

        if (file.exists()) {

            Intent unKnownSourceIntent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(Uri.parse(String.format("package:%s", activity.getPackageName())));

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

                if (!activity.getPackageManager().canRequestPackageInstalls()) {
                    startActivityForResult(unKnownSourceIntent, Constant.UNKNOWN_RESOURCE_INTENT_REQUEST_CODE);
                } else {
                    Uri fileUri = FileProvider.getUriForFile(activity.getBaseContext(), activity.getApplicationContext().getPackageName() + ".provider", 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);
                    startActivity(intent);
                    alertDialog.dismiss();
                }

            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

                Intent intent1 = new Intent(Intent.ACTION_INSTALL_PACKAGE);
                uri = FileProvider.getUriForFile(activity.getApplicationContext(), BuildConfig.APPLICATION_ID + ".provider", file);
                activity.grantUriPermission("com.abcd.xyz", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
                activity.grantUriPermission("com.abcd.xyz", uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                intent1.setDataAndType(uri,
                        "application/*");
                intent1.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                intent1.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                intent1.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                startActivity(intent1);

            } else {
                Intent intent = new Intent(Intent.ACTION_VIEW);

                uri = Uri.fromFile(file);

                intent.setDataAndType(uri,
                        "application/vnd.android.package-archive");
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
            }
        } else {

            Log.i(TAG, " file " + file.getPath() + " does not exist");
        }
    } catch (Exception e) {

        Log.i(TAG, "" + e.getMessage());

    }
}

오레오 이상 버전에서는 알 수 없는 리소스 설치 권한이 필요합니다.그래서 활동 결과에서 당신은 허가를 위해 결과를 확인해야 합니다.

    @Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {

        case Constant.UNKNOWN_RESOURCE_INTENT_REQUEST_CODE:
            switch (resultCode) {
                case Activity.RESULT_OK:
                    installApkProgramatically();

                    break;
                case Activity.RESULT_CANCELED:
                    //unknown resouce installation cancelled

                    break;
            }
            break;
    }
}

사용 권한 요청을 잊지 마십시오.

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

        }

    }

    ...

}

수신 앱을 하드 코딩할 필요가 없고 따라서 더 안전한 또 다른 솔루션:

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

그 을 DownloadManager다운로드를 시작하려면 반드시 외부 위치에 저장해야 합니다.setDestinationInExternalFilesDir(c, null, "<your name here>).apk";. . . . . . . . . . . .content:다를 .file: ()file:하지 않기l, 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);

두 달 전에 와서 알아냈을 거예요오늘 돌아왔는데 이해가 안가더라고요.내가 아는 한 내 설정에는 아무것도 변하지 않았으므로, 분명히 내가 생각해낸 과거는 현재의 나에게 충분하지 않았습니다.저는 마침내 뭔가를 다시 할 수 있게 되었습니다. 그래서 미래의 저와 또 다른 시도를 통해 이익을 얻을 수 있는 다른 사람들을 위해 여기에 그것을 기록해 둡니다.

이 시도는 원래 Android Java Install APK - Session API 예제의 Xamarin C# 직접 번역을 나타냅니다.아마 추가 작업이 필요할 수도 있겠지만 적어도 시작일 겁니다.안드로이드 11을 대상으로 하는 프로젝트는 있지만 안드로이드 9 기기에서 실행하고 있습니다.

InstallApkSessionApi.cs

namespace LauncherDemo.Droid
{
    using System;
    using System.IO;

    using Android.App;
    using Android.Content;
    using Android.Content.PM;
    using Android.OS;
    using Android.Widget;

    [Activity(Label = "InstallApkSessionApi", LaunchMode = LaunchMode.SingleTop)]
    public class InstallApkSessionApi : Activity
    {
        private static readonly string PACKAGE_INSTALLED_ACTION =
                "com.example.android.apis.content.SESSION_API_PACKAGE_INSTALLED";

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            this.SetContentView(Resource.Layout.install_apk_session_api);

            // Watch for button clicks.
            Button button = this.FindViewById<Button>(Resource.Id.install);
            button.Click += this.Button_Click;
        }

        private void Button_Click(object sender, EventArgs e)
        {
            PackageInstaller.Session session = null;
            try
            {
                PackageInstaller packageInstaller = this.PackageManager.PackageInstaller;
                PackageInstaller.SessionParams @params = new PackageInstaller.SessionParams(
                        PackageInstallMode.FullInstall);
                int sessionId = packageInstaller.CreateSession(@params);
                session = packageInstaller.OpenSession(sessionId);
                this.AddApkToInstallSession("HelloActivity.apk", session);

                // Create an install status receiver.
                Context context = this;
                Intent intent = new Intent(context, typeof(InstallApkSessionApi));
                intent.SetAction(PACKAGE_INSTALLED_ACTION);
                PendingIntent pendingIntent = PendingIntent.GetActivity(context, 0, intent, 0);
                IntentSender statusReceiver = pendingIntent.IntentSender;

                // Commit the session (this will start the installation workflow).
                session.Commit(statusReceiver);
            }
            catch (IOException ex)
            {
                throw new InvalidOperationException("Couldn't install package", ex);
            }
            catch
            {
                if (session != null)
                {
                    session.Abandon();
                }

                throw;
            }
        }

        
        private void AddApkToInstallSession(string assetName, PackageInstaller.Session session)
        {
            // It's recommended to pass the file size to openWrite(). Otherwise installation may fail
            // if the disk is almost full.
            using Stream packageInSession = session.OpenWrite("package", 0, -1);
            using Stream @is = this.Assets.Open(assetName);
            byte[] buffer = new byte[16384];
            int n;
            while ((n = @is.Read(buffer)) > 0)
            {
                packageInSession.Write(buffer, 0, n);
            }
        }

        // Note: this Activity must run in singleTop launchMode for it to be able to receive the intent
        // in onNewIntent().
        protected override void OnNewIntent(Intent intent)
        {
            Bundle extras = intent.Extras;
            if (PACKAGE_INSTALLED_ACTION.Equals(intent.Action))
            {
                PackageInstallStatus status = (PackageInstallStatus)extras.GetInt(PackageInstaller.ExtraStatus);
                string message = extras.GetString(PackageInstaller.ExtraStatusMessage);
                switch (status)
                {
                    case PackageInstallStatus.PendingUserAction:
                        // This test app isn't privileged, so the user has to confirm the install.
                        Intent confirmIntent = (Intent) extras.Get(Intent.ExtraIntent);
                        this.StartActivity(confirmIntent);
                        break;
                    case PackageInstallStatus.Success:
                        Toast.MakeText(this, "Install succeeded!", ToastLength.Short).Show();
                        break;
                    case PackageInstallStatus.Failure:
                    case PackageInstallStatus.FailureAborted:
                    case PackageInstallStatus.FailureBlocked:
                    case PackageInstallStatus.FailureConflict:
                    case PackageInstallStatus.FailureIncompatible:
                    case PackageInstallStatus.FailureInvalid:
                    case PackageInstallStatus.FailureStorage:
                        Toast.MakeText(this, "Install failed! " + status + ", " + message,
                                ToastLength.Short).Show();
                        break;
                    default:
                        Toast.MakeText(this, "Unrecognized status received from installer: " + status,
                                ToastLength.Short).Show();
                        break;
                }
            }
        }
    }
}

Android Manifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.launcherdemo" android:installLocation="auto">
    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="30" />
    <application android:label="LauncherDemo.Android" android:theme="@style/MainTheme" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
</manifest>

제가 이 접근 방식에 대해 특히 좋아하는 것은 방송 수신기, 파일 공급자 등이 없는 매니페스트에 특별한 작업이 필요하지 않다는 것입니다.물론, 이것은 앱의 자산에서 일부 APK를 소스로 사용하는 반면, 더 유용한 시스템은 아마도 일부 주어진 APK 경로를 사용할 것입니다.저는 그것이 어느 정도의 추가적인 복잡성을 도입할 것이라고 생각합니다.또한 안드로이드가 종료되기 전에는 Xamarin GC가 스트림을 닫는 데 문제가 발생한 적이 없습니다(적어도 제가 알 수 있는 한).저는 APK가 파싱되지 않는 것에 대해서도 아무런 문제가 없었습니다.서명된 APK(장치에 배포할 때 Visual Studio에서 생성한 APK가 정상적으로 작동함)를 사용하도록 확실히 했고, 이번에도 앱의 자산에서 APK를 사용한다는 이유만으로 파일 액세스 권한 문제가 발생하지 않았습니다.

여기에 제시된 다른 답변 중 한 가지는 사이드로딩 허가를 보다 간소화하자는 아이디어였습니다.Yabaze Cool은 다음과 같은 기능을 제공했습니다.

Intent unKnownSourceIntent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(Uri.parse(String.format("package:%s", activity.getPackageName())));

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

   if (!activity.getPackageManager().canRequestPackageInstalls()) {
       startActivityForResult(unKnownSourceIntent, Constant.UNKNOWN_RESOURCE_INTENT_REQUEST_CODE);
...

번역 테스트를 해보니 런처 데모와 설치한 앱을 모두 제거했습니다.수표를 제공하지 않음canRequestPackageInstalls추가 설정 버튼을 수동으로 눌러야 하는 곳으로 이동할 수 있습니다.ACTION_MANAGE_UNKNOWN_APP_SOURCES위와 같은 의도따라서 이 논리를 추가하면 사용자의 설치 프로세스를 다소 간소화하는 데 도움이 될 수 있습니다.

네, 가능합니다.하지만 이를 위해서는 검증되지 않은 소스를 설치하려면 전화기가 필요합니다.예를 들어 slideMe는 그렇게 합니다.애플리케이션이 존재하는지 확인하고 안드로이드 마켓에 대한 의향을 보내는 것이 최선이라고 생각합니다.당신은 안드로이드 마켓을 위해 url scheme을 사용해야 합니다.

market://details?id=package.name

활동을 어떻게 시작해야 할지 정확히는 모르겠지만 그런 종류의 url로 활동을 시작한다면요.안드로이드 마켓을 열고 앱을 설치할 수 있는 선택권을 주어야 합니다.

그냥 내선으로, 도서관이 필요한 사람이 있다면 이것이 도움이 될 것입니다.라그하브 덕분에

이거 먹어봐요.

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는 Android가 다른 앱 내부에서 APK 패키지를 설치할 수 있도록 API를 제공합니다.

온라인으로 업데이트를 정의하고 API를 앱에 통합하면 됩니다.
현재 API는 Beta 상태이지만 이미 몇 가지 테스트를 직접 할 수 있습니다.

또한 UpdateNode는 시스템을 통해 메시지를 표시하는 기능도 제공합니다. 이 기능은 사용자에게 중요한 정보를 전달하려는 경우 유용합니다.

저는 클라이언트 개발팀의 일원이며 적어도 제 안드로이드 앱에 메시지 기능을 사용하고 있습니다.

API를 통합하는 방법에 대한 설명은 여기를 참조하십시오.

시도해 보기 - 매니페스트에 쓰기:

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

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

@우로시 포드크리 ž니크의 답변을 토대로 합니다.

APK를 통해 애플리케이션을 설치하는 것은 안드로이드의 다양한 버전(API 레벨 21-30)에 따라 다를 수 있습니다.

private var uri: Uri? = null
private var manager: DownloadManager? = null
private var file: File? = null
private var request: DownloadManager.Request? = null

private val REQUEST_WRITE_PERMISSION = 786
private val REQUEST_INSTALL_PACKAGE = 1234

private var receiver: BroadcastReceiver? = null
private var installIntent: Intent? = null

...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    val externalStorageDir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        context?.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS)
    } else {
        @Suppress("DEPRECATION")
        Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
    }
    val destination = "$externalStorageDir/Application.apk"
    uri = Uri.parse("file://$destination")

    file = File(destination)
    file?.let { if (it.exists()) it.delete() }
    
    request = DownloadManager.Request(Uri.parse("https://path_to_file/application.apk"))
    request?.let {
        it.setDescription("Update App")
        it.setTitle("Application")
        it.setDestinationUri(uri)
    }
    manager = context?.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager

    // for level android api >= 23 needs permission to write to external storage
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (ContextCompat.checkSelfPermission(context!!, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
            // here you can display the loading diagram
            registerReceiver()
        } else {
            // request for permission to write to external storage
            requestPermissions(
                arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                REQUEST_WRITE_PERMISSION
            )
        }
    } else {
        // here you can display the loading diagram
        registerReceiver()
    }
}

수신기 만들기 및 등록:

private val onDownloadComplete = object : BroadcastReceiver() {
    // install app when apk is loaded
    override fun onReceive(ctxt: Context, intent: Intent) {
        val mimeType = "application/vnd.android.package-archive"
        receiver = this
        try {
            installIntent = Intent(Intent.ACTION_VIEW)
            installIntent?.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP

            // for android api >= 24 requires FileProvider
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                installIntent?.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

                val fileProviderURI = FileProvider.getUriForFile(
                    context!!,
                    context!!.applicationContext.packageName + ".provider",
                    file!!)

                installIntent?.setDataAndType(fileProviderURI, mimeType)

                // for android api >= 26 requires permission to install from APK in settings
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    if (context!!.applicationContext.packageManager.canRequestPackageInstalls()) {
                        installFromAPK()
                    } else goToSecuritySettings()
                } else installFromAPK()
            } else {
                // for android api < 24 used file:// instead content://
                // (no need to use FileProvider)
                installIntent?.setDataAndType(uri, mimeType)
                installFromAPK()
            }
        } catch (e: Exception) {
            // view error message
        }
    }
}

private fun registerReceiver() {
    manager!!.enqueue(request)
    context?.registerReceiver(
        onDownloadComplete,
        IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)
    )
}

private fun installFromAPK() {
    try {
        startActivity(installIntent)
        context?.unregisterReceiver(receiver)
        activity?.finish()
    } catch (e: Exception) {
        // view error message
    }
}

// go to settings for get permission install from APK
private fun goToSecuritySettings() {
    val intent = Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES).setData(
        Uri.parse(String.format(
            "package:%s",
            context!!.applicationContext.packageName
        ))
    )
    try {
        startActivityForResult(intent, REQUEST_INSTALL_PACKAGE)
    } catch (e: Exception) {
        // view error message
    }
}

권한 요청 WRITE_EXTER_STORY의 결과를 가로챘습니다.

override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>,
        grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    if (requestCode == REQUEST_WRITE_PERMISSION
            && grantResults.isNotEmpty()
            && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
        try {
            // here you can display the loading diagram
            registerReceiver()
        } catch (e: Exception) {
            // view error message
        }
    }
}

보안 설정에서 사용자 선택 결과 가로채기:

@RequiresApi(api = Build.VERSION_CODES.O)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    if (requestCode == REQUEST_INSTALL_PACKAGE
            && resultCode == AppCompatActivity.RESULT_OK) {
        if (context!!.applicationContext.packageManager.canRequestPackageInstalls()) {
            installFromAPK()
        }
    } else {
        // view error message
    }
}

매니페스트에 추가:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<application...>
    ...
    <provider
        android:name="androidx.core.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false"
        android:grantUriPermissions="true">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/provider_paths" />
    </provider>
    ...
</application>

res/xml에 provider_paths.xml 파일 추가:

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path name="external_files" path="."/>
</paths>

Android API 레벨 = 30의 경우 보안 설정에서 반환이 작동하지 않으므로 브라우저를 통해 사용한 설치:

try {
    val intent = Intent(Intent.ACTION_VIEW)
    intent.data = Uri.parse("https://path_to_file/application.apk")
    startActivity(intent)
    activity?.finish()
} catch (e: ActivityNotFoundException) { }

언급URL : https://stackoverflow.com/questions/4604239/install-application-programmatically-on-android

반응형