本篇博客重新整理一下关于集成极光推送的一些方法步骤。
这里是 极光官方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-->
以上代码需要注意的是,一个 MyReceiver
、 MyJPushMessageReceiver
这两个中,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 推送,可能会用到的一些方法。在此作为一个总结记录,便与后续快速集成。可希望能给有需要的朋友带来帮助。
部分参考博客:极光单独设置铃声,震动模式相关问题解决