Android中文API(126) —— Message

本文详细介绍了Android系统中的Message机制,包括其结构、关键字段、构造器、方法及其在Handler中的应用,旨在帮助开发者更好地理解并利用这一机制进行跨线程通信。

Message

译者署名: strongxu

译者微博:http://t.qq.com/strongxu

版本:Android 3.1 r1

 

结构

继承关系

public final class Message extends Object implements Parcelable

        

java.lang.Object

android.os.Message

 

类概述

定义一个包含任意类型的描述数据对象,此对象可以发送给Handler。对象包含两个额外的int字段和一个额外的对象字段,这样可以使得在很多情况下不用做分配工作。

尽管Message的构造器是公开的,但是获取Message对象的最好方法是调用Message.obtain()或者Handler.obtainMessage(), 这样是从一个可回收对象池中获取Message对象。

 

字段

public static final Creator<Message> CREATOR

 

 

public int arg1

如果只需要存储几个整型数据,arg1 arg2setData()的低成本替代品。

 

public int arg2

如果只需要存储几个整型数据,arg1 arg2setData()的低成本替代品。

 

public Object obj

发送给接收器的任意对象。当使用Message对象在线程间传递消息时,如果它包含一个Parcelable的结构类(不是由应用程序实现的类),此字段必须为非空(non-null)。其他的数据传输则使用setData(Bundle)方法。

注意Parcelable对象是从FROYO版本以后才开始支持的。

 

public Messenger replyTo

指明此message发送到何处的可选Messenger对象。具体的使用方法由发送者和接受者决定。

 

public int what

用户自定义的消息代码,这样接受者可以了解这个消息的信息。每个handler各自包含自己的消息代码,所以不用担心自定义的消息跟其他handlers有冲突。

 

公共构造器

  public      Message()       

  构造器(但是获取Message对象的最好方法是调用Message.obtain())。

 

公共方法

public void copyFrom (Message o)

使此message跟参数o相似。浅拷贝数据域。不拷贝源message的链表字段,时间戳和目标/回调。

 

public int describeContents ()

描述了包含在Parcelable对象排列信息中的特殊对象的类型。

返回值

         一个标志位,表明Parcelable对象特殊对象类型集合的排列。

 

public Runnable getCallback ()

获取回调对象,此对象会在message处理时执行。此对象必须实现Runnable接口。回调由接收此消息并分发的目标handler调用。如果没有设置回调,此消息会分发到接收handlerhandleMessage(Message)

 

public Bundle getData ()

获取附加在此事件上的任意数据的Bundle对象,需要时延迟创建。通过调用setData(Bundle)来设置Bundle的值。需要注意的是,如果通过Messenger对象在进程间传递数据时,需要调用Bundle类的Bundle.setClassLoader()方法来设置ClassLoader,这样当接收到消息时可以实例化Bundle里的对象。

         参考

                  peekData()

                  setData(Bundle)

 

public Handler getTarget ()

获取将接收此消息的Handler对象。此对象必须要实现Handler.handleMessage()方法。每个handler各自包含自己的消息代码,所以不用担心自定义的消息跟其他handlers有冲突。

 

public long getWhen ()

返回此消息的传输时间,以毫秒为单位。

 

public static Message obtain (Handler h, int what, int arg1, int arg2, Object obj)

obtain()一样,但是设置了target, what, arg1, arg2obj的值。

         参数

                   h                设置的target

                   what         设置的what

                   arg1          设置的arg1

                   arg2          设置的arg2

                   obj             设置的obj

         返回值

                   从全局池中分配的一个Message对象。

 

public static Message obtain (Handler h, int what, Object obj)

obtain()一样,但是设置了target, whatobj的值。

         参数

                   h                设置的target

                   what        设置的what

                   obj             设置的obj

         返回值

                   从全局池中分配的一个Message对象。

 

public static Message obtain (Handler h, int what)

obtain()一样,但是设置了targetwhat的值。

         参数

                   h                target的值

                   what         what的值

         返回值

                   从全局池中分配的一个Message对象。

 

public static Message obtain (Handler h)

obtain()一样,但是设置了target的值

         参数

                   h                消息对象的target成员的值

         返回值

                   从全局池中分配的一个Message对象。

 

public static Message obtain (Handler h, Runnable callback)

obtain(Handler)一样,但是设置回调函数,在Message返回时调用。

         参数

                   h                消息对象的target成员的值

                   callback    当消息处理时会调用的回调函数

         返回值

                   从全局池中分配的一个Message对象。

 

