Android-Handler源码解析-Message

2023-05-16

成员变量

// 标识Message
public int what;

// 存储简单数据,如果存储复杂的数据使用setData()方法。
public int arg1;
public int arg2;

// 发送给接收者的任意对象。
public Object obj;

// 回复给发送者,用于跨进程双向通信。发送message时给其replyTo赋值,接收到该message的进程,可以通过message.replyTo向发送方进程发送message,从而实现双向通信。
public Messenger replyTo;

public static final int UID_NONE = -1;
// 可选字段,表示发送消息的uid。这只对Messenger发布(跨进程发布)的消息有效;否则,它就是-1。
public int sendingUid = UID_NONE;
// 可选字段,指示导致该消息进入队列的uid。
public int workSourceUid = UID_NONE;

// 在用标记
/*package*/ static final int FLAG_IN_USE = 1 << 0;
// 异步标记
/*package*/ static final int FLAG_ASYNCHRONOUS = 1 << 1;
// copy的清空标记
/*package*/ static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
// 标记位
@UnsupportedAppUsage
/*package*/ int flags;

// 执行时刻,时刻基于SystemClock.uptimeMillis。
@UnsupportedAppUsage
public long when;

// 存储复杂的数据
/*package*/ Bundle data;

// 消息的目标处理器Handler,即由此Handler处理此消息。
@UnsupportedAppUsage
/*package*/ Handler target;

// 表示要处理的任务Callback,由Runnable实现,通过handler.post Runnable的时候,Runnable会被存放在Message中,分发的时候再进行回调通知。
@UnsupportedAppUsage
/*package*/ Runnable callback;

// 下个消息,用于实现链表结构。
@UnsupportedAppUsage
/*package*/ Message next;

// 消息池的同步锁对象,用户消息池的消息获取(obtain()方法)和消息回收(recycleUnchecked()方法)。
/** @hide */
public static final Object sPoolSync = new Object();
// 消息池链表的头,用于操作(增删改查)链表。
private static Message sPool;
// 消息池中消息的数量
private static int sPoolSize = 0;
// 消息池中消息的最大数量,为50个。
private static final int MAX_POOL_SIZE = 50;
// 是否检查消息的回收(recycle()方法),默认true,如果检查并且有问题,则抛出异常。
private static boolean gCheckRecycle = true;
复制代码

说明:

  1. Message为什么需要持有Handler,因为Message需要知道是哪个Handler要处理它。

创建Message

想要使用发送Message,首先要创建Message,所以我们接下来看下它是如何被创建的。

new Message()

public Message() {
}
复制代码

直接创建一个Message对象,之后可以设置它的属性,未使用复用不推荐使用。

Message.obtain()

Message.obtain()

public static Message obtain() {
    // 使用同步保证线程安全,因为此方法可以在任意线程调用。
    synchronized (sPoolSync) {
        if (sPool != null) {
            // 消息池头不为空,说明队列有内容,从中获取一条消息并返回。缓存的
            Message m = sPool; // 消息池的Head元素
            sPool = m.next; // 缓存池Head为下个元素(移除m)
            m.next = null; // 断开链接
            m.flags = 0; // 清除在用标记
            sPoolSize--; // 池数量减1
            return m; // 返回此消息
        }
    }
    // 消息池头为空,说明队列没有内容,直接创建一条消息并返回。
    return new Message();
}
复制代码

Message.obtain()静态方法,内部用了全局消息池,使用了复用推荐使用。

说明:

  1. 消息池获取消息,它会获取队列第一条消息,并将此消息此队列移除

Message.obtain(Message)

public static Message obtain(Message orig) {
    // 获取消息
    Message m = obtain();
    // 将orig的值复制到新消息中
    m.what = orig.what;
    m.arg1 = orig.arg1;
    m.arg2 = orig.arg2;
    m.obj = orig.obj;
    m.replyTo = orig.replyTo;
    m.sendingUid = orig.sendingUid;
    m.workSourceUid = orig.workSourceUid;
    if (orig.data != null) {
        m.data = new Bundle(orig.data);
    }
    m.target = orig.target;
    m.callback = orig.callback;

    return m;
}
复制代码

Message.obtain()相同,但将现有Message(包括其target)的值复制新的消息中。

