Android点将台:传令官[-BroadcastReciver-](使用级)

栏目: IOS · Android · 发布时间: 5年前

内容简介:现在才发现BroadcastReceiver原来这么精简,纯源码才260直接继承Object,没有实现接口,没有家庭背景,可以说是个很简单的类静态使用也就是配置在
1).BroadcastReceiver`静态`使用  
2).BroadcastReceiver`动态`使用   
3).BroadcastReceiver`有序`广播    
4).BroadcastReceiver和`系统`行为的结合  
5).小例子:使用BroadcastReceiver更新音乐播放器进度条
复制代码

2.BroadcastReceiver总览

现在才发现BroadcastReceiver原来这么精简,纯源码才260

直接继承Object,没有实现接口,没有家庭背景,可以说是个很简单的类

Android点将台:传令官[-BroadcastReciver-](使用级)
类名:BroadcastReceiver      父类:Object      修饰:public abstract
实现的接口:[]
包名:android.content   依赖类个数:9
内部类/接口个数:1
源码行数:653       源码行数(除注释):260
属性个数:2       方法个数:36       public方法个数:36
复制代码

一、BroadcastReceiver静态使用

静态使用也就是配置在 AndroidManifest.xml 中配置意图过滤器来匹配

关于intent的相关知识,见前一篇,这里不做解释

Android点将台:传令官[-BroadcastReciver-](使用级)

1.写一个类继承自BroadcastReceiver

/**
 * 作者:张风捷特烈<br></br>
 * 时间:2019/1/21/021:16:53<br></br>
 * 邮箱:1981462002@qq.com<br></br>
 * 说明:谈一个吐司的BroadcastReceiver
 */
class ToastBroadcastReceiver : BroadcastReceiver() {
    /**
     * 接收时调用的方法
     */
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "Toly", Toast.LENGTH_SHORT).show()
    }
}

---->[app/src/main/AndroidManifest.xml]------------------
<receiver android:name=".receiver.receiver.ToastBroadcastReceiver">
    <intent-filter>
        <action android:name="www.toly1994.com.br.toast"/>
        <category android:name="android.intent.category.DEFAULT"/>
    </intent-filter>
</receiver>
复制代码

2.测试的Activity