public static Message obtain ()

从全局池中返回一个新的Message实例。在大多数情况下这样可以避免分配新的对象。

 

public static Message obtain (Handler h, int what, int arg1, int arg2)

obtain()一样,但是设置了target, what, arg1arg2的值

         参数

                   h                设置的targe

                   what         设置的what

                   arg1          设置的arg1

                   arg2          设置的arg2

         返回值

                   从全局池中分配的一个Message对象。

 

public static Message obtain (Message obj)

obtain(),但是从一个已存在的消息中拷贝值(包括它的目标)。

         参数

                   orig           要拷贝的源消息

         返回值

                   从全局池中分配的一个Message对象。

 

public Bundle peekData ()

getData()相似,但是并不延迟创建Bundle。如果Bundle对象不存在返回null。更多信息见getData()

         参考

                   getData()

                   setData(Bundle)

 

public void recyle ()

向全局池中返回一个Message实例。一定不能在调用此函数后再使用Message——它会立即被释放。

 

public void sendToTarget ()

Handler发送此消息,getTarget()方法可以获取此Handler。如果这个字段没有设置会抛出个空指针异常。

 

public void setData (Bundle data)

设置一个任意数据值的Bundle对象。如果可以,使用arg1arg2域发送一些整型值以减少消耗。

参考

         getData()

         peekData()

 

public void setTarget (Handler target)

设置将接收此消息的Handler对象。

 

public String toString ()

返回一个Message对象简单的,可读懂的描述信息。鼓励子类重写此方法,实现时最好把对象的类型的数据考虑进去。默认的实现等同与以下表达式:

     

 

如果需要实现toString方法,参考Writing a useful toString method

                   返回值

                            一个代表此对象的可打印字符串

 

public void writeToParcel (Parcel dest, int flags)

将类的数据写入外部提供的Parcel

参数

                            dest   对象被写入的Parcel

                            flags 对象如何被写入的附加标志,可能是0PARCELABLE_WRITE_RETURN_VALUE