Message.obtain(Handler)

public static Message obtain(Handler h) {
    Message m = obtain();
    m.target = h;

    return m;
}
复制代码

Message.obtain()相同,但设置了target成员的值。

Message.obtain(Handler, Runnable)

public static Message obtain(Handler h, Runnable callback) {
    Message m = obtain();
    m.target = h;
    m.callback = callback;

    return m;
}
复制代码

Message.obtain()相同,但设置了targetcallback成员的值。

Message.obtain(Handler, int)

public static Message obtain(Handler h, int what) {
    Message m = obtain();
    m.target = h;
    m.what = what;

    return m;
}Message.
复制代码

Message.obtain()相同,但设置了targetwhat成员的值。

Message.obtain(Handler, int, Object)

public static Message obtain(Handler h, int what, Object obj) {
    Message m = obtain();
    m.target = h;
    m.what = what;
    m.obj = obj;

    return m;
}
复制代码

Message.obtain()相同,但设置了targetwhatobj成员的值。

Message.obtain(Handler, int, int, int)

public static Message obtain(Handler h, int what, int arg1, int arg2) {
    Message m = obtain();
    m.target = h;
    m.what = what;
    m.arg1 = arg1;
    m.arg2 = arg2;

    return m;
}
复制代码

Message.obtain()相同,但设置了targetwhatarg1arg2成员的值。

Message.obtain(Handler, int, int, int, Object)

public static Message obtain(Handler h, int what,
        int arg1, int arg2, Object obj) {
    Message m = obtain();
    m.target = h;
    m.what = what;
    m.arg1 = arg1;
    m.arg2 = arg2;
    m.obj = obj;

    return m;
}
复制代码

Message.obtain()相同,但设置了targetwhatarg1arg2obj成员的值。

小结

  1. Message创建,有两种方式:new MessageMessage.obtain()Message.obtain()内部使用了复用推荐使用

回收Message

由于Message.obtain()方式创建内部使用了复用,所以我们接下来看下它是如何被回收的。

recycle()

public void recycle() {
    // 判断是否在使用中
    if (isInUse()) {
        // 在使用中,直接返回,不进行回收。
        if (gCheckRecycle) {
            // gCheckRecycle为true,为检查消息的回收,并且回收时正在使用(有问题),则抛出异常。
            throw new IllegalStateException("This message cannot be recycled because it "
                    + "is still in use.");
        }
        return;
    }
    // 不在使用中,进行回收。
    recycleUnchecked();
}
复制代码

recycle()方法,为检查回收,如果此消息处于使用状态,则不进行回收否则调用recycleUnchecked()直接进行回收

isInUse()方法的使用看后面-其它-InUse,我们接下来看下gCheckRecycle属性,它在updateCheckRecycle()方法内进行更改

public static void updateCheckRecycle(int targetSdkVersion) {
    if (targetSdkVersion < Build.VERSION_CODES.LOLLIPOP) {
	// 小于SDK 21,为false。
        gCheckRecycle = false;
    }
}
复制代码

updateCheckRecycle()方法,为更新gCheckRecyclegCheckRecycle默认为trueupdateCheckRecycle()方法在ActivityThreadhandleBindApplication()方法(绑定App)内调用,即gCheckRecycletargetSdkVersion小于21false(不检查),大于、等于21ture(检查)。

recycleUnchecked()

void recycleUnchecked() {
    // 将此消息标记为在用,并判断是否添加到消息池中。
    // 清除所有属性
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = UID_NONE;
    workSourceUid = UID_NONE;
    when = 0;
    target = null;
    callback = null;
    data = null;

    // 同步,保证线程安全。
    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            // 小于最大值,则进行添加,把此Message添加到链表的头部。
            next = sPool; // 此Message的next指向链表的Head
            sPool = this; // 链表的Head为此Message 
            sPoolSize++; // 消息池数量加1
        }
    }
}
复制代码

recycleUnchecked()方法,为不检查回收,会清除消息的所有属性,以完成释放,并判断如果消息池消息数量没有到达最大数量,则进行添加,以完成回收

说明:

  1. 消息池添加消息,它会将此消息添加到队列头部,并将此消息作为新的头

