通常第三方推送平台支持两种推送消息:通知栏消息 和 透传消息
通知栏消息
通知栏消息就是,消息到达app后就直接在系统通知栏形式展示给用户。不会继续传递到App。
透传消息
透传消息是,该类消息在被送达用户的设备后,还会继续传递到App。
透传消息,对于消息的传递的通道不过问,通道只负责来传递消息,透传消息可以自定义消息体、自定义消息的展示方式、通知等相关、以及点击消息后续的动作。客户端接收到消息后,有客户端决定如何处理消息。
透传的流程:根据个推提供的API接口或者是在个推开发平台上推送消息,个推服务端接收到推送的消息后,不做任何处理,直接发送给目标客户。透传消息的消息体可以根据不同的需求传递不同的参数或者是格式。用户无感知的透传,如:更新相关信息,在主界面中相关栏位用红点标识进行弱提醒,推送一条命令用来检测用户是否有登录等。通知栏消息虽然方便的提醒用户,但也在一定程度上给用户带来了打扰,用户无感知的消息推送有时效果会更好。 用户有感知的透传:把透传消息处理成通知栏展示出来,提醒用户方便点击查看相关信息(如个人帐单信息),直接打开应用或跳转到指定的应用界面中(根据透传消息的相关参数来判断跳转到哪一个指定的界面,相关参数传递要打开的界面的类名或Intent即可)等。对于开发者,处理成通知栏的相关事件也是可以捕获的,如通知栏的展示、点击等事件都可以进行捕获,以方便进行后续的操作。 因透传消息可以自己处理成通知栏内容展示,所以通知栏的样式也可以根据需求来做对应的改变。在Android 4.4及以上的系统,通知栏可以是样式丰富的通知栏,放入图片和视频等;可以展示普通的通知,也可以展示多样化的通知。
自己理解的透传,比如说QQ进行聊天,我发送消息给另一个人,QQ的UI值设计好了聊天的结构体也就是聊天的样式、字体大小、颜色等,在QQ客户端发送消息时将消息和结构封装在一起发送到另一个客户端,消息透传就是通道不用知道消息的格式和消息内容,也不做处理,只用将数据传给客户端,客户端接收到消息后进行解析在聊天框进行显示。
如果说在发送消息的时候对应的应用没有被启动,透传类消息将不能顺利送达。所以如果消息是对送达率有要求就使用通知栏消息。所以说针对本App来说,由于安全防护需要及时通知安全的信息,所以对于安全的消息需要使用通知栏消息。
分析
除了在上一篇集成中的分析,在推送的消息,首先消息体内容需要从友盟平台写好,消息体的各个属性,比如说 消息体的描述、标题、内容、展示样式、图标、提醒方式以及后续的动作等等。在AndroidNotification定义中:
// Keys can be set in the payload level
protected static final HashSet<String> PAYLOAD_KEYS = new HashSet<>(Arrays.asList(
"display_type"
));
// Keys can be set in the body level,
// 提示文字、标题、内容、通知栏样式、通知图标、通知栏拉开后左侧图标ID
//通知栏大图标的URL链接。该字段的优先级大于largeIcon、提醒方式(震动、闪光、声音)
//收到通知是否发出声音,默认为"true"、点击"通知"的后续行为(打开应用、打开连接、打开指定页面、自定义行为)、
protected static final HashSet<String> BODY_KEYS = new HashSet<>(Arrays.asList(
"ticker", "title", "text", "builder_id", "icon", "largeIcon",
"img", "play_vibrate", "play_lights", "play_sound",
"sound", "after_open", "url", "activity", "custom"
));
public enum DisplayType {
NOTIFICATION {
public String getValue() {
return "notification";
}
},//通知:消息送达到用户设备后,由友盟SDK接管处理并在通知栏上显示通知内容。
MESSAGE {
public String getValue() {
return "message";
}
};//消息:消息送达到用户设备后,消息内容透传给应用自身进行解析处理。
public abstract String getValue();
}
public enum AfterOpenAction {
go_app,//打开应用
go_url,//跳转到URL
go_activity,//打开特定的activity
go_custom//用户自定义内容。
}
在BaseNotification中定义:
// Keys can be set in the root level
protected static final HashSet<String> ROOT_KEYS = new HashSet<>(Arrays.asList(
"appkey", "timestamp", "type", "device_tokens",
"alias", "alias_type", "file_id",
"filter", "production_mode", "feedback",
"description", "thirdparty_id"
));
// Keys can be set in the policy level
protected static final HashSet<String> POLICY_KEYS = new HashSet<>(Arrays.asList(
"start_time", "expire_time", "max_send_num"
));
在AndroidNotification中有上面定义的各个属性的设置值的方法,
//设置消息送达到用户设备后,由友盟SDK接管处理还是应用自身处理并在通知栏上显示通知内容
public void setDisplayType(DisplayType d) throws Exception {
setPredefinedKeyValue("display_type", d.getValue());
}
//通知栏提示文字
public void setTicker(String ticker) throws Exception {
setPredefinedKeyValue("ticker", ticker);
}
//通知标题
public void setTitle(String title) throws Exception {
setPredefinedKeyValue("title", title);
}
//通知文字描述
public void setText(String text) throws Exception {
setPredefinedKeyValue("text", text);
}
//用于标识该通知采用的样式。使用该参数时, 必须在SDK里面实现自定义通知栏样式。
public void setBuilderId(Integer builder_id) throws Exception {
setPredefinedKeyValue("builder_id", builder_id);
}
。。。。。。看具体代码
setPredefinedKeyValue 就是定义的一个通用赋值的一个方法,对于上面定义的ROOT_KEYS、POLICY_KEYS、PAYLOAD_KEYS、BODY_KEYS内的属性进行赋值。
在AndroidNotification、BaseNotification都是一些抽象的方法。具体实现是在UPushNotification中
public class UPushNotification {
/**
* 发送透传消息
*/
public static void send(String ticker, String title, String content) {
try {
Context context = UMGlobalContext.getAppContext();
final NotificationModel msg;
msg = new NotificationModel(PushConstants.APP_KEY, PushConstants.APP_MASTER_SECRET);
msg.setDeviceToken(PushAgent.getInstance(context).getRegistrationId());
msg.setTicker(ticker);
msg.setTitle(title);
msg.setText(content);
msg.goAppAfterOpen();
msg.setTestMode();
msg.setDisplayType(AndroidNotification.DisplayType.NOTIFICATION);
msg.setProductionMode();
Runnable runnable = new Runnable() {
@Override
public void run() {
try {
sendImpl(msg);
} catch (Exception e) {
e.printStackTrace();
}
}
};
new Thread(runnable).start();
} catch (Exception e) {
e.printStackTrace();
}
}
private static void sendImpl(BaseNotification msg) throws Exception {
String timestamp = Integer.toString((int) (System.currentTimeMillis() / 1000));
msg.setPredefinedKeyValue("timestamp", timestamp);
String url = "http://msg.umeng.com/api/send";
String postBody = msg.getPostBody();
String p_sign = "POST" + url + postBody + msg.getAppMasterSecret();
String sign = md5(p_sign);
url = url + "?sign=" + sign;
String response = HttpRequest.post(url).acceptJson().send(postBody).body("UTF-8");
JSONObject responseJson = new JSONObject(response);
String ret = responseJson.getString("ret");
Runnable runnable;
if (!ret.equalsIgnoreCase("SUCCESS")) {
runnable = new Runnable() {
@Override
public void run() {
Toast.makeText(UMGlobalContext.getAppContext(), "发送失败", Toast.LENGTH_LONG).show();
}
};
} else {
runnable = new Runnable() {
@Override
public void run() {
Toast.makeText(UMGlobalContext.getAppContext(), "发送成功", Toast.LENGTH_LONG).show();
}
};
}
new Handler(Looper.getMainLooper()).post(runnable);
}
private static String md5(String string) {
byte[] hash;
try {
hash = MessageDigest.getInstance("MD5").digest(string.getBytes());
} catch (Exception e) {
e.printStackTrace();
return "";
}
StringBuilder hex = new StringBuilder(hash.length * 2);
for (byte b : hash) {
int i = (b & 0xFF);
if (i < 0x10) hex.append('0');
hex.append(Integer.toHexString(i));
}
return hex.toString();
}
}
签名证书
签名证书有debug版本和release版本,
debug版本的是指在开发过程中从Android Studio中直接调试运行的apk使用的签名文件,一般是开发工具自己处理。
release版本是咋子提交测试或者是发布的时候,指定的签名文件进行打包的签名文件,一般同一个app使用同一个签名文件。
当应用发布上线的时候,需要使用jks文件对其签名,这样做可以防止应用被恶意篡改替换,同样也是开发者身份的标识,大大加强应用的安全性。创建jks文件时(很多种方法,比如使用指令,或者在studio中创建,这里不再多说)会隐式的创建密匙库(公匙和私匙)、证书等内容。这些内容会用于之后的签名校验中(应用安装时)。
SHA256算法
就是一个哈希函数,将数据或者消息压缩成摘要,使数据量变小,将数据的格式固定下来,对于任何的长度的数据,经过算法都可以变成256bit长的哈希值,成为消息摘要。
SHA1算法不是相对安全的签名算法,一般公司对于安全有要求使用的是SHA256签名算法。
对于如何看apk签名是SHA1还是SHA256:拿到apk文件,修改文件后缀名为.jar ;解压文件得到META-INF文件夹;用编辑器打开文件夹下的CERT.SF文件;看到的是SHA-256-Digest:说明是SHA256签名的,如果是SHA-1-则是SHA1签名的。
将签名算法改为SHA256:SDK版本至少需要为4.3 (api18)。
在Androidstudio中导出项目apk(生成签名证书)、并查看的方法:
一般我们测试app都是直接连接usb安装到手机上,如果要到处apk则可以使用以下步骤:
(生成签名证书)Build->Generare Signed Bundle/APK... 记住存放的位置。
在项目的路径下:D:\code\Android\MultiFunctionAndroidMavenDemo\app\release
会有
第一个是apk文件,第二个是产生第一个的中间产物。
生成签名证书指纹
打开命令窗口,并进入已安装JDK的bin目录下。cd C:\Program Files\Huawei\jdk1.8.0_292\bin
keytool -list -v -keystore 文件目录(生成的jks路径)keytool -list -v -keystore D:\code\Android\MultiFunctionAndroidMavenDemo\app\MultiFunctionAndroidMavenDemo.jks
信息中输入签名文件密钥库口令(口令即生成签名文件时的Password)。
获取对应SHA256指纹。
针对apk签名是SHA256,但是生成签名指纹只有SHA1
解决不了
总结:
在初始化SDK时,(继承sdk就是使用友盟的服务,我们需要集成sdk,集成之后才可以使用人家的服务,才可以形成桥梁,让我们在友盟上创建的应用的appkey等才可以和我们项目中的appkey等联系起来。)
首先需要注册,就是将这个手机注册在平台注册成功会返回该手机注册的devicetoken,只有注册成功,友盟才可以识别这个手机进行推送。【问题是,如果说应用发布,那么多不同的手机是如何获取到devicetoken的。】
获取推送消息实例,
接收推送消息,根据消息类型的不同,对推送消息作处理,处理通知栏消息、处理透传消息、自定义通知样式。同时在初始化时如果厂商推送在初始化的时候回进行不同的厂商的通道的初始化。
在androidNotification、BaseNotification、NotificationModle、UPushNotification处理透传消息的各个参数,比如点击消息后续动作、通知消息样式等设置。