Android语言基础教程(221)Android Handler消息传递机制之消息类(Message)简介:别让App卡成PPT!Android Handler消息类(Message)全解密,程序员

嘿,朋友们!今天咱们来聊一个让很多Android新手又爱又恨的东西——Handler消息机制里的Message类。如果你曾经因为App卡成PPT而被产品经理追杀,或者因为线程不同步导致数据乱飞,那么这篇文章就是你的救命稻草!

第一章 为什么需要Message?一个外卖小哥的比喻

想象一下,你正在厨房(主线程)里热火朝天地炒菜,突然顾客说要加一瓶可乐。你有两个选择:

  1. 自己跑去超市(网络请求)买可乐,让整个厨房停摆
  2. 叫个外卖小哥(Message)帮你买

只要脑子没进水,你肯定会选第二个方案对吧?在Android世界里,Message就是那个任劳任怨的外卖小哥!

为什么不能直接跨线程操作?

// 错误示范!这是在作死
public void updateUIFromWorkerThread() {
    new Thread(() -> {
        // 在子线程中直接更新UI
        textView.setText("Hello from background!"); // 崩溃警告!
    }).start();
}

看到没?Android系统是个强迫症患者,它规定:只有主线程才能更新UI!为什么?因为如果多个线程同时操作UI,你的屏幕可能会变成抽象画。

第二章 Message的身份证:揭秘核心字段

每个Message小哥出门送快递时,都得带上一张“送货单”,这就是Message的各个字段:

1. what - 这是送什么货?

// 定义消息类型
static final int MSG_ORDER_FOOD = 1;
static final int MSG_PAY_BILL = 2;
static final int MSG_COMPLAIN = 3;

Message msg = Message.obtain();
msg.what = MSG_ORDER_FOOD; // 明确这是下单消息

what就像快递单上的“货物类型”,让接收方一眼就知道这是什么业务。

2. arg1、arg2 - 轻量级数据

// 传递数量、价格等整型数据
msg.arg1 = 2; // 2份炸鸡
msg.arg2 = 99; // 单价99元

这两个是消息的“小口袋”,适合装一些简单的整数,比用对象高效多了!

3. obj - 大件货物

// 传递复杂对象
FoodOrder order = new FoodOrder("炸鸡", 2, "多加辣");
msg.obj = order; // 把整个订单对象塞进去

obj是Message的“后备箱”,什么大件货物都能装,但要注意序列化问题。

4. when - 准时达还是定时送

// 定时消息
handler.sendMessageDelayed(msg, 5000); // 5秒后再发送

这个字段控制消息什么时候被处理,实现延迟操作。

第三章 高级玩法:让Message变身特快专递

Bundle - 超级储物箱

// 如果要传递多个数据,用Bundle更合适
Message msg = Message.obtain();
Bundle bundle = new Bundle();
bundle.putString("food_name", "麻辣小龙虾");
bundle.putInt("quantity", 3);
bundle.putDouble("price", 188.0);
msg.setData(bundle);

Bundle就像给Message配了个多功能行李箱,各种类型的数据都能装。

callback - 带回执的快递

// 消息处理完后执行回调
Message msg = Message.obtain(handler, () -> {
    Log.d("Callback", "消息已妥投,请给五星好评!");
});
第四章 性能优化:别让Message把你内存撑爆

很多新手会犯这个错误:

// 错误示范:疯狂new Message
for (int i = 0; i < 1000; i++) {
    Message msg = new Message(); // 内存杀手!
    handler.sendMessage(msg);
}

正确的打开方式:

// 正确做法:使用对象池
for (int i = 0; i < 1000; i++) {
    Message msg = Message.obtain(); // 从池中获取,复用对象
    handler.sendMessage(msg);
}

为什么推荐Message.obtain()?因为它使用了对象池模式,相当于Message回收站,避免了频繁创建销毁对象的开销。

第五章 实战:打造一个不卡顿的聊天应用

下面我们来个完整的例子,实现一个简易聊天室:

1. 先定义消息类型

public class ChatActivity extends AppCompatActivity {
    private static final int MSG_RECEIVE_TEXT = 1;
    private static final int MSG_RECEIVE_IMAGE = 2;
    private static final int MSG_SEND_SUCCESS = 3;
    private static final int MSG_SEND_FAILED = 4;
    
    private TextView chatDisplay;
    private Handler uiHandler;
    
    // 模拟网络请求的线程
    private Thread networkThread;
}

2. 创建Handler - 消息调度中心

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_chat);
    
    chatDisplay = findViewById(R.id.chat_display);
    
    uiHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what) {
                case MSG_RECEIVE_TEXT:
                    String text = (String) msg.obj;
                    appendMessage("好友: " + text);
                    break;
                    
                case MSG_RECEIVE_IMAGE:
                    Bitmap image = (Bitmap) msg.obj;
                    displayImage(image);
                    break;
                    
                case MSG_SEND_SUCCESS:
                    showToast("发送成功");
                    break;
                    
                case MSG_SEND_FAILED:
                    showToast("发送失败: " + msg.arg1);
                    break;
            }
        }
    };
    
    startNetworkListening();
}