Android点将台:传令官[-BroadcastReciver-](使用级)
---->[BrActivity#onCreate]------------------
id_btn_send.setOnClickListener {
    val intent = Intent("www.toly1994.com.br.toast")
    sendBroadcast(intent)
}
复制代码

3.静态广播在Android8.0+

intent必须指定广播的component,才有效

---->[BrActivity#onCreate]------------------
id_btn_send.setOnClickListener {
    val intent = Intent("www.toly1994.com.br.toast")
    intent.component = ComponentName(
        "com.toly1994.tolyservice",//项目包名
        "com.toly1994.tolyservice.receiver.receiver.ToastBroadcastReceiver"//广播接收者全类名
    )
    sendBroadcast(intent)
}
复制代码

4.静态广播中的数据获取

广播接收者的onReceive回调中有intent: Intent,你应该明白怎么传数据了吧

Android点将台:传令官[-BroadcastReciver-](使用级)
---->[BrActivity#onCreate]------------------
id_btn_send.setOnClickListener {
    val intent = Intent("www.toly1994.com.br.toast")
    id_et_txt.text
    //为intent添加数据
    intent.putExtra("toast_data", id_et_txt.text.toString())
    intent.component = ComponentName(
        "com.toly1994.tolyservice",//项目包名
        "com.toly1994.tolyservice.receiver.receiver.ToastBroadcastReceiver"//广播接收者全类名
    )
    sendBroadcast(intent)
}

---->[ToastBroadcastReceiver]------------------
/**
 * 接收时调用的方法
 */
override fun onReceive(context: Context, intent: Intent) {
    val data = intent.getStringExtra("toast_data")
    //data?:"NO MSG"表示如果data是空,就取"NO MSG"
    Toast.makeText(context, data?:"NO MSG", Toast.LENGTH_SHORT).show()
}
复制代码

5.BroadcastReciver有什么用?

感觉从上面来看,BroadcastReciver的onReceive确实耦合性非常低

外部只需要用intent和context.sendBroadcast就能触发它

但似乎BroadcastReciver也没有太大的亮点,作用平平

为了说明他的亮点,现在我们新建一个app: Anotherapp

Android点将台:传令官[-BroadcastReciver-](使用级)
Android点将台:传令官[-BroadcastReciver-](使用级)

可以发现在另一个app里也能正常使用这个广播

这就有点意思了,我在A项目中写了一个类,它的方法可以在B项目中触发

这就是静态广播厉害的地方,也是我第一次接触的跨进程通信

(这说明解耦到一定的境界,就天下与我同,然而我将无处不在,手动滑稽)

Android点将台:传令官[-BroadcastReciver-](使用级)
Android点将台:传令官[-BroadcastReciver-](使用级)

二、BroadcastReceiver动态使用

BroadcastReceiver动态使用分为注册和注销, 不需要在AndroidManifest.xml注册
只有在注册后和注销前的时间段才能使用,否则广播无效(即onReceive方法不会掉)

Android点将台:传令官[-BroadcastReciver-](使用级)
Android点将台:传令官[-BroadcastReciver-](使用级)

1.注册广播与发送消息

/**
 * 注册广播
 */
private fun register() {
    val filter = IntentFilter()//创建意图过滤器
    filter.addAction("www.toly1994.com.br.toast2")//添加意图
    mReceiver = Toast2BroadcastReceiver()//创建 Toast2BroadcastReceiver
    registerReceiver(mReceiver, filter)//注册
}

/**
 * 发送广播
 */
private fun sendMsg() {
    val intent = Intent()
    intent.action = "www.toly1994.com.br.toast2"
    intent.putExtra("toast_data", id_et_txt.text.toString())
    sendBroadcast(intent)
}
复制代码

2.注销广播

你说,哥就不注销怎么样?---答:异常呗

如果不注销,崩了一个异常,源码也好心提醒你要 unregisterReceiver

2019-01-22 14:10:50.940 4892-4892/com.toly1994.tolyservice E/ActivityThread:
Activity com.toly1994.tolyservice.receiver.BrActivity has leaked IntentReceiver
com.toly1994.tolyservice.receiver.receiver.Toast2BroadcastReceiver@32500e2 that
was originally registered here. Are you missing a call to unregisterReceiver()?

        at android.app.LoadedApk$ReceiverDispatcher.<init>(LoadedApk.java:1333)
        at android.app.LoadedApk.getReceiverDispatcher(LoadedApk.java:1114)
        at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1405)
        at android.app.ContextImpl.registerReceiver(ContextImpl.java:1378)
        at android.app.ContextImpl.registerReceiver(ContextImpl.java:1366)
        at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:603)
        at com.toly1994.tolyservice.receiver.BrActivity.onCreate(BrActivity.kt:27)
复制代码
/**
  * 注销广播
  */
 private fun unRegister() {
     unregisterReceiver(mReceiver);
 }

override fun onDestroy() {
    super.onDestroy()
    unRegister()//注销广播
}
复制代码

3.静态和动态广播的区别

你可能会说:就一个200多行的类,还搞那么多事...

动态注册的广播
|---优势:可以自由的控制注册和取消,有很大的灵活性。
|---劣势:只有在注册之后才能起作用,在Activity的onDestroy后如果未被注销,会报异常
----所以动态注册的广播存活时间最长也就约等于Activity的生命周期长度

静态注册的广播
|---优势:不受程序是否启动的约束,随时使用
|---劣势:优势同样也是劣势,无法取消,什么时候都能用
复制代码

三、BroadcastReceiver有序广播

先讲个场景:男孩(Boy)说:一块石头的价值 1元

之后将石头给了雕刻家,并将预期的价值1000元传递给雕刻家

之后雕刻家将石头给了宝石家,并将预期的价值10W元传递给宝石家

之后宝石家将石头给了收藏家,并将预期的价值100W元传递给收藏家

收藏家向外称城自己的宝石价值100W

1.有序广播(没有指定顺序时,按注册顺序)

Android点将台:传令官[-BroadcastReciver-](使用级)
Android点将台:传令官[-BroadcastReciver-](使用级)

1.1:四个广播接收者

/**
 * 作者:张风捷特烈<br></br>
 * 时间:2019/1/21/021:16:53<br></br>
 * 邮箱:1981462002@qq.com<br></br>
 * 说明:男孩
 */
class BoyBReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, 
            "男孩:$resultData",//[1]获取结果并展示
            Toast.LENGTH_LONG
        ).show()
