After reading this no longer Android rights component design, I kneel on the clothes pad!

Take a look at Demo's code first

It is too laggy to record gif anymore. Gif is too big and web page is very card.Demo's ideas are as follows: normal judgment rights, there are three callbacks, the user confirms that the rights are granted, the user does not give, and the user clicks not to display the system rights pop-up window.Here we start the system permission settings page in the callback after the user does not display the pop-up window. After the user closes the permission settings page, we check again = Just after the user gives no permission, if no permission, we will display a pop-up window to prompt the user to close the page without permission.

The Demo code is as follows:

class PermissionActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_permission)

        btn_permission.setOnClickListener{

            PermissionManage
                    .with(this)
                    .permission(Manifest.permission.CALL_PHONE)
                    .permission(Manifest.permission.CAMERA)
                    .permission(Manifest.permission.READ_PHONE_STATE)
                    .onSuccess { Toast.makeText(this@PermissionActivity, "Application Successful", Toast.LENGTH_SHORT).show() }
                    .onDenial { Toast.makeText(this@PermissionActivity, "User Rejection", Toast.LENGTH_SHORT).show() }
                    .onDontShow { IntentUtils.startSettingActivityForResult(this, 200) }
                    .run()
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

        if (requestCode == 200) {

            var permissions = listOf(Manifest.permission.CALL_PHONE, Manifest.permission.CAMERA, Manifest.permission.READ_PHONE_STATE)
            if (PermissionManage.isHavePermissions(this, permissions)) {
                Toast.makeText(this, "You are welcome to give your permission", Toast.LENGTH_SHORT).show()
            } else {
                showDialog()
            }
        }
    }

    private fun showDialog() {

        var build: AlertDialog.Builder = AlertDialog.Builder(this)
        build.setMessage("Lack of permission, ask you to grant permission")
        build.setPositiveButton("Apply for permission", object : DialogInterface.OnClickListener {
            override fun onClick(dialog: DialogInterface?, which: Int) {
                IntentUtils.startSettingActivityForResult(this@PermissionActivity, 200)
                dialog?.dismiss()
// dialog?.cancel()
            }
        })
        build.setNegativeButton("Do not grant permission", object : DialogInterface.OnClickListener {
            override fun onClick(dialog: DialogInterface?, which: Int) {
                Toast.makeText(this@PermissionActivity, "Sorry, some privileges are required and cannot be run without privileges", Toast.LENGTH_SHORT).show()
                dialog?.dismiss()
                this@PermissionActivity.finish()
            }
        })
        build.show()
    }

}

Component encapsulation ideas

1. To look better, the idea of responsive functional programming provides chain calls

This is very important. Components are packaged to be used by small partners and themselves. It takes a lot of time and effort to use them, is not easy to understand, is not clear. Components that are not easy to understand and use are not qualified. From this point of view, the API of Fresco is not very good. Of course, Fresco is very complex, but another picture loaded companion Glide API isVery Nice

This part is the API of the permission component that I encapsulated, providing chain calls that mimic functional programming, so the code looks good and the logic is easy to understand.Functional programming has a natural advantage in code that handles continuous, complex logic, and is known for its clarity, making it our second choice for encapsulating tool class components

PermissionManage
                    .with(this)
                    .permission(Manifest.permission.CALL_PHONE)
                    .permission(Manifest.permission.CAMERA)
                    .permission(Manifest.permission.READ_PHONE_STATE)
                    .onSuccess { Toast.makeText(this@PermissionActivity, "Application Successful", Toast.LENGTH_SHORT).show() }
                    .onDenial { Toast.makeText(this@PermissionActivity, "User Rejection", Toast.LENGTH_SHORT).show() }
                    .onDontShow { IntentUtils.startSettingActivityForResult(this, 200) }
                    .run()

2. Third party library height, seamless and replaceable

I'm using AndPermission, an open source library, to see how I wrap AndPermission

2.1 - What does it do to extract the third-party permission Library of an interface, that is, to provide permission application verification, to sob its public function is 1, to give parameters and then execute, which is so simple, why, because the function is single, even if the request for verification permission

interface IPermissionExecuter {

    fun run(permissionConfig: PermissionConfig)
}

2.2 - Implement interfaces, wrap third-party libraries

class AndPermissinExecuterImpl : IPermissionExecuter {