小结

  1. Message回收,有两种方式:recycle()检查回收recycleUnchecked()不检查回收
  2. recycle(),判断了消息如果处于使用状态,则不进行回收否则调用recycleUnchecked()进行回收
  3. recycleUnchecked(),会清除消息的所有属性,并判断如果消息池数量没到消息池最大数量,则进行添加(添加到消息池的链表头部)。

跨进程通讯

由于Message实现了Parcelable接口,所以Message可以跨进程传输,所以我们接下来看下它的具体实现。

writeToParcel()

public void writeToParcel(Parcel dest, int flags) {
    if (callback != null) {
	// callback不能跨进程传输,抛出异常。
        throw new RuntimeException(
            "Can't marshal callbacks across processes.");
    }
    dest.writeInt(what);
    dest.writeInt(arg1);
    dest.writeInt(arg2);
    if (obj != null) {
	// obj不为空,跨进程传输任意对象,必须实现Parcelable接口,否则抛出异常。
        try {
            Parcelable p = (Parcelable)obj;
            dest.writeInt(1); // 传入1,标记有存入obj对象。
            dest.writeParcelable(p, flags); // 写入obj对象
        } catch (ClassCastException e) {
            throw new RuntimeException(
                "Can't marshal non-Parcelable objects across processes.");
        }
    } else {
        dest.writeInt(0); // 传入0,标记没有存入obj对象。
    }
    dest.writeLong(when);
    dest.writeBundle(data);
    Messenger.writeMessengerOrNullToParcel(replyTo, dest); // 使用Messenger进行写入
    dest.writeInt(sendingUid);
    dest.writeInt(workSourceUid);
}
复制代码

readFromParcel()

private void readFromParcel(Parcel source) {
    what = source.readInt();
    arg1 = source.readInt();
    arg2 = source.readInt();
    if (source.readInt() != 0) {
	// 有存入obj对象,进行读取。
        obj = source.readParcelable(getClass().getClassLoader());
    }
    when = source.readLong();
    data = source.readBundle();
    replyTo = Messenger.readMessengerOrNullFromParcel(source); // 使用Messenger进行读取
    sendingUid = source.readInt();
    workSourceUid = source.readInt();
}
复制代码

小结

  1. Message跨进程通讯callback不能有值,obj如有值必须实现Parcelable接口。

属性set、get方法

Messagewhentargetcallbackdata属性对外提供了setget方法,我们接下来看下它们的实现。

when

public long getWhen() {
    return when;
}
复制代码

target

public void setTarget(Handler target) {
    this.target = target;
}

public Handler getTarget() {
    return target;
}
复制代码

callback

public Runnable getCallback() {
    return callback;
}
复制代码

data

public Bundle getData() {
    if (data == null) {
        data = new Bundle();
    }
    return data;
}

public Bundle peekData() {
    return data;
}

public void setData(Bundle data) {
    this.data = data;
}
复制代码

其它

copyFrom()

public void copyFrom(Message o) {
    this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
    this.what = o.what;
    this.arg1 = o.arg1;
    this.arg2 = o.arg2;
    this.obj = o.obj;
    this.replyTo = o.replyTo;
    this.sendingUid = o.sendingUid;
    this.workSourceUid = o.workSourceUid;

    if (o.data != null) {
        this.data = (Bundle) o.data.clone();
    } else {
        this.data = null;
    }
}
复制代码

指定Message的属性,复制当前Message中。

sendToTarget()

public void sendToTarget() {
    target.sendMessage(this);
}
复制代码

此消息发送给自己目标Handler,如果未设置此字段,则抛出空指针异常。

asynchronous(异步)

public boolean isAsynchronous() {
    return (flags & FLAG_ASYNCHRONOUS) != 0;
}
复制代码

isAsynchronous()方法,判断消息是否是异步的,如果是,则返回true

public void setAsynchronous(boolean async) {
    if (async) {
        flags |= FLAG_ASYNCHRONOUS;
    } else {
        flags &= ~FLAG_ASYNCHRONOUS;
    }
}
复制代码

setAsynchronous()方法,设置消息是否是异步的,这意味着它不受Looper同步屏障的影响。