//      abortBroadcast();//[2]终止广播
        resultData = "价值1000元" //[3]传递数据---给下一个广播
    }
}

/**
 * 说明:雕刻家
 */
class GraverBReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "雕刻家:$resultData", //[1]获取结果并展示
            Toast.LENGTH_LONG).show()
//      abortBroadcast();//[2]终止广播
        resultData = "价值10W元"//[3]传递数据---给下一个广播
    }
}

/**
 * 说明:宝石家
 */
class RubyManBReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "宝石家:$resultData",
            Toast.LENGTH_LONG).show()
//      abortBroadcast();//[2]终止广播
        resultData = "价值100W元"//[3]传递数据---给下一个广播
    }
}

/**
 * 说明:收藏家
 */
class CollectorBReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context,
            "收藏家:$resultData", //获取结果并展示
            Toast.LENGTH_LONG).show()
    }
}
复制代码

1.2.动态注册并发送有序广播

/**
 * 注册广播
 */
private fun register() {
    val filter = IntentFilter()//创建意图过滤器
    filter.addAction("www.toly1994.com.br.toast2")//添加意图
    boyReceiver = BoyBReceiver()
    graverReceiver = GraverBReceiver()
    rubyManReceiver = RubyManBReceiver()
    registerReceiver(boyReceiver, filter)//注册
    registerReceiver(graverReceiver, filter)//注册
    registerReceiver(rubyManReceiver, filter)//注册
}


/**
 * 发送有序广播
 */
private fun sendOrder() {
    val intent = Intent()
    intent.action = "www.toly1994.com.br.toast2"
    val collectorBReceiver = CollectorBReceiver()
    sendOrderedBroadcast(
        intent, null,
        collectorBReceiver, null, 1,
        "价值1元", null
    )
}

/**
 * 注销广播
 */
private fun unRegister() {
    unregisterReceiver(boyReceiver)
    unregisterReceiver(graverReceiver)
    unregisterReceiver(rubyManReceiver)
}
复制代码

2.中途终止广播有序广播

Android点将台:传令官[-BroadcastReciver-](使用级)
Android点将台:传令官[-BroadcastReciver-](使用级)
* 说明:雕刻家
 */
class GraverBReceiver : BroadcastReceiver() {

    /**
     * 接收时调用的方法
     */
    override fun onReceive(context: Context, intent: Intent) {
        Toast.makeText(context, "雕刻家:$resultData", //[1]获取结果并展示
            Toast.LENGTH_LONG).show()
      abortBroadcast();//[2]终止广播
        resultData = "价值10W元"//[3]传递数据---给下一个广播
    }
}
复制代码

3.自定义广播顺序

Android点将台:传令官[-BroadcastReciver-](使用级)
Android点将台:传令官[-BroadcastReciver-](使用级)
/**
  * 注册广播
  */
 private fun register() {
     boyReceiver = BoyBReceiver()
     val boyFilter = IntentFilter()//创建意图过滤器
     boyFilter.addAction("www.toly1994.com.br.toast2")//添加意图
     boyFilter.priority = 10//指定过滤器优先级
     
     graverReceiver = GraverBReceiver()
     val graverFilter = IntentFilter()//创建意图过滤器
     graverFilter.addAction("www.toly1994.com.br.toast2")//添加意图
     graverFilter.priority = 20//指定过滤器优先级
     
     rubyManReceiver = RubyManBReceiver()
     val rubyManFilter = IntentFilter()//创建意图过滤器
     rubyManFilter.addAction("www.toly1994.com.br.toast2")//添加意图
     rubyManFilter.priority = 21//指定过滤器优先级
     
     registerReceiver(boyReceiver, boyFilter)//注册
     registerReceiver(graverReceiver, graverFilter)//注册
     registerReceiver(rubyManReceiver, rubyManFilter)//注册
 }
