快速集成 JPush(极光) 推送整理

本篇博客重新整理一下关于集成极光推送的一些方法步骤。

这里是 极光官方SDK集成文档

在集成 极光推送 时,我们需要注意的是 packageName 需要和 applicationId 保持一致,否则极光是没法正常推送的。当然关于修改应用包名(点击查看),我很早整理的一个博客就有操作步骤,这里就不过多叙述。

接下来,就来看我们如何快速的集成 JPush .

依赖配置

(在以下配置中,如有异常,可参照 JPush Android SDK集成指南 进行调整)

添加以下代码至 build.gradle(app) 中

android {
    ......
    defaultConfig {
        applicationId "com.xxx.xxx" //JPush上注册的包名.
        ......

        ndk {
            //选择要添加的对应cpu类型的.so库。
            abiFilters 'armeabi', 'armeabi-v7a', 'arm64-v8a'
            // 还可以添加 'x86', 'x86_64', 'mips', 'mips64'
        }

        manifestPlaceholders = [
            JPUSH_PKGNAME : applicationId,
            JPUSH_APPKEY : "你的appkey", //JPush上注册的包名对应的appkey.
            JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.
        ]
        ......
    }
    ......
}

dependencies {
    ......

    implementation 'cn.jiguang.sdk:jpush:3.1.5'  // 此处以JPush 3.1.5 版本为例。
    implementation 'cn.jiguang.sdk:jcore:1.2.3'  // 此处以JCore 1.2.3 版本为例。
    ......
}

AndroidManifests.xml 示例代码

上面的依赖配置完成之后,我们该到 manifests 文件中进行权限、服务等注册操作。

    <!-- Required -->
    <permission
        android:name="${applicationId}.permission.JPUSH_MESSAGE"
        android:protectionLevel="signature" />

    <!-- Required  一些系统要求的权限,如访问网络等-->
    <uses-permission android:name="${applicationId}.permission.JPUSH_MESSAGE" />
    <uses-permission android:name="android.permission.RECEIVE_USER_PRESENT" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_SETTINGS" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />

    <!-- Optional. Required for location feature -->
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" /> <!-- 用于开启 debug 版本的应用在6.0 系统上 层叠窗口权限 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.GET_TASKS" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <!--以上权限如果重复,可去除-->

    ........

     <!-- Rich push 核心功能 since 2.0.6-->
    <activity
        android:name="cn.jpush.android.ui.PopWinActivity"
        android:exported="false"
        android:theme="@style/MyDialogStyle" />

    <!-- Required SDK核心功能-->
    <activity
        android:name="cn.jpush.android.ui.PushActivity"
        android:configChanges="orientation|keyboardHidden"
        android:exported="false"
        android:theme="@android:style/Theme.NoTitleBar">
        <intent-filter>
            <action android:name="cn.jpush.android.ui.PushActivity" />

            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="${applicationId}" />
        </intent-filter>
    </activity>
        <!-- Required SDK 核心功能-->
        <!-- 可配置android:process参数将PushService放在其他进程中 -->
        <service
            android:name="cn.jpush.android.service.PushService"
            android:enabled="true"
            android:exported="false">
            <intent-filter>
                <action android:name="cn.jpush.android.intent.REGISTER" />
                <action android:name="cn.jpush.android.intent.REPORT" />
                <action android:name="cn.jpush.android.intent.PushService" />
                <action android:name="cn.jpush.android.intent.PUSH_TIME" />
            </intent-filter>
        </service>
        <!-- since 3.0.9 Required SDK 核心功能-->
        <provider
            android:name="cn.jpush.android.service.DataProvider"
            android:authorities="${applicationId}.DataProvider"
            android:exported="false" />

        <!-- since 1.8.0 option 可选项。用于同一设备中不同应用的JPush服务相互拉起的功能。 -->
        <!-- 若不启用该功能可删除该组件,将不拉起其他应用也不能被其他应用拉起 -->
        <service
            android:name="cn.jpush.android.service.DaemonService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="cn.jpush.android.intent.DaemonService" />
                <category android:name="${applicationId}" />
            </intent-filter>

        </service>
        <!-- since 3.1.0 Required SDK 核心功能-->
        <provider
            android:name="cn.jpush.android.service.DownloadProvider"
            android:authorities="${applicationId}.DownloadProvider"
            android:exported="true" />
        <!-- Required SDK核心功能-->
        <receiver
            android:name="cn.jpush.android.service.PushReceiver"
            android:enabled="true"
            android:exported="false">
            <intent-filter android:priority="1000">
                <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_PROXY" />   <!--Required  显示通知栏 -->
                <category android:name="${applicationId}" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.USER_PRESENT" />
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
            </intent-filter>
            <!-- Optional -->
            <intent-filter>
                <action android:name="android.intent.action.PACKAGE_ADDED" />
                <action android:name="android.intent.action.PACKAGE_REMOVED" />

                <data android:scheme="package" />
            </intent-filter>
        </receiver>

        <!-- Required SDK核心功能-->
        <receiver
            android:name="cn.jpush.android.service.AlarmReceiver"
            android:exported="false" />

        <!-- User defined.  For test only  用户自定义的广播接收器-->
        <receiver
            android:name=".jpush.MyReceiver"
            android:enabled="true"
            android:exported="false">
            <intent-filter>
                <action android:name="cn.jpush.android.intent.REGISTRATION" /> <!--Required  用户注册SDK的intent-->
                <action android:name="cn.jpush.android.intent.MESSAGE_RECEIVED" /> <!--Required  用户接收SDK消息的intent-->
                <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED" /> <!--Required  用户接收SDK通知栏信息的intent-->
                <action android:name="cn.jpush.android.intent.NOTIFICATION_OPENED" /> <!--Required  用户打开自定义通知栏的intent-->
                <action android:name="cn.jpush.android.intent.CONNECTION" /><!-- 接收网络变化 连接/断开 since 1.6.3 -->
                <category android:name="${applicationId}" />
            </intent-filter>
        </receiver>

        <!-- User defined.  For test only  用户自定义接收消息器,3.0.7开始支持,目前新tag/alias接口设置结果会在该广播接收器对应的方法中回调-->
        <!--<receiver android:name="com.example.jpushdemo.MyJPushMessageReceiver">-->
        <!--<intent-filter>-->
        <!--<action android:name="cn.jpush.android.intent.RECEIVE_MESSAGE" />-->
        <!--<category android:name="${applicationId}"></category>-->
        <!--</intent-filter>-->
        <!--</receiver>-->
        <!-- Required  . Enable it you can get statistics data with channel -->
        <meta-data
            android:name="JPUSH_CHANNEL"
            android:value="developer-default" />
        <meta-data
            android:name="JPUSH_APPKEY"
            android:value="${JPUSH_APPKEY}" /> <!--  </>值来自开发者平台取得的AppKey-->

