UniRx学习笔记

细节:

同一级(彼此之间没有大括号包含关系)中lambda表达式的参数是互通的
不同级之间lambda表达式的参数不能重名
只有订阅(加Subscribe)之后事件才能发生
JsonUtility不支持ReactiveCollection类型,可以用List,然后再转换为ReactiveCollection,
但转换完之后List内容的改变不能触发ReactiveCollection内容改变的相应方法,所以转换之后不要加监听
可以用ObserveEveryValueChanged来监听列表的某一个属性是否改变以完善上面的功能,示例如下:
Model.TodoItems.ObserveEveryValueChanged(todoItems => todoItems.Count).Subscribe(_ =>
{
    OnDataChanged();
});

一.基本:

一些常用的用法:

using UnityEngine;
using UniRx;

namespace UniRxLesson
{
    public class IntroExample : MonoBehaviour
    {
        //Subscribe的参数是一个lambda表达式,因为参数没有作用,所以用_表示(可以理解为一种习惯,实际上_和普通参数没有区别)
        void Start()
        {
            //监听第一次点击按钮的事件,并执行相应的回调方法
            Observable.EveryUpdate() // Observable是一个形容词,表示后面的EveryUpdate是可观察的,EveryUpdate时事件的发布者,它会每帧发送一个事件
                .Where(_ => Input.GetMouseButtonDown(0)) // 进行一个鼠标是否抬起的判断相对于if(Input.GetMouseButtonDown(0)),Where是在事件的发布者和接收者之间做一个过滤操作,过滤掉不满足条件的事件
                .First() // 过滤操作,只获取第一次的点击事件,也可以不用Where直接在First中写上面的方法,也就是相当于在条件判断的基础上加入了只执行一次的限制,次数大于一的操作会被过滤掉
                .Subscribe(_ => // 订阅/处理事件,Subscribe是事件的接收者,接受的是EveryUpdate发送的事件
                {
                    Debug.Log("Mouse clicked");
                })
                .AddTo(this);//当当前脚本或者游戏物体被销毁时此命令不再执行,防止空指针
            
            //实现等待5秒后执行相应方法
            //TimeSpan结构,表示一个时间间隔 
            Observable.Timer(TimeSpan.FromSeconds(5.0f))
                .Subscribe(_ =>
                {
                    Debug.Log("Do something");
                });

            //Merge用法
            var leftClickEvents = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(0));
            var rightClickEvents = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(1));

            Observable.Merge(leftClickEvents, rightClickEvents)//合并两个事件
                .Subscribe(_ =>
                {
                    Debug.Log("Mouse clicked");
                });

            //Select的用法见MVP模式
        }
    }
}

UI方面:

using UnityEngine;
using UnityEngine.UI;
using UniRx.Triggers;
using UniRx;

namespace UniRxLesson
{
    //UniRx对UI的支持
    public class UIExample : MonoBehaviour
    {
        void Start()
        {
            //button
            var button = transform.Find("Button").GetComponent<Button>();

            button.OnClickAsObservable()
                .Subscribe(_ => Debug.Log("Button clicked"));

            //toggle
            var toggle = transform.Find("Toggle").GetComponent<Toggle>();

            toggle.OnValueChangedAsObservable()
                .Where(on => on)
                .Subscribe(on => Debug.Log(on));

            //image
            var image = transform.Find("Image").GetComponent<Graphic>();

            //注意这三个方法最好一起用
            image.OnBeginDragAsObservable()
                .Subscribe(_ => Debug.Log("Begin drag"));
            image.OnDragAsObservable()
                .Subscribe(_ => Debug.Log("Dragging"));
            image.OnEndDragAsObservable()
                .Subscribe(_ => Debug.Log("End drag"));

            image.OnPointerClickAsObservable()
                .Subscribe(_ =>
                {
                    Debug.Log("On pointer click");
                });
        }
    }
}

ReactiveProperty:

using System;
using System.Collections;
using System.Collections.Generic;
using UniRx;
using UnityEngine;

public class ReactivePropertyExample : MonoBehaviour
{
    //改变一个变量的值+调用回调方法
    //委托写法:
    //public Action<int> OnAgeChanged = null;

    //private int mAge = 0;

    //public int Age
    //{
    //    get
    //    {
    //        return mAge;
    //    }

    //    set
    //    {
    //        if (mAge != value)
    //        {
    //            mAge = value;

    //            if (OnAgeChanged != null)
    //            {
    //                OnAgeChanged(value);
    //            }
    //        }
    //    }
    //}

    //void Start()
    //{
    //    OnAgeChanged += age =>
    //    {
    //        Debug.Log("Value changed: " + age);
    //    };
    //    Age = 10;
    //}

