您的位置:首页 > 其它

安卓6.0权限处理在项目中的实践

2016-01-31 00:09 363 查看
前言

相关开源项目
使用eclipse和grant开源库权限管理
更新api到23

获得v4包

项目实战开发

默认许可权限无需授权

总结和建议

前言

最近公司的app说装在安卓6.0的系统上程序直接崩溃了,然后呢crash日 志也没有捕获到,感觉好烦人因为公司压根就没有安卓6.0的测试机,最后呢我还是用genymotion来搞,由于用到了so库以前下载的那个jar也不知怎的5.0就运行不通过了,然后今天那到处弄Genymotion-ARM-Translation.zip,最后终于还是可以了。

由于公司是使用eclipse开发的也是醉了,然后呢网上对安卓6.0的权限有了还几个开源框架

相关开源项目

PermissionsDispatcher

使用标注的方式,动态生成类处理运行时权限,目前还不支持嵌套Fragment。

RxPermissions

基于RxJava的运行时权限检测框架

Grant

简化运行时权限的处理,比较灵活

android-RuntimePermissions

Google官方的例子



使用eclipse和grant开源库权限管理

1. 更新api到23

这里我们选择使用grant来进行项目的演示,因为是使用eclipse来开发,所以还是和android studio里面的还是有很多的区别,我们genymotion的6.0打开 ,因为是6.0系统的开发,所以呢我们的api的target 必须是23,如果不是呢那就麻烦你去update下。

2. 获得v4包

不多扯了,去你的sdk目录下拷贝出v4包放在你工程目录的libs下,不要问为什么因为在接下来的 项目里面需要用到。如下图目录图:



当然我们选择23.1.1下面的android-support-v4.jar,放入你项目的libs下面:

哈哈为什么要这样干呢?因为在里面我们会用到

这样做的话,代码会比较复杂,而在support library v4中,已经为我们准备好了更简单的方法,我们只需要采用library包中的方法来替换上面的方法即可:

- ContextCompat.checkSelfPermission()

不管当前运行的Android版本是多少,该方法都可以正确的方法权限的许可情况,允许或者拒绝.

- ActivityCompat.requestPermissions()

当该方法在6.0之前被调用时,onRequestPermissionsResult回调方法会立即被调用,并返回给你正确的PERMISSION_GRANTED 或者

PERMISSION_DENIED结果.

ActivityCompat.shouldShowRequestPermissionRationale()

在6.0之前调用该方法,将总会返回false.

切记,你应该总是使用这些方法来取代Activity自身的checkSelfPermission,requestPermissions和shouldShowRequestPermissionsRationale方法.这样,在不同的Android版本中,你的代码将总会完美的运行.

ContextCompat.checkSelfPermission();

ActivityCompat.requestPermissions();



上面的方法是v4包下的方法,然后呢低版本的v4包还不具有这样的方法,所以as的项目放在eclipse也是相当的蛋疼。

接下我们就实战项目了,



3. 项目实战开发

不多扯,来看代码:

[code]package com.richerpay.ryshop.permissions;

/**
 * Enum class to handle the different states
 * of permissions since the PackageManager only
 * has a granted and denied state.
 */
enum Permissions {
    GRANTED,
    DENIED,
    NOT_FOUND
}


上面呢就 是权限的类型了,简单翻译下注释就是 用枚举对应3种状态:

已授权,授权失败,未发现的权限。

再看下我们的PermissionsManager.java

[code]package com.richerpay.ryshop.permissions;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.util.Log;

import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

/**
 * 权限管理类 
 */
public class PermissionsManager {

    private static final String TAG = PermissionsManager.class.getSimpleName();

    private final Set<String> mPendingRequests = new HashSet<String>(1);
    private final Set<String> mPermissions = new HashSet<String>(1);
    private final List<WeakReference<PermissionsResultAction>> mPendingActions = new ArrayList<WeakReference<PermissionsResultAction>>(1);

    private static PermissionsManager mInstance = null;

    public static PermissionsManager getInstance() {
        if (mInstance == null) {
            mInstance = new PermissionsManager();
        }
        return mInstance;
    }

    private PermissionsManager() {
        initializePermissionsMap();
    }

