回調函數在-Kotlin-裏的奇妙玩法

本文涉及到的知識點有:擴展函數、Lambda 表達式的高級應用

在 Android 6.0 之後系統加強了對敏感權限的管理,一些敏感權限必須要通過動態權限申請來獲得,本文的內容就從這裏展開;

一個正常的權限申請流程大致是這樣的:

  1. 檢查是否存在權限
  2. 如果不存在則申請,存在則進入功能
  3. 如果用戶拒絕則彈出對話框告知用戶權限的用處,並提供跳轉到設置頁面的功能;

我使用的是網上比較流行的一個權限申請框架 # tbruyelle/RxPermissions ,當然本文的重點並不是如何使用這個庫。

如上所述,我們在一個應用中可能會有很多需要申請不同權限的位置,我們應該爲每處需要敏感權限的位置做類似的處理。雖然我們使用了 RxPermissions,但是還是需要在用戶拒絕的位置寫大量重複的彈窗提示代碼,這一點也不優雅。

我們都知道在 Kotlin 中函數也是可以作爲參數傳入的,說道到這裏不知道你有沒有想起點什麼,我們在使用 Java 編寫 Android 代碼時時常使用的各種 Listener,不就是類似這樣的一個情況麼?

我們調用 setXXXListener 函數,並且傳入一個接口的匿名內部類實現,這樣的操作我們稱之爲回調,我們傳入的這個 Listener 被中的方法稱爲回調函數,在 Kotlin 裏我們也可以按照這種方式來書寫:

//Java中的匿名內部類,在 Kotlin 中可以使用 object 實現
mBtnCallback.setOnClickListener(object :View.OnClickListener{
    override fun onClick(v: View?) {
        println("onclick")
    }

也許你在書寫類似代碼時已經發現了一些端倪:
Lambda 表達式

所有的類似接口,在 Kotlin 中都有一些新的簽名,這是因爲在 Kotlin 裏函數也是參數的一種,在 Java 中只包含一個方法的接口,在 Kotlin 中都可以使用 Lambda 表達式來達成一樣的效果。

這樣做最大的好處就是簡化代碼,當我們在閱讀代碼時更加簡潔易讀,比如上述代碼完全可以簡化成:

 mBtnCallback.setOnClickListener { println("onclick") }

非但如此,以前我們在寫一些耗時操作時,通常需要申明一些接口作爲回調函數使用,在調用時再用匿名內部類來操作得到的結果,現在你可以這樣書寫了:

fun doSomeThingNeedTime(call: (response:String) -> Unit) {
    Thread.sleep(4000)
    var response = "xxxx"  //我們假裝這裏進行了網絡請求
    call(response)
}

doSomeThingNeedTime { response->
            println(response)
        }

###實際應用
在我們實際應用時其實十分簡單,下面的代碼也許可以幫你加深對其瞭解:

//傳入權限與權限描述,在需要權限的功能打開之前調用
fun Activity.rxRequestPermissions(vararg permissions: String, describe: String, onGranted:()->Unit) {
    val keylistener = DialogInterface.OnKeyListener { _, keyCode, event ->
        keyCode == KeyEvent.KEYCODE_BACK && event.repeatCount == 0
    }
    var dialog = AlertDialog.Builder(this)
            .setTitle("權限申請")
            .setMessage("${describe}爲必選項,開通後方可正常使用APP,請在設置中開啓。")
            .setOnKeyListener(keylistener)
            .setCancelable(false)
            .setPositiveButton("去開啓") { _, _ ->
//                JumpPermissionManagement.GoToSetting(this)
                finish()
            }
            .setNegativeButton("結束") { _, _ ->
                Toast.makeText(this, "${describe}權限未開啓,不能使用該功能!", Toast.LENGTH_SHORT).show()
                finish()
            }
            .create()
    val rxPermissions = RxPermissions(this)
    //傳遞kotlin的可變長參數給Java的可變參數的時候需要使用修飾符 * ;這個修飾符叫做Speread Operator
    // 它只支持展開的Array 數組,不支持List集合,它只用於變長參數列表的實參,不能重載,它也不是運算符;
    rxPermissions.request(*permissions)
            .subscribe {granted ->
                if (granted) {
                    onGranted()
                } else {
                    dialog.show()
                }
            }
}

上述代碼就是我寫的 Activity 的一個擴展函數,用於實現我們前文提到的更方便的動態申請權限,請看我們的函數申明:

fun Activity.rxRequestPermissions(vararg permissions: String, describe: String, onGranted:()->Unit) {
 }

我們傳入的前三個參數是分別是:要申請的權限(對於我們的擴展函數而言是一個可變長參數),第二個參數我們使用“key = value” 這種形式傳遞,第三個參數就一個回調函數onGranted:()->Unit

其中 onGranted,是我們自己命名的函數名,冒號後面是我們的函數描述即:沒有傳入參數,返回值類型爲 Unit。在我們的擴展函數體中直接將他作爲一個函數調用即可,需要注意的是必須要填寫括號,AS的自動補全並不會補全這個括號,沒有括號時編譯器也不會報錯。

如何使用
在打開需要申請權限的功能位置,我們只要寫下以下的數行代碼即可:

mBtnRecord.setOnClickListener {
    rxRequestPermissions(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.RECORD_AUDIO, describe = "相機、存儲、錄音") {
        startActivityForResult(Intent(this@MainActivity, VideoRecordActivity::class.java), REQUEST_VIDEO)
    }
}

首先傳入需要申請的權限、權限描述(key = value),第三個參數爲一個Lambda表達式,這裏進行的是存在權限時需要執行的操作。Lambda 表達式作爲函數的最後的一個參數時,我們可以把它放在圓括號外書寫。

最終使用時的效果如下:
點擊時請求權限

未授予全部權限

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章