    //UniRx写法:
    //public ReactiveProperty<int> Age = new ReactiveProperty<int>(0);//默认赋值是在事件订阅之后完成的
    //不用泛型也可以这样写,这种写法可以序列化
    public IntReactiveProperty Age = new IntReactiveProperty(10);//默认赋值是在事件订阅之前完成的

    void Start()
    {
        Age.Subscribe(age =>
        {
            Debug.Log("Value changed: " + age);
        });

        Age.Value = 10;
    }
}

总结:实现一个简单的MVP模式:

UniRx实现MVP模式的原理图:

using UniRx;
using UnityEngine;
using UnityEngine.UI;

namespace UniRxLesson
{
    //View Hierarchy
    //Controller P(Presenter)
    public class EnemyExample : MonoBehaviour
    {
        EnemyModel mEnemy = new EnemyModel(200);

        void Start()
        {
            var attackBtn = transform.Find("Button").GetComponent<Button>();
            var HPText = transform.Find("Text").GetComponent<Text>();

            attackBtn.OnClickAsObservable()
                .Subscribe(_ =>
                {
                    mEnemy.HP.Value -= 99;
                });
            mEnemy.HP.SubscribeToText(HPText);

            mEnemy.IsDead
                .Where(isDead => isDead)
                .Select(isDead => !isDead)//表示类型转换或者同类型值变换
                .SubscribeToInteractable(attackBtn);
        }

    }

    //Model
    public class EnemyModel
    {
        public ReactiveProperty<long> HP;

        public IReadOnlyReactiveProperty<bool> IsDead;

        public EnemyModel(long initialHP)
        {
            HP = new ReactiveProperty<long>(initialHP);

            IsDead = HP.Select(hp => hp <= 0).ToReactiveProperty();
        }
    }
}

二.入门:

一些常用用法:

//下面方法具体的执行顺序可以在引擎中实践得出
Observable.EveryUpdate().Subscribe(_ => { Debug.Log("EveryUpdate"); });
Observable.EveryFixedUpdate().Subscribe(_ => { Debug.Log("EveryFixedUpdate"); });
Observable.EveryEndOfFrame().Subscribe(_ => { Debug.Log("EveryEndOfFrame"); });
Observable.EveryLateUpdate().Subscribe(_ => { Debug.Log("EveryLateUpdate"); });
Observable.EveryAfterUpdate().Subscribe(_ => { Debug.Log("EveryAfterUpdate"); });

//当前焦点是否位于本应用
Observable.EveryApplicationFocus().Subscribe(focus =>
{
    Debug.Log(focus);
});

//时间戳的用法,显示的时间为0时区的时间
var reactiveCommand = new ReactiveCommand<int>();

reactiveCommand.Where(x => x % 2 == 0).Subscribe(x => Debug.LogFormat("{0} 是 偶数", x));
reactiveCommand.Where(x => x % 2 != 0).Timestamp().Subscribe(x => Debug.LogFormat("{0} 是 奇数 {1}", x.Value, x.Timestamp));

reactiveCommand.Execute(10);
reactiveCommand.Execute(11);

全局与非全局的区别:

using UnityEngine;
using UniRx;
using UniRx.Triggers;

namespace UniRxLesson
{
    public class UnityAPIExample : MonoBehaviour
    {
        void Start()
        {
            //全局的,当前游戏物体销毁后依然执行
            //Observable.EveryUpdate().Subscribe(_ => 
            //{
            //    Debug.Log("EveryUpdate"); 
            //});

            //非全局的,当前游戏物体销毁后不执行,和Addto类似
            this.UpdateAsObservable().Subscribe(_ =>
            {
                Debug.Log("EveryUpdate");
            });

            //实际上Addto为下面方法的封装 
            this.OnDestroyAsObservable()
                .Subscribe(_ =>
                {
                    Debug.Log("On destroy");
                });
        }
    }
}

Coroutine:

//将事件源(Observable)转化为一个Coroutine中的yield对象
public class Observable2YieldExample : MonoBehaviour
    {
        IEnumerator Delay1Second()
        {
            yield return Observable.Timer(TimeSpan.FromSeconds(1.0f)).ToYieldInstruction();//转换
            Debug.Log("A");
        }

        void Start()
        {
            StartCoroutine(Delay1Second());
        }
    }