    /**
     * 此方法使用反射来读取清单类中的所有权限。
     *因为一些权限不存在于旧版本的安卓系统中,
     *因为他们不存在,他们将被拒绝时,你检查是否有权限
     *因为一个新的权限,往往是补充,那里没有以前的
     *所需的许可。我们初始化一组可用的权限,并检查组
     *检查是否有权限,当我们被拒绝时权限仍然不存在
     * 
     */
    private synchronized void initializePermissionsMap() {
        Field[] fields = Manifest.permission.class.getFields();
        for (Field field : fields) {
            String name = null;
            try {
                name = (String) field.get("");
            } catch (IllegalAccessException e) {
                Log.e(TAG, "Could not access field", e);
            }
            mPermissions.add(name);
        }
    }

    /**
     * 此方法检索在应用程序清单中声明的所有权限。
     * 它返回一个非空数组,可以声明的权限。
     *
     * @param  检查我们需要哪些权限
     * @return 返回在应用程序清单中声明的非空数组
     */
    @NonNull
    private synchronized String[] getManifestPermissions(@NonNull final Activity activity) {
        PackageInfo packageInfo = null;
        List<String> list = new ArrayList<String>(1);
        try {
            Log.d(TAG, activity.getPackageName());
            packageInfo = activity.getPackageManager().getPackageInfo(activity.getPackageName(), PackageManager.GET_PERMISSIONS);
        } catch (PackageManager.NameNotFoundException e) {
            Log.e(TAG, "A problem occurred when retrieving permissions", e);
        }
        if (packageInfo != null) {
            String[] permissions = packageInfo.requestedPermissions;
            if (permissions != null) {
                for (String perm : permissions) {
                    Log.d(TAG, "Manifest contained permission: " + perm);
                    list.add(perm);
                }
            }
        }
        return list.toArray(new String[list.size()]);
    }

    /**
     * 在permissionsresultaction对象,它将变更通知这些权限
     * @param 权限所需的操作的权限。
     * @param 将 动作添加到当前正在执行的动作列表中。
     */
    private synchronized void addPendingAction(@NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        if (action == null) {
            return;
        }
        action.registerPermissions(permissions);
        mPendingActions.add(new WeakReference<PermissionsResultAction>(action));
    }

    /**
     * 删除从队列中等待的动作和执行该动作
     * @param 移除动作
     */
    private synchronized void removePendingAction(@Nullable PermissionsResultAction action) {
        for (Iterator<WeakReference<PermissionsResultAction>> iterator = mPendingActions.iterator();
             iterator.hasNext(); ) {
            WeakReference<PermissionsResultAction> weakRef = iterator.next();
            if (weakRef.get() == action || weakRef.get() == null) {
                iterator.remove();
            }
        }
    }

    /**
     *这个静态方法可以用来检查你是否有一个特定的权限。
     *
     * @param 检查权限的上下文对象
     * @param 要检查的权限
     * @return 返回是否授权了此权限
     */
    public synchronized boolean hasPermission(@Nullable Context context, @NonNull String permission) {
        return context != null && (ActivityCompat.checkSelfPermission(context, permission)
                == PackageManager.PERMISSION_GRANTED || !mPermissions.contains(permission));
    }

    /**
     * 这个静态方法可以用来检查你是否有几个特定的权限。
     * 为每一个权限,并将简单地返回一个布尔值是否你有所有的权限
     * @param 需要检查 权限的上下文
     * @param 权限 数组
     * @return 返回你是否有所有的权限
     */
    public synchronized boolean hasAllPermissions(@Nullable Context context, @NonNull String[] permissions) {
        if (context == null) {
            return false;
        }
        boolean hasAllPermissions = true;
        for (String perm : permissions) {
            hasAllPermissions &= hasPermission(context, perm);
        }
        return hasAllPermissions;
    }

    /**
    *这种方法的是获取清单里面的所有权限。permissionsresultaction  用于通知允许用户允许或拒绝每一个权限。
     *
     * @param 需要检查权限的activity
     * @param permissionsresultaction用于权限接受通知你
     */
    public synchronized void requestAllManifestPermissionsIfNecessary(final @Nullable Activity activity,
                                                                      final @Nullable PermissionsResultAction action) {
        if (activity == null) {
            return;
        }
        String[] perms = getManifestPermissions(activity);
        requestPermissionsIfNecessaryForResult(activity, perms, action);
    }