复制代码

上面是BroadcastReceiver有序广播的动态注册形式的代码,

静态注册在AndroidManifest.xml里配置类似,就不废话了

还有一点注意的是sendOrderedBroadcast方法调用时传入的BroadcastReceiver

为最后调用的BroadcastReceiver, 不需要注册!

四、广播和系统行为的结合

以下皆使用动态注册,很多系统级的行为静态注册都是无效的

1.开屏锁屏广播

Android点将台:传令官[-BroadcastReciver-](使用级)
/**
 * 作者:张风捷特烈<br></br>
 * 时间:2019/1/22/022:16:43<br></br>
 * 邮箱:1981462002@qq.com<br></br>
 * 说明:开屏锁屏广播
 */
class ScreenBReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        //[1]获取到当前广播的事件类型
        val action = intent.action
        //[2]对当前广播事件类型做一个判断
        if ("android.intent.action.SCREEN_OFF" == action) {
            Log.e(TAG, "屏幕锁屏了")
        } else if ("android.intent.action.SCREEN_ON" == action) {
            Log.e(TAG, "屏幕解锁了")
        }
    }

    companion object {
        private const val TAG = "ScreenBReceiver"
    }
}

---->[ScreenBrActivity使用方法]------------------------------------
/**
 * 动态的去注册屏幕解锁和锁屏的广播
 */
private fun register() {
    // [1]动态的去注册屏幕解锁和锁屏的广播
    mScreenReceiver = ScreenBReceiver()
    // [2]创建intent-filter对象
    val filter = IntentFilter()
    // [3]添加要注册的action
    filter.addAction("android.intent.action.SCREEN_OFF")
    filter.addAction("android.intent.action.SCREEN_ON")
    // [4]注册广播接收者
    registerReceiver(mScreenReceiver, filter)
}

复制代码

2.短信监听广播

注意权限: <uses-permission android:name="android.permission.RECEIVE_SMS"/>
<uses-permission android:name="android.permission.READ_SMS"/>

Android点将台:传令官[-BroadcastReciver-](使用级)
/**
 * 作者:张风捷特烈<br></br>
 * 时间:2019/1/22/022:16:43<br></br>
 * 邮箱:1981462002@qq.com<br></br>
 * 说明:短信监听广播
 */
class SMSBReceiver : BroadcastReceiver() {
    //当短信到来的时候 就会执行这个方法
    override fun onReceive(context: Context, intent: Intent) {
        //[1]获取发短信送的号码  和内容
        val objects = intent.extras!!.get("pdus") as Array<*>
        val format = intent.getStringExtra("format")
        for (pdu in objects) {
            //[2]获取smsmessage实例
            val smsMessage =
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                    SmsMessage.createFromPdu(pdu as ByteArray, format)
                } else {
                    SmsMessage.createFromPdu(pdu as ByteArray)
                }
            //[3]获取发送短信的内容
            val body = smsMessage.messageBody
            val date = Date(smsMessage.timestampMillis)//时间
            val format = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA)
            //[4]获取发送者
            val address = smsMessage.originatingAddress
            val receiveTime = format.format(date)
            Log.e("SMSBReceiver", "body:$body---$address---$receiveTime")
        }
    }
}

---->[SMSBrActivity使用方法]------------------------------------
/**
 * 动态注册短信广播接收者
 */