//使用UniRx将Coroutine转换为事件源
public class CoroutineExample : MonoBehaviour
    {
        IEnumerator CoroutineA()
        {
            yield return new WaitForSeconds(1.0f);
            Debug.Log("A");
        }

        void Start()
        {
            //StartCoroutine(CoroutineA());
            Observable.FromCoroutine(_ => CoroutineA())//转换
                .Subscribe(_ =>
                {

                });//注意这里一定要订阅,否则事件不会发生
        }
    }

//将Coroutine转换为事件源的好处在于可以借助UniRx使协程更加强大,如实现协程并行
//协程并行,即只有两个协程都执行完才进行相应的方法
    public class CoroutineSpawnExample : MonoBehaviour
    {
        IEnumerator A()
        {
            yield return new WaitForSeconds(1.0f);
            Debug.Log("A");
        }

        IEnumerator B()
        {
            yield return new WaitForSeconds(2.0f);
            Debug.Log("B");
        }

        void Start()
        {
            var aStream = Observable.FromCoroutine(_ => A());//括号中直接写A也行,两种写法一致
            var bStream = Observable.FromCoroutine(_ => B());

            Observable.WhenAll(aStream, bStream)//主要就是whenAll关键字
                .Subscribe(_ =>
                {
                    Debug.Log("When all A and B");
                });
        }
    }

//WhenAll不仅仅只用于协程的并行
//实现所有按钮都被点击之后触发相应方法的功能
    public class AllButtonClickedOnceExample : MonoBehaviour
    {
        void Start()
        {
            var leftMouseClickedEvents = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(0)).First();//注意事件一定要是可以被完成的,这里的First不可以去掉
            var rightMouseClickedEvents = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonDown(1)).First();

            Observable.WhenAll(leftMouseClickedEvents, rightMouseClickedEvents)
                .Subscribe(_ =>
                {
                    Debug.Log("Mouse clicked");
                });
        }
    }

//关于WhenAll为什么可以获得一个事件的完成与否其实是通过Subscribe的第二个参数实现的
//Subscribe方法的第一个参数表示下一个事件的对应方法(如在EveryUpdate中每一帧都会执行这个方法,而在计时协程等相同功能的实现中只是到时间了才会执行,详情见下面的代码),
    //第二个参数表示事件完成对应的方法,要求事件具有可完成的特性
    public class OnCompletedExample : MonoBehaviour
    {
        void Start()
        {
            //Observable.Timer(TimeSpan.FromSeconds(1.0f)).Subscribe(_ =>
            //{
            //    Debug.Log("OnNext:after 1 second");
            //}, () => 
            //{
            //    Debug.Log("OnCompleted");
            //});
            //EveryUpdate及加AddTo的EveryUpdate都没有完成的特性

            Observable.FromCoroutine(A)
                .Subscribe(_ => 
                {
                    Debug.Log("OnNext:");
                }, ()=> 
                {
                    Debug.Log("OnCompleted:");
                });
        }

        IEnumerator A()
        {
            yield return new WaitForSeconds(2.0f);
        }
    }

线程:

var threadAStream = Observable.Start(() => //开启一个线程
{
    Thread.Sleep(TimeSpan.FromSeconds(1));
    return 10;
});

var threadBStream = Observable.Start(() =>
{
    Thread.Sleep(TimeSpan.FromSeconds(3));
    return 10;
});

Observable.WhenAll(threadAStream, threadBStream)
    .ObserveOnMainThread() //当上边两个线程结束则把值传回到主线程
    .Subscribe(results =>
    {
        Debug.LogFormat("{0}:{1}", results[0], results[1]);
    });

WWW的支持:

ObservableWWW.Get("http://sikiedu.com") //通过url获取一个网页的内容
    .Subscribe(responseText =>
    {
        Debug.Log(responseText);
    }, e => //发生错误便于查错,可以不加这个参数
    {
        Debug.LogError(e); 
    });

var sikieduStream = ObservableWWW.Get("http://www.sikiedu.com/");
var baiduStream = ObservableWWW.Get("https://www.baidu.com/");

//使用WhenAll
Observable.WhenAll(sikieduStream, baiduStream)
    .Subscribe(responseTexts =>
    {
        Debug.Log(responseTexts[0].Substring(0, 100));
        Debug.Log(responseTexts[1].Substring(0, 100));
    }, e => 
    {
        Debug.LogError(e);
    });

//下载资源并查看进度
var progressObservable = new ScheduledNotifier<float>(); //查看进度的变量

ObservableWWW.GetAndGetBytes("xxx", progress: progressObservable) //从xxxurl下载资源,进度存储到上面的变量,:用来指定与实参匹配的形参
    .Subscribe(bytes =>
    {
        //这里常用来指定资源下载到哪里
    });

progressObservable.Subscribe(progress => //输出进度
{
    Debug.LogFormat("进度为:{0}", progress);
});