以上代码需要注意的是,一个 MyReceiverMyJPushMessageReceiver 这两个中,MyReceiver 是用来接收 JPush 的一些官方配置的推送内容。
MyJPushMessageReceiver 是从 3.0.7 版本之后,用来接收用户定义了 tag/alias 之后的相关推送信息。

消息接收

这里 MyJPushMessageReceiver 目前还没怎么用到,我就先整理一下 MyReceiver,如以下代码:

class MyReceiver : BroadcastReceiver() {
    private val TAG = "JIGUANG-Example"

    override fun onReceive(context: Context, intent: Intent?) {
        if (intent != null) {
            try {
                val bundle = intent.extras
                LvLog.d(TAG, "[MyReceiver] onReceive - " + intent.action + ", extras: " + printBundle(bundle))

                if (JPushInterface.ACTION_REGISTRATION_ID == intent.action) {
                    val regId = bundle!!.getString(JPushInterface.EXTRA_REGISTRATION_ID)
                    LvLog.d(TAG, "[MyReceiver] 接收Registration Id : " + regId!!)
                    //send the Registration Id to your server...

                } else if (JPushInterface.ACTION_MESSAGE_RECEIVED == intent.action) {
                    LvLog.d(TAG, "[MyReceiver] 接收到推送下来的自定义消息: " + bundle!!.getString(JPushInterface.EXTRA_MESSAGE)!!)
                    processCustomMessage(context, bundle)

                } else if (JPushInterface.ACTION_NOTIFICATION_RECEIVED == intent.action) {
                    LvLog.d(TAG, "[MyReceiver] 接收到推送下来的通知")
                    val notifactionId = bundle!!.getInt(JPushInterface.EXTRA_NOTIFICATION_ID)
                    LvLog.d(TAG, "[MyReceiver] 接收到推送下来的通知的ID: " + notifactionId)
                    if (Utils.isAppRunningForeground(context) && Utils.isOpenScreen(context)) {
                        // 如果应用在前台,并且屏幕正在高亮状态(未锁屏)。
                        JPushInterface.clearAllNotifications(context)
                    }
                    // 发送本地通知
//                    LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(Code.ACTION_MAIN_FILETER))

                } else if (JPushInterface.ACTION_NOTIFICATION_OPENED == intent.action) {
                    LvLog.d(TAG, "[MyReceiver] 用户点击打开了通知")

                    //打开自定义的Activity(需要配置 SingleTask 模式)
                    val i = Intent(context, MainActivity::class.java)
                    i.putExtras(bundle!!)
                    context.startActivity(i)

                } else if (JPushInterface.ACTION_RICHPUSH_CALLBACK == intent.action) {
                    LvLog.d(TAG, "[MyReceiver] 用户收到到RICH PUSH CALLBACK: " + bundle!!.getString(JPushInterface.EXTRA_EXTRA)!!)
                    //在这里根据 JPushInterface.EXTRA_EXTRA 的内容处理代码,比如打开新的Activity, 打开一个网页等..

                } else if (JPushInterface.ACTION_CONNECTION_CHANGE == intent.action) {
                    val connected = intent.getBooleanExtra(JPushInterface.EXTRA_CONNECTION_CHANGE, false)
                    LvLog.d(TAG, "[MyReceiver]" + intent.getAction() + " connected state change to " + connected)
                } else {
                    LvLog.d(TAG, "[MyReceiver] Unhandled intent - " + intent.action!!)
                }
            } catch (e: Exception) {

            }
        }

    }

