1、工具栏菜单 首先来看看Android工具栏的布局文件,如下所示:
<?xml version="1.0" encoding="utf-8"?><menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <!--Item对应于工具栏菜单的具体选项--> <item android:id="@+id/menu_item_new_crime" android:icon="@drawable/ic_menu_add" android:title="@string/new_crime" <!--showAsAction属性用于指定item是否显示在工具栏上 --> <!--ifRoom表示空间足够时,才会添加到工具栏 --> app:showAsAction="ifRoom|withText"/> <item android:id="@+id/menu_item_show_subtitle" android:title="@string/show_subtitle" app:showAsAction="ifRoom"/></menu>在Fragment中添加menu需要重写以下方法:
@Override//创建Menupublic void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); //将布局文件中的item填充到Menu实例中 inflater.inflate(R.menu.fragment_crime_list, menu); .............} @Override //定义选中item后的操作public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_item_new_crime: ........... //正常处理则返回true return true; case R.id.menu_item_show_subtitle: ........... return true; default: return super.onOptionsItemSelected(item); }}最后,若需要在Fragment中显示出菜单,还需调用setHasOptionsMenu函数,例如:
@Overridepublic void onCreate(Bundle bundle) { super.onCreate(bundle); setHasOptionsMenu(true); .............}此外,使用AppCompat包中的工具栏时,还支持设置子标题,如下图红色区域所示: 
子标题设置的代码,类似如下示例:
..........AppCompatActivity activity = (AppCompatActivity) getActivity();if (activity.getSupportActionBar() != null) { activity.getSupportActionBar().setSubtitle(subtitle);}..........2、实现层级导航 层级导航就是指工具栏中的向上箭头。 修改AndroidManifest.xml,就可以指定当前Activity的上一级,例如:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="stark.a.is.zhang.criminalintentapp"> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" <activity android:name=".activity.CrimeListActivity"> <intent-filter> <action android:name="android.intent.action.MAIN"/> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> <activity android:name=".activity.CrimePagerActivity" android:label="@string/app_name" <!--指定CrimePagerActivity的上一级为CrimeListActivity--> <!--因此,在CrimePagerActivity中点击向上箭头,将返回到CrimeListActivity--> android:parentActivityName=".activity.CrimeListActivity"> </activity> </application></manifest>点击向上箭头,实际上是创建一个带有clear_top标志的Intent,拉起AndroidManifest.xml中指定的parentActivity,类似于如下代码:
Intent intent = new Intent(this, CrimeListActivity.class);intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);startActivity(intent);finish();利用层级导航回到父Activity时,父Activity将被完全重新创建,其生命周期从onCreate重新开始。
3、数据库SQLite 定义数据库的表名和列名:
public class CrimeDbSchema { public static final class CrimeTable { public static final String NAME = "crimes"; public static final class Cols { public static final String UUID = "uuid"; public static final String TITLE = "title"; public static final String DATE = "date"; public static final String SOLVED = "solved"; } }}Android中数据库的创建,一般依赖于继承SQLiteOpenHelper的子类,例如:
public class CrimeBaseHelper extends SQLiteOpenHelper{ PRivate static final int VERSION = 1; private static final String DATABASE_NAME = "crimeBase.db"; public CrimeBaseHelper(Context context) { super(context, DATABASE_NAME, null, VERSION); } @Override //重写onCreate函数,完成数据库的创建 public void onCreate(SQLiteDatabase sqLiteDatabase) { //调用execSQL创建数据库 //指定表的名称,及其中的列 sqLiteDatabase.execSQL("create table " + CrimeTable.NAME + "(" + "_id integer primary key autoincrement, " + CrimeTable.Cols.UUID + ", " + CrimeTable.Cols.TITLE + ", " + CrimeTable.Cols.DATE + ", " + CrimeTable.Cols.SOLVED + ")"); } @Override //数据库升级时才会调用,通过VERSION来判断 public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { }}代码中通过SQLiteOpenHelper的接口就可以获得数据库,类似的代码如下:
//在调用SQLiteOpenHelper的getWritableDatabase时,SQLiteOpenHelper会判断数据是否创建过数据库//没有创建过,则回调子类的onCreate函数进行创建; 否则,根据版本的变化情况,决定是否调用onUpgrademDataBase = new CrimeBaseHelper(context.getApplicationContext()).getWritableDatabase();向数据库写入信息时,一般会用到ContentValues,例如:
private static ContentValues getContentValues(Crime crime) { //ContentValues以键值对的方式写入信息 //其中键就是数据库中的列 ContentValues values = new ContentValues(); values.put(CrimeTable.Cols.UUID, crime.getId().toString()); values.put(CrimeTable.Cols.TITLE, crime.getTitle()); values.put(CrimeTable.Cols.DATE, crime.getDate().getTime()); values.put(CrimeTable.Cols.SOLVED, crime.isSolved() ? 1 : 0); return values;}有了ContentValues后,就可对数据库进行增、删、改、查操作,例如:
public void addCrime(Crime c) { ContentValues values = getContentValues(c); //数据库的插入操作 mDataBase.insert(CrimeTable.NAME, null, values);}public void removeCrime(Crime c) { //数据库删除操作 mDataBase.delete(CrimeTable.NAME, CrimeTable.Cols.UUID + " = ?", new String[] {c.getId().toString()});}public void updateCrime(Crime c) { String uuidString = c.getId().toString(); ContentValues values = getContentValues(c); //数据库更新的操作 mDataBase.update(CrimeTable.NAME, values, CrimeTable.Cols.UUID + " = ?", new String[] {uuidString});}private CrimeCursorWrapper queryCrimes(String whereClause, String[] whereArgs) { //query接口将返回一个Cursor对象 Cursor cursor = mDataBase.query( CrimeTable.NAME, null, whereClause, whereArgs, null, null, null); //从Cursor对象中,再进一步解析出数据 //由于Cursor解析数据的过程比较繁琐,因此一般定义一个CursorWrapper来封装对应的操作 return new CrimeCursorWrapper(cursor);}以上API参数的具体含义,参考SQLiteDatabase中的注释即可,此处不做进一步描述。
CursorWrapper实现的例子,如下:
public class CrimeCursorWrapper extends CursorWrapper{ public CrimeCursorWrapper(Cursor cursor) { super(cursor); } public Crime getCrime() { //CursorWrapper继承Cursor的全部方法 String uuidString = getString(getColumnIndex(CrimeTable.Cols.UUID)); String title = getString(getColumnIndex(CrimeTable.Cols.TITLE)); long date = getLong(getColumnIndex(CrimeTable.Cols.DATE)); int isSolved = getInt(getColumnIndex(CrimeTable.Cols.SOLVED)); Crime crime = new Crime(UUID.fromString(uuidString)); crime.setTitle(title); crime.setDate(new Date(date)); crime.setSolved(isSolved != 0); return crime; }}当然,在获取到Cursor后,需要主动关闭它,例如:
public List<Crime> getCrimes() { List<Crime> crimes = new ArrayList<>(); CrimeCursorWrapper cursor = queryCrimes(null, null); //前面已经提到过,CursorWrapper继承了Cursor的所有方法 try { cursor.moveToFirst(); while(!cursor.isAfterLast()) { crimes.add(cursor.getCrime()); cursor.moveToNext(); } } finally { cursor.close(); } return crimes;}4、隐式Intent Android 5.0以后,不能以隐式Intent的方式启动Service,但仍然可以用隐式Intent来启动Activity。 对应的代码类似于:
..........//指定动作Intent i = new Intent(Intent.ACTION_SEND);//指定数据类型i.setType("text/plain");//放入数据i.putExtra(Intent.EXTRA_TEXT, getCrimeReport());i.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.crime_report_subject));//隐式Intent可能被多个Activity响应,因此可以显示创建一个选择器i = Intent.createChooser(i, getString(R.string.send_report));//拉起ActivitystartActivity(i);..........上面的内容比较容易,我们主要看看Intent的createChooser方法:
public static Intent createChooser(Intent target, CharSequence title) { return createChooser(target, title, null);}public static Intent createChooser(Intent target, CharSequence title, IntentSender sender) { //实际上就是显示的拉起Chooser Intent intent = new Intent(ACTION_CHOOSER); //将目的Intent当作数据放入chooser intent.putExtra(EXTRA_INTENT, target); //chooser将处理title信息 if (title != null) { intent.putExtra(EXTRA_TITLE, title); } //根据target中的信息,进一步调整intent ............. return intent;}从这段代码可以看出,显示创建选择器,其实就是显示的拉起Chooser Activity。
P.S. : 对于构建发送信息的Intent而言,Android的兼容库中定义了ShareCompat.IntentBuilder类。
Android的支持文档中,对应的描述如下: IntentBuilder is a helper for constructing ACTION_SEND and ACTION_SEND_MULTIPLE sharing intents and starting activities to share content. The ComponentName and package name of the calling activity will be included.
对于上述的代码,使用IntentBuilder的示例代码如下:
.............mReportButton = (Button) v.findViewById(R.id.crime_report);mReportButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { ShareCompat.IntentBuilder intentBuilder = ShareCompat.IntentBuilder.from(getActivity()); intentBuilder.setType("text/plain"); intentBuilder.setSubject(getString(R.string.crime_report_subject)); intentBuilder.setText(getCrimeReport()); intentBuilder.setChooserTitle(R.string.send_report); //通过chooser来拉起真正的目的Activity intentBuilder.startChooser(); }});..............5、访问联系人数据 联系人提供了自己ContentProvider,因此可以使用ContentResolver来访问它的数据。 举例来说:
.............//创建一个隐式Intentfinal Intent pickIntent = new Intent(Intent.ACTION_PICK, ContactsContract.Contacts.CONTENT_URI);mSuspectButton = (Button)v.findViewById(R.id.crime_suspect);mSuspectButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //启动联系人,并要求返回结果 startActivityForResult(pickIntent, REQUEST_CONTACT); }});..............处理返回结果的代码如下:
@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) { ........... } else if (requestCode == REQUEST_CONTACT && data != null) { //返回结果中包含了,选择的数据对应的地址 //根据这个地址,就可以访问联系人中联系对应的ContentProvider的数据 Uri contactUri = data.getData(); //指定需要访问的内容 String[] queryFields = new String[] { ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts._ID }; //得到对应的Cursor Cursor c = getActivity().getContentResolver() .query(contactUri, queryFields, null, null, null); if (c == null) { return; } try { if (c.getCount() == 0) { return; } c.moveToFirst(); String suspect = c.getString(0); mCrime.setSuspect(suspect); mSuspectButton.setText(suspect); mSuspectContactId = c.getString(1); mDialButton.setEnabled(true); } finally { c.close(); } } ................}联系人应用返回结果Intent时,会添加Intent.FLAG_GRANT_READ_URI_PERMISSION标志。 该标志赋予当前应用访问contactUri对应的联系人数据的权限。
6、利用PackageManager判断系统中是否存在与Intent匹配的组件 之前的博客分析过PKMS,我们知道在系统启动时PKMS将会解析所有APK对应的AndroidManifest.xml。 同时系统安装或卸载APK时,PKMS也会更新对应的内容。
因此当我们利用Intent拉起Activity或服务时,可以利于PackageManager中的接口, 判断系统中是否有该Intent对应的组件,避免组件不存在时带来的异常。
示例代码如下:
..........PackageManager packageManager = getActivity().getPackageManager();//接口分别传入Intent和标志位if (packageManager.resolveActivity(pickIntent, PackageManager.MATCH_DEFAULT_ONLY) == null) { mSuspectButton.setEnabled(false);}..........如果resolveActivity返回的值不为null,则表明系统中存在可以处理Intent的组件。
7、运行时权限 一直以来,为了保证最大的安全性,安装Android应用时,系统总是让用户选择是否同意该应用所需的所有权限。
一旦安装应用,就意味着该应用所需的所有权限均已获得。 若在使用某个功能时用到了某个权限,系统将不会提醒用户该权限正在被获取(比如微信需要使用摄像头拍照, 在Android 6.0以前的设备上,用户将不会被系统告知正在使用“使用系统摄像头”的权限)。
这在安全性上是个隐患:在不经用户同意的情况下,一些应用在后台可以自由地收集用户隐私信息而不被用户察觉。
为了解决这个问题,从Android 6.0版本开始,在安装应用时,该应用无法取得任何权限。 相反,在使用应用的过程中,若某个功能需要获取某个权限,系统会弹出一个对话框,显式地由用户决定是否将该权限赋予应用。 只有得到了用户的许可,该功能才可以被使用。
需要注意的是,赋予权限的对话框并不会自动弹出,而需要由开发者手动调用。 若程序调用的某个方法需要用户赋予相应权限,而此时该权限并未被赋予时,那么程序就会抛出异常并崩溃。 除此之外,用户还可以在任何时候,通过设置中的应用管理撤销赋予过的权限。
应用的targetSDKVersion < 23时,权限检查仍是早期的形式(仅在安装时赋予权限,使用时将不被提醒); 应用的targetSDKVersion ≥ 23时,则将使用新的运行时权限规则。
以下罗列了在安装应用时,自动赋予应用的权限,这些权限无法在安装后手动撤销,我们称其为基本权限(Normal Permission)。 开发者仅需要在AndroidManifest.xml中声明这些权限,应用就能自动获取无需用户授权。
android.permission.access_LOCATION_EXTRA_COMMANDSandroid.permission.ACCESS_NETWORK_STATEandroid.permission.ACCESS_NOTIFICATION_POLICYandroid.permission.ACCESS_WIFI_STATEandroid.permission.ACCESS_WIMAX_STATEandroid.permission.BLUETOOTHandroid.permission.BLUETOOTH_ADMINandroid.permission.BROADCAST_STICKYandroid.permission.CHANGE_NETWORK_STATEandroid.permission.CHANGE_WIFI_MULTICAST_STATEandroid.permission.CHANGE_WIFI_STATEandroid.permission.CHANGE_WIMAX_STATEandroid.permission.DISABLE_KEYGUARDandroid.permission.EXPAND_STATUS_BARandroid.permission.FlashLIGHTandroid.permission.GET_ACCOUNTSandroid.permission.GET_PACKAGE_SIZEandroid.permission.INTERNETandroid.permission.KILL_BACKGROUND_PROCESSESandroid.permission.MODIFY_AUDIO_SETTINGSandroid.permission.NFCandroid.permission.READ_SYNC_SETTINGSandroid.permission.READ_SYNC_STATSandroid.permission.RECEIVE_BOOT_COMPLETEDandroid.permission.REORDER_TASKSandroid.permission.REQUEST_INSTALL_PACKAGESandroid.permission.SET_TIME_ZONEandroid.permission.SET_WALLPAPERandroid.permission.SET_WALLPAPER_HINTSandroid.permission.SUBSCRIBED_FEEDS_READandroid.permission.TRANSMIT_IRandroid.permission.USE_FINGERPRINTandroid.permission.VIBRATEandroid.permission.WAKE_LOCKandroid.permission.WRITE_SYNC_SETTINGScom.android.alarm.permission.SET_ALARMcom.android.launcher.permission.INSTALL_SHORTCUTcom.android.launcher.permission.UNINSTALL_SHORTCUT运行时权限被划分成权限组(Permission Group),如下表所示:

若应用被赋予了某个权限组中的一个权限(比如READ_CONTACTS权限被赋予), 那么该组中的其他权限将被自动获取(WRITE_CONTACTS和GET_ACCOUNTS权限被自动获取)。
检查和申请权限的方法分别是Activity.checkSelfPermission()和Activity.requestPermissions,这两个方法是在 API 23 中新增的。 代码示例如下:
............mDialButton = (Button) v.findViewById(R.id.crime_dial);mDialButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (Build.VERSION.SDK_INT >= 23) { //我是在Fragment里写代码的,因此调用getActivity //如果不想判断SDK,可以使用ActivityCompat的接口来检查和申请权限 int hasReadContactsPermission = getActivity().checkSelfPermission( android.Manifest.permission.READ_CONTACTS); if (hasReadContactsPermission != PackageManager.PERMISSION_GRANTED) { getActivity().requestPermissions( new String[] {Manifest.permission.READ_CONTACTS}, ASK_READ_CONTACTS_PERMISSION); return; } //高版本中检查是否有运行时权限,具有权限时才调用 getPhoneNumberAndDial(); } else { //在AndroidManifest.xml中仍然声明使用"android.permission.READ_CONTACTS" //在低版本中直接调用该函数 getPhoneNumberAndDial(); } }});............如前文所述,应用初始安装时,即使声明了运行时权限,Android系统也不会为其赋予任何权限。 如下图所示: 
此时,点击按键调用Activity的requestPermissions时,将会弹出对话框,类似于下图所示(不同设备商有不同的定制): 
无论选择的是“允许”还是“拒绝”,系统都将回调Activity.onRequestPermissionsResult()方法, 并将选择的结果传到方法的第三个参数中。 此时的处理代码示例如下:
@Overridepublic void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case ASK_READ_CONTACTS_PERMISSION: if (grantResults[0] == PackageManager.PERMISSION_GRANTED) { getPhoneNumberAndDial(); } else { Toast.makeText(getContext(), "READ_CONTACTS Denied", Toast.LENGTH_SHORT) .show(); } return; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); }}注意到目前,如果在原生的Fragment中调用Activity.requestPermissions函数时,onRequestPermissionsResult必须实现在包含该Fragment的Activity中。 如果是在android.support.v4.app.Fragment中实现相同功能时,就可以直接在Fragment中定义onRequestPermissionsResult函数,此时代码如下:
................mDialButton = (Button) v.findViewById(R.id.crime_dial);mDialButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //checkSelfPermission、requestPermissions均是调用兼容库中的函数,此时不再需要判断SDK int hasReadContactsPermission = checkSelfPermission(getContext(), android.Manifest.permission.READ_CONTACTS); if (hasReadContactsPermission != PackageManager.PERMISSION_GRANTED) { requestPermissions( new String[] {Manifest.permission.READ_CONTACTS}, ASK_READ_CONTACTS_PERMISSION); return; } getPhoneNumberAndDial(); }});..................关于该问题的讨论可以参考:onRequestPermissionsResult not being called in dialog fragment和Android M Permissions: onRequestPermissionsResult() not being called
如上图所示,每当系统申请权限时,弹出的对话框会有一个类似于“拒绝后不再询问”的勾选项。 若用户打了勾,并选择拒绝,那么下次程序调用Activity.requestPermissions()方法时,将不会弹出对话框,权限也不会被赋予。
这种没有反馈的交互并不是一个好的用户体验。 所以,下次启动时,程序应弹出一个对话框,提示用户类似于“您已经拒绝了使用该功能所需要的权限,若需要使用该功能,请手动开启权限”的信息, 此时应调用Activity.shouldShowRequestPermissionRationale()方法,示例代码如下:
............mDialButton = (Button) v.findViewById(R.id.crime_dial);mDialButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int hasReadContactsPermission = checkSelfPermission(getContext(), android.Manifest.permission.READ_CONTACTS); if (hasReadContactsPermission != PackageManager.PERMISSION_GRANTED) { if (!shouldShowRequestPermissionRationale(android.Manifest.permission.READ_CONTACTS)) { showMessageOKCancel("You need to allow access to Contacts", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { requestPermissions( new String[] {Manifest.permission.READ_CONTACTS}, ASK_READ_CONTACTS_PERMISSION); } }); return; } requestPermissions( new String[] {Manifest.permission.READ_CONTACTS}, ASK_READ_CONTACTS_PERMISSION); return; } getPhoneNumberAndDial(); }});...............private void showMessageOKCancel(String message, DialogInterface.OnClickListener okListener) { new AlertDialog.Builder(getContext()) .setMessage(message) .setPositiveButton("OK", okListener) .setNegativeButton("Cancel", null) .create() .show();}此时,应用第一次申请权限及用户勾选了“不再询问”复选框时,均会弹出类似如下的对话框: 
若第一次申请时点击OK,将会弹出权限申请的界面; 用户勾选过“拒绝后不再询问时”,点击OK不会再次拉起申请界面, 因为onRequestPermissionsResult中收到的结果为PackageManager.PERMISSION_DENIED。
最后看看同时申请多个运行时权限的代码示例,其思想基本与前文一致,这段代码是直接借鉴过来的:
final private int REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS = 124;private void insertDummyContactWrapper() { //提示用户需要手动开启的权限集合 List<String> permissionsNeeded = new ArrayList<String>(); //功能所需权限的集合 final List<String> permissionsList = new ArrayList<String>(); //若用户拒绝了该权限申请,则将该申请的提示添加到“用户需要手动开启的权限集合”中 if (!addPermission(permissionsList, Manifest.permission.ACCESS_FINE_LOCATION)) permissionsNeeded.add("GPS"); if (!addPermission(permissionsList, Manifest.permission.READ_CONTACTS)) permissionsNeeded.add("Read Contacts"); if (!addPermission(permissionsList, Manifest.permission.WRITE_CONTACTS)) permissionsNeeded.add("Write Contacts"); //若在AndroidManiFest中配置了所有所需权限,则让用户逐一赋予应用权限,若权限都被赋予,则执行方法并返回 if (permissionsList.size() > 0) { //若用户赋予了一部分权限,则需要提示用户开启其余权限并返回,该功能将无法执行 if (permissionsNeeded.size() > 0) { // Need Rationale String message = "You need to grant access to " + permissionsNeeded.get(0); for (int i = 1; i < permissionsNeeded.size(); i++) message = message + ", " + permissionsNeeded.get(i); //弹出对话框,提示用户需要手动开启的权限 showMessageOKCancel(message, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS); } }); return; } requestPermissions(permissionsList.toArray(new String[permissionsList.size()]), REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS); return; } insertDummyContact();}//判断用户是否授予了所需权限 private boolean addPermission(List<String> permissionsList, String permission) { //若配置了该权限,返回true if (checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) { //若未配置该权限,将其添加到所需权限的集合,返回true permissionsList.add(permission); // 若用户勾选了“永不询问”复选框,并拒绝了权限,则返回false if (!shouldShowRequestPermissionRationale(permission)) return false; } return true;}@Overridepublic void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case REQUEST_CODE_ASK_MULTIPLE_PERMISSIONS: { //初始化Map集合,其中Key存放所需权限,Value存放该权限是否被赋予 Map<String, Integer> perms = new HashMap<String, Integer>(); // 向Map集合中加入元素,初始时所有权限均设置为被赋予(PackageManager.PERMISSION_GRANTED) perms.put(Manifest.permission.ACCESS_FINE_LOCATION, PackageManager.PERMISSION_GRANTED); perms.put(Manifest.permission.READ_CONTACTS, PackageManager.PERMISSION_GRANTED); perms.put(Manifest.permission.WRITE_CONTACTS, PackageManager.PERMISSION_GRANTED); // 将第二个参数回传的所需权限及第三个参数回传的权限结果放入Map集合中,由于Map集合要求Key值不能重复,所以实际的权限结果将覆盖初始值 for (int i = 0; i < permissions.length; i++) perms.put(permissions[i], grantResults[i]); // 若所有权限均被赋予,则执行方法 if (perms.get(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED && perms.get(Manifest.permission.READ_CONTACTS) == PackageManager.PERMISSION_GRANTED && perms.get(Manifest.permission.WRITE_CONTACTS) == PackageManager.PERMISSION_GRANTED) { // All Permissions Granted insertDummyContact(); } //否则弹出toast,告知用户需手动赋予权限 else { // Permission Denied Toast.makeText(MainActivity.this, "Some Permission is Denied", Toast.LENGTH_SHORT) .show(); } } break; default: super.onRequestPermissionsResult(requestCode, permissions, grantResults); }}新闻热点
疑难解答