说明:

  1. 消息,分为同步消息异步消息默认同步消息
  2. 同步屏障,会屏障同步消息,确保只有异步消息执行。
  3. 某些操作(比如视图失效)可能会在Looper消息队列中引入同步屏障,以阻止后续消息满足某些条件之前被传递。在view invalidation的情况下,调用android.view.View.invalidate后发布的消息会被一个同步屏障挂起,直到下一帧准备被绘制。同步屏障确保在恢复之前完全处理了无效请求。
  4. 异步消息免于同步屏障。它们通常表示中断输入事件其它必须独立处理的信号,即使其它工作已经暂停。
  5. 请注意,异步消息可能是按同步消息顺序交付的,尽管它们之间总是按顺序交付的。如果这些消息的相对顺序很重要,那么它们可能一开始就不应该是异步的,谨慎使用

InUse

/*package*/ boolean isInUse() {
    return ((flags & FLAG_IN_USE) == FLAG_IN_USE);
}
复制代码

isInUse()方法,判断是否处于使用状态。

/*package*/ void markInUse() {
    flags |= FLAG_IN_USE;
}
复制代码

markInUse()方法,标记处于使用状态。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android-Handler源码解析-Message 的相关文章

  • Android:使用 OAuth 访问 google 任务时出现问题

    由于 google 任务没有公共 api 我想编写解决方法并像浏览器一样请求数据 然后解析结果以进一步显示 为了访问数据 我使用 google 实现了 OAuth 身份验证来访问此 url https mail google com htt
  • 通过 WhatsApp 发送消息

    由于我发现了一些较旧的帖子 表明 Whatsapp 不支持此功能 我想知道是否发生了变化 以及是否有办法打开与我通过意图发送的号码进行 Whatsapp 聊天 UPDATE请参阅https faq whatsapp com en andro
  • 安卓定位不准确

    我正在尝试获取当前用户的位置 我试图重构我的代码以获得更好的结果 但我只是不断得到关于准确度的荒谬位置 它在 900 600 米之间 如何才能得到更好的结果 使其精度达到50m以内 这是我的代码 package com agam mapsl
  • 如何在android上的python kivy中关闭应用程序后使服务继续工作

    我希望我的服务在关闭应用程序后继续工作 但我做不到 我听说我应该使用startForeground 但如何在Python中做到这一点呢 应用程序代码 from kivy app import App from kivy uix floatl
  • 使用完成处理程序在 Swift 中调用连续动画

    我正在制作一个可以显示化学反应动画的应用程序 每个原子都是一个 SCNSphere 并通过 SCNActions 进行动画处理 我尝试使用 runAction 中的完成处理程序在当前操作完成后调用下一个动画 因为每个原子必须做出很多不同的运
  • 调试:在 Android 1.0 中找不到文件

    今天我更新到 Android Studio v 1 0 在尝试编译任何项目时出现以下错误 app build intermediates classes debug 找不到文件 问题是在更新之前我没有任何问题 这是我实际尝试编译的代码 构建
  • 如何为发布而不是调试创建密钥库?扑

    我按照使用此网站部署 flutter 的步骤进行操作https flutter io android release https flutter io android release 当我运行 flutter build apk 时出现此错
  • 如何在android中设置权限WRITE_SECURE_SETTINGS? [复制]

    这个问题在这里已经有答案了 我正在尝试启用 4 0 以上的辅助功能服务设置 但它显示异常 即 引起原因 java lang SecurityException 权限拒绝 写入安全设置需要 android permission WRITE S
  • 使用 Retrofit2 和 Mockito 或 Robolectric 进行 Android 单元测试

    我可以测试 Retrofit2beta4 的真实响应吗 我需要 Mockito 或 Robolectic 吗 我的项目中没有活动 它将是一个库 我需要测试服务器是否正确响应 现在我有这样的代码并卡住了 Mock ApiManager api
  • Android 服务是否有办法检测设备何时锁定?

    我有一个 Android 服务 我希望在设备锁定时执行操作 我想澄清一下 我对屏幕开 关状态不感兴趣 我知道如何使用带有 Intent ACTION USER PRESENT 和 KeyguardManager inKeyguardRest
  • 使用 gradlew assembleRelease 从 React Native 创建发布 apk 时出现错误

    我想发布 apk 但我收到错误 文件已存在 mkdir D mobile 它在 d 驱动器中生成名为 mobile 的文件 删除文件后 再次执行 gradlew assembleRelease 创建该文件并抛出错误 任务 app bundl
  • 画透明圆,外面填充

    我有一个地图视图 我想在其上画一个圆圈以聚焦于给定区域 但我希望圆圈倒转 也就是说 圆的内部不是被填充 而是透明的 其他所有部分都被填充 请参阅这张图片了解我的意思 http i imgur com zxIMZ png 上半部分显示了我可以
  • 将 Firebase 云消息传递与 Windows 应用程序结合使用

    我在 Android 和 iOS 应用程序中使用 Firebase Cloud Messaging 但是我还有此应用程序的 Windows Mac OS 版本 我想保留相同的逻辑 我知道 Firebase Cloud Messaging 可
  • 在 React Native 中调试应用程序崩溃

    我是 React Native 新手 我正在尝试安装 React Native Facebook SDK 以便我可以使用我的应用程序进行 Facebook 登录 我按照此处列出的步骤操作 https tylermcginnis com in
  • 有关 ListView 自定义行布局项目上的 onClick() 事件的帮助

    我有一个 ListView 其行由我格式化 每行都有 ImageView 和 TextView 的混合 我还实现了自己的适配器 并且能够通过它绘制每一行 现在 我想要这样的东西 用户单击 ImageView 不是行上的其他任何位置 但只有此
  • Android:监听状态栏通知

    有没有办法在状态栏被下拉时监听通知 1 用于检测状态栏变化 您可以注册一个监听器来获取系统UI可见性变化的通知 因此 要在您的活动中注册侦听器 Detecting if the user swipe from the top down to
  • 如何在 Android 中使用 C# 生成的 RSA 公钥?

    我想在无法假定 HTTPS 可用的情况下确保 Android 应用程序和 C ASP NET 服务器之间的消息隐私 我想使用 RSA 来加密 Android 设备首次联系服务器时传输的对称密钥 RSA密钥对已在服务器上生成 私钥保存在服务器
  • 在 KitKat 4.4.2 中获取 SDard 路径和大小

    我在 Google Play 上有一个设备信息应用程序 在该应用程序中我有存储信息 我知道 Android 4 4 在访问外部 SD 卡方面发生了一些变化 内部似乎没有给我带来问题 我的问题是 如何可靠地获取 KitKat 上 SD 卡的大
  • Android studio - 如何查找哪个库正在使用危险权限?

    我正在尝试将 apk 上传到 google play 商店 但令我惊讶的是 我正在使用以下权限 Your APK is using permissions that require a privacy policy android perm
  • javafx android 中的文本字段和组合框问题

    我在简单的 javafx android 应用程序中遇到问题 问题是我使用 gradle javafxmobile plugin 在 netbeans ide 中构建了非常简单的应用程序 其中包含一些文本字段和组合框 我在 android

