Java 在 Android 中使用 Observable

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/42983101/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 00:34:29  来源:igfitidea点击:

Using Observable in Android

javaandroidobservable

提问by Kaushik NP

I want to implement a Navigation Viewwith many fragments that depend totally on a value defined in the MainActivity. I know that variables in MainActivity can be accessed using method defined in MainActivity from other Fragments to get the value, but the catch here is that the value of the variable in MainActivity may change(which runs on an AsyncThread). Now, I either change the code such that my Fragments update their valuebased on some event in the fragment itselfor use SharedPreference. But I don't want to use SharedPreferences, neither have to check for change in the value unnecessarily many times.

我想实现一个Navigation View包含许多完全依赖于MainActivity. 我知道可以使用 MainActivity 中定义的方法从其他 Fragment访问 MainActivity 中的变量以获取 value,但这里的问题是 MainActivity 中变量的值可能会更改(在AsyncThread上运行)。现在,我要么更改代码,以便我的片段根据片段本身中的某个事件更新它们的值,要么使用SharedPreference. 但我不想使用 SharedPreferences,也不需要多次检查值的变化。

I know in RxJS, we use Observablethat runs Asynchronously and works in a fashion similar to a conveyor belt. A bit of googling through the official docs : Observableconfirmed my suspicion of something similar being available in Android, but couldn't find any proper Tutorial or explanation on how to implement it. So, am looking for a simple code snippet that might give clarity to how Observable works in Android and if it is Async and if its based similar to RxJS implementation of it. (No, I don't want RxJS implementation)

我知道在 RxJS 中,我们使用异步运行的Observable并以类似于传送带的方式工作。谷歌搜索官方文档:Observable证实了我对 Android 中可用的类似内容的怀疑,但找不到任何正确的教程或关于如何实现它的解释。所以,我正在寻找一个简单的代码片段,它可以清楚地说明 Observable 在 Android 中的工作方式,以及它是否是异步的,以及它是否基于类似于它的 RxJS 实现。(不,我不想要 RxJS 实现)

Test Case:

测试用例:

MainActivity : int a, b (need observable for both variables)
Frag1 : int a1 , b1, a1changed(),b1changed()
Frag2 : int a2 , b2, a2Changed(), b2changed()

MainActivity contains integers whose value when changed should reflect in corresponding integers across the Fragments and calls separate function for each Fragment on the change being noticed.

MainActivity 包含整数,其值在更改时应反映在 Fragment 中的相应整数中,并在注意到更改时为每个 Fragment 调用单独的函数。

回答by Benjamin Mesing

Reactive is not part of Android but you are probably looking for this library: https://github.com/JakeWharton/RxBinding

Reactive 不是 Android 的一部分,但您可能正在寻找这个库:https: //github.com/JakeWharton/RxBinding

The landing page is missing an introductory example, so you have to look at the javadoc. This post should give you a good start: How to create an Observable from OnClick Event Android?Here is the code sample from Matt to get you started

登录页面缺少介绍性示例,因此您必须查看 javadoc。这篇文章应该给你一个良好的开端:How to create an Observable from OnClick Event Android? 这是来自 Matt 的代码示例,可帮助您入门

RxView.clicks(myButton)
    .subscribe(new Action1<Void>() {
        @Override
        public void call(Void aVoid) {
            /* do something */
        }
    });

回答by Ga?tan M.

Here is a simple example with an Activityand a single Fragmentbut it will be the same with other fragments.

这是一个简单的例子,带有一个Activity和一个,Fragment但它与其他片段相同。

First you need to create a class standing for the value you want to observe, in your case it's a simple intso create a class containing this intand that extends Observable(it implements Serializableto simplify exchange between activity and fragment):

首先,您需要创建一个代表要观察的值的类,在您的情况下,它很简单,int因此创建一个包含 thisint和 that extends的类Observable(它实现Serializable以简化活动和片段之间的交换):

...
import java.util.Observable;

public class ObservableInteger extends Observable implements Serializable {

    private int value;

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
        this.setChanged();
        this.notifyObservers(value);
    }
}

Then use this observable int in an activity (activity layout contains a Buttonand a FrameLayoutused to display a Fragment):

然后在活动中使用这个 observable int(活动布局包含 aButton和 aFrameLayout用于显示 a Fragment):

public class MainActivity extends Activity {

    private ObservableInteger a;
    private Button button;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Create observable int
        a = new ObservableInteger();
        // Set a default value to it
        a.setValue(0);

        // Create frag1
        Frag1 frag1 = new Frag1();
        Bundle args = new Bundle();
        // Put observable int (That why ObservableInteger implements Serializable)
        args.putSerializable(Frag1.PARAM, a);
        frag1.setArguments(args);

        // Add frag1 on screen
        getFragmentManager().beginTransaction().add(R.id.container, frag1).commit();

        // Add a button to change value of a dynamically
        button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Set a new value in a
                a.setValue(a.getValue() + 1);
            }
        });
    }
}

Finally, create a Fragmentthat listen a value change:

最后,创建一个Fragment监听值变化的:

...
import java.util.Observer;