3. 模拟网络线程接收消息

private void startNetworkListening() {
    networkThread = new Thread(() -> {
        try {
            // 模拟接收网络消息
            while (!Thread.currentThread().isInterrupted()) {
                Thread.sleep(2000); // 每2秒收一条消息
                
                Message msg = Message.obtain();
                msg.what = MSG_RECEIVE_TEXT;
                msg.obj = "你好啊!现在是" + new Date();
                
                uiHandler.sendMessage(msg);
            }
        } catch (InterruptedException e) {
            Log.d("Chat", "网络线程结束");
        }
    });
    networkThread.start();
}

4. 发送消息的方法

public void sendMessage(String text) {
    // 立即在UI上显示自己发的消息
    appendMessage("我: " + text);
    
    // 模拟网络发送
    new Thread(() -> {
        try {
            Thread.sleep(1000); // 模拟网络延迟
            
            Message resultMsg = Message.obtain();
            // 90%概率成功,10%概率失败
            if (Math.random() > 0.1) {
                resultMsg.what = MSG_SEND_SUCCESS;
            } else {
                resultMsg.what = MSG_SEND_FAILED;
                resultMsg.arg1 = (int) (Math.random() * 1000); // 错误码
            }
            uiHandler.sendMessage(resultMsg);
            
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
}

5. 界面交互

public void onSendButtonClick(View view) {
    EditText input = findViewById(R.id.message_input);
    String text = input.getText().toString();
    if (!text.isEmpty()) {
        sendMessage(text);
        input.setText("");
    }
}

private void appendMessage(String text) {
    String current = chatDisplay.getText().toString();
    chatDisplay.setText(current + "\n" + text);
}

private void displayImage(Bitmap image) {
    // 显示图片的逻辑
}

private void showToast(String text) {
    Toast.makeText(this, text, Toast.LENGTH_SHORT).show();
}
第六章 那些年我们踩过的坑

坑1:内存泄漏(经典必考!)

// 错误示范:匿名内部类持有外部引用
public class LeakyActivity extends AppCompatActivity {
    private Handler leakyHandler = new Handler() { // 警告:可能引起内存泄漏!
        @Override
        public void handleMessage(Message msg) {
            // 隐式持有LeakyActivity的引用
        }
    };
}

解决方案:

// 使用静态内部类 + 弱引用
public class SafeActivity extends AppCompatActivity {
    private static class SafeHandler extends Handler {
        private final WeakReference<SafeActivity> activityRef;
        
        SafeHandler(SafeActivity activity) {
            activityRef = new WeakReference<>(activity);
        }
        
        @Override
        public void handleMessage(Message msg) {
            SafeActivity activity = activityRef.get();
            if (activity != null) {
                // 处理消息
            }
        }
    }
}

坑2:消息堆积导致卡顿

// 如果消息处理太慢,会导致消息队列堆积
handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        try {
            Thread.sleep(1000); // 每条消息处理1秒 → 灾难!
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
};

解决方案:

// 繁重任务在子线程处理,只把结果用Message发回UI线程
handler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        // 只做轻量的UI更新
        updateUI(msg.obj);
    }
};

// 繁重任务在后台处理
new Thread(() -> {
    Object result = doHeavyWork();
    Message msg = Message.obtain(handler, 0, result);
    handler.sendMessage(msg);
}).start();
第七章 Message的高级用法

1. 定时任务

// 实现轮询
private void startPolling() {
    final int POLL_INTERVAL = 5000; // 5秒
    
    Handler handler = new Handler();
    Runnable pollTask = new Runnable() {
        @Override
        public void run() {
            checkForUpdates();
            handler.postDelayed(this, POLL_INTERVAL);
        }
    };
    handler.postDelayed(pollTask, POLL_INTERVAL);
}

2. 消息优先级

// 紧急消息优先处理
Message normalMsg = Message.obtain();
normalMsg.what = NORMAL_MSG;

Message urgentMsg = Message.obtain();
urgentMsg.what = URGENT_MSG;

handler.sendMessage(normalMsg);
handler.sendMessageAtFrontOfQueue(urgentMsg); // 插队!
总结

看到这里,恭喜你已经从Message新手村毕业了!记住这几个核心要点:

  1. Message是线程间的信使,让UI线程和工作线程各司其职
  2. 善用what字段明确消息意图,代码更清晰
  3. 多用obtain(),少用new Message(),性能更好
  4. 警惕内存泄漏,使用静态Handler+弱引用
  5. 繁重任务别放handleMessage里,否则会阻塞UI

Message机制就像Android开发的交通规则,掌握好了,你的App就能在信息的车水马龙中畅通无阻。现在就去重构你的代码吧,让产品经理对你刮目相看!

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值