private fun register() {
    //注册短信广播接收者
    val smsFilter = IntentFilter()
    smsFilter.addAction("android.provider.Telephony.SMS_RECEIVED")
    mSmsReceiver = SMSBReceiver()
    registerReceiver(mSmsReceiver, smsFilter)
}
复制代码

3.监听电量变化广播

这里传入一个Textview用于显示电量

Android点将台:传令官[-BroadcastReciver-](使用级)
/**
 * 作者:张风捷特烈<br></br>
 * 时间:2019/1/22/022:16:43<br></br>
 * 邮箱:1981462002@qq.com<br></br>
 * 说明:监听电量变化
 */
class BatteryBReceiver(private val mTV:TextView ) : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        // 当前电量
        val currLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0)
        // 总电量
        val total = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 1)
        val percent = currLevel * 100 / total
        Log.e("BatteryBReceiver", "battery: $percent%")
        mTV.setTextColor(randomRGB())
        mTV.text = "battery: $percent%"
    }

    /**
     * 返回随机颜色
     *
     * @return 随机颜色
     */
    fun randomRGB(): Int {
        val random = Random()
        val r = 30 + random.nextInt(200)
        val g = 30 + random.nextInt(200)
        val b = 30 + random.nextInt(200)
        return Color.rgb(r, g, b)
    }
}

---->[BatteryBrActivity使用方法]------------------------------------
/**
 * 动态电量广播接收者
 */
private fun register() {
    val filter = IntentFilter()
    filter.addAction(Intent.ACTION_BATTERY_CHANGED)
    mBatteryChangeReceiver = BatteryBReceiver(id_tv_info)
    registerReceiver(mBatteryChangeReceiver, filter)
}
复制代码

4.app安装/卸载改变时广播监听

Android点将台:传令官[-BroadcastReciver-](使用级)
/**
 * 作者:张风捷特烈<br></br>
 * 时间:2019/1/22/022:16:43<br></br>
 * 邮箱:1981462002@qq.com<br></br>
 * 说明:app安装/卸载改变时广播监听
 */
class AppBReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        val action = intent.action
        val uri = intent.data
        if (action == "android.intent.action.PACKAGE_ADDED") {
            Log.e("AppBReceiver", uri.toString() + "被安装了")
        } else if (action == "android.intent.action.PACKAGE_REPLACED") {
            Log.e("AppBReceiver", uri.toString() + "被更新了")
        } else if (action == "android.intent.action.PACKAGE_REMOVED") {
            Log.e("AppBReceiver", uri.toString() + "被卸载了")
        }
    }
}

---->[AppBrActivity使用方法]------------------------------------
 /**
  * 动态注册app安装/卸载改变时广播监听
  */
 private fun register() {
     val filter = IntentFilter()
     filter.addAction("android.intent.action.PACKAGE_ADDED")
     filter.addAction("android.intent.action.PACKAGE_REPLACED")
     filter.addAction("android.intent.action.PACKAGE_REMOVED")
     filter.addDataScheme("package")
     mAppReceiver = AppBReceiver()
     registerReceiver(mAppReceiver, filter)
 }
 
 //但是貌似这个用动态注册并不怎么有用
 //因为一般卸载,安装都不是在当前Activity中,加了一下静态,便可以了
 //注意,在测试中发现,只加静态的配置也是无效的
 <receiver android:name=".receiver.receiver.AppBReceiver">
    <intent-filter >
        <action android:name="android.intent.action.PACKAGE_ADDED"/>
        <action android:name="android.intent.action.PACKAGE_REPLACED"/>
        <action android:name="android.intent.action.PACKAGE_REMOVED"/>
        <data android:scheme="package"/>
    </intent-filter>
</receiver>
复制代码

还有一些系统行为套路都差不多,需要的时候查查对应的action就行了

五、使用广播更新音乐进度条

绝命暗杀官[-Service-] 中实现过一个音乐播放条,其中音乐的播放进度是靠Handler+回调实现的

BroadcastReciver本职就在于通知,在这里用BroadcastReciver实现 音乐的播放进度