    override fun run(permissionConfig: PermissionConfig) {
        AndPermission.with(permissionConfig.context)
                .permission(permissionConfig.permissions.toTypedArray())
                // User granted permission
                .onGranted({ permissions: List<String> -> permissionConfig.onSuccessAction() })
                // User denies permission, including no longer displaying permission pop-ups
                .onDenied({ permissions: List<String> ->
                    // Determine if the user is no longer displaying the permissions pop-up, and if not enter the permissions settings page
                    if (AndPermission.hasAlwaysDeniedPermission(permissionConfig.context, permissions)) {
                        // Open Permission Settings Page
                        permissionConfig.onDontShowAction()
                        return@onDenied
                    }
                    permissionConfig.onDenialAction()
                })
                .start()
    }
}

Third-party permission libraries require essentially the same parameters. Whatever it is, we wrap it in PermissionConfig and pass it in through PermissionConfig so that if we want to replace a third-party implementation, we just need to write another implementation class for IPermissionExecuter

2.3 - Provide a factory class for switch management

Needless to say, factory mode, hit familiar routines

object ExecuterFactor {

    @JvmField
    val AND_PERMISSION = "AND_PERMISSION"

    @JvmField
    val RX_PERMISSION = "RX_PERMISSION"

    @JvmField
    val DEFAULT_EXECUTER = AND_PERMISSION

    @JvmStatic
    fun getInstance(): IPermissionExecuter {
        return getInstance(DEFAULT_EXECUTER)
    }

    @JvmStatic
    private fun getInstance(type: String): IPermissionExecuter {

        return when (type) {
            AND_PERMISSION -> AndPermissinExecuterImpl()
            DEFAULT_EXECUTER -> AndPermissinExecuterImpl()
            else -> AndPermissinExecuterImpl()
        }
    }
}

3. Extract common parameters and use build to build

Above we wrapped the parameters required by a third party into a class called PermissionConfig. Let's look at this class

class PermissionConfig {

    lateinit var context: Context
    // Callback on successful permission application
    var onSuccessAction: () -> Unit = {}
    // Callback if permission application fails
    var onDenialAction: () -> Unit = {}
    // Callback when user settings do not display permission request pop-up window
    var onDontShowAction: () -> Unit = {}
    // Permission Set
    var permissions = mutableListOf<String>()

    private var type: String = ExecuterFactor.DEFAULT_EXECUTER

    /**
     * add permission
     */
    fun addPermission(permission: String) {
        if (!permission.isEmpty()) permissions.add(permission)
    }

    /**
     * Setting Type
     */
    fun setType(type: String): PermissionConfig {
        if (!type.isEmpty()) this.type = type
        return this
    }

    /**
     * Perform operation
     */
    fun run() {
        ExecuterFactor.getInstance().run(this)
    }
}

With the help of kotlin's language, we should no longer write an interface like java, just declare an empty implementation, and there is no null problem. Then provide the parameters for setting these parameters.Number method is sufficient

But let's not use PermissionConfig directly, because it will change over time and we need a unified place to build parameter wrapper objects. This is the familiar build pattern.

class PermissionBuild(var context: Context) {

    var permissionConfig: PermissionConfig = PermissionConfig()

    init {
        permissionConfig.context = this.context
    }

    /**
     * Setting Type
     */
    fun type(type: String): PermissionBuild {
        if (!type.isEmpty()) permissionConfig.setType(type)
        return this
    }

    /**
     * add permission
     */
    fun permission(permission: String): PermissionBuild {
        if (!permission.isEmpty()) permissionConfig?.addPermission(permission)
        return this
    }

    /**
     * Add Successful Action
     */
    fun onSuccess(onSuccessAction: () -> Unit): PermissionBuild {
        if (onSuccessAction != null) permissionConfig.onSuccessAction = onSuccessAction
        return this
    }

    /**
     * Add Failed Action
     */
    fun onDenial(onDenialAction: () -> Unit): PermissionBuild {
        if (onDenialAction != null) permissionConfig.onDenialAction = onDenialAction
        return this
    }

    /**
     * Add no permission pop-up operation
     */
    fun onDontShow(onDontShowAction: () -> Unit): PermissionBuild {
        if (onDontShowAction != null) permissionConfig.onDontShowAction = onDontShowAction
        return this
    }

    /**
     * Perform operation
     */
    fun run() {
        permissionConfig.run()
    }
}

Here, the build logic is very simple, it can be written without writing, according to the principle of practice, or thanks to kotlin's grammar, the method can directly receive function parameters, and we do not need to write interfaces anymore. It is really convenient, especially when we write, it can be done at once, without switching classes back and forth, and without interrupting our thinking is very good.