    /**
     *该方法将请求的权限,如果
     *他们需要被要求(即我们没有许可),并会增加
     * permissionsresultaction到队列被通知的权限被授予或
     *否认。在预Android棉花糖的情况下,将立即授予权限。
     *活动变量为空,但如果它是无效的,不能执行的方法。
     *这是唯一可作为一种礼貌片段,getactivity()可能产量空
     *如果该片段没有添加到其父活动中
     * @param 请求权限的活动
     * @param 权限列表
     * @param permissionsresultaction通知当权限授予或拒绝。
     */
    public synchronized void requestPermissionsIfNecessaryForResult(@Nullable Activity activity,
                                                                    @NonNull String[] permissions,
                                                                    @Nullable PermissionsResultAction action) {
        if (activity == null) {
            return;
        }
        addPendingAction(permissions, action);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            doPermissionWorkBeforeAndroidM(activity, permissions, action);
        } else {
            List<String> permList = getPermissionsListToRequest(activity, permissions, action);
            if (permList.isEmpty()) {
                //if there is no permission to request, there is no reason to keep the action int the list
                removePendingAction(action);
            } else {
                String[] permsToRequest = permList.toArray(new String[permList.size()]);
                mPendingRequests.addAll(permList);
                ActivityCompat.requestPermissions(activity, permsToRequest, 1);
            }
        }
    }

    /**
     * 该方法将请求的权限,如果
     *他们需要被要求(即我们没有许可),并会增加
     * permissionsresultaction到队列被通知的权限被授予或
     *否认。在预Android棉花糖(6.0)的情况下,将立即授予权限。
     *但如果 getactivity()返回null,这方法
     *将无法工作作为活动引用,必须检查权限。
     * @param 需要检查 权限的fragmnet
     * @param 需要请求的权限列表
     * @param permissionsresultaction通知当权限授予或拒绝。  
     */
    public synchronized void requestPermissionsIfNecessaryForResult(@NonNull Fragment fragment,@NonNull String[] permissions,
@Nullable PermissionsResultAction action) {
        Activity activity = fragment.getActivity();
        if (activity == null) {
            return;
        }
        addPendingAction(permissions, action);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            doPermissionWorkBeforeAndroidM(activity, permissions, action);
        } else {
            List<String> permList = getPermissionsListToRequest(activity, permissions, action);
            if (permList.isEmpty()) {
                //if there is no permission to request, there is no reason to keep the action int the list
                removePendingAction(action);
            } else {
                String[] permsToRequest = permList.toArray(new String[permList.size()]);
                mPendingRequests.addAll(permList);
                fragment.requestPermissions(permsToRequest, 1);
            }
        }
    }

    /**
     * 这个方法通知permissionsmanager,权限已经改变。如果你正在做
     *使用活动的权限请求,则应调用此方法,将通知所有悬而未决  的,permissionsresultaction当前对象 
     *在队列中,并将从挂起的请求列表中删除权限请求。
     * @param  已更改的权限。
     * @param  每个权限的值
     */
    public synchronized void notifyPermissionsChange(@NonNull String[] permissions, @NonNull int[] results) {
        int size = permissions.length;
        if (results.length < size) {
            size = results.length;
        }
        Iterator<WeakReference<PermissionsResultAction>> iterator = mPendingActions.iterator();
        while (iterator.hasNext()) {
            PermissionsResultAction action = iterator.next().get();
            for (int n = 0; n < size; n++) {
                if (action == null || action.onResult(permissions
, results
)) {
                    iterator.remove();
                    break;
                }
            }
        }
        for (int n = 0; n < size; n++) {
            mPendingRequests.remove(permissions
);
        }
    }

    /**
     * 在安卓设备前要求权限(安卓6,api23),根据权限状态,直接进行或拒绝工作
     * @param 检测权限的activity
     * @param 权限数组
     * @param 我们执行某项操作后权限检查   
     */
    private void doPermissionWorkBeforeAndroidM(@NonNull Activity activity, @NonNull String[] permissions, @Nullable PermissionsResultAction action) {
        for (String perm : permissions) {
            if (action != null) {
                if (!mPermissions.contains(perm)) {
                    action.onResult(perm, Permissions.NOT_FOUND);
                } else if (ActivityCompat.checkSelfPermission(activity, perm)
                        != PackageManager.PERMISSION_GRANTED) {
                    action.onResult(perm, Permissions.DENIED);
                } else {
                    action.onResult(perm, Permissions.GRANTED);
                }
            }
        }
    }

    /**
     * @param 检查权限的activity
     * @param 所有权限的名字
     * @param 我们执行某项操作后权限检查
     * @return 尚未授予的权限名称列表
     */
    @NonNull
    private List<String> getPermissionsListToRequest(@NonNull Activity activity,
                                                   @NonNull String[] permissions,@Nullable PermissionsResultAction action) {
        List<String> permList = new ArrayList<String>(permissions.length);
        for (String perm : permissions) {
            if (!mPermissions.contains(perm)) {
                if (action != null) {
                    action.onResult(perm, Permissions.NOT_FOUND);
                }
            } else if (ActivityCompat.checkSelfPermission(activity, perm) != PackageManager.PERMISSION_GRANTED) {
                if (!mPendingRequests.contains(perm)) {
                    permList.add(perm);
                }
            } else {
                if (action != null) {
                   action.onResult(perm,Permissions.GRANTED);
                }
            }
        }
        return permList;
    }

}