public class Frag1 extends Fragment {
    public static final String PARAM = "param";

    private ObservableInteger a1;
    private Observer a1Changed = new Observer() {
        @Override
        public void update(Observable o, Object newValue) {
            // a1 changed! (aka a changed)
            // newValue is the observable int value (it's the same as a1.getValue())
            Log.d(Frag1.class.getSimpleName(), "a1 has changed, new value:"+ (int) newValue);
        }
    };

    public Frag1() {
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            // Get ObservableInteger created in activity
            a1 = (ObservableInteger) getArguments().getSerializable(PARAM);
            // Add listener for value change
            a1.addObserver(a1Changed);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_blank, container, false);
    }
}

I try to name my variables the same as yours, I hope it will help you.

我尝试将我的变量命名为与您相同的名称,希望对您有所帮助。

回答by Think Twice Code Once

There is an good example about using Observable of Android (java.util.Observable)here:https://andhradroid.wordpress.com/2012/04/05/object-observer-pattern-in-android/

这里有一个关于使用 Android Observable (java.util.Observable)的好例子:https : //andhradroid.wordpress.com/2012/04/05/object-observer-pattern-in-android/

And another example about using Observer pattern in Java:http://www.journaldev.com/1739/observer-design-pattern-in-java.

另一个关于在 Java 中使用观察者模式的例子:http : //www.journaldev.com/1739/observer-design-pattern-in-java

Generally, there are two ways:

一般有两种方式:

  • Use java.util.Observable.
  • Design your own Observable(more flexible, help us understand more deeply).
  • 使用java.util.Observable
  • 设计自己的Observable(更灵活,帮助我们更深入的理解)。

I like the second way more, for example: (Sorry, I want to make sure it works so I make a complete example)

我更喜欢第二种方式,例如:(对不起,我想确保它有效,所以我做了一个完整的例子

The Observable:

可观察的

public interface MyObservable {
    void addObserver(MyObserver myObserver);
    void removeObserver(MyObserver myObserver);
    void notifyObserversAboutA();
    void notifyObserversAboutB();
}

The Observer:

观察员:

public interface MyObserver {
    void onAChange(int newValue);
    void onBChange(int newValue);
}

The MainActivity:

主要活动:

public class MainActivity extends AppCompatActivity implements MyObservable {

    private List<MyObserver> myObservers;
    private int a, b;
    private EditText etA;
    private EditText etB;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        myObservers = new ArrayList<>();

        ViewPager vpContent = (ViewPager) findViewById(R.id.activity_main_vp_content);
        etA = (EditText) findViewById(R.id.et_a);
        etB = (EditText) findViewById(R.id.et_b);
        Button btnOk = (Button) findViewById(R.id.btn_ok);

        //add fragments to viewpager
        List<Fragment> fragments = new ArrayList<>();
        Fragment1 fragment1 = new Fragment1();
        addObserver(fragment1);
        Fragment2 fragment2 = new Fragment2();
        addObserver(fragment2);

        fragments.add(fragment1);
        fragments.add(fragment2);
        MyFragmentPagerAdapter fragmentPagerAdapter
                = new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments);
        vpContent.setAdapter(fragmentPagerAdapter);

        btnOk.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String a = etA.getText().toString().trim();
                String b = etB.getText().toString().trim();

                if (!a.equals("") && !b.equals("")) {
                    setA(Integer.parseInt(a));
                    setB(Integer.parseInt(b));
                }
            }
        });
    }

    private void setA(int value) {
        a = value;
        notifyObserversAboutA();
    }

    private void setB(int value) {
        b = value;
        notifyObserversAboutB();
    }

    @Override
    public void addObserver(MyObserver myObserver) {
        myObservers.add(myObserver);
    }

    @Override
    public void removeObserver(MyObserver myObserver) {
        myObservers.remove(myObserver);
    }

    @Override
    public void notifyObserversAboutA() {
        for (MyObserver observer : myObservers) {
            observer.onAChange(this.a);
        }
    }

    @Override
    public void notifyObserversAboutB() {
        for (MyObserver observer : myObservers) {
            observer.onBChange(this.b);
        }
    }
}

The Fragment1:

片段1:

public class Fragment1 extends Fragment implements MyObserver {

    private TextView tvA;
    private TextView tvB;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {

        View contentView = inflater.inflate(R.layout.fragment_basic, container, false);

        tvA = (TextView) contentView.findViewById(R.id.tv_a);
        tvB = (TextView) contentView.findViewById(R.id.tv_b);

        return contentView;
    }

    @Override
    public void onAChange(int newValue) {
        tvA.setText(String.valueOf("New value of a: " + newValue));
    }

    @Override
    public void onBChange(int newValue) {
        tvB.setText(String.valueOf("New value of b: " + newValue));
    }
}

The Fragment2:

片段2:

public class Fragment2 extends Fragment implements MyObserver {