4. Provide unified access

As a tool class, there must be a unified entry, either static or new object. Here, static method is recommended for easy understanding.

class PermissionManage {

    /**
     * Provides relevant static entries, mimicking Glide's binding context via with
     *
     */
    companion object {

        @JvmStatic
        fun with(context: Context): PermissionBuild {
            return PermissionBuild(context)
        }

        @JvmStatic
        fun isHavePermission(context: Context, permission: String): Boolean {
            return PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(context, permission)
        }

        @JvmStatic
        fun isHavePermissions(context: Context, permissions: List<String>): Boolean {

            for (it in permissions) {
                if (!(PackageManager.PERMISSION_GRANTED == ContextCompat.checkSelfPermission(context, it))) return false
            }
            return true
        }
    }
}

Similar to Glide.with returning the PermissionBuild builder to add data after adding context, there are other tool methods to query permissions

I am moving backwards and forwards in order, the idea is based on step-by-step implementation, the intermediate classes are based on the purpose of I want to package third-party libraries step by step, simple function libraries are basically based on this perspective

Look at the class structure

This component is very simple. Actually, I have written this permission component in Java before. The same way of thinking is here: Simple functional encapsulation of the permission open source library. At first, I wanted to change java to kotlin, but overwrite it. Finally, I wrote it once, basically discarding the previous reorganization idea. The old component now seems to be too much rubbish and I'm poorNot more than half of the classes were deleted, and I reconsidered the naming, which was basically changed in half. This naming was the hardest one for me

New class structure:

Old Class Structure:

Isn't that easy to see? It's simple but it's good to accomplish our goals, wrap third-party components, provide unified API implementations, and dynamically seamlessly switch third-party implementations

Number We used several routines:

  • Extract the same, Abstract different - template schema wraps third-party implementations, extract run performs this action, wraps all parameters into a uniform configuration class
  • Unified switch between different third-party implementations-factory mode
  • build Unified build Data-Constructor Mode
  • Provide Unified Interface-Door Panel Mode

The above are basically routines. The function of this component is single. You may not realize the magic of these routines. This thing can only be understood and deepened by masturbation. You have to write your own handwriting to have a personal experience, so you can finally glory through. UML class diagrams can be put on again if you have time

Last spit

When I wrote this component, Nima forgot to declare the required permissions in the configuration file during the test. How can I debug it? I checked the code many times and found no problem. I was so frustrated and depressed that I was in a bad mood and wasted more than two hours. Then it came to mind that Nima pumped his mouth for 5 minutes, tooLost.

I advise you to be calm when you encounter problems. The consequence of not being calm is wasting your life. It's very simple, and the code is well written. Once you've done it, you forget the configuration file. Everything else is OK, but you prefer things to go bad. Oops, be calm. Never be calm when things happen, or you will be guilty.

There is also fragmentation problem, millet, charm race, Huawei mobile phone public code is unable to open the permission settings page, it is really a pain to adapt, I took Baidu next, good to find directly, wrote the tool class, but after considering, this tool opens the system page, I did not put it in the permission components, talk about "Wind Horses and Cows" from the work energy point of viewAnd things can not be put together, this class is called: IntentUtils, specific I can not find this class in Demo.

Source address:
https://github.com/zb25810045/BW_Libs

Last

In fact, for programmers, there are too many knowledge content and technology to learn. To not be obsolete by the environment, we can only continuously improve ourselves. We are always adapting to the environment, not the environment to adapt to us!

Attached here are dozens of interview questions from Tencent, Headlines, Ali, Meituan and other companies related to the above technical system maps. The technical points are organized into videos and PDF s (which actually take a lot more effort than expected), including the knowledge context + many details. Due to the limited space, this part is shown in the form of pictures.

I believe it will bring you a lot of benefits:

The above [High Definition Technological Brain Map] and [Supporting Architectural Technologies PDF] can be obtained for free by adding me wx:X1524478394!

It's easy to be a programmer, to be a good programmer, to learn constantly, from a junior programmer to a senior programmer, from a junior architect to a senior architect, or to move to management, from a technical manager to a technical director, each stage requires different abilities.Set your career direction early so that you can shake off your peers in your work and capacity development.

Tags: Android Programming Java Mobile github

Posted on Thu, 09 Jan 2020 13:18:30 -0500 by wednesday