接下来我们来看PermissionsResultAction.java

[code]package com.xxxx.xxxx.permissions;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.Looper;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;
import android.util.Log;

import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

/**
 *将实例传递给 requestpermissionsifnecessaryforresult方法。结  *果将被送回你无论是ongranted(所有的权限已被授予),或ondenied(所需*的权限被拒绝)。
*你把你的ongranted方法和通知功能,用户什么都在ondenied方法不工作。
 */
public abstract class PermissionsResultAction {

    private static final String TAG = PermissionsResultAction.class.getSimpleName();
    private final Set<String> mPermissions = new HashSet<String>(1);
    private Looper mLooper = Looper.getMainLooper();

    /**
     * Default Constructor
     */
    public PermissionsResultAction() {}

    /**
     *如果您是在后台线程中使用权限请求,则希望
     *回调是在UI线程中,通过looper刷新ui
     */
    public PermissionsResultAction(@NonNull Looper looper) {mLooper = looper;}

    /**
     *当所有权限已被用户授予,把所有你的权限敏感的代码,可以只需执行所                        需权限
     */
    public abstract void onGranted();

    /**
     *当一个权限被拒绝的时候调用
     *
     * @param 被拒绝的权限
     */
    public abstract void onDenied(String permission);

    /**
     * 忽视未发现的权限
     */
    @SuppressWarnings({})
    public synchronized boolean shouldIgnorePermissionNotFound(String permission) {
        Log.d(TAG, "Permission not found: " + permission);
        return true;
    }
     /*
     *返回授权 结果
     */
    @CallSuper
    protected synchronized final boolean onResult(final @NonNull String permission, int result) {
        if (result == PackageManager.PERMISSION_GRANTED) {
            return onResult(permission, Permissions.GRANTED);
        } else {
            return onResult(permission, Permissions.DENIED);
        }

    }

    /**
     *当一个特定的权限已更改。
     *此方法将调用所有权限,所以该方法确定
     *如果授权影响到该状态,是否可以继续进行
     */
    @CallSuper
    protected synchronized final boolean onResult(final @NonNull String permission, Permissions result) {
         //先从权限列表里移除当前权限
        mPermissions.remove(permission);
        if (result == Permissions.GRANTED) {
            if (mPermissions.isEmpty()) {
                new Handler(mLooper).post(new Runnable() {
                    @Override
                    public void run() {
                        onGranted();
                    }
                });
                return true;
            }
        } else if (result == Permissions.DENIED) {//权限被拒
            new Handler(mLooper).post(new Runnable() {
                @Override
                public void run() {
                    onDenied(permission);
                }
            });
            return true;
        } else if (result == Permissions.NOT_FOUND) {
            if (shouldIgnorePermissionNotFound(permission)) {
                if (mPermissions.isEmpty()) {//权限为空
                    new Handler(mLooper).post(new Runnable() {
                        @Override
                        public void run() {
                            onGranted();//去授权
                        }
                    });
                    return true;
                }
            } else {
                new Handler(mLooper).post(new Runnable() {
                    @Override
                    public void run() {
                         //拒绝权限
                        onDenied(permission);
                    }
                });
                return true;
            }
        }
        return false;
    }

