RxPermissions 源码解析之举一反三

[toc]

RxPermissions 源码解析

简介

RxPermissions 是基于 RxJava 开发的用于帮助 在Android 6.0 中处理运行时权限检测的框架。在 Android 6.0 中增加了对危险权限的动态申请,而不是像 Android 6.0 之前的默认全部获取的方式。

原始动态权限的获取

如果按照以往的获取权限方式的话,那么我们获取权限一般需要有 3 个步骤,第一步是先判断当前是否已经获取到该权限了;第 2 步申请对应的权限;第 3 步在 Activity 或者 Fragment 中处理获取权限的结果。具体的实现步骤如下:

  • step 1:判断权限是否已经获取。
if (ActivityCompat.shouldShowRequestPermissionRationale(this,Manifest.permission.CAMERA)) {//用于开发者提示用户权限的用途} else {//申请权限}
复制代码
  • step 2:申请权限
ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.CAMERA},REQUEST_CAMERA);
复制代码
  • step 3:结果处理
@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) {//授权成功} else {//授权失败}} else {super.onRequestPermissionsResult(requestCode, permissions, grantResults);}
}复制代码

RxPermissions 的简单使用

其实 RxPermissions 的使用方式有两种

  • 方式 1:
RxPermissions rxPermissions = new RxPermissions(MainActivity.this);rxPermissions.request(Manifest.permission.READ_EXTERNAL_STORAGE,Manifest.permission.WRITE_EXTERNAL_STORAGE)//这里填写所需要的权限.subscribe(new Consumer<Boolean>() {@Overridepublic void accept(Boolean aBoolean) throws Exception {if (aBoolean) {// 通过}else{// 拒绝}}});
复制代码
  • 方式 2:结合 RxBinding 来使用