    // 打印所有的 intent extra 数据
    private fun printBundle(bundle: Bundle): String {
        val sb = StringBuilder()
        for (key in bundle.keySet()) {
            if (key == JPushInterface.EXTRA_NOTIFICATION_ID) {
                sb.append("\nkey:" + key + ", value:" + bundle.getInt(key))
            } else if (key == JPushInterface.EXTRA_CONNECTION_CHANGE) {
                sb.append("\nkey:" + key + ", value:" + bundle.getBoolean(key))
            } else if (key == JPushInterface.EXTRA_EXTRA) {
                if (TextUtils.isEmpty(bundle.getString(JPushInterface.EXTRA_EXTRA))) {
                    LvLog.i(TAG, "This message has no Extra data")
                    continue
                }

                try {
                    val json = JSONObject(bundle.getString(JPushInterface.EXTRA_EXTRA))
                    val it = json.keys()

                    while (it.hasNext()) {
                        val myKey = it.next()
                        sb.append("\nkey:" + key + ", value: [" +
                                myKey + " - " + json.optString(myKey) + "]")
                    }
                } catch (e: JSONException) {
                    LvLog.e(TAG, "Get message extra JSON error!")
                }

            } else {
                sb.append("\nkey:" + key + ", value:" + bundle.getString(key))
            }
        }
        return sb.toString()
    }

    //send msg to MainActivity
    private fun processCustomMessage(context: Context, bundle: Bundle) {
        // 判断当前应用是否在前台运行
        if (!Utils.isAppRunningForeground(context)) {
            val message = bundle.getString(JPushInterface.EXTRA_MESSAGE)
            val content = JSONObject(message).getString("content")
            NotificationUtil.getInstance().send(context, content)
        }
        // 发送本地通知
//        LocalBroadcastManager.getInstance(context).sendBroadcast(Intent(Code.ACTION_MAIN_FILETER))
    }
}

在这里需要注意的是,当我们在接收到消息之后,要判断当前应用是否正在前台运行(是否正在 App 里面操作),并且屏幕是否亮着。然后在根据自己的推送需求,进行设置显示 Notification 。
上面 MyReceiver 中用到的一些工具方法如下:

object Utils {
    /**
     * 判断当前应用是否在前台运行
     */
    fun isAppRunningForeground(context: Context): Boolean {
        val manager = context.getSystemService(android.content.Context.ACTIVITY_SERVICE) as ActivityManager
        try {
            val infos = manager.getRunningTasks(1)
            if (infos != null && infos.size > 0) {
                return context.packageName.equals(infos[0].baseActivity.packageName, true)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }
        return false
    }


    /**
     * 屏幕是否开着。
     */
    fun isOpenScreen(context: Context): Boolean {
        val pm = context.getSystemService(Context.POWER_SERVICE) as PowerManager
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
            pm.isInteractive
        } else {
            pm.isScreenOn
        }
    }
}


class NotificationUtil {
// 设置本地 Notification 通知栏
    companion object {
        fun getInstance(): NotificationUtil = NotificationUtil()
    }

    private lateinit var notificationManager: NotificationManager

    fun send(context: Context, content: String) {
        notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        val intent = Intent(context, MainActivity::class.java)
//        intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
        val pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_ONE_SHOT)
//        val s = UserUtil.isSound()
//        val v = UserUtil.isVibrate()
//        var def = 0
//        if (s && v) {
//            def = DEFAULT_SOUND or DEFAULT_VIBRATE
//        } else if (s) {
//            def = DEFAULT_SOUND
//        } else if (v) {
//            def = DEFAULT_VIBRATE
//        }

