android问题解决途径
For more than 4 years, Android Permissions have devoured our time in development to provide our beloved users more transparent, and safer apps for Android platform.
四年多来,Android Permissions吞噬了我们的开发时间,为心爱的用户提供了更加透明,更安全的Android平台应用程序。
This article is not a complaint but a new idea about the adventure of Android permissions. I don’t claim that the approach will cover all Android permission resolutions. The idea may probably be practiced by the most of you, but I am talking about creating a norm about it. If we can achieve this, the platform will overcome a considerable obstacle which is gathering storms in our brains.
本文不是抱怨,而是有关Android权限冒险的新思路。 我并不是说该方法将涵盖所有Android权限解决方案。 多数人可能会实践该想法,但是我正在谈论创建有关它的规范。 如果我们能够做到这一点,该平台将克服一个巨大的障碍,即在我们的大脑中聚集风暴。
As we all know, Android permissions requires lots of boilerplate code including massive AlertDialog
s. I never liked them. They make the code too large and hard to read for me for years.
众所周知,Android权限需要大量样板代码,包括大量的AlertDialog
。 我从不喜欢他们。 它们使代码太大,并且使我多年难以阅读。
Some Android developers created useful libraries to reduce the number of lines in code. I used them too, but I am not really satisfied with their approach. I believe Android permissions are strongly related to business concerns, but those libraries focuses on just refactoring the code. Their approach is not easily manageable, and reusable. Moreover they don’t force us to provide a stable UX for our users. A typical Android user expects the same behavior for native platform events in every apps, as the Android platform itself expects us to maintain it too!
一些Android开发人员创建了有用的库来减少代码行数。 我也使用过它们,但是我对它们的方法并不满意。 我相信Android权限与业务问题密切相关,但是这些库只专注于重构代码。 他们的方法不易管理,不可重用。 而且,它们不会强迫我们为用户提供稳定的用户体验。 一个典型的Android用户期望每个应用程序中的本机平台事件具有相同的行为,因为Android平台本身也希望我们对其进行维护!
I don’t believe this can be handled by a third party library, but I believe that it can be resolved by a pattern. Android native permission process seems very low level for me according to our innocent activities and fragments. That should get high as much as possible so our activities and fragments can breathe fresh air.
我不认为这可以由第三方库来处理,但是我相信可以通过某种模式来解决。 根据我们的无辜活动和片段,Android本机许可过程对我来说似乎很低。 那应该尽可能高,以便我们的活动和碎片可以呼吸新鲜空气。
This article will explain how we approached to permissions in Android app of arabam.com and show you our real code to provide a real experience that we faced. I will also provide a sample project on Github.
本文将说明我们如何在arabam.com的 Android应用程序中获取权限, 并向您展示我们的真实代码以提供我们所面对的真实体验。 我还将在Github上提供一个示例项目。
Let’s start from the highest point of our pattern: BusinessRequest
让我们从模式的最高点开始: BusinessRequest
If you need an Android permission, you absolutely need to solve a business logic in your app, and the permission is probably sitting in the middle of it. That’s why BusinessRequest
answers a base question: What are you going to do with these permissions?
如果您需要Android权限,则绝对需要解决应用程序中的业务逻辑,并且该权限可能位于其中。 这就是为什么BusinessRequest
回答一个基本问题的原因: 这些权限将如何处理?
enum class BusinessRequest {
ADD_PHOTO_TO_ADVERT_FROM_CAMERA,
READ_QR_FROM_CAMERA,
TAKE_PHOTO_FOR_FIRM_LOGO_FROM_CAMERA,
ADD_PHOTO_TO_ADVERT_FROM_GALLERY,
PICK_PHOTO_FOR_FIRM_LOGO_FROM_GALLERY,
LOCATION_TO_SHOW_ON_MAP,
VIDEO_CALL;
}
Our activities and fragments will tell what they want to do using it. We also need to define the real permissions remaining in the settings menu:
我们的活动和片段将告诉他们他们想使用它做什么。 我们还需要定义设置菜单中剩余的实际权限:
enum class PermissionRequest(val request: Int) {
CAMERA(1),
STORAGE(2),
LOCATION(4),
MICROPHONE(8),
PHONE(16);
fun getPermissionTextArray(): Array<String> {
return when (this) {
CAMERA -> arrayOf(Manifest.permission.CAMERA)
STORAGE -> arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE)
LOCATION -> arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
MICROPHONE -> arrayOf(Manifest.permission.RECORD_AUDIO, Manifest.permission.MODIFY_AUDIO_SETTINGS)
PHONE -> arrayOf(Manifest.permission.READ_PHONE_STATE)
}
}
companion object {
fun getPermissionRequestFromText(permission: String): PermissionRequest? {
return when (permission) {
Manifest.permission.CAMERA -> CAMERA
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE -> STORAGE
Manifest.permission.ACCESS_FINE_LOCATION -> LOCATION
Manifest.permission.RECORD_AUDIO,
Manifest.permission.MODIFY_AUDIO_SETTINGS -> MICROPHONE
Manifest.permission.READ_PHONE_STATE -> PHONE
else -> null
}
}
}
}
We will map BusinessRequest
to PermissionRequest
later. These classes will help us better understand what permissions our business needs. More readable!
稍后我们将把BusinessRequest
映射到PermissionRequest
。 这些类将帮助我们更好地了解我们的业务需要哪些权限。 更具可读性!
We need to identify our activities and fragments easily which are using permissions, so they will become children of abstract PermissionActivity
, PermissionFragment
and PermissionDialogFragment
.
我们需要轻松识别使用权限的活动和片段,因此它们将成为抽象PermissionActivity
, PermissionFragment
和PermissionDialogFragment
子代。
As you see we have 3 different abstract classes which they will do almost the same work. How can we manage them to do the same work without copying and pasting the code? The answer is, we need a manager class that will work as a field inside them and handle all business logic in one place and provide the same behavior in all permission requests in the entire app.
如您所见,我们有3个不同的抽象类,它们将完成几乎相同的工作。 我们如何管理他们在不复制和粘贴代码的情况下完成相同的工作? 答案是,我们需要一个经理类,将其用作其中的一个字段,并在一个地方处理所有业务逻辑,并在整个应用程序的所有权限请求中提供相同的行为。
Our manager class will be PermissionManager
. It will check whether permissions are granted, and show necessary dialogs like rationale & navigating to settings menu in case of user’s permanent denial. It will handle user choices and will let our 3 abstract classes know what happened in the end.
我们的经理类将是PermissionManager
。 它将检查是否授予了权限,并在用户永久拒绝的情况下显示必要的对话框,如基本原理和导航到设置菜单。 它会处理用户的选择,并使我们的3个抽象类最终知道发生了什么。
Wait a second, how can they know what happened in our mysterious PermissionManager
class? We need a messenger between PermissionManager
and 3 abstract classes. As you guess, we need a listener that will invoke essential events performed by PermissionManager
. We call it PermissionListener
.
等一下,他们怎么知道我们神秘的PermissionManager
类中发生了什么? 我们需要PermissionManager
和3个抽象类之间的使者。 如您所料,我们需要一个侦听器,该侦听器将调用PermissionManager
执行的基本事件。 我们称它为PermissionListener
。
Our architecture is ready, let’s see how it works to see the big picture:
我们的架构已准备就绪,让我们看一下它如何工作以查看大图:
![Android Permission Architecture](https://i-blog.csdnimg.cn/blog_migrate/b8a16cd793c5102639b9fe12b1dcaf63.png)
Let’s start our adventure with an example for an Android permission process: When user wants to make a video call with an advertiser, s/he needs to give following permissions, Camera, Microphone & Phone. We have VideoCallActivity
. It will extend PermissionActivity
.
让我们从一个Android权限流程示例开始我们的冒险:当用户想要与广告客户进行视频通话时,他/她需要授予以下权限,即相机,麦克风和电话。 我们有VideoCallActivity
。 它将扩展PermissionActivity
。
We will start calling checkPermissionsAreGranted(BusinessRequest.VIDEO_CALL, true)
method implemented in PermissionActivity
. It simply wants to make a video call, doesn’t worry about what permissions are needed. Lovely. Let’s see what happens in the method:
我们将开始调用在PermissionActivity
实现的checkPermissionsAreGranted(BusinessRequest.VIDEO_CALL, true)
方法。 它只是想进行视频通话,而不用担心需要哪些权限。 可爱。 让我们看看该方法会发生什么:
fun checkPermissionsAreGranted(businessRequest: BusinessRequest, exitOnDenied: Boolean = false) {
this.businessRequest = businessRequest
this.exitOnDenied = exitOnDenied
if (::permissionManager.isInitialized) {
val permissionRequests = permissionManager.getPermissionRequestsFromBusiness(businessRequest)
if (permissionManager.areAllPermissionsGranted(this, permissionRequests)) {
onPermissionsGranted(businessRequest)
} else {
permissionManager.prepareNativePermissionRequest(permissionRequests)
}
}
}
We pass optional parameter exitOnDenied
true because the permissions are critical for VideoCallActivity
. If user denies any of the permissions, we can’t do anything, so we will finish the activity in that case when exitOnDenied
is true.
我们传递可选参数exitOnDenied
true,因为权限对于VideoCallActivity
至关重要。 如果用户拒绝任何权限,我们将无能为力,因此在exitOnDenied
为true的情况下,我们将完成活动。
We put ourselves in PermissionManager
‘s arms. It will call getPermissionRequestsFromBusiness(businessRequest)
mapping BusinessRequest.VIDEO_CALL
to an array of PermissionRequest.CAMERA
, PermissionRequest.MICROPHONE
and PermissionRequest.PHONE
. Then, it will check whether all these permissions are granted by calling areAllPermissionsGranted(this, permissionRequests)
:
我们将自己置于PermissionManager
的怀抱中。 它将调用getPermissionRequestsFromBusiness(businessRequest)
将BusinessRequest.VIDEO_CALL
映射到PermissionRequest.CAMERA
, PermissionRequest.MICROPHONE
和PermissionRequest.PHONE
的数组。 然后,它将检查是否通过调用areAllPermissionsGranted(this, permissionRequests)
授予所有这些权限:
fun getPermissionRequestsFromBusiness(businessRequest: BusinessRequest): Array<PermissionRequest> {
return when (businessRequest) {
BusinessRequest.ADD_PHOTO_TO_ADVERT_FROM_CAMERA,
BusinessRequest.READ_QR_FROM_CAMERA,
BusinessRequest.TAKE_PHOTO_FOR_FIRM_LOGO_FROM_CAMERA -> arrayOf(PermissionRequest.CAMERA)
BusinessRequest.ADD_PHOTO_TO_ADVERT_FROM_GALLERY,
BusinessRequest.PICK_PHOTO_FOR_FIRM_LOGO_FROM_GALLERY -> arrayOf(PermissionRequest.STORAGE)
BusinessRequest.LOCATION_TO_SHOW_ON_MAP -> arrayOf(PermissionRequest.LOCATION)
BusinessRequest.VIDEO_CALL -> arrayOf(PermissionRequest.CAMERA, PermissionRequest.MICROPHONE, PermissionRequest.PHONE)
}
}
fun areAllPermissionsGranted(context: Context, permissionRequests: Array<out PermissionRequest>): Boolean {
return permissionRequests.all { permissionRequest ->
permissionRequest.getPermissionTextArray().all { permission ->
val permissionStatus = ContextCompat.checkSelfPermission(context, permission)
permissionStatus == PackageManager.PERMISSION_GRANTED
}
}
}
If all permissions are granted, it will simply call abstract function onPermissionsGranted(businessRequest)
and VideoCallActivity
will call makeVideoCall()
function inside of it. However, if any of permissions are not granted yet, we will start preparation for permission process by calling prepareNativePermissionRequest(permissionsRequests)
.
如果授予所有权限,它将仅调用抽象函数onPermissionsGranted(businessRequest)
而VideoCallActivity
将在其内部调用makeVideoCall()
函数。 但是,如果尚未授予任何权限,我们将通过调用prepareNativePermissionRequest(permissionsRequests)
开始准备权限过程。
It will prepare an Android’s native permission request; an array of strings including Manifest.permission
s, and a requestCode
integer value to match the result:
它将准备一个Android的本地权限请求; 包含Manifest.permission
的字符串数组,以及与结果匹配的requestCode
整数值:
fun prepareNativePermissionRequest(permissionRequests: Array<out PermissionRequest>) {
val permissions = getPermissions(permissionRequests)
val requestCode = getRequestCode(permissionRequests)
permissionListener.onNativePermissionRequestReady(permissions, requestCode)
}
private fun getPermissions(permissionRequests: Array<out PermissionRequest>): Array<String> {
val permissions = ArrayList<String>()
permissionRequests.forEach { permissionRequest ->
val permissionTexts = permissionRequest.getPermissionTextArray()
permissions.addAll(permissionTexts)
}
return permissions.toTypedArray()
}
private fun getRequestCode(permissionRequests: Array<out PermissionRequest>) = permissionRequests.sumBy { it.request }
When they are ready, the function will let PermissionActivity
know that the request is ready by invoking onNativePermissionRequestReady(permissions, requestCode)
method. Then, PermissionActivity
will simply call ActivityCompat.requestPermissions(this, permissions, requestCode)
inside of it.
准备就绪后,该函数将通过调用onNativePermissionRequestReady(permissions, requestCode)
方法让PermissionActivity
知道请求已准备就绪。 然后, PermissionActivity
将简单地在其中调用ActivityCompat.requestPermissions(this, permissions, requestCode)
。
The result will fall into onRequestPermissionResult
method inside PermissionActivity
. Our superhero, PermissionManager
comes along to aid us again! We call its handleRequestPermissionsResult(...)
method to handle all the result logic for us:
结果将落入PermissionActivity
内部的onRequestPermissionResult
方法中。 我们的超级英雄PermissionManager
再次提供帮助! 我们调用其handleRequestPermissionsResult(...)
方法来为我们处理所有结果逻辑:
fun handleRequestPermissionsResult(activity: Activity, businessRequest: BusinessRequest, permissions: Array<out String>, grantResults: IntArray) {
val deniedPermissions = ArrayList<String>()
var areAllPermissionsGranted = true
grantResults.indices.forEach { i ->
val grantResult = grantResults[i]
if (grantResult == PackageManager.PERMISSION_DENIED) {
areAllPermissionsGranted = false
deniedPermissions.add(permissions[i])
}
}
if (areAllPermissionsGranted) {
permissionListener.grantPermissions(businessRequest)
} else {
val shouldShowRationale = deniedPermissions.any { deniedPermission ->
ActivityCompat.shouldShowRequestPermissionRationale(activity, deniedPermission)
}
if (shouldShowRationale) {
showPermissionRationaleDialog(activity, businessRequest)
} else {
showPermissionPermanentDenialDialog(activity, businessRequest, deniedPermissions)
}
}
}
private fun showPermissionRationaleDialog(activity: Activity, businessRequest: BusinessRequest) {
if (!activity.isFinishing) {
val title = getPermissionTitle(businessRequest)
val message = getMessageForRationale(activity, businessRequest)
AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(message)
.setCancelable(false)
.setPositiveButton(R.string.retry) { _, _ ->
permissionListener.retry()
}
.setNegativeButton(R.string.cancel) { dialog, _ ->
if (!activity.isFinishing) {
dialog.dismiss()
permissionListener.deny()
}
}
.show()
}
}
private fun showPermissionPermanentDenialDialog(activity: Activity, businessRequest: BusinessRequest, deniedPermissions: ArrayList<String>) {
if (!activity.isFinishing) {
val title = getPermissionTitle(businessRequest)
val message = getMessageForPermanentDenial(activity, businessRequest, deniedPermissions)
AlertDialog.Builder(activity)
.setTitle(title)
.setMessage(message)
.setCancelable(false)
.setPositiveButton(R.string.settings) { _, _ ->
permissionListener.navigateToSettings()
}
.setNegativeButton(R.string.cancel) { dialog, _ ->
if (!activity.isFinishing) {
dialog.dismiss()
permissionListener.deny()
}
}
.show()
}
}
It will check whether all permissions are granted. If user grants all permissions, it will let PermissionActivity
know by invoking grantPermissions(businessRequest)
method. If user denies any of permissions, it will check whether any of denied permissions should show a rationale dialog. If so, we will call showPermissionRationaleDialog(...)
, otherwise we must call showPermissionPermanentDenialDialog(...)
because user denies all permissions permanently.
它将检查是否所有权限都被授予。 如果用户授予所有权限,则将通过调用grantPermissions(businessRequest)
方法来通知PermissionActivity
。 如果用户拒绝任何权限,它将检查是否任何拒绝的权限都应显示基本原理对话框。 如果是这样,我们将调用showPermissionRationaleDialog(...)
,否则我们必须调用showPermissionPermanentDenialDialog(...)
因为用户会永久拒绝所有权限。
Rationale dialog will ask user to try again. If user taps on the positive button, it will invoke retry()
method and all process will start over from checkPermissionsAreGranted(...)
method. If user taps on the negative button, it will invoke deny()
method and the process will be terminated.
基本原理对话框将要求用户重试。 如果用户点击肯定按钮,它将调用retry()
方法,所有过程将从checkPermissionsAreGranted(...)
方法开始。 如果用户点击否定按钮,它将调用deny()
方法,该过程将终止。
Permanent Denial dialog will ask user to navigate to the settings menu of the application and grant necessary permissions for further process. If user taps on the positive button, it will invoke navigateToSettings()
method and PermissionActivity
will navigate to settings making isComingFromSettings
as true to handle the result on onResume()
method. (I once tried to handle it on onActivityResult
but it was never called). If user taps on the negative button, it will invoke deny()
method and the process will be terminated.
永久拒绝对话框将要求用户导航到应用程序的设置菜单,并授予进行进一步处理所需的权限。 如果用户点击肯定按钮,它将调用isComingFromSettings
navigateToSettings()
方法,并且PermissionActivity
将导航到将isComingFromSettings
设置为true的设置,以处理onResume()
方法上的结果。 (我曾经尝试在onActivityResult
上处理它,但从未调用过它)。 如果用户点击否定按钮,它将调用deny()
方法,该过程将终止。
We handle the result by the following code in PermissionActivity
:
我们通过PermissionActivity
的以下代码处理结果:
override fun onResume() {
super.onResume()
if (isComingFromSettings && ::businessRequest.isInitialized) {
isComingFromSettings = false
permissionManager.handleResume(this)
}
}
PermissionManager
helps us again with handleResume(this)
to handle the result whether user grants permissions in the settings menu:
PermissionManager
再次通过handleResume(this)
帮助我们处理是否用户在设置菜单中授予权限的结果:
fun handleResume(activity: Activity) {
if (!activity.isFinishing) {
AlertDialog.Builder(activity)
.setTitle(R.string.retry)
.setMessage(R.string.retry_business)
.setCancelable(false)
.setPositiveButton(R.string.yes) { _, _ ->
permissionListener.retry()
}
.setNegativeButton(R.string.no) { dialog, _ ->
if (!activity.isFinishing) {
dialog.dismiss()
permissionListener.deny()
}
}
.show()
}
}
It will ask user to try again if s/he grants permissions in the settings menu. If user taps on the positive button, it will invoke retry()
method and all process will start over from checkPermissionsAreGranted(...)
method. If user taps on the negative button, it will invoke deny()
method and the process will be terminated.
它将要求用户重试是否在设置菜单中授予权限。 如果用户点击肯定按钮,它将调用retry()
方法,所有过程将从checkPermissionsAreGranted(...)
方法开始。 如果用户点击否定按钮,它将调用deny()
方法,该过程将终止。
As you see, our VideoCallActivity
did almost nothing about about the permission. Everything is handled by the help of PermissionActivity
and PermissionManager
.
如您所见,我们的VideoCallActivity
对许可几乎没有做任何事情。 一切都由PermissionActivity
和PermissionManager
。
Once upon a time, VideoCallActivity
was quite fat and sad with permission works:
曾几何时, VideoCallActivity
在许可工作方面颇为可悲:
package com.ehamutcu.gloriouspermissions.sample.ui
import android.app.AlertDialog
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.provider.Settings
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
class VideoCallActivity: AppCompatActivity() {
private var isComingFromSettings = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_video_call)
checkPermission()
}
private fun checkPermission() {
val permissions = arrayOf(Manifest.permission.CAMERA,
Manifest.permission.RECORD_AUDIO,
Manifest.permission.MODIFY_AUDIO_SETTINGS
Manifest.permission.READ_PHONE_STATE)
val areAllPermissionsGranted = permissions.all { permission ->
val permissionStatus = ContextCompat.checkSelfPermission(context, permission)
permissionStatus == PackageManager.PERMISSION_GRANTED
}
if (areAllPermissionsGranted) {
makeVideoCall()
} else {
ActivityCompat.requestPermissions(this, permissions, requestCode)
}
}
fun makeVideoCall() {
// continue process
}
private fun showPermissionRationaleDialog(activity: Activity) {
if (!activity.isFinishing) {
AlertDialog.Builder(activity)
.setTitle(R.string.permission_title_application)
.setMessage(R.string.permission_rationale_video_call)
.setCancelable(false)
.setPositiveButton(R.string.retry) { _, _ ->
checkPermission()
}
.setNegativeButton(R.string.cancel) { dialog, _ ->
if (!activity.isFinishing) {
dialog.dismiss()
finish()
}
}
.show()
}
}
private fun showPermissionPermanentDenialDialog(activity: Activity, deniedPermissions: ArrayList<String>) {
if (!activity.isFinishing) {
AlertDialog.Builder(activity)
.setTitle(R.string.permission_title_application)
.setMessage(R.string.permission_permanent_denial_video_call)
.setCancelable(false)
.setPositiveButton(R.string.settings) { _, _ ->
isComingFromSettings = true
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS, Uri.fromParts("package", packageName, null))
startActivity(intent)
}
.setNegativeButton(R.string.cancel) { dialog, _ ->
if (!activity.isFinishing) {
dialog.dismiss()
finish()
}
}
.show()
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
val deniedPermissions = ArrayList<String>()
var areAllPermissionsGranted = true
grantResults.indices.forEach { i ->
val grantResult = grantResults[i]
if (grantResult == PackageManager.PERMISSION_DENIED) {
areAllPermissionsGranted = false
deniedPermissions.add(permissions[i])
}
}
if (areAllPermissionsGranted) {
makeVideoCall()
} else {
val shouldShowRationale = deniedPermissions.any { deniedPermission ->
ActivityCompat.shouldShowRequestPermissionRationale(activity, deniedPermission)
}
if (shouldShowRationale) {
showPermissionRationaleDialog(activity)
} else {
showPermissionPermanentDenialDialog(activity, deniedPermissions)
}
}
}
override fun onResume() {
super.onResume()f
if (isComingFromSettings) {
isComingFromSettings = false
if (!activity.isFinishing) {
AlertDialog.Builder(activity)
.setTitle(R.string.retry)
.setMessage(R.string.retry_business)
.setCancelable(false)
.setPositiveButton(R.string.yes) { _, _ ->
checkPermission()
}
.setNegativeButton(R.string.no) { dialog, _ ->
if (!activity.isFinishing) {
dialog.dismiss()
finish()
}
}
.show()
}
}
}
companion object {
private const val VIDEO_CALL_PERMISSION_REQUEST = 1
}
}
It had 136 lines of code, most of them work for permission issue. Our activities just don’t deserve this. Finally, it met with our savior PermissionManager
and become a PermissionActivity
:
它有136行代码,其中大多数用于许可问题。 我们的活动不值得这样做。 最后,它遇到了我们的救星PermissionManager
并成为PermissionActivity
:
package com.ehamutcu.gloriouspermissions.sample.ui
import android.os.Bundle
import com.ehamutcu.gloriouspermissions.ui.PermissionActivity
import com.ehamutcu.gloriouspermissions.sample.permission.BusinessRequest
class VideoCallActivity: PermissionActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_video_call)
checkPermissionsAreGranted(BusinessRequest.VIDEO_CALL, true)
}
override fun onPermissionsGranted(businessRequest: BusinessRequest) {
makeVideoCall()
}
fun makeVideoCall() {
// continue process
}
}
Oh my goodness! We have now 22 lines of code. We saved a total number of 114 lines! Now VideoCallActivity
happily lives ever after. This is just an example, imagine what happens when you apply this pattern in your entire project? I am curiously expecting your results!
哦,我的天啊! 现在,我们有22行代码。 我们总共保存了114行! 现在, VideoCallActivity
过着幸福的生活。 这只是一个例子,想象一下在整个项目中应用此模式时会发生什么? 我很期待您的结果!
I hope this article will open a new path to glory for approaching to Android permissions, making a platform-friendly applications to hold the users by our side.
我希望本文将为获得Android权限开辟新的道路,使之成为一个平台友好的应用程序来吸引用户。
I put a link to the sample repository for having the pattern in your projects. I certainly need your help and feedback to make the pattern much better, and cover more issues out of my sight.
我放置了一个到示例存储库的链接,以在您的项目中包含模式。 我当然需要您的帮助和反馈,以使此模式更好,并解决我看不见的更多问题。
翻译自: https://medium.com/arabamlabs/android-permissions-a-new-path-to-glory-f1e318daac36
android问题解决途径