    private TextView tvA;
    private TextView tvB;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
                             @Nullable Bundle savedInstanceState) {

        View contentView = inflater.inflate(R.layout.fragment_basic, container, false);

        tvA = (TextView) contentView.findViewById(R.id.tv_a);
        tvB = (TextView) contentView.findViewById(R.id.tv_b);

        return contentView;
    }

    @Override
    public void onAChange(int newValue) {
        tvA.setText(String.valueOf("New value of a: " + newValue));
    }

    @Override
    public void onBChange(int newValue) {
        tvB.setText(String.valueOf("New value of b: " + newValue));
    }
}

The Adapter:

适配器:

public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    private List<Fragment> fragments;

    public MyFragmentPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
        super(fm);
        this.fragments = fragments;
    }

    @Override
    public Fragment getItem(int position) {
        return fragments.get(position);
    }

    @Override
    public int getCount() {
        return fragments.size();
    }
}

The main layout activity_main.xml:

主要布局activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="codeonce.thinktwice.testobserverpattern.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:id="@+id/linearLayout">


        <EditText
            android:id="@+id/et_a"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:inputType="number"
            android:hint="Type value for a"/>

        <EditText
            android:id="@+id/et_b"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:inputType="number"
            android:hint="Type value for b"/>

        <Button
            android:id="@+id/btn_ok"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:layout_gravity="center_horizontal"
            android:text="OK"/>

    </LinearLayout>

    <android.support.v4.view.ViewPager
        android:id="@+id/activity_main_vp_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@+id/linearLayout"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true">

    </android.support.v4.view.ViewPager>

</RelativeLayout>

The fragment layout fragment_basic.xml :

片段布局 fragment_basic.xml :

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center_horizontal">

    <TextView
        android:layout_marginTop="20dp"
        android:id="@+id/tv_a"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Value of a will appear here"
        android:textSize="20sp"/>

    <TextView
        android:id="@+id/tv_b"
        android:layout_marginTop="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Value of b will appear here"
        android:textSize="20sp"/>

</LinearLayout>

回答by Benjamin Mesing

Android now offers ViewModels with LiveData for your purpose. A view model is bound to an object with a lifecycle (Fragment, Activity) and lives as long as this object. In your case you would create a view model bound to the actvity which is accessible by all fragments.

Android 现在为您提供带有 LiveData 的 ViewModel。视图模型绑定到具有生命周期的对象(Fragment、Activity),并且与该对象的生命周期一样长。在您的情况下,您将创建一个绑定到所有片段都可以访问的活动的视图模型。

From the Android documentation:

从 Android 文档

It's very common that two or more fragments in an activity need to communicate with each other. Imagine a common case of master-detail fragments, where you have a fragment in which the user selects an item from a list and another fragment that displays the contents of the selected item. This case is never trivial as both fragments need to define some interface description, and the owner activity must bind the two together. In addition, both fragments must handle the scenario where the other fragment is not yet created or visible.

This common pain point can be addressed by using ViewModel objects. These fragments can share a ViewModel using their activity scope to handle this communication

一个活动中的两个或多个片段需要相互通信是很常见的。想象一下主从片段的常见情况,其中有一个片段,其中用户从列表中选择一个项目,另一个片段显示所选项目的内容。这种情况绝非易事,因为两个片段都需要定义一些接口描述,并且所有者活动必须将两者绑定在一起。此外,两个片段都必须处理另一个片段尚未创建或不可见的情况。

这个常见的痛点可以通过使用 ViewModel 对象来解决。这些片段可以使用其活动范围共享一个 ViewModel 来处理此通信

To use a view model for sharing data between fragments you need to:

要使用视图模型在片段之间共享数据,您需要:

  1. create a view model which inherits from ViewModel() and contains MutableLiveData-fields
  2. access the view model from the fragments by calling ViewModelProviders.of(activity).get(YourViewModel::class.java)
  3. change values of the view model by calling setValue for the MutableLiveData-fields
  4. register on changes of the MutableLiveData-fields by calling .observe()
  1. 创建一个从 ViewModel() 继承并包含 MutableLiveData 字段的视图模型
  2. 通过调用 ViewModelProviders.of(activity).get(YourViewModel::class.java) 从片段访问视图模型
  3. 通过为 MutableLiveData 字段调用 setValue 来更改视图模型的值
  4. 通过调用 .observe() 注册 MutableLiveData 字段的更改

Here is the central code snippet from the Android documentationon how to use ViewModels for a master-detail-view:

以下是Android 文档中有关如何将 ViewModel 用于主从视图的中心代码片段:

class SharedViewModel : ViewModel() {
    val selected = MutableLiveData<Item>()

    fun select(item: Item) {
        selected.value = item
    }
}

class MasterFragment : Fragment() {
    private lateinit var itemSelector: Selector
    private lateinit var model: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        model = activity?.run {
            ViewModelProviders.of(this).get(SharedViewModel::class.java)
        } ?: throw Exception("Invalid Activity")
        itemSelector.setOnClickListener { item ->
        // Update the UI
        }
    }
}

class DetailFragment : Fragment() {
    private lateinit var model: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        model = activity?.run {
            ViewModelProviders.of(this).get(SharedViewModel::class.java)
        } ?: throw Exception("Invalid Activity")
        model.selected.observe(this, Observer<Item> { item ->
            // Update the UI
        })
    }
}