        val noti = NotificationCompat.Builder(context)
                .setSmallIcon(context.applicationInfo.icon)    //设置小图标,用于状态栏左上角显示
                .setContentTitle(context.packageManager.getApplicationLabel(context.applicationInfo))    //设置标题
                .setContentText(content)    //设置内容
//                .setDefaults(def)    // 设置提醒样式
//                .setTicker("一闪而过")  //  通知到来时状态栏闪过的信息,
//                .setNumber(10)      //  //已经被 setSubText取代,显示在时间下方的内容
//                .setLargeIcon(BitmapFactory.decodeResource(resources, R.mipmap.img_default)) // 设置大图标 用于通知栏里显示
                .setAutoCancel(true)    //点击之后自动清除
                .setContentIntent(pendingIntent)
                .build()
        notificationManager.notify(System.currentTimeMillis().toInt(), noti) // notify Id 相同的消息会被覆盖
    }

}

到这里,我们的依赖、manifests注册,消息接收基本上都已经 ok 了,接下来就是该进行调用相关操作了。

这里我也整理了一个部分相关操作的类,

JPushUtil

object JPushUtil {

    /**
     * 初始化推送 - Application onCreate 方法中调用。
     */
    fun initPush(context: Context) {
        JPushInterface.setDebugMode(BuildConfig.DEBUG)
        JPushInterface.init(context)
        setNotificationStyle(context)
    }

    /**
     * 获取极光Id
     */
    fun getJPushId(): String? {
        val id = JPushInterface.getRegistrationID(App.mContext)
        return id
    }    

    // 停止推送 
    fun stopPush() {
        JPushInterface.stopPush(App.mContext)
    }

    // 恢复推送
    fun resumePush() {
        if (JPushInterface.isPushStopped(App.mContext)) {
            JPushInterface.resumePush(App.mContext)
        }
    }

    // 设置别名
    fun setAlias(alias:String) {
        JPushInterface.setAliasAndTags(App.mContext, alias, null, mAliasCallback)
    }

    fun clearAlias(){
        // 如果需要清除当前别名,只需要设置为 空字符串即可。
        JPushInterface.setAliasAndTags(App.mContext, "", null, mAliasCallback)
    }

    /**
     * 设置别名Alias结果回调
     */
    private val mAliasCallback: TagAliasCallback = TagAliasCallback { code, alias, tags ->
        val logs: String
        when (code) {
            0 -> {
                logs = "Set alias success"
                LvLog.i(logs)
            }

            6002 -> {
                logs = "Failed to set alias due to timeout. Try again after 60s."
                LvLog.i(logs)
            }

            else -> {
                logs = "Failed alias with errorCode = $code"
                LvLog.i(logs)
            }
        }
    }    


    /**
     * 设置极光推送通知样式
     */
    fun setNotificationStyle(context: Context) {
        val builder = BasicPushNotificationBuilder(context)
        builder.statusBarDrawable = context.applicationInfo.icon
        builder.notificationFlags = Notification.FLAG_AUTO_CANCEL   //设置为自动消失和呼吸灯闪烁

        // 设置为铃声、震动、呼吸灯闪烁都要
        var notification = Notification.DEFAULT_ALL
        /**
        * UserUtil.isSound 是否需要声音
        * UserUtil.isVibrate 是否需要震动
        **/
        if (!UserUtil.isSound() && !UserUtil.isVibrate()) {
            notification = Notification.DEFAULT_LIGHTS
        } else {
            notification = if (UserUtil.isVibrate() && UserUtil.isSound()) {
                Notification.DEFAULT_VIBRATE or Notification.DEFAULT_SOUND
            } else {
                if (UserUtil.isSound()) {
                    Notification.DEFAULT_SOUND
                } else {
                    Notification.DEFAULT_VIBRATE
                }
            }

        }
        builder.notificationDefaults = notification
// 参照文档调用 setPushNotificationBuilder方法一直没成功 ,换成以下方法方可执行。       
        JPushInterface.setDefaultPushNotificationBuilder(builder)
    }

}

以上则是目前所使用 JPush 推送,可能会用到的一些方法。在此作为一个总结记录,便与后续快速集成。可希望能给有需要的朋友带来帮助。

部分参考博客:极光单独设置铃声,震动模式相关问题解决

接收自定义消息之后,控制声音和震动以及顶部 Notification

测试代码

猜你喜欢

转载自blog.csdn.net/lv_fq/article/details/78709132
今日推荐