2025-11-13 05:37:57:788 [ERROR][user][10.0.2.16][52][JafLoggingAspect:onError:204]-[JAFRSHS99300P004UseCaseImpl:invoke-gIAlu-s:221] – 処理エラー java.lang.IllegalStateException: Method addObserver must be called on the main thread at androidx.lifecycle.LifecycleRegistry.enforceMainThreadIfNeeded(LifecycleRegistry.jvm.kt:297) at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.jvm.kt:172) at androidx.activity.result.ActivityResultRegistry$LifecycleContainer.addObserver(ActivityResultRegistry.kt:420) at androidx.activity.result.ActivityResultRegistry.register(ActivityResultRegistry.kt:126) at androidx.activity.ComponentActivity.registerForActivityResult(ComponentActivity.kt:828) at androidx.activity.ComponentActivity.registerForActivityResult(ComponentActivity.kt:837) at jp.or.jaf.syg.domain.usecase.jafrshs99.jafrshs99300.impl.JAFRSHS99300P004UseCaseImpl.invoke-gIAlu-s(JAFRSHS99300P004UseCaseImpl.kt:221) at jp.or.jaf.syg.domain.usecase.jafrshs99.jafrshs99300.impl.JAFRSHS99300P004UseCaseImpl$invoke$1.invokeSuspend(Unknown Source:21) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at jp.or.jaf.syg.core.common.aspect.CombinedInvocationHandler$CombinedLoggingContinuation.resumeWith(CombinedInvocationHandler.kt:259) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) at jp.or.jaf.syg.core.common.aspect.CombinedInvocationHandler$CombinedLoggingContinuation.resumeWith(CombinedInvocationHandler.kt:259) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) at jp.or.jaf.syg.core.common.aspect.CombinedInvocationHandler$CombinedLoggingContinuation.resumeWith(CombinedInvocationHandler.kt:259) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104) at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:111) at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:99) at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:811) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:715) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:702) 2025-11-13 13:37:57.896 11183-11214 JafCommonLog jp.or.jaf.syg.dev E 2025-11-13 05:37:57:882 [INFO][user][10.0.2.16][52][JafViewModel:showError:312]-[jp.or.jaf.syg.feature.jafrsho07.jafrsho07030.JAFRSHO07030ViewModel.Method addObserver must be called on the main thread:-1] – 処理エラー | duration -1ms Method addObserver must be called on the main thread java.lang.IllegalStateException: Method addObserver must be called on the main thread at androidx.lifecycle.LifecycleRegistry.enforceMainThreadIfNeeded(LifecycleRegistry.jvm.kt:297) at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.jvm.kt:172) at androidx.activity.result.ActivityResultRegistry$LifecycleContainer.addObserver(ActivityResultRegistry.kt:420) at androidx.activity.result.ActivityResultRegistry.register(ActivityResultRegistry.kt:126) at androidx.activity.ComponentActivity.registerForActivityResult(ComponentActivity.kt:828) at androidx.activity.ComponentActivity.registerForActivityResult(ComponentActivity.kt:837) at jp.or.jaf.syg.domain.usecase.jafrshs99.jafrshs99300.impl.JAFRSHS99300P004UseCaseImpl.invoke-gIAlu-s(JAFRSHS99300P004UseCaseImpl.kt:221) at jp.or.jaf.syg.domain.usecase.jafrshs99.jafrshs99300.impl.JAFRSHS99300P004UseCaseImpl$invoke$1.invokeSuspend(Unknown Source:21) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at jp.or.jaf.syg.core.common.aspect.CombinedInvocationHandler$CombinedLoggingContinuation.resumeWith(CombinedInvocationHandler.kt:259) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) at jp.or.jaf.syg.core.common.aspect.CombinedInvocationHandler$CombinedLoggingContinuation.resumeWith(CombinedInvocationHandler.kt:259) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) at jp.or.jaf.syg.core.common.aspect.CombinedInvocationHandler$CombinedLoggingContinuation.resumeWith(CombinedInvocationHandler.kt:259) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:104) at kotlinx.coroutines.internal.LimitedDispatcher$Worker.run(LimitedDispatcher.kt:111) at kotlinx.coroutines.scheduling.TaskImpl.run(Tasks.kt:99) at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:584) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:811) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:715) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:702)
最新发布
12-02
你遇到的这个错误是 Android 开发中非常典型的一个 **线程安全问题**: --- ## 🔥 错误信息 ```log java.lang.IllegalStateException: Method addObserver must be called on the main thread at androidx.lifecycle.LifecycleRegistry.enforceMainThreadIfNeeded(LifecycleRegistry.jvm.kt:297) at androidx.lifecycle.LifecycleRegistry.addObserver(LifecycleRegistry.jvm.kt:172) ... at jp.or.jaf.syg.domain.usecase.jafrshs99.jafrshs99300.impl.JAFRSHS99300P004UseCaseImpl.invoke-gIAlu-s(JAFRSHS99300P004UseCaseImpl.kt:221) ``` > ❌ 在非主线程(background thread)调用了 `Lifecycle.addObserver()` 或使用了 `registerForActivityResult()`。 --- ## ✅ 根本原因分析 ### 📌 问题代码位置: ```kotlin JAFRSHS99300P004UseCaseImpl.kt:221 ``` 你在 **UseCase 层**(属于业务逻辑层,通常运行在协程后台线程)尝试调用 Android Framework 的 UI 相关 API,比如: ```kotlin // 示例:错误地在 UseCase 中注册 Activity Result val launcher = activity.registerForActivityResult(StartActivityForResult()) { result -> // handle result } ``` 或者间接调用了某个需要生命周期观察者的组件。 --- ### ⚠️ 关键知识点 | 组件 | 必须在主线程? | 原因 | |------|----------------|------| | `LifecycleRegistry.addObserver()` | ✅ 是 | 防止并发修改 UI 状态 | | `ComponentActivity.registerForActivityResult()` | ✅ 是 | 内部依赖 Lifecycle 和 Handler | | ViewModel / Repository / UseCase 默认线程 | ❌ 后台线程 | 协程调度器如 `Dispatchers.IO` | > 💥 所以:**不能在 UseCase 或任何数据层中直接操作 Activity、Fragment 或注册 Launcher!** --- ## ✅ 正确架构设计原则(Clean Architecture) ``` [View] (Activity/Fragment) → [ViewModel] → [UseCase] → [Repository] → [DAO/Remote] ↑ ↖ ↖ └── registerForActivityResult launchWhenStarted 数据获取 ``` ✅ 正确做法: - `registerForActivityResult` 必须在 **Activity/Fragment 中创建** - 将返回结果通过回调或事件传递给 ViewModel - ViewModel 调用 UseCase 处理业务逻辑 --- ## ✅ 解决方案 ### ✅ 方案一:【推荐】将 `ActivityResultLauncher` 移到 Activity/Fragment #### 修改前 ❌(错误写法 —— 在 UseCase 中) ```kotlin // ❌ JAFRSHS99300P004UseCaseImpl.kt class JAFRSHS99300P004UseCaseImpl @Inject constructor( private val activity: FragmentActivity ) { fun someFunction() { val launcher = activity.registerForActivityResult(...) // ← 崩溃! } } ``` #### ✅ 修改后:移到 UI 层 ```kotlin // ✅ MainActivity.kt 或 Fragment class MainActivity : ComponentActivity() { private lateinit var someLauncher: ActivityResultLauncher<Intent> override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) someLauncher = registerForActivityResult(StartActivityForResult()) { result -> if (result.resultCode == RESULT_OK) { // 成功回调 viewModel.handleActivityResult(result.data) } } viewModel.startSomeOperation.observe(this) { intent -> someLauncher.launch(intent) } } } ``` #### ViewModel 层作为桥梁: ```kotlin // ✅ ViewModel class MyViewModel @Inject constructor( private val useCase: JAFRSHS99300P004UseCase ) : ViewModel() { private val _startSomeOperation = MutableLiveData<Intent>() val startSomeOperation: LiveData<Intent> = _startSomeOperation private val _resultEvent = SingleLiveEvent<String>() // 避免重复触发 val resultEvent: LiveData<String> = _resultEvent fun doBusinessLogic() { // 某些条件满足后,启动外部操作 val intent = useCase.prepareExternalOperation() _startSomeOperation.value = intent } fun handleActivityResult(data: Intent?) { val result = useCase.processResult(data) _resultEvent.value = result } } ``` #### UseCase 层只负责逻辑处理: ```kotlin // ✅ UseCase 只做准备和解析工作,不涉及 UI 注册 class JAFRSHS99300P004UseCaseImpl @Inject constructor() : JAFRSHS99300P004UseCase { override fun prepareExternalOperation(): Intent { return Intent(Intent.ACTION_PICK).apply { setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI) } } override fun processResult(data: Intent?): String { val uri = data?.data ?: throw BusinessException("No image selected") return "Selected: $uri" } } ``` --- ### ✅ 方案二:如果你必须从 UseCase 触发并等待结果 → 使用“命令模式” 定义一个密封类表示“要执行的操作”: ```kotlin sealed class UiCommand { data class LaunchActivity(val intent: Intent) : UiCommand() data class ShowToast(val message: String) : UiCommand() object OpenCamera : UiCommand() } ``` ViewModel 发出命令: ```kotlin private val _uiCommand = SingleLiveEvent<UiCommand>() val uiCommand: LiveData<UiCommand> = _uiCommand fun executeWithPermissionCheck() { val intent = useCase.createIntent() _uiCommand.value = UiCommand.LaunchActivity(intent) } ``` Activity 监听并执行: ```kotlin viewModel.uiCommand.observe(this) { command -> when (command) { is UiCommand.LaunchActivity -> launcher.launch(command.intent) is UiCommand.ShowToast -> Toast.makeText(this, command.message, Toast.LENGTH_SHORT).show() } } ``` --- ## ✅ 如何避免此类问题? | 最佳实践 | 说明 | |--------|------| | ❌ 不要在 Domain / Data 层引用 `Context`, `Activity`, `FragmentManager` 等 | 违反分层原则 | | ✅ 使用回调接口或事件通信 | 解耦 UI 与逻辑 | | ✅ 使用 `SingleLiveEvent` 或 `Channel` 处理一次性事件 | 防止事件被多次消费 | | ✅ 在 `@Module` 中提供 `@ActivityContext` 注入时格外小心 | 避免滥用 Context 注入 | --- ## ✅ 总结 | 项目 | 内容 | |------|------| | ❌ 错误原因 | 在后台线程调用 `registerForActivityResult` → 触发生命周期注册 → 要求主线程 | | 🧩 报错堆栈起点 | `LifecycleRegistry.addObserver` → 来自 `ActivityResultRegistry.register` | | ✅ 正确做法 | 将 `ActivityResultLauncher` 创建放在 `Activity/Fragment` 中 | | 🔄 数据流方向 | ViewModel → 发出 Intent → Activity 启动 → 结果回传给 ViewModel → UseCase 处理 | | 🛡️ 架构建议 | UseCase 只负责准备参数和解析结果,不参与 UI 生命周期管理 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值