一、遇到奇葩問題
最近的項目需求有個NFC刷卡的功能,按照正常的流程,在AndroidManifest.xml
中添加<uses-permission android:name="android.permission.NFC" />
,然後在Activity中正常處理NFC邏輯就行了,我也是這麼幹的,我的一加七妥妥的正常運行,但是測試拿着一臺小米9來跟我說有bug,what the fxxx?
如上圖,NFC的權限級別不是normal麼?爲什麼小米會有這個選項?我的一加七就沒有。重點是小米如果把這個NFC權限設爲拒絕,那就不能正常讀卡了。那麼問題來,如何判斷用戶是否已經允許了這個NFC權限呢?
SoEasy啦,很自然的敲下代碼,ContextCompat.checkSelfPermission(this, Manifest.permission.NFC)
,結果呢?無論我把NFC設置成“允許
”還是“拒絕
”,這個方法都返回PERMISSION_GRANTED
!!!那麼問題又來了,官方的API已經不能滿足了,我們該如何優雅的判斷用戶是否已經允許了NFC權限呢?
二、解決方法
這裏直接上解決方法,後面再一步一步記錄一下是怎麼找到這個方法的,趕進度的你們直接複製這個代碼就行了,後面不用看了。
/**
* @param op
* * op=10016 對應 NFC
* * op=10021 對應 後臺彈出界面
* * 其它未知,根據博客的方法自己去找你需要的
* @return true爲允許,false爲詢問或者拒絕。
*/
fun checkOpPermission(context: Context, op: Int): Boolean {
return try {
val manager = context.getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
val method = manager.javaClass.getMethod("checkOpNoThrow", Int::class.java, Int::class.java, String::class.java)
val result = method.invoke(manager, op, Process.myUid(), context.packageName)
AppOpsManager.MODE_ALLOWED == result
} catch (e: Exception) {
e.printStackTrace()
false
}
}
三、尋找解決方法的過程
碰到問題肯定先百度啦,百度了各種方法都不起作用,各種被虐啊,後來經同事提醒,找到了這個博客https://blog.csdn.net/GuangkuoDing/article/details/100324162,這種方法很類似啊,但是它是判斷“後臺彈出界面”權限的,op=10021
,一個op對應這一個權限選項,那麼問題來了,我怎麼知道“NFC”權限的op是多少呢?
經過各種摸索,終於讓我找到了思路,理論上不僅可以找到NFC的,還可以找到所有你看得到的,你需要的。
其實這之前我還嘗試了從源碼中尋找啊,查看系統LOG啊等等方法,但是都走不通,纔想到這個的,本來想全都寫下來的,想想還是算了,簡單粗暴點,直接給結果。
思路:
1、遍歷一個範圍內的op,看看那些沒有拋異常,就證明那些op有對應的選項,這樣就可以知道一個大概範圍了。
2、手動把全部權限都關了,只允許NFC權限,遍歷一下剛纔那個範圍的op,會得到一堆已允許的op,其中必定有一個是NFC。
3、把NFC權限也拒絕了,遍歷一下上面得到的那一堆已允許的op,其中必定有一個是拒絕的,這個就是NFC了。
1、遍歷一定範圍op
for (op in 10000..10030) {
try {
val manager = getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
val method = manager.javaClass.getMethod("checkOpNoThrow", Int::class.java, Int::class.java, String::class.java)
method.invoke(manager, op, Process.myUid(), packageName)
Log.i("hello", "op=$op 正常op")
} catch (e: Exception) {
Log.i("hello", "op=$op 拋異常,無意義op")
}
}
這裏只是遍歷了10000…10030,是我隨機寫的,沒想到剛剛好,下面是打印結果,從結果來看op的範圍就是10001-10027。
2019-11-28 16:37:47.779 11103-11103/com.audient.androidall I/hello: op=10000 拋異常,無意義op
2019-11-28 16:37:47.780 11103-11103/com.audient.androidall I/hello: op=10001 正常op
2019-11-28 16:37:47.780 11103-11103/com.audient.androidall I/hello: op=10002 正常op
2019-11-28 16:37:47.780 11103-11103/com.audient.androidall I/hello: op=10003 正常op
2019-11-28 16:37:47.781 11103-11103/com.audient.androidall I/hello: op=10004 正常op
2019-11-28 16:37:47.781 11103-11103/com.audient.androidall I/hello: op=10005 正常op
2019-11-28 16:37:47.781 11103-11103/com.audient.androidall I/hello: op=10006 正常op
2019-11-28 16:37:47.781 11103-11103/com.audient.androidall I/hello: op=10007 正常op
2019-11-28 16:37:47.782 11103-11103/com.audient.androidall I/hello: op=10008 正常op
2019-11-28 16:37:47.782 11103-11103/com.audient.androidall I/hello: op=10009 正常op
2019-11-28 16:37:47.782 11103-11103/com.audient.androidall I/hello: op=10010 正常op
2019-11-28 16:37:47.782 11103-11103/com.audient.androidall I/hello: op=10011 正常op
2019-11-28 16:37:47.782 11103-11103/com.audient.androidall I/hello: op=10012 正常op
2019-11-28 16:37:47.783 11103-11103/com.audient.androidall I/hello: op=10013 正常op
2019-11-28 16:37:47.783 11103-11103/com.audient.androidall I/hello: op=10014 正常op
2019-11-28 16:37:47.783 11103-11103/com.audient.androidall I/hello: op=10015 正常op
2019-11-28 16:37:47.784 11103-11103/com.audient.androidall I/hello: op=10016 正常op
2019-11-28 16:37:47.784 11103-11103/com.audient.androidall I/hello: op=10017 正常op
2019-11-28 16:37:47.784 11103-11103/com.audient.androidall I/hello: op=10018 正常op
2019-11-28 16:37:47.784 11103-11103/com.audient.androidall I/hello: op=10019 正常op
2019-11-28 16:37:47.785 11103-11103/com.audient.androidall I/hello: op=10020 正常op
2019-11-28 16:37:47.785 11103-11103/com.audient.androidall I/hello: op=10021 正常op
2019-11-28 16:37:47.785 11103-11103/com.audient.androidall I/hello: op=10022 正常op
2019-11-28 16:37:47.785 11103-11103/com.audient.androidall I/hello: op=10023 正常op
2019-11-28 16:37:47.786 11103-11103/com.audient.androidall I/hello: op=10024 正常op
2019-11-28 16:37:47.786 11103-11103/com.audient.androidall I/hello: op=10025 正常op
2019-11-28 16:37:47.786 11103-11103/com.audient.androidall I/hello: op=10026 正常op
2019-11-28 16:37:47.786 11103-11103/com.audient.androidall I/hello: op=10027 正常op
2019-11-28 16:37:47.787 11103-11103/com.audient.androidall I/hello: op=10028 拋異常,無意義op
2019-11-28 16:37:47.787 11103-11103/com.audient.androidall I/hello: op=10029 拋異常,無意義op
2019-11-28 16:37:47.787 11103-11103/com.audient.androidall I/hello: op=10030 拋異常,無意義op
2、權限全關,只允許NFC權限,繼續遍歷
val allowedList = ArrayList<Int>()
for (op in 10001..10027) {
val allowed = checkOpPermission(this, op)
if (allowed) {
allowedList.add(op)
}
}
LogUtils.e("hello", allowedList)
allowedList代表當前所有已允許的權限op列表。
checkOpPermission在文章開頭有。
輸出結果爲:[10002, 10003, 10005, 10006, 10007, 10009, 10010, 10011, 10012, 10013, 10014, 10015, 10016, 10018, 10022, 10023, 10024, 10027]
證明NFC權限必然在這個列表裏面,繼續往下走。
3、拒絕NFC權限,繼續遍歷
for (op in arrayOf(10002, 10003, 10005, 10006, 10007, 10009, 10010, 10011, 10012, 10013, 10014, 10015, 10016, 10018, 10022, 10023, 10024, 10027)) {
val allowed = checkOpPermission(this, op)
if (!allowed) {
LogUtils.e("hello", "NFC權限就是這貨了,op=$op")
}
}
上面代碼輸出:NFC權限就是這貨了,op=10016
,大功告成!
四、總結
功夫不負有心人。
權限動態申請已經封裝成了一個kotlin文件了,拿來就用。傳送:https://blog.csdn.net/qiantujava/article/details/103402239