Android摸索记录--HMS消息推送

208 阅读3分钟

原文转载自道招网

我一直都对消息提醒内容类功能有点执念的,因为我希望自己能有各种眼线,能去四面八方获取我关注的内容,同时那些内容更新了能够及时通知给我,因此我比较喜欢捣鼓爬虫、消息提醒、消息推送、闹钟、日历之类的东西。

自己作为一个前端,尝试过几个前端的方式来实现消息推送的功能。

尝试一:PWA

PWA是service-worker的,可以用来push消息的,这个需要借助于浏览器,现在主流的是chrome,但是走谷歌的推送的话,如果手机没有科学上网就收不到消息。用firfox安装PWA的话效果还好一点,至少没有被墙。

鉴于感觉目前PWA已经不温不火了,之前的Instagram都在PWA推送用户使用app了,再加上如果浏览器进程被杀掉的话,消息推送应该也会受到影响,所以在很早的时候给道招网加入了PWA功能后简单用过一段时间后就没有继续深入尝试这个方案了。

尝试二:微信小程序

自己接入微信的access_token以及模板消息后也使用过一段时间,但是微信的很多高级api都需要企业认证的账号才能使用,再加上微信的进程也可能被手机系统给杀掉,当然劝退我的还是企业认证这个门槛。

尝试三:APP

为什么不搞个app呢,自己不是很喜欢app的吗?自己开始喜欢搞编程是为了什么,不就是因为看到了Android,充满了好奇吗?

说干就干,就走这条对前端来说更难的路,demo走起,先接入华为推送,作为原荣耀系的手机用户,接入华为推送这个系统级的功能,不用太担心进程被杀的问题。

接入HMS

引入包

项目级别build.gradle(只列举了HMS相关内容)


buildscript {

    ext.kotlin_version = '1.4.10'

    repositories {

        maven { url 'https://developer.huawei.com/repo/' }

    }

    dependencies {

        classpath 'com.huawei.agconnect:agcp:1.6.0.300'

    }

}

allprojects {

    repositories {

        google()

        maven { url 'https://developer.huawei.com/repo/' }

    }

}

app级别build.gradle(只列举了HMS相关内容)


plugins {

    id 'com.android.application'

    id 'kotlin-android'

    id 'com.huawei.agconnect'

}

dependencies {

    // HMS Push kit

    implementation 'com.huawei.hms:push:6.3.0.302'

}

编码

我是在fragment的编码的


import com.huawei.hms.aaid.HmsInstanceId

import com.huawei.hms.common.ApiException

import com.huawei.hms.push.HmsMessaging

import com.huawei.hms.push.HmsProfile

class DashboardFragment : Fragment(), View.OnClickListener {

    companion object {

        private const val TAG: String = "DashboardFragment"

        private const val GET_AAID = 1

        private const val DELETE_AAID = 2

        private const val CODELABS_ACTION: String = "com.daozhao.push.action"

    }

    override fun onCreateView(

        inflater: LayoutInflater,

        container: ViewGroup?,

        savedInstanceState: Bundle?

    ): View? {

        dashboardViewModel =

        ViewModelProvider(this).get(DashboardViewModel::class.java)

        _binding = FragmentDashboardBinding.inflate(inflater, container, false)

        root = binding.root

        FirebaseApp.initializeApp(requireContext()!!)

        return root

    }

    fun showLog(log: String?) {

        activity?.runOnUiThread {

            val textView = root!!.findViewById<TextView?>(R.id.text_dashboard)

            textView.text = log

        }

    }

    private fun getToken() {

        showLog("getToken:begin")

        object : Thread() {

            override fun run() {

                try {

                    // read from agconnect-services.json

                    val appId = "xxx"

                    val token = HmsInstanceId.getInstance(mContext).getToken(appId, "HCM")

                    storeTokenProfile(token)

                } catch (e: ApiException) {

                    Log.e(TAG, "get token failed, $e")

                    showLog("get token failed, $e")

                }

            }

        }.start()

    }

    private fun storeTokenProfile(token: String?) {

        Log.i(TAG, "get token:$token")

        showLog("get token:$token")

        if (!TextUtils.isEmpty(token)) {

            // 将token存储在自己的服务器,方便后期推送

            sendRegTokenToServer(token);

            // 添加当前设备上的用户与应用的关系。

            val hmsProfileInstance: HmsProfile = HmsProfile.getInstance(mContext);

            if (hmsProfileInstance.isSupportProfile) {

                hmsProfileInstance.addProfile(HmsProfile.CUSTOM_PROFILE, "9105385871708200535")

                .addOnCompleteListener { task ->

                    // 获取结果

                    if (task.isSuccessful){

                        Log.i(TAG, "add profile success.")

                    } else{

                        Log.e(TAG, "add profile failed: " + task.exception.message)

                    }

                }

            }

        }

    }

    private fun sendRegTokenToServer(token: String?) {

        Log.i(TAG, "sending token to server. token:$token")

    // 借助Firebase获取一个唯一的id作为设备标识

    FirebaseInstallations.getInstance().id.addOnCompleteListener { it ->

        run {

            if (it.isComplete) {

                var uuid = it.result.toString();

                showLog("uuid complete " + uuid);

                var formBody: FormBody.Builder = FormBody.Builder();

                formBody.add("id", uuid);

                formBody.add("pushToken",token);

                val request: Request = Request.Builder()

                .url("https://example.com/HMS/storePushToken")

                .post(formBody.build())

                .build()

                val call: Call = client.newCall(request)

                    call.enqueue(object : Callback {

                        override fun onFailure(call: Call?, e: IOException?) {

                            showLog("fetch failed " + uuid)

                            Log.e(TAG, "fetch failed " + uuid);

                        }

                        @Throws(IOException::class)

                        override fun onResponse(call: Call?, response: Response) {

                            val result: String = response.body().string()

                            showLog("fetch success " + uuid)

                            Log.i(TAG, "fetch success " + uuid);

                        }

                    })

                }

            }

        }

    }

}

后面我们就可根据这个token进行推送消息了,消息可以分成通知消息和透传消息,服务端的发送消息的消息体如下

通知栏消息体示例:


{

    "validate_only": false,

    "message": {

        "android": {

            "notification": {

                "title": "test title",

                "body": "test body",

                    "click_action": {

                        "type": 3

                    }

                }

        },

        "token": ["pushtoken1"]

    }

}

透传消息体示例:


{

    "validate_only": false,

    "message": {

        "data": "{'param1':'value1','param2':'value2'}",

        "token": ["pushtoken1"]

    }

}

更多细节参考官网文档发送下行消息

文档中的pushToken就是在getToken中获取的内容,这样我们就发送消息至手机客户端了。

如果来不及开发自己的服务端,可以先用官网给的工具来发送调试消息

file

file

file