android进阶4step3:Android常用框架——OTTO事件总线

具体定义:之前有mark过一篇Otto事件框架总线的文章:android进阶3step1:Android组件通信——事件框架总线Otto

 Otto 官方网站:http://square.github.io/otto/

Github上的源代码:https://github.com/square/otto

学习步骤

  • OTTO简介
  • OTTO的使用方法
  • OTTO的实际应用

使用场景

  • FragmentActivity之间的数据传递: Interface -Android Fragment(碎片)基本使用

  • ActivityActivity之间的数据传递: startActivityForResult

  • FragmentFragment之间的数据传递: Interface

    PS:OTTO事件总线的出现将简化这一切。

Activity和Fragment

主要流程: 

主要的方法:

OTTO主要方法

  • register(Object o):注册,注册以后可以订阅事件

  • unregister(Object o):注销,放弃对之前的订阅的所有事件

  • post(Object o):发布事件,会被有Subscribe注解的方法获取到

注解:

  •  @Subscribe:
  • - 调用了register后有效
  • - 表示订阅了一个事件,并且方法用 public 修饰的 - 方法名可以随意取,重点是参数
  •  @Produce:
  • - 通知Bus该函数是一个事件产生者,产生的事件类型为该函数的返回值。

初始化BUS

Bus bus = new Bus();
Bus bus2 = new Bus(ThreadEnforcer.MAIN);//主线程

可以指定@Subscribe@Produce标注的回调方法所运行的线程,默认是MainThread。也可以使用ThreadEnforcer.ANY 。

 订阅事件

@Subscribe 
public void answerAvailable(AnswerAvailableEvent event) 
{

// TODO: React to the event somehow! 

}

注意:subscribe方法接收的参数类型需要和post参数的类型一致 或者是post参数类型的父类

发布事件

bus.post(new AnswerAvailableEvent(42));

bus.register(this)
  • register方法调用后,Otto会寻找所有带有@Subscribe或者 @Produce注解的方法,并将这些方法缓存下来。

  • 只有在调用了register之后,该类里面标注了@Subscribe或者 @Produce的方法才会在适当的时候被调用。

  • 当不需要订阅事件的时候,可以调用unregister来取消订阅。

生产者

有时候当订阅某个事件的时候,希望能够获取当前的一个值,比如订阅位置变化事件的时候,希望能拿到当前的位置信息。

Otto@Produce正是 扮演了这么一个生产者的角色。@Produce也是用于方法,并且这个方法 的参数必须为空返回值是你要订阅的事件的类型

@Produce 
public AnswerAvailableEvent produceAnswer() { 

// Assuming ‘lastAnswer’exists,
return new AnswerAvailableEvent(this.lastAnswer);

}
  • 使用标签@Produce之后,也需要调用bus.register()
  • 调用了register方法之后,所有之前订阅AnswerAvailableEvent事件的方法都会被执行一次,参数就是produceAnswer方法的返回值
  • 之后任何新的订阅AnswerAvailableEvent事件的方法,也都会立即调用produceAnswer方法

解读Sample Code

一、实现Activity与Activity之间进行数据传递

A跳到B 点击B中的按钮将数据传递给A

使用:

第一步:在模块中添加依赖

    implementation 'com.squareup:otto:1.3.8'

BusProvider.java  从头到尾都是一个Bus  所以写出单例

/**
 * 单例提供bus对象
 */
public class BusProvider {

    private static final Bus mBus = new Bus();
    private BusProvider(){}

    public static Bus getInstance() {
        return mBus;
    }
}

注意:如果想在子线程post数据 改成:

    private static Bus mBus = new Bus(ThreadEnforcer.ANY);

 事件的封装:这里简单的输出一个整型数 

ActivityEvent.java

public class ActivityEvent {
    private int Event;

    public ActivityEvent(int event) {
        this.Event = event;
    }

    public int getEvent() {
        return Event;
    }
}

MainAcitivity.java

public class MainActivity extends AppCompatActivity {

    private Button mBtn2Second;
    private int i;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mBtn2Second = findViewById(R.id.id_btn_main);
        mBtn2Second.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(MainActivity.this, SecondActivity.class));
            }
        });
        //注册
        BusProvider.getInstance().register(this);
    }

    /**
     * 订阅者
     * 每次post都会拿到传来的数据
     *
     * @param event
     */
    @Subscribe
    public void onActivityEvent(ActivityEvent event) {
        Log.e("TAG", "这是" + (++i) + "次,接收到Post传来的事件 event=" + event.getEvent());
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //注销
        BusProvider.getInstance().unregister(this);

    }
}

