Activity A跳转B,B跳转C,A不能直接跳转到C,A如何传递消息给C?(美团)
这道题想考察什么?
- 是否了解Activity间信息传递?
考察的知识点
- 消息传递的机制
- 事件总线
- 本地数据存储
考生应该如何回答
消息通信机制
Android 开发之中我们常常需要应用到消息传递,消息的传递有多种方式。消息传递的作用不必多说,主要是在不同的组件之间传播消息或者共享某些状态等,以下是几种常用的消息传递机制:
- 静态变量
- 全局变量 及Application
- Android系统剪切板
- 本地化存储方式
- Android组件
- EventBus
- LiveDataBus
静态变量 和 全局变量、Application && Android 系统剪切板
这三种方式其实非常类似,静态变量和全局变量都可以采用static的方式来声明,如果采用这种方式还是推荐用一个专门的类结合单体模式进行管理,尽量减少对内存的消耗。 而使用系统剪切板的方式一般也很少用,比较多限制,容易丢失数据,几乎没有看到有这样用的。
Application: 可以通过在Application 中的全局静态变量来实现
这里还有利用Application进行共享Handler来消息传递,方法很简单,就是在Application中定义一个全局的Handler,虽然这种方法可以实现,但是却保留了在整个App中保留了全局的Handler,如果在Handler的设置中引用了某个Activity,就容易造成内存泄露了。
本地化存储方式
本地存储方式有如下三种:
SharedPreference SQLite File
这三种方式的好处就是他们是持久存储的,只要不卸载APP或者不删除文件就可以一直保存下去,而且也几乎没有大小的限制,可以做一些统计。不过缺点也比较明显,这三种方式最好是采用多线程来进行读写,尤其是数据量大的时候,我们知道,IO的操作是非常耗费时间的,所以尽量不要在UI线程中使用这三种方式读写。
示例代码:
SharedPreference:
// 发送消息
SharedPreferences.Editor editor = MainActivity.this.getSharedPreferences("SEND", Context.MODE_PRIVATE).edit();
editor.putString("SEND","SharedPreferences的消息");
editor.apply();
startActivity(new Intent(MainActivity.this,ReceiveActivity.class));
// 接收消息
SharedPreferences sharedPreferences = getSharedPreferences("SEND", MODE_PRIVATE);
textView.setText(sharedPreferences.getString("SEND", ""));
附支持的数据类型如下:
SQLite
SQLite需要先创建数据库,后面向数据库中插入和读取数据实现信息共享。 首先要继承SQLiteOpenHelper并在onCreate方法中创建数据库:
sqLiteDatabase.execSQL("CREATE TABLE Teacher(teacherId INTEGER PRIMARY KEY" +
" AUTOINCREMENT,userId VARCHAR(20) UNIQUE,name VARCHAR(20),password VARCHAR(20))");
向数据库中插入:
String type = "Teacher";
sqLiteDatabase.execSQL("INSERT INTO " + type + "(userId,name,password) values (?,?,?)",
new String[]{userId, userName, userPassword});
从数据库中读取信息:
String type = "Teacher";
Cursor cursor = sqLiteDatabase.rawQuery("SELECT * FROM " + type + " WHERE userId = ?",
new String[]{userId.getText().toString()});
if(cursor.moveToFirst()){
String userId = cursor.getString(cursor.getColumnIndex("userId"));
String name = cursor.getString(cursor.getColumnIndex("name"));
}
cursor.close();
File
文件方式要记得申请权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
写入数据:
try {
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/data/temp.txt");
FileOutputStream out = new FileOutputStream(file);
out.write("message".getBytes(Charset.forName("UTF-8")));
out.flush();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
读取数据:
try {
FileInputStream in = new FileInputStream(Environment.getExternalStorageDirectory().getAbsolutePath() + "/temp.txt");
byte[] reader = new byte[256];
int read = in.read(reader);
String content = "";
if (read > 0)
content = new String(reader, 0, read, Charset.forName("UTF-8"));
Toast.makeText(this, content, Toast.LENGTH_SHORT).show();
} catch (IOException e) {
e.printStackTrace();
}
Broadcast方式
使用组件也就是说利用Broadcast进行消息传递。 优选LocalBroadcast:
LocalBroadcastManager.getInstance(context).registerReceiver(@NonNull BroadcastReceiver receiver, @NonNull IntentFilter filter) LocalBroadcastManager.getInstance(context).sendBroadcast(intent); 如果说使用Android 进行消息传递的话,Broadcast是最好的了,顾名思义的我们清楚,广播就是有一个发送消息和接受消息的过程,所以可以用于消息传递。 示例:
注册广播和接受消息
// 定义广播
final Button broadButton = findViewById(R.id.broadcast);
final BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
broadButton.setText("" + intent.getStringExtra("data"));
}
};
broadButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
registerReceiver(receiver,new IntentFilter("broadsend.action"));
startActivity(new Intent(MainActivity.this,ReceiveActivity.class));
}
});
发送广播:
Intent intent = new Intent("broadsend.action");
intent.putExtra("data","send");
sendBroadcast(intent);
这种方式有一些限制,因为接收事件要比发送事件先定义好,所以只能在当前Activity中注册广播,在跳转的Activity发送,所以严格说这不能算是消息传递,因为是单向传递的。
Service方式
Service可以结合Broadcast进行消息传递,不过这样子就不能算是Service了。 使用Service进行消息传递,我们可以定义接口,并利用接口进行消息传递。 定义消息接收的接口:
public static MessageCallback messageCallback = new MessageCallback() {
@Override
public void onMessage(String message) {
Log.d("tag","" + message);
}
};
public interface MessageCallback{
public void onMessage(String message);
}
进行消息发送:
MainActivity.messageCallback.onMessage("message"); 有的人讲这种方式不久和共享变量一样了吗,不不不,这是完全不一样的,如果是共享变量的话,当变量被改变了是不是还得程序员或者用户去响应这种改变呢,这就很不好了,而采用这种静态接口的方法,只要函数被调用,就立刻可以进行响应并处理,不是很方便吗。当然也可以想办法将接口的对象进行传递,例如利用Broadcast来进行传递。
EventBus
EventBus 是一款针对Android的发布以及订阅事件总线,使用它可以很方便的进行信息传递,而且使用起来很方便。 首先是定义一个消息:
public class Event {
private String message;
public Event(){
message = "EventBus message";
}
public void setMessage(String message){
this.message = message;
}
public String getMessage(){
return message;
}
}
发送消息:
这里使用了postSticky,这是发送的粘性广播,使用这个发送就可以先发送信息再进行注册,后注册的也能接收到前面发送的广播。当然还有其他的使用方式。
EventBus.getDefault().postSticky(new Event());
startActivity(new Intent(MainActivity.this,ReceiveActivity.class));
注册事件的订阅者:
EventBus.getDefault().register(this); 接受粘性广播:
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEventThread(Event event) {
textView.setText(event.getMessage());
}
LiveDataBus
LiveDataBus是通过包装LiveData实现的消息总线组件,如果同学对LiveData不熟悉的同学,可以找到相对应的章节先学习一下。
LiveDataBus 的封装
- 通过 map 维护一个消息事件和 MutableLiveData 的映射关系,MutableLiveData 的类型默认为 Object,接收任意类型,实现总线通信将 LiveDataBus 封装为一个单例类。
- 消息注册时,如果当前 map 中不存在,则先将消息和对应的 MutableLiveData 对象放入维护的 map 中,添加映射关系,返回当前 map 中缓存的 MutableLiveData 对象
LiveDataBus 的组成
-
消息 消息可以是任何的Object,可以定义不同类型的消息,如 Boolean、String。也可以定义自定义类型的消息。
-
消息总线 消息总线通过单例实现,不同的消息通道存放在一个 HashMap中。
-
订阅 订阅者通过 with 方式获取消息通道,然后调用 observe 订阅这个通道的消息。
-
发布 发布者通过 with 获取消息通道,然后调用 setValue 或者 postValue 发布消息。
LiveDataBus 的实现
public final class LiveDataBus {
private final Map<String, MutableLiveData<Object>> bus;
private LiveDataBus() {
bus = new HashMap<>();
}
private static class SingletonHolder {
private static final LiveDataBus DATA_BUS = new LiveDataBus();
}
public static LiveDataBus get() {
return SingletonHolder.DATA_BUS;
}
public <T> MutableLiveData<T> with(String target, Class<T> type) {
if (!bus.containsKey(target)) {
bus.put(target, new MutableLiveData<>());
}
return (MutableLiveData<T>) bus.get(target);
}
public MutableLiveData<Object> with(String target) {
return with(target, Object.class);
}
}
注册订阅
LiveDataBus.get().with("key_test", Boolean.class)
.observe(this, new Observer<Boolean>() {
@Override
public void onChanged(@Nullable Boolean aBoolean) {
}
});
发送消息
LiveDataBus.get().with("key_test").setValue(true);
详细关注公众号:Android老皮
还能解锁 《Android十大板块文档》 ,让学习更贴近未来实战。已形成PDF版
内容如下:
1.Android车载应用开发系统学习指南(附项目实战)
2.Android Framework学习指南,助力成为系统级开发高手
3.2023最新Android中高级面试题汇总+解析,告别零offer
4.企业级Android音视频开发学习路线+项目实战(附源码)
5.Android Jetpack从入门到精通,构建高质量UI界面
6.Flutter技术解析与实战,跨平台首要之选
7.Kotlin从入门到实战,全方面提升架构基础
8.高级Android插件化与组件化(含实战教程和源码)
9.Android 性能优化实战+360°全方面性能调优
10.Android零基础入门到精通,高手进阶之路