随机推荐

  • AndroidStudio-快捷键-格式化代码

    Windows Ctrl 43 Alt 43 L Ctrl 43 Shift 43 F 无效 亲测 和qq热键冲突 我的解决方式是把qq除捕获屏幕外的热键全部设置为无 Mac OPTION 43 CMD 43 L
  • 安装APK时报错:Failure [INSTALL_FAILED_TEST_ONLY: installPackageLI]

    使用AS自动运行时会在app build outputs apk debug文件夹下自动生成测试APK xff1a app debug apk xff0c 用命令adb install app debug apk时报错 xff1a Fail
  • 计算机网络-划分子网 四大类必会题型

    必记知识点 A类 xff1a 0 126 xff0c 默认子网掩码 xff1a 255 0 0 0 B类 xff1a 128 191 xff0c 默认子网掩码 xff1a 255 255 0 0 C类 xff1a 192 223 xff0c
  • C语言-解释复杂声明

    基本术语 xff1a 声明符 xff1a int a 就是一个声明符 标识符 定义的变量名字 xff0c 如 xff1a int a xff0c 那么a就是一个标识符 1 两个原则 xff1a 始终从内往外读声明符 xff0c 括号优先级高
  • Android EditText 不自动获取焦点

    在Activity上面显示一个EditText xff0c 进入该页面时想阻止这个EditText自动获取焦点而自动调起键盘 思路如下 xff1a 可以采取让父级控件来获取焦点就可以了 例如说在这个EditText外面包一个LinearLa
  • Wireshark中无法显示网卡列表的解决方法

    1 问题描述 打开Wireshark时 xff0c 都会有一个网卡列表 xff0c 在该列表中显示了电脑的所有网卡 但是 xff0c 有时打开Wireshark时 xff0c 该网卡列表不显示 xff0c 如图1所示 图1 不显示网卡列表
  • Android事件分发与事件处理源码分析

    一 前言 Android中事件分发与事件处理是一个老生常谈的问题了 xff0c 自己在网上也看过很多文章 xff0c 但是大部分人都只是抛出一些结论或是一些流程图或者干脆就是一些运行demo的截图等 xff0c 对于这些结论和流程图是怎么来
  • 解决小米pad USB安装apk时AS报错:INSTALL_FAILED_USER_RESTRICTED

    设置 更多设置 开发者选项 gt 取消启用MIUI优化 用USB接口 xff0c 选择传输文件 xff08 MTP xff09
  • git 通过 comment 关键字查找 commit

    git log grep 61 word 比如 xff1a git log grep 61 同步
  • git合并多个 Commit

    在使用 Git 作为版本控制的时候 xff0c 我们可能会由于各种各样的原因提交了许多临时的 commit xff0c 而这些 commit 拼接起来才是完整的任务 那么我们为了避免太多的 commit 而造成版本控制的混乱 xff0c 通
  • git查看某次提交的文件列表

    Git操作常用 xff1a 一 查看某次提交的文件列表 首先使用git log查看历史提交记录 xff1a 复制你想要查看记录的某个提交代号9ddc9dca00b 使用命令git show 9ddc9dca00b stat查看详细文件列表
  • Android SoundPool插入耳机后依然有外放声音

    使用soundPool播放声音 xff0c 当手机已经接通耳机时 xff0c 还会有外放声音 xff0c 是因为在初始化soundpool是用的流类型 xff08 streamType xff09 导致的 xff0c 有些流类型系统是一定会
  • 一文搞懂Android JetPack组件原理之Lifecycle、LiveData、ViewModel与源码分析技巧

    Lifecycle LiveData和ViewModel作为AAC架构的核心 xff0c 常常被用在Android业务架构中 在京东商城Android应用中 xff0c 为了事件传递等个性化需求 xff0c 比如ViewModel间通信 V
  • Linux下安装npm

    1 root 登录linux 2 没有目录就自己创建一个 cd usr local node 3 下载安装包 wget https npm taobao org mirrors node v4 4 7 node v4 4 7 linux x
  • AudioService之音频输出通道切换

    前言 xff1a 音频输出的方式有很多种 xff0c 外放即扬声器 xff08 Speaker xff09 听筒 xff08 Telephone Receiver xff09 有线耳机 xff08 WiredHeadset xff09 蓝牙
  • Android音频——音量调节

    一 音量相关概念 1 相关术语解释 track volume 单个App设置音量时设置的是这个 xff0c 它只影响本App的音量 stream volume xff1a 设置某一stream的音量 xff0c Android系统中支持10
  • 电话状态权限及IMEI获取流程源码分析

    IMEI是设备唯一性的一个重要指标 xff0c 这篇文章对IMEI获取做一些分析 xff0c 以达到以下两个目的 xff1a 1 梳理Android源码中获取IMEI流程 2 理解获取IMEI时 xff0c 源码中权限调用流程 备注 xff
  • Android Handler深入学习(源码分析)

    目录 xff1a 1 背景 在分析源码之前 xff0c 先来了解一下Message MessageQueue Looper这几个对象 1 1 Message 消息 定义 xff1a 是线程间通讯的数据单元 xff0c 包含着描述信息及任意数
  • git 合并两个不同仓库

    在日常开发过程中 xff0c 可能会遇到需要将两个不同的仓库合并成到一个仓库的场景 这里介绍一下怎么将两个不同的仓库合并到一个仓库中 合并两个不同仓库 思路 xff1a 添加两个远程仓库 xff0c 将两个代码作为两个分支 xff0c 然后
  • Android-Handler源码解析-Message

    成员变量 标识Message public int what 存储简单数据 xff0c 如果存储复杂的数据使用setData 方法 public int arg1 public int arg2 发送给接收者的任意对象 public Obj