一、背景
根据不同的需求App会集成第三方消息推送,如极光,个推等并且他们都有自己的快捷集成方式。当公司项目比较多时,每个项目都要重新集成 很麻烦。刚好公司有新的系统上线,系统里面包含几大模块。如淘宝,携程,美团等业务模块很多,项目用组件化方式去写。顺便把消息推送重新封装,每次只需要做最基本的配置就可以了。
实现项目集成开发或组件开发时都已轻松的使用消息推送。当有新通知并点击,可以跳转到想到达的页面。需求很简单,现实很无奈,不断的尝试,才有了这篇文章。
二、推送组件
1.项目结构
image.png随着项目的业务越多,公共组件common应该越来越小。本着此原则,决定把消息推送独立出来(90%独立)。实现的效果: 集成开发时,点击消息通知,根据消息的类型,去跳转所在的module_demoX。组件开发时,点击消息,跳转到此组件的主页面。很绕口,简单说lib中如何跳转到其他lib中的页面。
2.引入个推
- 1). 基本配置及添加项目依赖
新建library 命名common_push。在此lib中做个推基本配置(其过程一把心酸一把泪)。
1.添加maven库
allprojects {
repositories {
google()
jcenter()
//Maven URL地址
maven {
url "http://mvn.gt.igexin.com/nexus/content/repositories/releases/"
}
}
}
2.gradle.properties配置
android.useDeprecatedNdk=true
3.添加NDK和manifestPlaceholders
defaultConfig {
...........
ndk {
abiFilters "armeabi", "armeabi-v7a", "x86_64"
}
manifestPlaceholders = [
GETUI_APP_ID : "4XEAxE8s4F6J64oPemLGZ7",
GETUI_APP_KEY : "NLJ1HSIpZr6siNGID87cb3",
GETUI_APP_SECRET: "lRCYFUiCirAG9EChR1KcG"
]
}
4.添加第三方gson,公共组件common的依赖
此项目使用组件化开发,路由是必备,这里引入common组件也不算过分。自我感觉良好,哈哈哈。
使用common中路由来实现页面跳转,因此造成90%的独立common组件,如更好的方法,欢迎留言。
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
api 'com.getui:sdk:2.12.5.0'
implementation 'com.google.code.gson:gson:2.2.4'
implementation 'com.android.support:appcompat-v7:26.1.0'
implementation project(':common')
}
5.添加权限
<!-- iBeancon功能所需权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- 个推3.0电子围栏功能所需权限 -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!-- 配置的第三方参数属性 -->
<uses-permission android:name="android.permission.VIBRATE" />
<application android:usesCleartextTraffic="true">
<service
android:name=".PushServie.PushService"
android:exported="true"
android:label="PushService"
android:process=":pushservice" />
<service android:name=".PushServie.PushIntentService" />
<service android:name=".PushServie.IntentActService" />
</application>
6.超级大坑(只显示结果,不记录过程)
在空壳main组件和app中的gradle,分别配置manifestPlaceholders ,否则编译报错
manifestPlaceholders = [
GETUI_APP_ID : "4XEAxE8s4F6J64oPemLGZ7",
GETUI_APP_KEY : "NLJ1HSIpZr6siNGID87cb3",
GETUI_APP_SECRET: "lRCYFUiCirAG9EChR1KcG"
]
7.消息到达的目的不同
集成模式下:在空壳main组件gradle添加common_push依赖。
组件模式下:这里是为module添加的消息推送,所以在gradle中添加common_push的依赖。
-
2).消息服务重写以及生成通知
image.png
1.新建PushIntentService 继承GTIntentService ,PushService 继承Service ,这是个推文档里的要求,这里不做过多的描述
2.新建PushNotification工具类。
这里采用透传消息方式,所以需要自己新建一个通知。通知包含自定义铃声,显示应用的logo,名字等。
3.页面跳转(最蛋疼的地方)的通知
如果此工具类在存在业务组件中,页面跳转很简单,只需要制定一个Activity即可。但这里是lib形式的组件,Activity无法直接指定,造成无法跳转,摸索了大半天。
中间想过使用广播,静态注册,利用隐式跳转的方式,在需要跳转的组件中接收次广播即可做跳转的操作。大家都知道在android O中 谷歌取消了大部分广播的静态注册方式,只保留了部分广播的静态注册,此方法废弃。
静态不行,动态这次可以了吧,但是思考一下,还是放弃了。原因你懂的........
最后还是采用service的方式。点击通知时启动service,在service中通过路由去跳转页面。
Intent mainIntent = new Intent(context, IntentActService.class);
mainIntent.putExtra(ConfigPush.intentActData, String.valueOf(bean.getType()));
PendingIntent mainPendingIntent = PendingIntent.getService(context, 0, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- 3).实现思路
伪代码
1.新建IntentActService 服务,在onStartCommand 中接收 通知跳转带来的值 ,并调用PushUtils中的IntentActAroute方法实现跳转。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String data = intent.getExtras().getString(ConfigPush.intentActData);
Log.i("stf", "--IntentActService-->" + data);
PushUtils.getInstance(IntentActService.this).IntentActAroute(data);
handler.postDelayed(new Runnable() {
@Override
public void run() {
stopSelf();
}
}, 3000);
return START_NOT_STICKY;
}
2.PushUtils工具类中的路由相关
// 根据不同的路由跳转
public void IntentActAroute(String type) {
Log.i("stf", "--IntentActAroute--->" + type.toString());
try {
Map<String, String> aRouteMap = getARouteMap();
if (aRouteMap != null && aRouteMap.size() > 0) {
String url = aRouteMap.get(type);
Log.i("stf", "--url--->" + url);
ARouter.getInstance().build(url).navigation();
}
} catch (Exception e) {
e.fillInStackTrace();
Log.i("stf", "-IntentActAroute--->" + e.getMessage());
}
}
// 将需要跳转的页面的路由地址与通知的类型绑定,初始化的时候把数据带进来
public static Boolean setARouteMap(Map<String, String> mapAroute) {
boolean result = false;
JSONArray mJsonArray = new JSONArray();
SharedPreferences shared = context.getSharedPreferences(ConfigPush.sharedMsg, Context.MODE_MULTI_PROCESS);
SharedPreferences.Editor edit = shared.edit();
edit.remove(ConfigPush.saveMap).commit();
try {
Iterator<Map.Entry<String, String>> iterator = mapAroute.entrySet().iterator();
JSONObject object = new JSONObject();
while (iterator.hasNext()) {
Map.Entry<String, String> entry = iterator.next();
object.put(entry.getKey(), entry.getValue());
}
mJsonArray.put(object);
edit.putString(ConfigPush.saveMap, mJsonArray.toString());
result = true;
} catch (Exception e) {
e.fillInStackTrace();
result = false;
}
edit.apply();
return result;
}
// 取出路由路径的map
private Map<String, String> getARouteMap() {
SharedPreferences shared = context.getSharedPreferences(ConfigPush.sharedMsg, Context.MODE_MULTI_PROCESS);
Map<String, String> map = new LinkedHashMap<>();
map.clear();
String json = shared.getString(ConfigPush.saveMap, "1");
if (json.equals("1")) {
return map;
}
try {
JSONArray array = new JSONArray(json);
JSONObject itemObject = array.getJSONObject(0);
JSONArray names = itemObject.names();
if (names != null) {
for (int j = 0; j < names.length(); j++) {
String name = names.getString(j);
String value = itemObject.getString(name);
map.put(name, value);
}
}
} catch (Exception e) {
e.fillInStackTrace();
Log.i("stf", "-getARouteMap--->" + e.fillInStackTrace());
}
return map;
}
- 4)初始化个推以及相关数据
1.MyApp中初始化路由
集成模式或组件模式分别有自己的appliction
if (ApiUtils.isAppDeBug()) {
ARouter.openDebug();
ARouter.openLog();
}
ARouter.init(this);
2. 集成模式下的空壳main组件中初始化路由路径
PushUtils.init(this);
PushUtils.initialize();
PushUtils.turnOnPush();
PushUtils.setPushUtils(this);//需要实现接口
这里的map的key :1,2,3是后台发出来通知的json数据,有个type的字段。
Map<String, String> mapAroute = new LinkedHashMap<>();
mapAroute.put("1", Config.demo1_mainAct);
mapAroute.put("2", Config.demo2_mainAct);
mapAroute.put("3", Config.mainAct);
PushUtils.setARouteMap(mapAroute);
3.组件模式下module_dem2的loginAct页面中
PushUtils.init(this);
PushUtils.initialize();
PushUtils.turnOnPush();
PushUtils.setPushUtils(this);//需要实现接口
Map<String, String> mapAroute = new LinkedHashMap<>();
mapAroute.put("2", Config.demo2_mainAct);
PushUtils.setARouteMap(mapAroute);
4.页面的路由配置
组件中如:
@Route(path = Config.demo2_mainAct)
public class MainActMod2 extends AppCompatActivity implements PushInterface {
}
@Route(path = Config.mainAct)
public class MainActivity extends BaseActivity{
}
5.个推的clientid,通知消息数据的获取
比如进入某页面启动个推时,可以在次页面中实现PushInterface 接口并实现其onReceiveClientId(String id),
onReceiveMessageData(String msg)方法,即可获取clientid和推送json数据。
另一种获取clientid的方式,可以调用PushUtils.getInstance(this).getClientId()方法,其返回值就是clientid。
注意当期返回值为null时,需要再利用onReceiveClientId方法来获取。
@Route(path = Config.demo2_mainAct)
public class MainActMod2 extends AppCompatActivity implements PushInterface {
}
public void getClientId(View view) {
String clientId = PushUtils.getInstance(this).getClientId();
Log.i("stf", "-LoginActiMode2-clientId--->" + clientId);
}
@Override
public void onReceiveClientId(String id) {
Log.i("stf", "-LoginActiMode2-clientId--->" + id);
}
@Override
public void onReceiveMessageData(String msg) {
Log.i("stf", "-LoginActiMode2-msg--->" + msg);
}
- 5)大功告成,此时可以随意切换集成模式和组件模式,来测试通知,亲测可行。
网友评论