    /**
     *注册指定的权限对象的permissionsresultaction
     *让它知道哪些权限来查找更改。
     *
     * @param permissions名单
     */
    @CallSuper
    protected synchronized final void registerPermissions(@NonNull String[] perms) {
        //把名单加到权限集合里
        Collections.addAll(mPermissions, perms);
    }
}


好了,最后我们就来使用权限管理:

// 先对app获取所有需要的授权(6.0需要去获取权限)

grantPermissions();

[code]/***
* 获取清单文件中的所有权限
*/
private void grantPermissions() {
@Override                       public void onGranted() {                                   
    Toast.makeText(MainActivity.this,                                         
    Toast.LENGTH_SHORT).show();
}   
@Override                       public void onDenied(String permission) {                            
String message = String.format(                                       
Locale.getDefault(),"Permission \"%1$s\" has been denied",                                   permission);                          Toast.makeText(MainActivity.this, message,                                   
        Toast.LENGTH_SHORT).show();
}});
}


上面呢 我们在程序一开始呢就去授权所有的清单里面的权限,你会看到和ios一样的权限dialog提示,这里呢我们直接来图片展示下。

如下图所示:



接下来我们要去通知权限授权的改变

[code]@Override
    public void onRequestPermissionsResult(intrequestCode,@NonNull String[] permissions, @NonNull int[]   grantResults) {
    PermissionsManager.getInstance().notifyPermissionsChange(permissions,grantResults);
    }


这里呢我们以百度定位的例子来使用:

[code]        /**
     * 定位信息
     */
    private void initLocation() {
        LocationClientOption option = new LocationClientOption();
        // 可选,默认高精度,设置定位模式,高精度(LocationMode.Hight_Accuracy),
        // 低功耗(LocationMode.Battery_Saving),
        // 仅设备(LocationMode.Device_Sensors)
        option.setLocationMode(LocationMode.Hight_Accuracy);
        option.setCoorType("bd09ll");// 可选,默认gcj02,设置返回的定位结果坐标系,
                                        // tempcoor="gcj02";//国家测绘局标准"bd09"//百度墨卡托标准"bd09ll"//百度经纬度标准
        // int span=1000;
        option.setScanSpan(0);// 可选,默认0,即仅定位一次,设置发起定位请求的间隔需要大于等于1000ms才是有效的
        option.setIsNeedAddress(true);// 可选,设置是否需要地址信息,默认不需要
        option.setOpenGps(true);// 可选,默认false,设置是否使用gps
        option.setLocationNotify(true);// 可选,默认false,设置是否当gps有效时按照1S1次频率输出GPS结果
        option.setIgnoreKillProcess(false);// 可选,默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认不杀死
        mLocationClient.setLocOption(option);
    }

/**
*页面 停止状态下关闭定位
*/
@Override
    protected void onStop() {
        if (mLocationClient != null) {
            mLocationClient.stop();// 界面消失后取消定位
            mLocationClient = null;
        }

        super.onStop();
    }


我们先来判断定位权限是否已授权成功:

[code]// 判断是否授权定位权限
        boolean hasPermission = PermissionsManager.getInstance().hasPermission(
                this, Manifest.permission.ACCESS_FINE_LOCATION);


接下来我们根据是否授权成功来进行相关的操作:

[code]    if (hasPermission == false) {//没有授权成功
            grantLoactionPermissons();//去授权
        }else{//授权成功去定位
            mLocationClient = ((RYApplication) getApplication()).mLocationClient;
            initLocation();// 定位
            mLocationClient.start();// 定位SDK
            // start之后会默认发起一次定位请求,开发者无须判断isstart并主动调用request
            mLocationClient.requestLocation();
        }


根据定位需要的权限去授权

