Contents

Android 通信简单应用

Activity 之间通信

从一个 Activity 跳转到另一个 Activity 时,可以携带一些数据进去,这个就是 Activity 之间的通信 跳转到 Activity 之后可以响应通信,不过需要发起端调用 startActivityForResult ,接收端调用 setResult 这种通信方法我不能类比到组件之间的通信,组件之间通信都是单向数据流,这个是 双向数据流

原来的通信方法

通信发起端

跳转需要借助 Intent 对象,指定来源的上下文和目标的类,调用 startActivity 即可

Intent intent = new Intent(this, TargetClass.class);
startActivity(intent)

如果需要携带数据,创建一个 Bundle 对象绑定到 Intent 即可

Intent intent = new Intent(this, TargetClass.class);
Bundle bundle = new Bundle();
bundle.putString("message", "Hello World");
intent.putExtras(bundle);
startActivity(intent);

其实也可以不用 Bundle ,直接给 Intent 来添加

通信接收端

跳转到接收端后,使用 getIntent 来获取对端的 Intent 对象,并查看绑定的数据

Intent intent = getIntent();
Bundle bundle = intent.getExtras();

双向数据流

通信发起端调用 startActivityForResult 并携带数据是一个方向,通信接收端调用 setResult 并携带数据与对端是另一个方向 在发起端

Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("message", "Hello");
intent.putExtras(bundle);

startActivityForResult(intent, 2)

注意 startActivityForResult 第二个参数要大于0,才会接收对端的数据,并跳转到 onActivityResult 回调

protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    // 对端将数据写入 data ,可以从里面获取数据
}

在接收端回复

Intent intent = new Intent();
Bundle bundle = new Bundle();
bundle.putString("result", "Fuck You");

intent.putExtras(bundle);
setResult(RESULT_OK, intent);
finish() // 结束声明周期,跳转到原来的 Activity

Result API 方法

Result API 是官方支持的方法,可以替代原来的通信方法,通信之间可以更加安全吧,应该

两边协定

Result API 下,两端的通信更像是函数,输入一个数据,函数会输出一个结果 两边需要定义一个协议,规定输入的数据类型和输出的数据类型

public class ResultContract extends ActivityResultContract<String, String> 

为其定义 createIntent 方法,表示如何输入数据

public Intent createIntent(@NonNull Context context, String input) {
    Intent intent = new Intent(context, SecondActivity.class);
    intent.putExtra("name", input);
    return intent;
}

为其定义 parseIntent 方法,表示如何解析接收到的输出数据

public String parseResult(int resultCode, @Nullable Intent intent) {
    String data = intent.getStringExtra("result");
    if(resultCode == Activity.RESULT_OK && data != null) {
	return data;
    } else {
	return null;
    }
}

输入端

使用 Result API ,页面跳转的方式也要改变了,需要一个 Launcher 来进行操作 这个 Launcher 的构造需要上面的两边协定和一个回调,这个回调用来处理 paserResult 后的数据 这里设置用 Toast 回显

ActivityResultLauncher activityLauncher = registerForActivityResult(
	new ResultContract(), result -> {
	    Toast.makeText(getApplicationContext(), result, Toast.LENGTH_SHORT).show();
	});

输出端

输出端的做法没有多大改变,一样的

button.setOnClickListener(view -> {
	Intent intent = new Intent();
	Bundle bundle = new Bundle();
	bundle.putString("result", "Hello Mother Fucker");
	intent.putExtras(bundle);
	setResult(RESULT_OK, intent);
	finish();
    });

Fragment 之间通信

Framgent 之间的通信也想函数那样,输入一个值,输出一个值 不同的是,输入端需要在 FragmentManager 上设置一个 Listener 来接收输出值,并设置键 requestKey 提供位置 输出端需要通过 FragmentManager 来设置输出值到 requestKey /ox-hugo/2022-03-08_20-46-33_screenshot.png

输入端

挑重要的来讲,输入端需要设置 requestKey 和处理输出值的回调

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    getParentFragmentManager().setFragmentResultListener(
	    "requestKey",
	    this,
	    (requestKey, bundle) -> {
		String result = bundle.getString("bundleKey");
		resultTextView.setText("Fuck You" + result);
	    });
}

输出端

public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    final View view = inflater.inflate(R.layout.fragment2, container, false);
    Button button = view.findViewById(R.id.button);
    button.setOnClickListener(_view -> {
	    Bundle result = new Bundle();
	    result.putString("bundleKey", "result");
	    getParentFragmentManager().setFragmentResult("requestKey", result);
	});

    return view;
}

补充

以上都是同一级的 Fragment 之间通信,没涉及到父子之间通信,其实也是类似的 /ox-hugo/2022-03-08_20-50-31_screenshot.png 只需将 getParentFragmentManager 改为 getChildFragmentManager 即可 注意,他们都是用同一个 FragmentManager 来通信的