SecondActivity.java

public class SecondActivity extends AppCompatActivity {

    private Button mBtnSend2Main;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        mBtnSend2Main = findViewById(R.id.id_btn_send);
        mBtnSend2Main.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //post event to main activity
                //发布事件,会被有Subscribe注解的方法获取到
                BusProvider.getInstance().post(new ActivityEvent(40));
            }
        });
        //注册
        BusProvider.getInstance().register(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //注册
        BusProvider.getInstance().unregister(this);
    }
}

 结果:

Log输出:

 E/TAG: 我是MainActivity,这是第1次,接收到Post传来的事件 event=40
 E/TAG: 我是MainActivity,这是第2次,接收到Post传来的事件 event=40
 E/TAG: 我是MainActivity,这是第3次,接收到Post传来的事件 event=40
 E/TAG: 我是MainActivity,这是第4次,接收到Post传来的事件 event=40
 E/TAG: 我是MainActivity,这是第5次,接收到Post传来的事件 event=40

二、实现同一个Activity中两个Fragment之间传递数据:

布局文件:fragment_up.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_up"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#e68a8a"
    android:orientation="vertical">

    <Button
        android:id="@+id/id_btn_up"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Fragment Up" />

    <TextView
        android:id="@+id/id_tv_up"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="我是Fragment Up"
        android:textSize="20sp"
        android:textStyle="bold" />
</LinearLayout>

fragment_down.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/layout_down"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#8fe6e7"
    android:orientation="vertical">

    <Button
        android:id="@+id/id_btn_down"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Fragment Down" />

    <TextView
        android:id="@+id/id_tv_down"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textStyle="bold"
        android:textSize="20sp"
        android:text="我是Fragment Down"/>
</LinearLayout>

装fragment的activity的布局 activity.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">
    <!--装fragment 的viewgroup-->
    <FrameLayout
        android:id="@+id/layout_up"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"></FrameLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="#000000" />

    <FrameLayout
        android:id="@+id/layout_down"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"></FrameLayout>
</LinearLayout>

java 代码:

FragmentUp.java

public class FragmentUp extends Fragment {

    private Button mBtnUp;
    private TextView mTvUp;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.frament_up, container, false);
        mBtnUp = view.findViewById(R.id.id_btn_up);
        mTvUp = view.findViewById(R.id.id_tv_up);
        mBtnUp.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //发送数据
                BusProvider.getInstance().post(new myEvent(myEvent.FROM_FRAGMENT_UP, "from fragment up"));
            }
        });
        //注册
        BusProvider.getInstance().register(this);
        return view;
    }

    @Subscribe
    public void setOnDownListener(myEvent event) {
        if (event.mEvent == myEvent.FROM_FRAGMENT_DOWN) {
            mTvUp.setText(mTvUp.getText() + "\n" + event.mMsg);
        }
    }


    @Override
    public void onDestroyView() {
        super.onDestroyView();
        //注销
        BusProvider.getInstance().unregister(this);
    }
}

FragmentDown.java

public class FragmentDown extends Fragment {

    private Button mBtnDown;
    private TextView mTvDown;

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.frament_down, container, false);
        mBtnDown = view.findViewById(R.id.id_btn_down);
        mTvDown = view.findViewById(R.id.id_tv_down);
        mBtnDown.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //发送数据
                BusProvider.getInstance().post(new myEvent(myEvent.FROM_FRAGMENT_DOWN, "from fragment down"));
            }
        });
        //注册
        BusProvider.getInstance().register(this);
        return view;
    }

    /**
     * 订阅者
     *
     * @param event
     */
    @Subscribe
    public void setOnUpLinstener(myEvent event) {
        //如果是来自FragmentUp的则默认显示一个数据
        if (event.mEvent == myEvent.FROM_FRAGMENT_UP)
            mTvDown.setText(mTvDown.getText() + "\n" + event.mMsg);
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        //注销
        BusProvider.getInstance().unregister(this);
    }
}

 myEvent.java 封装的数据类

public class myEvent {
    //标识符
    public static final int FROM_FRAGMENT_UP = 1001;
    public static final int FROM_FRAGMENT_DOWN = 1002;
    //数据
    public int mEvent;
    public String mMsg;

    //构造
    public myEvent(int event, String msg) {
        this.mEvent = event;
        this.mMsg = msg;
    }

}

Bus的单例类


/**
 * 单例提供bus对象
 */
public class BusProvider {

    private static final Bus mBus = new Bus();

    private BusProvider(){}

    public static Bus getInstance() {
        return mBus;
    }

    
}

 Acitivity.java  将两个自定义fragment 装载并显示

public class Activity extends AppCompatActivity {

    private Fragment mUp;
    private Fragment mDown;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity);
        mUp = new FragmentUp();
        mDown = new FragmentDown();
        FragmentManager fm = getSupportFragmentManager();
        //通过fragmentManager对象去开启一个事务
        FragmentTransaction transaction = fm.beginTransaction();
        //要将fragment添加到哪个容器ViewGroup中去,装你要替换的新的fragment的具体对象
        transaction.add(R.id.layout_up, mUp);
        transaction.add(R.id.layout_down, mDown);
        transaction.addToBackStack(null);
        transaction.commit();

    }
}

 GitHub的Otto案例 :

/*
* 1. 添加OTTO框架到自己的项目中:build.gradle 添加OTTO的依赖
* 2. 创建一个单例,来给APP中所有的Activity或者Fragment提供一个Bus
* 3. register,unregister 事件总线
* 4. 在要发送事件的地方调用bus.post(Event)
* 5. 接收的地方定义订阅的函数@Subscribe ...
* */

案例框架:

在MainAcitivity中有两个fragment

  • 一个HistoryFragment显示经纬度的listview
  • 一个MapFragment显示通过百度API通过经纬度下载的静态图片的Image

当点击MOVELOCATION时发送一个随机经纬度

HistoryFragment 订阅者:接收到经纬度的数据,更新adapter

MapFragment 订阅者: 也接受到经纬度通过aysncTask在doInBackground中使用百度的API下载

onPostExcute 中 返回下载好的drawable图像 此时post给自己(本身的fragment)显示图片

注意添加网络权限

    <uses-permission android:name="android.permission.INTERNET"></uses-permission>

注意:android 9.0 使用这个百度API 会报错

解决方法:转 android高版本联网失败报错:Cleartext HTTP traffic to xxx not permitted解决方法

完整代码:

模块的build.gradle中添加otto框架的依赖

    implementation 'com.squareup:otto:1.3.8'

布局文件:

activity_main.xml

这里的fragment是静态添加的  注意修改为自己的包名

        class="包名.LocationMapFragment"

<?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">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_clear_location"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Clear Location" />

        <Button
            android:id="@+id/bt_move_location"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="Move Location" />
    </LinearLayout>

    <!--静态加载fragment-->
    <fragment
        android:id="@+id/fragment_map"
        class="com.demo.ottosample.LocationMapFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

    <fragment
        android:id="@+id/fragment_history"
        class="com.demo.ottosample.LocationHistoryFragment"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />


</LinearLayout>

Fragment:

LocationHistroyFragment.java

显示历史经纬度的Fragment

import android.app.ListFragment;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;

import com.squareup.otto.Subscribe;

import java.util.ArrayList;
import java.util.List;

/**
 * 显示历史坐标的fragment
 */
public class LocationHistoryFragment extends ListFragment {
    private final List<String> locationEvents = new ArrayList<>();
    private ArrayAdapter<String> adapter;


    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        adapter = new ArrayAdapter<String>(getActivity(),
                android.R.layout.simple_list_item_1, locationEvents);
        setListAdapter(adapter);
    }

    @Override
    public void onResume() {
        super.onResume();
        //注册Bus
        BusProvider.getInstance().register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        //注销Bus
        BusProvider.getInstance().unregister(this);
    }

    /**
     * 订阅 坐标移动的事件 更新 listview
     *
     * @param event
     */
    @Subscribe
    public void onLocationMoveEvent(LocationMoveEvent event) {
        float lng = event.longitude;
        float lat = event.latitude;
        locationEvents.add(String.format("[%s, %s]", lng, lat));
        adapter.notifyDataSetChanged();
    }

    /**
     * 订阅 坐标 清除的事件 清除list数据
     *LocationClearEvent 暂时用不上 所以为空类
     * @param event
     */
    @Subscribe
    public void onLocationClearEvent(LocationClearEvent event) {
        locationEvents.clear();
        adapter.notifyDataSetChanged();
    }
}

 LocationMapFragment.java

显示地图的静态图片的Fragment

import android.app.Fragment;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;

import com.squareup.otto.Subscribe;

import java.net.URL;

/**
 * 通过传来的经纬度在地图的api中查找返回一张照片显示
 */