[code]/**
     * 授权定位权限
     */
    private void grantLoactionPermissons() {  PermissionsManager.getInstance().requestPermissionsIfNeces         saryForResult(this,new String[] {
  Manifest.permission.ACCESS_COARSE_LOCATION,                
  Manifest.permission.ACCESS_FINE_LOCATION,              
  Manifest.permission.WRITE_EXTERNAL_STORAGE },new PermissionsResultAction() {                      @Override                       public void onGranted() {                        
      mLocationClient = ((RYApplication)    getApplication()).mLocationClient;                           
       initLocation();// 定位                                
       mLocationClient.start();// 定位SDK                          // start之后会默认发起一次定位请求,开发者无须判断isstart并主动调用 
       requestmLocationClient.requestLocation();
    }
@Override                        
public void onDenied(String permission) {

    }
    });
    }


当然还有一些权限你无须去判断:

默认许可权限(无需授权)

还有一些权限是在安装的时候默认许可并且不可撤销的,我们把它们叫做普通权限,这些权限如下:

android.permission.ACCESS_LOCATION_EXTRA_COMMANDS

android.permission.ACCESS_NETWORK_STATE

android.permission.ACCESS_NOTIFICATION_POLICY

android.permission.ACCESS_WIFI_STATE

android.permission.ACCESS_WIMAX_STATE

android.permission.BLUETOOTH

android.permission.BLUETOOTH_ADMIN

android.permission.BROADCAST_STICKY

android.permission.CHANGE_NETWORK_STATE

android.permission.CHANGE_WIFI_MULTICAST_STATE

android.permission.CHANGE_WIFI_STATE

android.permission.CHANGE_WIMAX_STATE

android.permission.DISABLE_KEYGUARD

android.permission.EXPAND_STATUS_BAR

android.permission.FLASHLIGHT

android.permission.GET_ACCOUNTS

android.permission.GET_PACKAGE_SIZE

android.permission.INTERNET

android.permission.KILL_BACKGROUND_PROCESSES

android.permission.MODIFY_AUDIO_SETTINGS

android.permission.NFC

android.permission.READ_SYNC_SETTINGS

android.permission.READ_SYNC_STATS

android.permission.RECEIVE_BOOT_COMPLETED

android.permission.REORDER_TASKS

android.permission.REQUEST_INSTALL_PACKAGES

android.permission.SET_TIME_ZONE

android.permission.SET_WALLPAPER

android.permission.SET_WALLPAPER_HINTS

android.permission.SUBSCRIBED_FEEDS_READ

android.permission.TRANSMIT_IR

android.permission.USE_FINGERPRINT

android.permission.VIBRATE

android.permission.WAKE_LOCK

android.permission.WRITE_SYNC_SETTINGS

com.android.alarm.permission.SET_ALARM

com.android.launcher.permission.INSTALL_SHORTCUT

com.android.launcher.permission.UNINSTALL_SHORTCUT

这些权限和以前一样使用,你无需检查它们是否被撤销了

总结和建议

现在你应该很清楚整个新的权限系统了,同时你也应该知道我们面临的问题了.在Android6.0中动态权限系统已经取代了原有的权限系统,我们别无选择,退无可退,唯一能做的就是去做好适配工作.

好消息是需要进行动态申请的权限是少量的,大部分常用的权限是默认获得许可,并不需要你处理的.总而言之,你只需要修改一小部分代码来完成适配工作.

在这里给出两个建议:

1.优先对重要的部分进行适配,确保不会出现崩溃问题.

2.在完成适配工作前,不要将应用的targetSdkVersion设为23,尤其是在使用AndroidStudio创建新工程的时候,记得手动修改一下工程的targetSdkVersion,因为AndroidStudio会默认使用最新的SDK版本.



说到修改代码,我必须承认工作量很大.如果之前的架构设计不合理的话,你可能需要花费一些时间来重新设计了,就像之前说的,我们别无选择,除了去做好它.


我建议你列出应用的功能所依赖的所有的权限,然后考虑如果权限被拒绝,怎样使你的应用的功能尽可能可用,并考虑好如果部分权限被拒绝的情况下,你应该怎么做.恩,这也很复杂,最好能记录好各种情况.



对于安卓6.0的权限运行问题,你可移步Android 6.0 运行时权限处理

好累啊,一不小心到12点,睡觉,加油吧。第一次使用markDown感觉好辛苦,写个东西都
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: