安卓6.0权限处理在项目中的实践
- 前言
- 相关开源项目
- 使用eclipse和grant开源库权限管理
- 更新api到23
- 获得v4包
- 项目实战开发
- 默认许可权限无需授权
- 使用eclipse和grant开源库权限管理
- 总结和建议
前言
最近公司的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. 项目实战开发
不多扯,来看代码:
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
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 返回在应用程序清单中声明的非空数组*/@NonNullprivate 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 listremovePendingAction(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 listremovePendingAction(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[n], results[n])) {iterator.remove();break;}}}for (int n = 0; n < size; n++) {mPendingRequests.remove(permissions[n]);}}/*** 在安卓设备前要求权限(安卓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 尚未授予的权限名称列表*/@NonNullprivate 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
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;}/**返回授权 结果*/@CallSuperprotected 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);}}/***当一个特定的权限已更改。*此方法将调用所有权限,所以该方法确定*如果授权影响到该状态,是否可以继续进行*/@CallSuperprotected 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() {@Overridepublic void run() {onGranted();}});return true;}} else if (result == Permissions.DENIED) {//权限被拒new Handler(mLooper).post(new Runnable() {@Overridepublic void run() {onDenied(permission);}});return true;} else if (result == Permissions.NOT_FOUND) {if (shouldIgnorePermissionNotFound(permission)) {if (mPermissions.isEmpty()) {//权限为空new Handler(mLooper).post(new Runnable() {@Overridepublic void run() {onGranted();//去授权}});return true;}} else {new Handler(mLooper).post(new Runnable() {@Overridepublic void run() {//拒绝权限onDenied(permission);}});return true;}}return false;}/***注册指定的权限对象的permissionsresultaction*让它知道哪些权限来查找更改。** @param permissions名单*/@CallSuperprotected synchronized final void registerPermissions(@NonNull String[] perms) {//把名单加到权限集合里Collections.addAll(mPermissions, perms);}
}
好了,最后我们就来使用权限管理:
// 先对app获取所有需要的授权(6.0需要去获取权限)
grantPermissions();
/***
* 获取清单文件中的所有权限
*/
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提示,这里呢我们直接来图片展示下。
如下图所示:
接下来我们要去通知权限授权的改变
@Overridepublic void onRequestPermissionsResult(intrequestCode,@NonNull String[] permissions, @NonNull int[] grantResults) {PermissionsManager.getInstance().notifyPermissionsChange(permissions,grantResults);}
这里呢我们以百度定位的例子来使用:
/*** 定位信息*/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,设置是否使用gpsoption.setLocationNotify(true);// 可选,默认false,设置是否当gps有效时按照1S1次频率输出GPS结果option.setIgnoreKillProcess(false);// 可选,默认true,定位SDK内部是一个SERVICE,并放到了独立进程,设置是否在stop的时候杀死这个进程,默认不杀死mLocationClient.setLocOption(option);}/**
*页面 停止状态下关闭定位
*/
@Overrideprotected void onStop() {if (mLocationClient != null) {mLocationClient.stop();// 界面消失后取消定位mLocationClient = null;}super.onStop();}
我们先来判断定位权限是否已授权成功:
// 判断是否授权定位权限boolean hasPermission = PermissionsManager.getInstance().hasPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
接下来我们根据是否授权成功来进行相关的操作:
if (hasPermission == false) {//没有授权成功grantLoactionPermissons();//去授权}else{//授权成功去定位mLocationClient = ((RYApplication) getApplication()).mLocationClient;initLocation();// 定位mLocationClient.start();// 定位SDK// start之后会默认发起一次定位请求,开发者无须判断isstart并主动调用requestmLocationClient.requestLocation();}
根据定位需要的权限去授权
/*** 授权定位权限*/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感觉好辛苦,写个东西都
安卓6.0权限处理在项目中的实践相关推荐
- 玩安卓从 0 到 1 之项目总结
玩安卓从 0 到 1 之项目总结 前言 这篇文章是这个系列的第七篇文章了,也是我准备写的这个系列的最后一篇文章.下面是前六篇文章: 1.玩安卓从 0 到 1 之总体概览 2.玩安卓从 0 到 1 之项 ...
- [异常解决] 安卓6.0权限问题导致老蓝牙程序出现异常解决办法:Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission...
[异常解决] 安卓6.0权限问题导致老蓝牙程序出现异常解决办法:Need ACCESS_COARSE_LOCATION or ACCESS_FINE_LOCATION permission... 参考 ...
- Effective Dart 文档注释在Flutter项目中的实践
前言 什么是注释? 在编程语言中,注释就是对代码的解释和说明,其目的是让人们能够更加轻松地了解代码. 也有一句话是这样说的:程序员都讨厌两件事,1.别人不写注释 2.自己写注释 在开发者社区里,我不止 ...
- Protobuf 的 import 功能在 Go 项目中的实践
业务场景 我们会有这样的需求:在不同的文件夹中定义了不同的 proto 文件,这些不同的文件夹可能是一些不同的 gRPC 服务.因为不想重复定义某一个 message,所以其中一个服务可能会用到其他服 ...
- 数加平台在数据挖掘项目中的实践
目录 背景 数据源 数据处理 模型训练及预测 数据可视化 总结 1. 背景 返回目录 随着越来越多的公司开始重视数据的积累和产品的敏捷开发,数据量的不断增加和开发周期的不断缩短,就形成了一对明显的矛盾 ...
- 数加平台在数据挖掘项目中的实践 1
目录 背景 数据源 数据处理 模型训练及预测 数据可视化 总结 1. 背景 返回目录 随着越来越多的公司开始重视数据的积累和产品的敏捷开发,数据量的不断增加和开发周期的不断缩短,就形成了一对明显的矛盾 ...
- 安卓7.0权限及预览版问题
昨天一觉醒来,发现安卓7.0的首个开发者预览版竟然发布了,惊喜之余立马抓起Nexus5准备升级,结果立马被迎头了一盆冷水,支持的设备只有Nexus6.Nexus 5X.Nexus 6P.Nexus 9 ...
- methods vue过滤器 和_数据动态过滤技巧在 Vue 项目中的实践
这个问题是在下在做一个 Vue 项目中遇到的实际场景,这里记录一下我遇到问题之后的思考和最后怎么解决的(老年程序员记性不好 -.-),过程中会涉及到一些Vue源码的概念比如 $mount. rende ...
- 网关 Apache APISIX 在 360 基础运维平台项目中的实践
女主宣言 今天小编为大家分享一篇关于Apache APISIX的文章,文章从开发者的角度讲述了 Apache APISIX 网关在 360 基础运维平台的落地实践,希望能对大家有所帮助. PS:丰富的 ...
最新文章
- Only the original thread that created a view hierarchy can touch its views
- 九、数据库群集部署、配置 (二)
- Java并发编程(8):多线程环境中安全使用集合API(含代码)
- android studio 动画效果图,Android Studio如何动画移动视图?
- Spring下载地址
- element ui后台html_GitHub上10个开源且优秀的后台管理系统UI面板
- Jmeter(三)_配置元件
- pythonjson序列化_Python Json序列化与反序列化的示例
- 端口映射 - 专业术语 - 发问篇
- 没有服务器配置信息是怎么回事,isp服务器未配置怎么回事
- %1$s %1$d Android string
- iOS 11.2 版本紧急修复黑屏 Bug,却遇 FaceID 识别错误,真实原因在此
- python核心理念_《三天搞定Python基础概念之第一天》中文版
- 如何保证kafka消息的可靠性传递?
- tmdb数据集_数据科学第2部分的数据管道tmdb api数据搜寻器
- pdf怎么转换成word格式不变?
- 每天做好一件事,坦然微笑地面对生活
- 中国气象台API(2017.09)
- .NET前后分离解决方案
- 浏览器 本地html 图片不显示,浏览网页图片无法显示怎么回事 网页图片显示不出来的解决方法...
热门文章
- wbin笔记本商务版博通机型装(原版黑苹果)单MacOS流程记录(备忘)
- 微信h5页面图片预览
- 酒店小程序线上线下推广引流策略
- 计算机休眠移动硬盘不休眠,关闭硬盘盒自动休眠软件
- 电脑横屏和竖屏双屏幕设置不一样的壁纸
- centos8安装RabbitMQ和erlang
- 永洪BI携手华为云FusionInsight,让数据分析提效20倍
- CF727E. Games on a CD (Hash)
- 计算机机房年度重点工作,信息中心计算机的机房建设要求最新.doc
- 点卯.三维视频融合助力国家全面推进实景三维中国建设, 构建数字中国建设基座 ;开放C++源代码 点卯-魔镜系列