什么是Lollipop上的 "PackageInstaller "类,以及如何使用它?[英] What's "PackageInstaller" class on Lollipop, and how to use it?

本文是小编为大家收集整理的关于什么是Lollipop上的 "PackageInstaller "类,以及如何使用它?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

背景

我注意到packagemanager上有一个名为" getPackageInstaller ",Minapi 21(棒棒糖).

我到达了" packageinstaller "类,这就是它写的:

提供安装,升级和删除应用程序的能力 设备.这包括支持作为单个包装的应用程序 "单片"APK,或作为多个"拆分"APKS的应用程序.

应用程序通过a提供安装 packageinstaller.session,任何应用程序都可以创建.一旦会议 是创建的,安装程序可以将一个或多个APKS流式传输到位 它决定提交或销毁会话.犯下5月 要求用户干预完成安装.

会话可以安装全新的应用程序,升级现有应用程序或添加新的应用程序 分成现有应用程序.

问题

  1. 这个课程是什么?它甚至可用于第三方应用程序(我没有看到这一点)吗?
  2. 它真的可以安装应用程序吗?
  3. 它在背景中做到了吗?
  4. 有什么限制?
  5. 它是否需要权限?如果是的话,哪个?
  6. 是有没有如何使用它的教程?

推荐答案

好的,我发现了一些答案:

  1. 可用于安装/更新APK文件,包括拆分文件.也许更多.
  2. 是的,但用户需要确认,一个应用程序之后.
  3. 可能是如果应用程序内置.
  4. 似乎在请求用户安装之前,它需要阅读整个APK文件/s.
  5. 需要权限 request_install_packages
  6. 尚未找到任何,但有人在这里向我展示了如何安装split-apk文件,这是如何使用saf,with actackinstaller来完成单个文件.请注意,这只是一个样本.我不认为这是一个很好的做法,可以在UI线程上完成.

清单

<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
          package="com.android.apkinstalltest">
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

    <application tools:ignore="AllowBackup,GoogleAppIndexingWarning"
                 android:allowBackup="true"
                 android:icon="@mipmap/ic_launcher"
                 android:label="@string/app_name"
                 android:roundIcon="@mipmap/ic_launcher_round"
                 android:supportsRtl="true"
                 android:theme="@style/AppTheme">
        <activity
                android:name=".MainActivity"
                android:label="@string/app_name"
                android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service android:name=".APKInstallService"/>

    </application>

</manifest>

apkkstallservice

class APKInstallService : Service() {
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE)) {
            PackageInstaller.STATUS_PENDING_USER_ACTION -> {
                Log.d("AppLog", "Requesting user confirmation for installation")
                val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
                confirmationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                try {
                    startActivity(confirmationIntent)
                } catch (e: Exception) {
                }
            }
            PackageInstaller.STATUS_SUCCESS -> Log.d("AppLog", "Installation succeed")
            else -> Log.d("AppLog", "Installation failed")
        }
        stopSelf()
        return START_NOT_STICKY
    }

    override fun onBind(intent: Intent) = null
}

混播

class MainActivity : AppCompatActivity() {
    private lateinit var packageInstaller: PackageInstaller

    @TargetApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)
        packageInstaller = packageManager.packageInstaller
        val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
        intent.addCategory(Intent.CATEGORY_OPENABLE)
        intent.type = "application/vnd.android.package-archive"
        startActivityForResult(intent, 1)
    }

//    override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
//        super.onActivityResult(requestCode, resultCode, resultData)
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && requestCode == 1 && resultCode == Activity.RESULT_OK && resultData != null) {
//            val uri = resultData.data
//            grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
//            val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)//
//                    .setDataAndType(uri, "application/vnd.android.package-archive")
//                    .putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
//                    .putExtra(Intent.EXTRA_RETURN_RESULT, false)
//                    .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
//            startActivity(intent)
//        }

    override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
        super.onActivityResult(requestCode, resultCode, resultData)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && requestCode == 1 && resultCode == Activity.RESULT_OK && resultData != null) {
            val uri = resultData.data ?: return
            grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
            val installParams = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
            var cursor: Cursor? = null
            var outputStream: OutputStream? = null
            var inputStream: InputStream? = null
            var session: PackageInstaller.Session? = null
            try {
                cursor = contentResolver.query(uri, null, null, null, null)
                if (cursor != null) {
                    cursor.moveToNext()
                    val fileSize = cursor.getLong(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_SIZE))
                    val fileName = cursor.getString(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME))
                    installParams.setSize(fileSize)
                    cursor.close()
                    val sessionId = packageInstaller.createSession(installParams)
                    Log.d("AppLog", "Success: created install session [$sessionId] for file $fileName")
                    session = packageInstaller.openSession(sessionId)
                    outputStream = session.openWrite(System.currentTimeMillis().toString(), 0, fileSize)
                    inputStream = contentResolver.openInputStream(uri)
                    inputStream.copyTo(outputStream)
                    session.fsync(outputStream)
                    outputStream.close()
                    outputStream = null
                    inputStream.close()
                    inputStream = null
                    Log.d("AppLog", "Success: streamed $fileSize bytes")
                    val callbackIntent = Intent(applicationContext, APKInstallService::class.java)
                    val pendingIntent = PendingIntent.getService(applicationContext, 0, callbackIntent, 0)
                    session!!.commit(pendingIntent.intentSender)
                    session.close()
                    session = null
                    Log.d("AppLog", "install request sent. sessions:" + packageInstaller.mySessions)
                }
            } catch (e: Exception) {
                Log.d("AppLog", "error:$e")
            } finally {
                outputStream?.close()
                inputStream?.close()
                session?.close()
                cursor?.close()
            }
        }
    }
}