public class LocationMapFragment extends Fragment {
    private ImageView mImageView;
    // %s 可以用来格式化
    private final String URL = "http://api.map.baidu.com/staticimage?width=1000&height=1000&center=%s,%s&zoom=15";

    //下载图片
    private DownloadTask mTask;

    @Override
    public void onResume() {
        super.onResume();
        BusProvider.getInstance().register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        BusProvider.getInstance().unregister(this);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        mImageView = new ImageView(getActivity());

        return mImageView;
    }

    private class ImageAvailableEvent {
        public Drawable image;

        public ImageAvailableEvent(Drawable img) {
            image = img;
        }
    }

    @Subscribe
    public void onLocationMoveEvent(LocationMoveEvent event) {
        mTask = new DownloadTask();
        //格式化Url
        String downloadUrl = String.format(URL, event.longitude, event.latitude);
        //将这个Url 传到doInBackground 中
        mTask.execute(downloadUrl);
    }

    @Subscribe
    public void onImageAvailableEvent(ImageAvailableEvent event) {
        if (event.image != null) {
            mImageView.setImageDrawable(event.image);
        }
    }


    private class DownloadTask extends AsyncTask<String, Void, Drawable> {

        @Override
        protected Drawable doInBackground(String... params) {
            String downloadUrl = params[0];
            try {
                //返回下载好的drawable
                return BitmapDrawable.createFromStream(new URL(downloadUrl).openStream(), "bitmap.jpg");
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        }

        @Override
        protected void onPostExecute(Drawable drawable) {
            super.onPostExecute(drawable);
            //show drawable
            //mImageView.setImageDrawable(drawable);
            //post Image available event
            //将下载好的drawable post 通知这个fragment显示图片
            BusProvider.getInstance().post(new ImageAvailableEvent(drawable));
        }
    }
}

MainActivity.java 装两个Fragment

public class MainActivity extends AppCompatActivity {

    private Button mClearButton;
    private Button mMoveButton;

    private float DEFAULT_LONGITUDE = 116.413554f;
    private float DEFAULT_LATITUDE = 39.911013f;

    private float longitude;
    private float latitude;

    private float OFFSET = 0.1f;
    private Random random = new Random();

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

    private void initEvent() {
        mClearButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //post data 让list清除
                BusProvider.getInstance().post(new LocationClearEvent());
            }
        });
        mMoveButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //在默认的坐标下 偏移随机量 产生新的 经纬度
                longitude = DEFAULT_LONGITUDE + OFFSET * random.nextFloat();
                latitude = DEFAULT_LATITUDE + OFFSET * random.nextFloat();
                //将这个数据post 给 订阅者
                BusProvider.getInstance().post(new LocationMoveEvent(longitude, latitude));
            }
        });
    }

    private void initViews() {
        mClearButton = findViewById(R.id.bt_clear_location);
        mMoveButton = findViewById(R.id.bt_move_location);
    }

    @Override
    protected void onResume() {
        super.onResume();
        BusProvider.getInstance().register(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        BusProvider.getInstance().unregister(this);
    }
}

 BusProvider.java 总线单例对象 

import com.squareup.otto.Bus;


/*
* 1. 添加OTTO框架到自己的项目中:build.gradle 添加OTTO的依赖
* 2. 创建一个单例,来给APP中所有的Activity或者Fragment提供一个Bus
* 3. register,unregister 事件总线
* 4. 在要发送事件的地方调用bus.post(Event)
* 5. 接收的地方定义订阅的函数@Subscribe ...
* */
public class BusProvider {
    private final static Bus bus = new Bus();
    public static Bus getInstance() {
        return bus;
    }

    private BusProvider()
    {}
}

事件:

移动坐标的事件(封装的实体类,用来传递数据的)

LocationMoveEvent.java

public class LocationMoveEvent {
    public float longitude;
    public float latitude;

    public LocationMoveEvent(float lng, float lat) {
        this.longitude = lng;
        this.latitude = lat;
    }
}

LocationClearEvent.java 清除list的数据 这里比较简单,可以定义为空 


/**
 * 清除坐标的事件
 */
public class LocationClearEvent {
}

 完成。

总结

otto 是一个非常优秀和强大的框架.

  • BusProvider.getInstance().post(object)
  • Subscribe订阅事件就一定要register这个类了
  • otto 传递事件的时候,参数最后用一个实体类包裹

猜你喜欢

转载自blog.csdn.net/qq_17846019/article/details/85090217
今日推荐