[6] —— 回撥函式在 Kotlin 裡的奇妙玩法
本文涉及到的知識點有:擴充套件函式、Lambda 表示式的高階應用
在 Android 6.0 之後系統加強了對敏感許可權的管理,一些敏感許可權必須要通過動態許可權申請來獲得,本文的內容就從這裡展開;
一個正常的許可權申請流程大致是這樣的:
- 檢查是否存在許可權
- 如果不存在則申請,存在則進入功能
- 如果使用者拒絕則彈出對話方塊告知使用者許可權的用處,並提供跳轉到設定頁面的功能;
我使用的是網上比較流行的一個許可權申請框架 # ofollow,noindex">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 表示式來達成一樣的效果。
在我們實際應用時其實十分簡單,下面的程式碼也許可以幫你加深對其瞭解:
//傳入許可權與許可權描述,在需要許可權的功能開啟之前呼叫 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 表示式作為函式的最後的一個引數時,我們可以把它放在圓括號外書寫。
最終使用時的效果如下:

點選時請求許可權

未授予全部許可權