本文地址:https://www.itbaoku.cn/post/1937962.html

问题描述

Background

I've noticed there is a new function on the PackageManager called "getPackageInstaller" , with minAPI 21 (Lollipop).

I've reached the "PackageInstaller" class, and this is what it is written about it:

Offers the ability to install, upgrade, and remove applications on the device. This includes support for apps packaged either as a single "monolithic" APK, or apps packaged as multiple "split" APKs.

An app is delivered for installation through a PackageInstaller.Session, which any app can create. Once the session is created, the installer can stream one or more APKs into place until it decides to either commit or destroy the session. Committing may require user intervention to complete the installation.

Sessions can install brand new apps, upgrade existing apps, or add new splits into an existing app.

Questions

  1. What is this class used for? Is it even available for third party apps (I don't see any mentioning of this) ?
  2. Can it really install apps?
  3. Does it do it in the background?
  4. What are the restrictions?
  5. Does it require permissions? If so, which?
  6. Is there any tutorial of how to use it?

推荐答案

OK, I've found some answers:

  1. Can be used for installing/updating APK files, including split-APK files. Maybe even more.
  2. Yes, but the user will need to confirm, one app after another.
  3. Maybe if the app is built in.
  4. Seems it requires to read the entire APK file/s before requesting the user to install.
  5. Needs permission REQUEST_INSTALL_PACKAGES
  6. Haven't found any, but someone showed me here how to install split-apk files, and here's how to do it for a single file using SAF, with and without PackageInstaller. Note that this is just a sample. I don't think it's a good practice to do it all on the UI thread.

manifest

<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
          package="com.android.apkinstalltest">
    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>

    <application tools:ignore="AllowBackup,GoogleAppIndexingWarning"
                 android:allowBackup="true"
                 android:icon="@mipmap/ic_launcher"
                 android:label="@string/app_name"
                 android:roundIcon="@mipmap/ic_launcher_round"
                 android:supportsRtl="true"
                 android:theme="@style/AppTheme">
        <activity
                android:name=".MainActivity"
                android:label="@string/app_name"
                android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>

        <service android:name=".APKInstallService"/>

    </application>

</manifest>

APKInstallService

class APKInstallService : Service() {
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE)) {
            PackageInstaller.STATUS_PENDING_USER_ACTION -> {
                Log.d("AppLog", "Requesting user confirmation for installation")
                val confirmationIntent = intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
                confirmationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                try {
                    startActivity(confirmationIntent)
                } catch (e: Exception) {
                }
            }
            PackageInstaller.STATUS_SUCCESS -> Log.d("AppLog", "Installation succeed")
            else -> Log.d("AppLog", "Installation failed")
        }
        stopSelf()
        return START_NOT_STICKY
    }

    override fun onBind(intent: Intent) = null
}

MainActivity

class MainActivity : AppCompatActivity() {
    private lateinit var packageInstaller: PackageInstaller

    @TargetApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        setSupportActionBar(toolbar)
        packageInstaller = packageManager.packageInstaller
        val intent = Intent(Intent.ACTION_OPEN_DOCUMENT)
        intent.addCategory(Intent.CATEGORY_OPENABLE)
        intent.type = "application/vnd.android.package-archive"
        startActivityForResult(intent, 1)
    }

//    override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
//        super.onActivityResult(requestCode, resultCode, resultData)
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && requestCode == 1 && resultCode == Activity.RESULT_OK && resultData != null) {
//            val uri = resultData.data
//            grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
//            val intent = Intent(Intent.ACTION_INSTALL_PACKAGE)//
//                    .setDataAndType(uri, "application/vnd.android.package-archive")
//                    .putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
//                    .putExtra(Intent.EXTRA_RETURN_RESULT, false)
//                    .addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
//            startActivity(intent)
//        }

    override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
        super.onActivityResult(requestCode, resultCode, resultData)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && requestCode == 1 && resultCode == Activity.RESULT_OK && resultData != null) {
            val uri = resultData.data ?: return
            grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
            val installParams = PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL)
            var cursor: Cursor? = null
            var outputStream: OutputStream? = null
            var inputStream: InputStream? = null
            var session: PackageInstaller.Session? = null
            try {
                cursor = contentResolver.query(uri, null, null, null, null)
                if (cursor != null) {
                    cursor.moveToNext()
                    val fileSize = cursor.getLong(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_SIZE))
                    val fileName = cursor.getString(cursor.getColumnIndex(DocumentsContract.Document.COLUMN_DISPLAY_NAME))
                    installParams.setSize(fileSize)
                    cursor.close()
                    val sessionId = packageInstaller.createSession(installParams)
                    Log.d("AppLog", "Success: created install session [$sessionId] for file $fileName")
                    session = packageInstaller.openSession(sessionId)
                    outputStream = session.openWrite(System.currentTimeMillis().toString(), 0, fileSize)
                    inputStream = contentResolver.openInputStream(uri)
                    inputStream.copyTo(outputStream)
                    session.fsync(outputStream)
                    outputStream.close()
                    outputStream = null
                    inputStream.close()
                    inputStream = null
                    Log.d("AppLog", "Success: streamed $fileSize bytes")
                    val callbackIntent = Intent(applicationContext, APKInstallService::class.java)
                    val pendingIntent = PendingIntent.getService(applicationContext, 0, callbackIntent, 0)
                    session!!.commit(pendingIntent.intentSender)
                    session.close()
                    session = null
                    Log.d("AppLog", "install request sent. sessions:" + packageInstaller.mySessions)
                }
            } catch (e: Exception) {
                Log.d("AppLog", "error:$e")
            } finally {
                outputStream?.close()
                inputStream?.close()
                session?.close()
                cursor?.close()
            }
        }
    }
}