ReactiveCommand:

//ReactiveCommand有一个只读属性CanExecute和一个可读写的方法Execute,当CanExecute为true时方法的调用才能执行,否则不会执行
var reactiveCommand = new ReactiveCommand(); //默认情况下CanExecute为true

reactiveCommand.Subscribe(_ =>
{
    Debug.Log("Execute");
}); //注册好Execute方法

reactiveCommand.Execute();//调用
reactiveCommand.Execute();
reactiveCommand.Execute();

//下面是一个实例:
public class MouseUpDownExample : MonoBehaviour
{
    void Start()
    {
        var mouseDownStream = Observable.EveryUpdate().Where(_ => Input.GetMouseButton(0)).Select(_ => true);
        var mouseUpStream = Observable.EveryUpdate().Where(_ => Input.GetMouseButtonUp(0)).Select(_ => false);

        var isMouseUp = Observable.Merge(mouseDownStream, mouseUpStream);

        var reactiveCommand = new ReactiveCommand(isMouseUp, false); //这里是设置了事件源和CanExecute的默认值

        reactiveCommand.Subscribe(_ =>
        {
            Debug.Log("Reactive command executed");
        });

        Observable.EveryUpdate().Subscribe(_ =>
        {
            reactiveCommand.Execute();
        );
    }
}

ReactiveCollection和ReactiveDictionary:

//Collection相当于List
public class ReactiveCollectionExample : MonoBehaviour
{
    ReactiveCollection<string> mNames = new ReactiveCollection<string>
    {
        "name1",
        "name3"
    }; //声明并初始化

    void Start()
    {
        foreach (var url in mNames)
        {
            Debug.Log(url);
        } //遍历

        mNames.ObserveAdd().Subscribe(addUrl => Debug.LogFormat("Add:{0}", addUrl)); //监听添加事件
        mNames.ObserveRemove().Subscribe(removedUrl => Debug.LogFormat("Remove:{0}", removedUrl)); //监听移除事件
        mNames.ObserveCountChanged().Subscribe(count => Debug.LogFormat("Count:{0}", count)); //监听数量改变事件

        //相应操作
        mNames.Add("name2");
        mNames.Remove("name3");
    }
}

//Dictionary
public class ReactiveDictionaryExample : MonoBehaviour
{
    ReactiveDictionary<string, string> mLanguageCode = new ReactiveDictionary<string, string>
    {
        { "En", "英文"},
        { "Cn", "中文"}
    }; //声明并初始化
    void Start()
    {
        mLanguageCode.ObserveAdd().Subscribe(addedLanguage => Debug.LogFormat("Add:{0}", addedLanguage)); //监听增加事件
        mLanguageCode.ObserveRemove().Subscribe(removedLanguage => Debug.LogFormat("Remove:{0}", removedLanguage)); //监听移除事件
        mLanguageCode.ObserveCountChanged().Subscribe(count => Debug.LogFormat("count:{0}", count)); //监听数量改变事件

        //操作
        mLanguageCode.Add("Jp", "日文");
        mLanguageCode.Remove("En");
    }
}

 

好的,这里给您举一个使用RxJava框架的实例: 假设有一个需求,要求从一个API接口中获取用户信息并显示在界面上。我们可以使用RxJava来实现这个需求: 首先,在对应的Activity或Fragment中,我们定义一个Observable对象,用来发出网络请求并获取用户信息: ```java Observable<User> userObservable = Api.getUserInfo(userId); ``` 然后,我们可以使用subscribeOn()方法指定请求在IO线程中执行,使用observeOn()方法指定结果在主线程中回调: ```java userObservable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<User>() { @Override public void onSubscribe(Disposable d) { // 可以在这里做一些初始化操作,比如显示loading等 } @Override public void onNext(User user) { // 获取到用户信息后,更新UI显示 updateUi(user); } @Override public void onError(Throwable e) { // 出现异常时,可以做一些错误处理,比如弹出Toast提示等 showError(e.getMessage()); } @Override public void onComplete() { // 请求完成后,可以在这里做一些清理工作,比如隐藏loading等 } }); ``` 在上面的代码中,我们通过实现Observer接口来处理请求结果。在onNext()方法中,我们可以拿到获取到的用户信息,然后更新UI显示。在onError()方法中,我们可以处理请求出现异常的情况,比如弹出Toast提示。在onComplete()方法中,我们可以做一些清理工作,比如隐藏loading。 这就是一个简单的使用RxJava来获取用户信息的示例。通过使用RxJava,我们可以简化异步编程的复杂度,使代码更加清晰易读。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值