更換頭像

更換頭像的功能基本在每個app都有的,而且是最基本的,我前段時間做了下項目中的更換頭像功能,記錄下遇到的坑

  • android 7.0之後需要動態申請權限
  • Android 7.0 file://開頭的被認爲是不合法的uri,需要轉成content://形式的uri

第一個坑,權限問題

如果您的應用在其清單中列出正常權限(即,不會對用戶隱私或設備操作造成很大風險的權限),系統會自動授予這些權限。如果您的應用在其清單中列出危險權限(即,可能影響用戶隱私或設備正常操作的權限),系統會要求用戶明確授予這些權限。Android 發出請求的方式取決於系統版本,而系統版本是應用的目標:

  • 如果設備運行的是 Android 6.0(API 級別 23)或更高版本,並且應用的 targetSdkVersion 是 23 或更高版本,則應用在運行時向用戶請求權限。用戶可隨時調用權限,因此應用在每次運行時均需檢查自身是否具備所需的權限。
  • 如果設備運行的是 Android 5.1(API 級別 22)或更低版本,並且應用的 targetSdkVersion 是 22 或更低版本,則系統會在用戶安裝應用時要求用戶授予權限。如果將新權限添加到更新的應用版本,系統會在用戶更新應用時要求授予該權限。用戶一旦安裝應用,他們撤銷權限的唯一方式是卸載應用。

系統權限分類:

  • 正常權限涵蓋應用需要訪問其沙盒外部數據或資源,但對用戶隱私或其他應用操作風險很小的區域。例如,設置時區的權限就是正常權限。如果應用聲明其需要正常權限,系統會自動向應用授予該權限。
  • 危險權限涵蓋應用需要涉及用戶隱私信息的數據或資源,或者可能對用戶存儲的數據或其他應用的操作產生影響的區域。例如,能夠讀取用戶的聯繫人屬於危險權限。如果應用聲明其需要危險權限,則用戶必須明確嚮應用授予該權限。

當你進行拍照和從相冊中選圖片的時候,都需要一個讀和寫的權限即Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE,

申請權限邏輯:

判斷用戶是否進行過授權

1.沒有授權
	1.授權被拒絕過,則彈出彈窗引導用戶進行授權
	2.授權沒有被拒絕過,則彈出系統請求權限的彈窗即可
2.已經授權,則按照業務流程繼續往下走

具體代碼如下:

/**
         * 檢查並且獲取權限
         *
         * @param context              上下文
         * @param permissionParms      申請權限參數
         * @param requestCode          回調code
         * @param needShowCustomDialog 是否需要彈自定義對話框
         * @return
         */
        fun checkAndRequestPermission(context : Context, permissionParms : String ,requestCode : Int, needShowCustomDialog : Boolean) : Boolean {
            //用來記錄是否向系統申請過權限
            val isInvokedSysMethod = SPUtil.getInstance().getBoolean(PERMISSION_PREFIX + permissionParms, false)
            try {
                val permission = ContextCompat.checkSelfPermission(context, permissionParms)
                if (hasPermission(permission, permissionParms)) {
                    return true
                } else {
                    /** 是否拒絕過授權  */
                    val isRejected =
                        ActivityCompat.shouldShowRequestPermissionRationale(context as Activity, permissionParms)
                    if ((isRejected || permission == PackageManager.PERMISSION_GRANTED || isInvokedSysMethod) && needShowCustomDialog) {
                        /**
                         * 1. 用戶拒絕過授權
                         * 2. 系統接口判斷爲已經授權,但實際上沒有授權的
                         */

                        //TODO 彈出彈窗引導用戶
                    } else {
                        SPUtil.getInstance().putBoolean(PERMISSION_PREFIX + permissionParms, true)
                        ActivityCompat.requestPermissions(context as Activity, arrayOf(permissionParms), requestCode)
                    }
                    return false
                }
            } catch (e: Exception) {
                LogUtil.printeException(e)
            }

            return false

        }

當你在檢測用戶是否授權的時候,會碰到一個機型適配的問題

/**
         * 是否真的獲取了權限
         *
         * @param apiPermissionResult 系統接口返回的權限結果
         * @param permissonParams     權限參數
         * @return
         */
        fun hasPermission(apiPermissionResult: Int, permissonParams: String): Boolean {

            /**
             * 沒有授權,直接返回false
             */
            if (apiPermissionResult != PackageManager.PERMISSION_GRANTED) {
                return false
            }

            /**
             * EUI系統,返回接口值
             */
            if (mIsEui) {
                return true
            }

            /**
             * 小米手機(以及zuk等其他機型),即使接口返回授權,也未必真的授權,需要通過AppOpsManager再次判斷
             */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                val appOpsManager = CommonLib.getApp().getSystemService(Context.APP_OPS_SERVICE) as AppOpsManager
                var opStr = AppOpsManager.OPSTR_READ_EXTERNAL_STORAGE
                if (TextUtils.isEmpty(opStr)) {
                    throw RuntimeException("不在監控權限池,請先添加!!!")
                }
                try {
                    val checkOp = appOpsManager.checkOp(opStr, Process.myUid(), CommonLib.getApp().packageName)
                    return checkOp == AppOpsManager.MODE_ALLOWED
                } catch (e: Exception) {
                    e.printStackTrace()
                }

            }
            /**
             * 未判斷到小米是否真的授權,返回接口值,出問題小米自己背鍋
             */
            return true
        }

到目前爲止動態申請權限問題解決了

第二個坑就是Android 7.0,file://開頭的被認爲是不合法的uri,需要轉成content://形式的uri,否則會報錯FileUriExposedException

1.第一步

    <provider
                   android:name="android.support.v4.content.FileProvider"
                   android:authorities="${applicationId}.fileprovider"
                   android:exported="false"
                   android:grantUriPermissions="true">
               <meta-data
                       android:name="android.support.FILE_PROVIDER_PATHS"
                       android:resource="@xml/file_paths"/>
   </provider>

2.第二 步
file_paths
3.第三步

USER_HEAD_PHOTO_FILE_NAME = System.currentTimeMillis().toString() + USER_HEAD_PHOTO_FILE_NAME
        var headFile = File(Environment.getExternalStorageDirectory(), USER_HEAD_PHOTO_FILE_NAME)
        var contentUri: Uri
        var intentCamera = Intent()
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

            if (PermissionUtil.checkAndRequestPermission(this, Manifest.permission.CAMERA, REQUEST_CAMERA, true)) {
                //畫重點
                intentCamera.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
                contentUri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".fileprovider", headFile)
            } else {
                return
            }
        } else {
            contentUri = Uri.fromFile(headFile)
        }
        intentCamera.action = MediaStore.ACTION_IMAGE_CAPTURE
        intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, contentUri)
        startActivityForResult(intentCamera, REQUESTCODE_TAKE)

到此結束

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