本文連接:http://blog.csdn.net/qq_16628781/article/details/61623502css
Android之運行時相機權限和聯繫人權限獲取(最後又源碼能夠下載下來看)html
知識點:java
一、Android M 及以上系統的動態權限申請;android
二、知識名詞記錄git
{github
CameraPreview:自定義相機預覽類app
ViewAnimator:配合framelayout使用,在兩個view之間切換時,會有切換動畫ide
ContactsContract:佈局
CursorLoader:動畫
ContentProviderOperation:插入聯繫人用
讀取聯繫人列表;
}(這裏我決定,每篇文章中遇到的「新名詞」我都會記錄在這裏,做爲本身下一步須要瞭解的知識;你們有興趣的話,也能夠循着這些新的專業名詞,一步一步的走向Android更深的「泥潭」)
若是你有持續關注Android官方最新的SDK版本的話,你就會知道,Android官方對權限的管理變得原來越嚴格了。在5.0以後,權限再也不是所有都在manifest文件裏頭申請就OK了,你APP要的權限,當用到的時候,系統纔會賦予給你。
固然,你能夠這樣作,可是要把你的targetSdkVersion 變爲22如下,這樣是能夠暫時避免動態申請權限引發的「麻煩事」,可是這是一個不可逆的趨勢,咱們必需要順從它,而不是抗拒它,何況緊靠幾我的幾個應用,那是螳臂當車。
權限分爲:普通權限,危險權限和系統權限;
普通權限:這些權限對於用戶隱私和設備操做不會形成太多危險,主要有:
ACCESS_LOCATION_EXTRA_COMMANDS ACCESS_NETWORK_STATE ACCESS_NOTIFICATION_POLICY ACCESS_WIFI_STATE BLUETOOTH BLUETOOTH_ADMIN BROADCAST_STICKY CHANGE_NETWORK_STATE CHANGE_WIFI_MULTICAST_STATE CHANGE_WIFI_STATE DISABLE_KEYGUARD EXPAND_STATUS_BAR GET_PACKAGE_SIZE INSTALL_SHORTCUT INTERNET KILL_BACKGROUND_PROCESSES MODIFY_AUDIO_SETTINGS NFC READ_SYNC_SETTINGS READ_SYNC_STATS RECEIVE_BOOT_COMPLETED REORDER_TASKS REQUEST_INSTALL_PACKAGES SET_ALARM SET_TIME_ZONE SET_WALLPAPER SET_WALLPAPER_HINTS TRANSMIT_IR UNINSTALL_SHORTCUT USE_FINGERPRINT VIBRATE WAKE_LOCK WRITE_SYNC_SETTINGS
危險權限,主要是包含產生費用或者是讀取用戶隱私的權限,包含:
group:android.permission-group.CONTACTS permission:android.permission.WRITE_CONTACTS permission:android.permission.GET_ACCOUNTS permission:android.permission.READ_CONTACTS group:android.permission-group.PHONE permission:android.permission.READ_CALL_LOG permission:android.permission.READ_PHONE_STATE permission:android.permission.CALL_PHONE permission:android.permission.WRITE_CALL_LOG permission:android.permission.USE_SIP permission:android.permission.PROCESS_OUTGOING_CALLS permission:com.android.voicemail.permission.ADD_VOICEMAIL group:android.permission-group.CALENDAR permission:android.permission.READ_CALENDAR permission:android.permission.WRITE_CALENDAR group:android.permission-group.CAMERA permission:android.permission.CAMERA group:android.permission-group.SENSORS permission:android.permission.BODY_SENSORS group:android.permission-group.LOCATION permission:android.permission.ACCESS_FINE_LOCATION permission:android.permission.ACCESS_COARSE_LOCATION group:android.permission-group.STORAGE permission:android.permission.READ_EXTERNAL_STORAGE permission:android.permission.WRITE_EXTERNAL_STORAGE group:android.permission-group.MICROPHONE permission:android.permission.RECORD_AUDIO group:android.permission-group.SMS permission:android.permission.READ_SMS permission:android.permission.RECEIVE_WAP_PUSH permission:android.permission.RECEIVE_MMS permission:android.permission.RECEIVE_SMS permission:android.permission.SEND_SMS permission:android.permission.READ_CELL_BROADCASTS
上圖:
這裏我就只是貼一個主要的頁面代碼就行了,我這裏只是要一個觸發相機和聯繫人的動做就行了,代碼裏頭都有說明。
import android.Manifest; import android.content.pm.PackageManager; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; import android.support.v4.app.FragmentTransaction; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.ViewAnimator; import com.example.android.common.logger.Log; import com.example.android.common.logger.LogFragment; import com.example.android.common.logger.LogWrapper; import com.example.android.common.logger.MessageOnlyLogFilter; import com.example.android.system.runtimepermissions.camera.CameraPreviewFragment; import com.example.android.system.runtimepermissions.contacts.ContactsFragment; import common.activities.SampleActivityBase; public class MainActivity extends SampleActivityBase implements ActivityCompat.OnRequestPermissionsResultCallback { public static final String TAG = "MainActivity"; /* 相機請求碼 */ private static final int REQUEST_CAMERA = 0; /* 聯繫人請求碼 */ private static final int REQUEST_CONTACTS = 1; /* 請求讀取聯繫人權限 */ private static String[] PERMISSIONS_CONTACT = {Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS}; // 標誌log fragment是否顯示 private boolean mLogShown; /* 主頁的佈局,依靠動態加載進來 */ private View mLayout; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLayout = findViewById(R.id.sample_main_layout); if (savedInstanceState == null) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); RuntimePermissionsFragment fragment = new RuntimePermissionsFragment(); transaction.replace(R.id.sample_content_fragment, fragment); transaction.commit(); } initializeLogging(); } /** * 點擊顯示聯繫人按鈕相應 * <p> * 回調已經被定義好了 */ public void showCamera(View view) { Log.i(TAG, "檢查權限是否被受理!"); // 檢查是否想要的權限申請是否彈框。若是是第一次申請,用戶不經過, // 那麼第二次申請的話,就要給用戶說明爲何須要申請這個權限 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { // 權限未被授予 requestCameraPermission(); } else { Log.i(TAG, "相機權限已經被受理,開始預覽相機!"); showCameraPreview(); } } /** * 申請相機權限 */ private void requestCameraPermission() { Log.i(TAG, "相機權限未被授予,須要申請!"); // 相機權限未被授予,須要申請! if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)) { // 若是訪問了,可是沒有被授予權限,則須要告訴用戶,使用此權限的好處 Log.i(TAG, "申請權限說明!"); Snackbar.make(mLayout, R.string.permission_camera_rationale, Snackbar.LENGTH_INDEFINITE) .setAction(R.string.ok, new View.OnClickListener() { @Override public void onClick(View view) { // 這裏從新申請權限 ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA); } }) .show(); } else { // 第一次申請,就直接申請 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA); } } public void showContacts(View v) { // 判斷權限是否擁有 if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED) { Log.i(TAG, "讀寫聯繫人權限未被授予,須要申請!"); // 讀寫聯繫人權限未被授予,須要申請! requestContactsPermissions(); } else { // 權限已經被授予,顯示細節頁面! Log.i(TAG, "權限已經被授予,顯示細節頁面!"); showContactDetails(); } } /** * 申請聯繫人讀取權限 */ private void requestContactsPermissions() { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_CONTACTS)) { // 若是是第二次申請,須要向用戶說明爲什麼使用此權限,會帶出一個再也不詢問的複選框! Log.i(TAG, "若是是第二次申請,須要向用戶說明爲什麼使用此權限,會帶出一個再也不詢問的複選框!"); Snackbar.make(mLayout, R.string.permission_contacts_rationale, Snackbar.LENGTH_INDEFINITE) .setAction(R.string.ok, new View.OnClickListener() { @Override public void onClick(View view) { ActivityCompat .requestPermissions(MainActivity.this, PERMISSIONS_CONTACT, REQUEST_CONTACTS); } }) .show(); } else { // 第一次申請此權限,直接申請 ActivityCompat.requestPermissions(this, PERMISSIONS_CONTACT, REQUEST_CONTACTS); } } /** * 顯示相機預覽界面 */ private void showCameraPreview() { getSupportFragmentManager().beginTransaction() .replace(R.id.sample_content_fragment, CameraPreviewFragment.newInstance()) .addToBackStack("contacts") .commit(); } /** * 顯示聯繫人頁面 */ private void showContactDetails() { getSupportFragmentManager().beginTransaction() .replace(R.id.sample_content_fragment, ContactsFragment.newInstance()) .addToBackStack("contacts") .commit(); } /** * 申請權限的回調, * * @param requestCode requestCode * @param permissions permissions * @param grantResults grantResults 多個權限一塊兒返回 */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_CAMERA) { if (grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { Snackbar.make(mLayout, R.string.permision_available_camera, Snackbar.LENGTH_SHORT).show(); } else { Snackbar.make(mLayout, R.string.permissions_not_granted, Snackbar.LENGTH_SHORT).show(); } } else if (requestCode == REQUEST_CONTACTS) { // 這裏有個多權限的檢查,須要檢查每個權限是否都被受權了 if (PermissionUtil.verifyPermissions(grantResults)) { // true,全部權限已經被授予 Snackbar.make(mLayout, R.string.permision_available_contacts, Snackbar.LENGTH_SHORT) .show(); } else { // false,並非全部權限都被授予 Snackbar.make(mLayout, R.string.permissions_not_granted, Snackbar.LENGTH_SHORT).show(); } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); return true; } @Override public boolean onPrepareOptionsMenu(Menu menu) { MenuItem logToggle = menu.findItem(R.id.menu_toggle_log); logToggle.setVisible(findViewById(R.id.sample_output) instanceof ViewAnimator); logToggle.setTitle(mLogShown ? R.string.sample_hide_log : R.string.sample_show_log); return super.onPrepareOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_toggle_log: mLogShown = !mLogShown; ViewAnimator output = (ViewAnimator) findViewById(R.id.sample_output); if (mLogShown) { output.setDisplayedChild(1); } else { output.setDisplayedChild(0); } supportInvalidateOptionsMenu(); return true; } return super.onOptionsItemSelected(item); } /** * 初始化log日誌 */ @Override public void initializeLogging() { LogWrapper logWrapper = new LogWrapper(); Log.setLogNode(logWrapper); MessageOnlyLogFilter msgFilter = new MessageOnlyLogFilter(); logWrapper.setNext(msgFilter); LogFragment logFragment = (LogFragment) getSupportFragmentManager() .findFragmentById(R.id.log_fragment); msgFilter.setNext(logFragment.getLogView()); } public void onBackClick(View view) { // 由於咱們對fragment入棧處理,按返回鍵的時候,出棧處理 getSupportFragmentManager().popBackStack(); } }
ActivityCompat.requestPermissions(final @NonNull Activity activity, final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode){}
第二,是申請的結果回調方法:
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults){}
以上就是主要的兩個方法了。
固然,還要注意一下,是不是第一次申請權限?需不須要想用戶說明我這個權限是用來幹嗎的?若是用戶不受權,個人APP該如何來操做防止崩潰?用戶隨時能夠取消對你APP的受權,那時咱們又該如何來作?
問題好多好多,咱們要走的路還很長。可是我這裏有幾個方法,適合你們去咀嚼咀嚼。
第一個:
ActivityCompat.shouldShowRequestPermissionRationale(this,
Manifest.permission.READ_CONTACTS)
這個方法呢,說的是,當你第一次去請求權限的時候,若是用戶拒絕了,而後第二次再去申請此權限,那麼這個方法會返回一個true的結果,告訴你,上一次用戶不一樣意給你這個權限,而後此次你就須要向用戶說明爲何你須要這個權限。
咱們看到的效果圖,是有相機預覽和查看聯繫人的頁面的,這裏我就不一一把代碼貼出來,我會在最後面把工程代碼放到GitHub上面,給你們下載。裏面有更加詳細的說明。
代碼下載:點擊打開連接
若有任何問題,請及時與我聯繫,謝謝!