RxPermissions rxPermissions = new RxPermissions(MainActivity.this);
// Must be done during an initialization phase like onCreate
RxView.clicks(findViewById(R.id.enableCamera)).compose(rxPermissions.ensure(Manifest.permission.CAMERA)).subscribe(granted -> {// R.id.enableCamera has been clicked});复制代码

源码分析

整体介绍

接着我们来对这个 RxPermissions 进行一个源码的解析,但是打开源码的时候,我们可以发现,这个库里面,其实就只有 3 个类:RxPermissions、RxPermissionsFragment、Permission

  • RxPermissions
    • 最主要的实现类,利用 rxjava,为我们提供了方便权限申请的类
  • RxPermissionsFragment
    • 是一个 fragment,主要的动态权限获取类
  • Permission
    • 定义的权限的 model 类

源码分析

RxPermissions 实例创建

对于源码的分析,我们应该先从简单的使用入手。下面我们可以先看看实例化 RxPermissionsFragment 的时候是做了什么?

    RxPermissionsFragment mRxPermissionsFragment;public RxPermissions(@NonNull Activity activity) {mRxPermissionsFragment = getRxPermissionsFragment(activity);}复制代码

我们可以看到,上面的代码中,实例化 RxPermissionsFragment 的时候,里面先创建了一个 RxPermissionsFragment 的实例。我们再接着看 getRxPermissionsFragment 这个方法的实现。

    private RxPermissionsFragment getRxPermissionsFragment(Activity activity) {//  查找 RxPermissionsFragment 是否已经被添加了RxPermissionsFragment rxPermissionsFragment = findRxPermissionsFragment(activity);boolean isNewInstance = rxPermissionsFragment == null;if (isNewInstance) {rxPermissionsFragment = new RxPermissionsFragment();FragmentManager fragmentManager = activity.getFragmentManager();fragmentManager.beginTransaction().add(rxPermissionsFragment, TAG).commitAllowingStateLoss();fragmentManager.executePendingTransactions();}return rxPermissionsFragment;}复制代码

在 getRxPermissionsFragment() 这个方法中,首先是先查找当前是否已经添加了这个 rxPermissionsFragment 的实例,如果已经添加,那么直接返回已经添加的实例,如果没有添加过的话,那么就重新再创建一个 RxPermissionsFragment 实例并提交;

    private RxPermissionsFragment findRxPermissionsFragment(Activity activity) {return (RxPermissionsFragment) activity.getFragmentManager().findFragmentByTag(TAG);}
复制代码

到此,rxPermissionsFragment 的实例化已经完成,接着我们需要看看 request 这个方法中实现了什么。

request 方法

    public Observable<Boolean> request(final String... permissions) {return Observable.just(TRIGGER).compose(ensure(permissions));}
复制代码
    static final Object TRIGGER = new Object();
复制代码

从上面的代码中,我们可以看到,request 方法中需要传入的参数是一个 权限的数组,返回值是 Observable 对象。Observable.just(TRIGGER) 是快捷创建一个 Observable 的方式,由于 TRIGGER 是一个空的 Object 对象,所以 TRIGGER 就是一个占位符而已,Observable.just(TRIGGER) 创建的是一个 Observable,之后通过 compose 将 Observable 转化为 Observable 并返回。在 compose 中需要的参数是一个 ObservableTransformer,那么我们接着看 ensure() 这个方法。

ensure(permissions);

public <T> ObservableTransformer<T, Boolean> ensure(final String... permissions) {// 创建一个Transformer对象返回return new ObservableTransformer<T, Boolean>() {@Overridepublic ObservableSource<Boolean> apply(Observable<T> o) {//request(o, permissions) 方法返回 Observable<Permission> 对象return request(o, permissions)// 将 Observable<Permission> 转换为 Observable<Boolean>,在这里会等待所有的权限都返回了一次性发射数据。.buffer(permissions.length).flatMap(new Function<List<Permission>, ObservableSource<Boolean>>() {@Overridepublic ObservableSource<Boolean> apply(List<Permission> permissions) throws Exception {// 如果permissions为空那么直接返回Observable.empty();if (permissions.isEmpty()) {// Occurs during orientation change, when the subject receives onComplete.// In that case we don't want to propagate that empty list to the// subscriber, only the onComplete.return Observable.empty();}// Return true if all permissions are granted.for (Permission p : permissions) {if (!p.granted) {return Observable.just(false);}}return Observable.just(true);}});}};}复制代码

在 ensure 的这个方法中,最终会返回的是 ObservableTransformer<T, Boolean> 对象。接着我们看看 ObservableTransformer 的匿名实现类里面的 apply 方法,这里实现的就是将 Observable 转换为 Observable 的操作。我们对 apply 这个方法里面的代码进行简化一下。

return request(o,permissions).buffer(permissions.length).flatMap(new Function<List<Permission>, ObservableSource<Boolean>>{});
复制代码
  • request() 方法返回 Observable 对象
  • buffer(len) 操作符将一个 Observable 变换为 Observable<List>,原来的 Observable 正常发射数据,变换产生的 Observable 发射这些数据的缓存集合。buffer 将数据缓存到一个集合当中,然后在适当(比如:所有请求的权限结果都返回了)的时机一起发送。
  • flatMap() 方法将 Observable<List> 转化为 Observable

request(o, permissions);

    private Observable<Permission> request(final Observable<?> trigger, final String... permissions) {if (permissions == null || permissions.length == 0) {throw new IllegalArgumentException("RxPermissions.request/requestEach requires at least one input permission");}return oneOf(trigger, pending(permissions)).flatMap(new Function<Object, Observable<Permission>>() {@Overridepublic Observable<Permission> apply(Object o) throws Exception {return requestImplementation(permissions);}});}复制代码

在 request 这个方法里面,其实 oneOf() 和 pending() 方法我们可以忽略的,主要的话,我们应该关注 requestImplementation(final String... permissions) 这个方法,在这个方法里面,主要实现了权限的请求。

requestImplementation

@TargetApi(Build.VERSION_CODES.M)private Observable<Permission> requestImplementation(final String... permissions) {List<Observable<Permission>> list = new ArrayList<>(permissions.length);List<String> unrequestedPermissions = new ArrayList<>();// In case of multiple permissions, we create an Observable for each of them.// At the end, the observables are combined to have a unique response.for (String permission : permissions) {mRxPermissionsFragment.log("Requesting permission " + permission);if (isGranted(permission)) {// Already granted, or not Android M// Return a granted Permission object.// 权限已经被同意或者不是 Android 6.0 以上版本,创建一个 同意的 Permission 对象。list.add(Observable.just(new Permission(permission, true, false)));continue;}if (isRevoked(permission)) {// 权限被拒绝,返回一个 拒绝的 Permission 对象。list.add(Observable.just(new Permission(permission, false, false)));continue;}PublishSubject<Permission> subject = mRxPermissionsFragment.getSubjectByPermission(permission);// 如果 subject 不存在,那么创建一个 subject。if (subject == null) {unrequestedPermissions.add(permission);subject = PublishSubject.create();mRxPermissionsFragment.setSubjectForPermission(permission, subject);}list.add(subject);}// 还未提起申请的权限进行申请if (!unrequestedPermissions.isEmpty()) {String[] unrequestedPermissionsArray = unrequestedPermissions.toArray(new String[unrequestedPermissions.size()]);requestPermissionsFromFragment(unrequestedPermissionsArray);}// 严格按照顺序发射数据return Observable.concat(Observable.fromIterable(list));}复制代码

onRequestPermissionsResult()

@TargetApi(Build.VERSION_CODES.M)public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode != PERMISSIONS_REQUEST_CODE) return;boolean[] shouldShowRequestPermissionRationale = new boolean[permissions.length];for (int i = 0; i < permissions.length; i++) {shouldShowRequestPermissionRationale[i] = shouldShowRequestPermissionRationale(permissions[i]);}onRequestPermissionsResult(permissions, grantResults, shouldShowRequestPermissionRationale);}void onRequestPermissionsResult(String permissions[], int[] grantResults, boolean[] shouldShowRequestPermissionRationale) {for (int i = 0, size = permissions.length; i < size; i++) {log("onRequestPermissionsResult  " + permissions[i]);// Find the corresponding subjectPublishSubject<Permission> subject = mSubjects.get(permissions[i]);if (subject == null) {// No subject foundLog.e(RxPermissions.TAG, "RxPermissions.onRequestPermissionsResult invoked but didn't find the corresponding permission request.");return;}// 发射权限申请结果mSubjects.remove(permissions[i]);boolean granted = grantResults[i] == PackageManager.PERMISSION_GRANTED;subject.onNext(new Permission(permissions[i], granted, shouldShowRequestPermissionRationale[i]));subject.onComplete();}}复制代码

RxJava 操作符

Observable.just()

just 操作符是将一个对象转化为 Observable 的操作符。这个对象可以是一个数字、字符串或者是数组对象等,是 RxJava 中快速创建一个 Observable 对象的操作符。如果有 subscriber 订阅的话,那么会依次调用 onNext() 和 OnComplete() 方法。所以这里只是创建了一个 Observable 对象,方便后续的调用。

compose(Transformer)操作符

compose 操作符是对 Observable 对象的整体转化。例如:通过 Transformer,我们可以将 Observable 对象转换成 Observable 对象了。

    public static ObservableTransformer<String,Boolean> getTransformer(){return new ObservableTransformer<String, Boolean>() {@Overridepublic ObservableSource<Boolean> apply(Observable<String> upstream) {return upstream.flatMap(new Function<String, ObservableSource<Boolean>>() {@Overridepublic ObservableSource<Boolean> apply(String s) throws Exception {return Observable.just(true);}});}};}
复制代码
/*** 线程切换* @return*/public static <T> ObservableTransformer<T,T> getScheduler(){return new ObservableTransformer<T, T>() {@Overridepublic ObservableSource<T> apply(Observable<T> upstream) {return upstream.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());}};}复制代码

buffer 操作符

buffer 操作符将一个 Observable 变换为另一个,原来的 Observable 正常发射数据,变换产生的 Observable 发射这些数据的缓存集合。buffer将数据缓存到一个集合当中,然后在适当的时机一起发送。 buffer(count) 以列表(List)的形式发射非重叠的缓存,每一个缓存至多包含来自原始Observable的count项数据(最后发射的列表数据可能少于count项)

  • 例如:缓存 2 个数据之后,再发送数据(调用 buffer(count) 函数)
                Observable.just(1,2,3,4,5,6).buffer(2).subscribe(integers -> {Log.i(TAG, "accept size: "+integers.size());for (Integer integer : integers) {Log.i(TAG, "accept: "+integer);}});
复制代码
  • 输出结果
2018-12-14 11:16:28.452 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept size: 2
2018-12-14 11:16:28.452 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 1
2018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 2
2018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept size: 2
2018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 3
2018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 4
2018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept size: 2
2018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 5
2018-12-14 11:16:28.453 28126-28126/com.luwei.lwbaselib I/LwBaseActivity: accept: 6
复制代码
  • 例如:缓存 3 个数据,再发送数据,每次移动 1 步
                Observable.just(1,2,3,4).buffer(3,1).subscribe(integers -> {Log.i(TAG, "accept size: "+integers.size());for (Integer integer : integers) {Log.i(TAG, "accept: "+integer);}});
复制代码
  • 输出结果
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept size: 3
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 1
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 2
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 3
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept size: 3
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 2
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 3
2018-12-14 11:24:31.455 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 4
2018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept size: 2
2018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 3
2018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 4
2018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept size: 1
2018-12-14 11:24:31.456 29164-29164/com.luwei.lwbaselib I/LwBaseActivity: accept: 4
复制代码

concat 操作符

是接收若干个Observables,发射数据是有序的,不会交叉。

Subject

  • 作为 Observable 和 Observer 之间的桥梁
  • 可以当做 Observable
  • 可以当做 Observer

PublishSubject

继承至 Subject,它的 Observer 只会接收到 PublishSubject 被订阅之后发送的数据。示例代码如下;我们只会接收到 publishSubject3 和 publishSubject4;

                PublishSubject<String> publishSubject = PublishSubject.create();publishSubject.onNext("publishSubject1");publishSubject.onNext("publishSubject2");publishSubject.subscribe(new Consumer<String>() {@Overridepublic void accept(String s) throws Exception {Log.i(TAG, "accept: "+s);}});publishSubject.onNext("publishSubject3");publishSubject.onNext("publishSubject4");复制代码
  • 执行结果
2018-12-14 11:33:18.168 29916-29916/com.luwei.lwbaselib I/LwBaseActivity: accept: publishSubject3
2018-12-14 11:33:18.168 29916-29916/com.luwei.lwbaselib I/LwBaseActivity: accept: publishSubject4
复制代码

举一反三

可以看到,在 RxPermissions 这个获取权限的开源框架中,往 Activity 中添加了一个空的 Fragment,这个 fragment 才是用来发起申请权限和处理权限的请求,最后再将结果返回,这样子就避免了我们发送请求之后,还需要在 onRequestPermissionsResult 中进行处理,并判断 requestCode 等繁琐操作。想到这里,我们平时使用 startActivityForResult 时,我们也可以同样采用这样的思路来简化我们的请求。

同样的,我们采用添加空白的 fragment,来做 startActivityForResult 请求,主要的实现类有 SimpleForResult 和 SimpleOnResultFragment,ActivityResultInfo 是请求 model,接下我们先看代码。

SimpleForResult


/*** @Author: chenjianrun* @Time: 2018/12/7* @Description:   避免调用 startActivity 时,需要 onActivityResult 处理的类*/
public class SimpleForResult {private static final String TAG = "SimpleForResult";private SimpleOnResultFragment mSimpleOnResultFragment;public SimpleForResult(AppCompatActivity activity) {mSimpleOnResultFragment = getOnResultFragment(activity.getSupportFragmentManager());}public SimpleForResult(Fragment fragment){mSimpleOnResultFragment = getOnResultFragment(fragment.getChildFragmentManager());}private SimpleOnResultFragment getOnResultFragment(FragmentManager fragmentManager) {SimpleOnResultFragment simpleOnResultFragment = findSimpleOnResultFragment(fragmentManager);if (simpleOnResultFragment == null) {simpleOnResultFragment = new SimpleOnResultFragment();fragmentManager.beginTransaction().add(simpleOnResultFragment, TAG).commitAllowingStateLoss();fragmentManager.executePendingTransactions();}return simpleOnResultFragment;}private SimpleOnResultFragment findSimpleOnResultFragment(FragmentManager fragmentManager) {return (SimpleOnResultFragment) fragmentManager.findFragmentByTag(TAG);}public Observable<ActivityResultInfo> startForResult(Intent intent) {return mSimpleOnResultFragment.startForResult(intent);}public Observable<ActivityResultInfo> startForResult(Class<?> clazz) {Intent intent = new Intent(mSimpleOnResultFragment.getActivity(), clazz);return startForResult(intent);}public void startForResult(Intent intent, Callback callback) {mSimpleOnResultFragment.startForResult(intent, callback);}public void startForResult(Class<?> clazz, Callback callback) {Intent intent = new Intent(mSimpleOnResultFragment.getActivity(), clazz);startForResult(intent, callback);}public interface Callback {void onActivityResult(int requestCode, int resultCode, Intent data);}
}复制代码

SimpleOnResultFragment


/*** @Author: chenjianrun* @Time: 2018/12/7* @Description:    真正调用 startActivity 和处理 onActivityResult 的类。*/
public class SimpleOnResultFragment extends Fragment {private static Map<Integer, PublishSubject<ActivityResultInfo>> mSubjects = new HashMap<>();private static Map<Integer, SimpleForResult.Callback> mCallbacks = new HashMap<>();public SimpleOnResultFragment() {}@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setRetainInstance(true);}public Observable<ActivityResultInfo> startForResult(final Intent intent) {int requestCode = generateRequestCode();PublishSubject<ActivityResultInfo> subject = PublishSubject.create();mSubjects.put(requestCode, subject);startActivityForResult(intent, requestCode);return subject;}public void startForResult(Intent intent, SimpleForResult.Callback callback) {int requestCode = generateRequestCode();mCallbacks.put(requestCode, callback);startActivityForResult(intent, requestCode);}@Overridepublic void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);//rxjava方式的处理PublishSubject<ActivityResultInfo> subject = mSubjects.remove(requestCode);if (subject != null) {subject.onNext(new ActivityResultInfo(requestCode, resultCode, data));subject.onComplete();}//callback方式的处理SimpleForResult.Callback callback = mCallbacks.remove(requestCode);if (callback != null) {callback.onActivityResult(requestCode, resultCode, data);}}private int generateRequestCode(){Random random = new Random();for (;;){int code = random.nextInt(65536);if (!mSubjects.containsKey(code) && !mCallbacks.containsKey(code)){return code;}}}
}
复制代码

ActivityResultInfo

package com.luwei.util.forresult;import android.content.Intent;/*** @Author: chenjianrun* @Time: 2018/12/7* @Description:*/
public class ActivityResultInfo {private int requestCode;private int resultCode;private Intent data;public ActivityResultInfo(int requestCode, int resultCode, Intent data) {this.requestCode = requestCode;this.resultCode = resultCode;this.data = data;}public int getRequestCode() {return requestCode;}public void setRequestCode(int requestCode) {this.requestCode = requestCode;}public ActivityResultInfo(int resultCode, Intent data) {this.resultCode = resultCode;this.data = data;}public int getResultCode() {return resultCode;}public void setResultCode(int resultCode) {this.resultCode = resultCode;}public Intent getData() {return data;}public void setData(Intent data) {this.data = data;}
}复制代码

简单使用示例

  • 简单的 Activity 调用
// 简化调用 startActivityForResult 及避免在 onActivityResult 中处理繁琐的结果SimpleForResult simpleForResult = new SimpleForResult(this);simpleForResult.startForResult(ToastActivity.class).subscribe((resultInfo) -> {if (resultInfo.getData() != null) {ToastUtils.showLong(resultInfo.getData().getStringExtra("result"));}});
复制代码
  • 调用摄像头
    /*** 打开摄像头*/private void openCamera() {try {mTmpFile = FileUtils.createTmpFile(this);} catch (IOException e) {e.printStackTrace();}simpleForResult.startForResult(getOpenCameraIntent(this, mTmpFile)).subscribe((resultInfo -> {if (resultInfo.getResultCode() == RESULT_OK) {mHeadUrl = mTmpFile.getAbsolutePath();ImageLoaderUtils.loadCircleImage(this, ivHeader, mHeadUrl);// 裁剪(如果没有要求可裁剪,也可以不要)startPictureZoom(mTmpFile);}}));}/*** 获取打开照相机的 intent,适配 Android 7.0* @param activity* @param mTmpFile* @return*/public static Intent getOpenCameraIntent(Activity activity,File mTmpFile){Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);if (intent.resolveActivity(activity.getPackageManager()) != null) {if (mTmpFile != null && mTmpFile.exists()) {if (Build.VERSION.SDK_INT >= 24) {// 适配 Android 7.0intent.putExtra(MediaStore.EXTRA_OUTPUT,FileProvider.getUriForFile(activity, activity.getPackageName()+".provider",mTmpFile));} else {intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(mTmpFile));}} else {Toast.makeText(activity, me.nereo.image_selector.R.string.error_image_not_exist, Toast.LENGTH_SHORT).show();}} else {Toast.makeText(activity, me.nereo.image_selector.R.string.msg_no_camera, Toast.LENGTH_SHORT).show();}return intent;}复制代码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/449962.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

总结Selenium WebDriver中一些鼠标和键盘事件的使用

在使用 Selenium WebDriver 做自动化测试的时候&#xff0c;会经常模拟鼠标和键盘的一些行为。比如使用鼠标单击、双击、右击、拖拽等动作&#xff1b;或者键盘输入、快捷键使用、组合键使用等模拟键盘的操作。在 WebDeriver 中&#xff0c;有一个专门的类来负责实现这些测试场…

最快浮点数取绝对值

做视频算法10多年&#xff0c;经常要算绝对值&#xff0c;整数的绝对值有快速算法&#xff0c;但浮点数的绝对值没看到有快速算法&#xff0c;经常不段发现&#xff0c;得到如下浮点数的快速算法&#xff1a; 快6倍多&#xff0c; #include <Windows.h> #include <ios…

Linux ln命令、软链接和硬链接的区别

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 Linux ln命令是一个非常重要命令&#xff0c;它的功能是为某一个文件在另外一个位置建立一个同步的链接。 当我们需要在不同的目录&…

Android应用开发——文件目录

Android 存储位置及 API 一、内部存储 应用安装后都会在Android 根目录生成 /data/data/packagename&#xff0c;当前应用读取不需要读写权限 注意&#xff1a; 有些开发者可能看到过应用的根目录为 /data/user/0/packagename 的情况&#xff0c;这里解释一下&#xff0c;And…

git常用命令及冲突解决

2019独角兽企业重金招聘Python工程师标准>>> git常用命令 git config --global user.name chenhongjiang git config --global user.email 123qq.com git init 建立厂库 git status 查看状态 git add . 添加当前目录 git add a.php 添加文件…

C 语言常见问题集

从 http://c-faq-chn.sourceforge.net/ccfaq/index.html 转载过来&#xff0c;学习C的好助手。 目录1. 前言2. 声明和初始化 2.1 我如何决定使用那种整数类型&#xff1f;2.2 64 位机上的 64 位类型是什么样的&#xff1f;2.3 怎样定义和声明全局变量和函数最好&#xff1f;2.4…

【题解】quake

【题解】\(quake\) 题目大意 我们共有报酬\(f\)元&#xff0c;一条边有它的价值\(w_i\),有它的建造时间\(t_i\)。要求建一些边&#xff0c;生成一颗树。求最大的利润率。 数据范围 \(n\le 400\) \(m\le10000\) \(Solution\) 实际上\(n,m\)出到\(\le 100000\)应该也是没问题的。…

Android应用开发——service连接泄露异常:android.app.ServiceConnectionLeaked: that was originally bound here

在做service开发过程中&#xff0c;大部分可能会遇到以下异常&#xff0c;该异常仅通过log输出&#xff0c;并不会导致app crash。 E/ActivityThread: Activity com.example.image.all_samples.Main2Activity has leaked ServiceConnection com.example.image.all_samples.Mai…

Linux more命令、Linux rhmask命令

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 Linux more 命令类似 cat &#xff0c;不过会以一页一页的形式显示&#xff0c;更方便使用者逐页阅读&#xff0c;而最基本的指令就是按…

从零开始学习PYTHON3讲义(二)把Python当做计算器

《从零开始PYTHON3》第二讲 上一讲我们说过了如何启动Python IDLE集成开发学习环境&#xff0c;macOS/Linux都可以在命令行执行idle3。Windows则从开始菜单中去寻找IDLE程序的图标。 上一讲我们还见到了Python的两种工作模式&#xff0c;交互模式和程序模式。 通常在一个大型的…

Tranquility

本页目录与Kafka集群交互Druid使用Tranquility Kafka本文以Kafka为例&#xff0c;介绍在E-MapReduce中如何使用Tranquility从Kafka集群采集数据&#xff0c;并实时推送至Druid集群。 Tranquility是一个以push方式向Druid实时发送数据的应用。它替用户解决了分区、多副本、服务发…

Iot相关杂烩

人工智能就像人的大脑&#xff0c;而 IoT 就像人的神经网络 1&#xff09;在天空中巨大的鸟群里&#xff0c;每一只鸟儿都实时判断自己和四周同伴的距离。这时&#xff0c;它们各自都是一个物联网节点。2&#xff09;这些“节点”并不是简单地收集数据&#xff0c;而是在实时计…

水滴石穿C语言之指针、数组和函数

基本解释   1、指针的本质是一个与地址相关的复合类型&#xff0c;它的值是数据存放的位置&#xff08;地址&#xff09;&#xff1b;数组的本质则是一系列的变量。   2、数组名对应着&#xff08;而不是指向&#xff09;一块内存&#xff0c;其地址与容量在生命期内保持…

告诉你银行在年底为存储做的小动作

25年前&#xff0c;银行的存款利率是10.98%&#xff0c;可谓巅峰时刻。15年前&#xff0c;银行的存款利率开始下降&#xff0c;降到了8%的利率。 到了5年前&#xff0c;银行的存款利率毫无回转之势&#xff0c;直线下降到了5%的利率。 而如今&#xff0c;我们无可奈何地接受了2…

爬虫学习(五)——百度贴吧的爬取

import osimport timeimport urllib.requestimport urllib.parse# 输入目标页码和吧名def header(): url "https://tieba.baidu.com/f?" baming input("请输入要爬取的吧名") start_page int(input("请输入起始页")) end_page …

什么是嵌入式设备?/ 嵌入式设备的定义

什么是嵌入式设备&#xff1f;/ 嵌入式设备的定义 区别于通用计算机的其他设备都可以称之为嵌入式设备 &#xff08;个人电脑&#xff0c;服务器&#xff09; 一段时期内&#xff0c;必备的硬件配置。 嵌入式开发包括哪些部分&#xff1a; 底层驱动开发&#xff1a; 关键字…

Linux mv命令、Linux cp命令、Linux scp命令

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到教程。 Linux mv命令用来为文件或目录改名、或将文件或目录移入其它位置。 语法 mv [options] source dest mv [options] source... director…

创业者谈:畏惧失败,但也要拥抱失败

摘要&#xff1a;本文作者为Paydirt创始人Tristan Gamilis&#xff0c;他在文中分享了如何面对创业过程中的失败。作为一个创业者&#xff0c;开始的时候并非全才&#xff0c;很多知识都是经历了创业中的失败&#xff0c;摸爬滚打之后才学会的。所以&#xff0c;我们在创业过程…

基于STM32F4移植W5500官方驱动库ioLibrary_Driver(转)

源&#xff1a; 基于STM32F4移植W5500官方驱动库ioLibrary_Driver 参考&#xff1a; 基于STM32W5500 的Ethernet和Internet移植 Upgrade W5500 Throughput on Nucleo STM32F401RE Using SPI DMA

redis 资料

redis是什么: Redis is an open source, BSD licensed, advanced key-value store. It is often referred to as a data structure server since keys can contain strings, hashes, lists, sets and sorted sets. redis是开源,BSD许可,高级的key-value存储系统. 可以用来存储字…