Android点将台:传令官[-BroadcastReciver-](使用级)
---->[常量类]-----------------------------
public class Cons {
    //广播更新进度--数据
    public static final String DATA_MUSIC_POSITION = "data_music_position";
    //广播更新进度--Action
    public static final String ACTION_UPDATE = "action_update";
}

---->[MusicPlayerWithBrStub]-----------------------------
mTimer.schedule(new TimerTask() {
    @Override
    public void run() {
        if (mPlayer.isPlaying()) {
            int pos = mPlayer.getCurrentPosition();
            int duration = mPlayer.getDuration();
            //发送广播更新进度
    --->    Intent intent = new Intent(Cons.ACTION_UPDATE);
    --->    int progress = (int) (pos * 100.f / duration);
    --->    intent.putExtra(Cons.DATA_MUSIC_POSITION, progress);
    --->    mContext.sendBroadcast(intent);
        }
    }
}, 0, 1000);

 ---->[MusicActivity#registerReceiver]-----------------------------
|-- 这里我新建一个类,你也可以直接在Activity中建个内部类,要简单些
public class UpdateReceiver extends BroadcastReceiver {
    @Nullable
    private ProgressView progressView;
    public UpdateReceiver(@Nullable ProgressView progressView) {
        this.progressView = progressView;
    }
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Cons.ACTION_UPDATE.equals(intent.getAction())) {
            int progress = intent.getIntExtra(Cons.DATA_MUSIC_POSITION, 0);
            if (progressView != null) {
        --->    progressView.setProgress(progress);
            }
        }
    }
}

/**
 * 注册广播
 */
---->[MusicActivity#registerReceiver]-----------------------------
private fun registerReceiver() {
    mReceiver = UpdateReceiver(id_pv_pre)
    val filter = IntentFilter()
    filter.addAction(Cons.ACTION_UPDATE)
    registerReceiver(mReceiver, filter)//注册
}

---->[MusicActivity#onDestroy]-----------------------------
override fun onDestroy() {
    super.onDestroy()
    unregisterReceiver(mReceiver)//注销广播
    mMusicPlayer.release()
}
复制代码

其实也就是发广播-->收广播-->操作,用起来并不困难

至于BroadcastReciver的源码,暂时就不读了(读了一下,没怎么读得通...),以后再开篇吧!

后记:捷文规范

1.本文成长记录及勘误表

项目源码 日期 附录
V0.1--无 2018-2-27

发布名: Android点将台:传令官[-BroadcastReciver-]
捷文链接: juejin.im/post/5c469b…

2.更多关于我

笔名 QQ 微信
张风捷特烈 1981462002 zdl1994328

我的github: github.com/toly1994328

我的简书: www.jianshu.com/u/e4e52c116…

我的简书: www.jianshu.com/u/e4e52c116…

个人网站:www.toly1994.com

3.声明

1----本文由张风捷特烈原创,转载请注明

2----欢迎广大编程爱好者共同交流

3----个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正

4----看到这里,我在此感谢你的喜欢与支持

Android点将台:传令官[-BroadcastReciver-](使用级)

以上所述就是小编给大家介绍的《Android点将台:传令官[-BroadcastReciver-](使用级)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

PHP 6与MySQL 5基础教程

PHP 6与MySQL 5基础教程

(美)厄尔曼 / 陈宗斌 等 / 人民邮电出版社 / 2008-11-1 / 65.00元

本书是一部经典的入门级著作,采用基于任务的方法来讲授PHP和MySQL,使用大量图片指导读者深入学习语言,并向读者展示了如何构造动态Web站点。书中用简洁、直观的步骤和讲解提供了学习任务和概念的最快方式。通过学习本书,读者可以快速、高效地掌握PHP和MySQL,成为一位构建Web站点的高手。 本书适合初中级Web应用开发和设计人员阅读。 本书是讲述PHP和MySQL技术的畅销书,以深入......一起来看看 《PHP 6与MySQL 5基础教程》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

MD5 加密
MD5 加密

MD5 加密工具