JPush (极光推送) For Xamarin.Android
官方教程上讲的是 GCM (Google Cloud Messaging) , 不过 GFW 是 GCM 过不去的坎.
极光推送 JPush 是国内的一个不错的替代方案.
JPush 提供的 API 易于理解, 使用也很简单, 但是要使用于 Xamarin 还是要费些周章: 要转制成 Binding Library.
JPush 提供了一篇转制的示例:
http://smilehyh.blog.163.com/blog/static/123343886201362110857402/
按照过程走一遍:
1, 下载 JPush SDK
http://docs.jpush.io/resources/
2, 新建一个 BindingLibrary
3, 将 SDK内的 JPush.xxx.jar 放到 Jars 目录下, 将 SDK 内的 arm64-v8a, armeabi, armeabi-v7a 放到 libs 目录下.
4, 指定jar 文件为 EmbeddedJar, xxx.so 为 EmbeddedNativeLibrary
具体 EmbeddedNativeLibrary 和 EmbeddedJar 各代表什么意思, 请参考:
5,编译, 并在 Xamarin.Android工程中引用该项目, 修改 Android 项目属性, 指定 Package Name. 注意: 首字母要小写, 否则运会报错
6, 按照文档 http://docs.jpush.io/guideline/android_guide/ 中的说明, 修改 Properties/AndroidManifest.xml,
这一步可用编码来实现, 后面会讲到.
7, 在 MainActivity 的 OnCreate 方法里加入 (红色部分):
protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); global::Xamarin.Forms.Forms.Init(this, bundle); FormsAppCompatActivity.ToolbarResource = Resource.Layout.toolbar; FormsAppCompatActivity.TabLayoutResource = Resource.Layout.tabs; LoadApplication(new App(IoC.Get<SimpleContainer>())); 11 JPushInterface.SetDebugMode(true); 12 JPushInterface.Init(this); }
8, 如果你是用 Visual Studio Emulator For Android 模拟器进行调试, 不出意外, 你会在输出中看到以下信息:
[JPushGlobal] Get sdk version fail![获取sdk版本失败!] W/System.err( 2063): java.lang.UnsatisfiedLinkError: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/notification.Droid-1/base.apk"],nativeLibraryDirectories=[/data/app/notification.Droid-1/lib/x86, /vendor/lib, /system/lib]]] couldn't find "libjpush205.so" ...
很显然, VS Emulator For Android 是基于 x86 的. 要解决这个问题, 请下载 JPush SDK 的 x86 版本 , 重复 2 ~ 4 的步骤, 新建另外一个 Binding Library, 并添加引用到 Xamarin.Android 项目.
9, 编译,运行, 确认模拟器或设备连接到网络 , 如果配置没有错误, 你会看到以下信息:
10, 发送测试:
---------------------------------分隔线-------------------------------
该部份只是一个偿试,不建议使用, 直接修改 Manifest 添加配置就挺好的。
第一眼看到极光的 AndroidManifest.xml 配置, 我头大了, 这都什么和什么啊 (对 Android 还是不太了解)!
android.permission.RECEIVE_USER_PRESENT 这个 Permission 在 VS 的 Manifest 选项卡中居然没有!
有好几处需要替换为 Package Name
<category android:name="您应用的包名"/> <permission android:name="您应用的包名.permission.JPUSH_MESSAGE" android:protectionLevel="signature" /> <uses-permission android:name="您应用的包名.permission.JPUSH_MESSAGE" />
这里的 "包名" 即 上面的 第5步 里指定的 Package name.
Xamarin.Android 提供了很多 Attribute , 编译的时候, 会跟据这些 Attribute 对 Manifest 文件自动进行修改, 比如:
[Activity(Label = "Notification", Theme = "@style/MyTheme", Icon = "@drawable/icon", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)] public class MainActivity : FormsAppCompatActivity {
这个 ActivityAttribute 在最终的 Manifest 文件 (xxx\obj\Debug\android\AndroidManifest.xml) 中输出这样:
<activity android:configChanges="orientation|screenSize" android:icon="@drawable/icon" android:label="Notification" android:theme="@style/MyTheme" android:name="md5d2dc98e5b163ec8eaa18490df03b0a9d.MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
[Service] [IntentFilter(new string[] { "Xamarin.BallService" })] public class BallService : Service, View.IOnTouchListener, View.IOnClickListener {
这个 ServiceAttribute 和 IntentFilterAttribute 最终会输出成这样:
<service android:name="md5e15bd1e8131f68eb8f9fc22d8285f415.BallService"> <intent-filter> <action android:name="Xamarin.BallService" /> </intent-filter> </service>
在 Binding Library 项目下, 有个Additions 目录, 它的作用 就是为了在转制的时候 加入一些自定义方法 或 是自定义类, 或是对 Java 的类进行扩展:
Additions allow you to add arbitrary C# to the generated classes before they are compiled. This can be helpful for providing convenience methods or adding pure C# classes.
如果是对 Java 类进行扩展, 需要使用 partical
跟据这个牛B的特性, 我在 Additions 目录下加了这几个类:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Android.App; using Android.Content; using Android.OS; using Android.Runtime; using Android.Views; using Android.Widget; namespace CN.Jpush.Android.Service { [BroadcastReceiver(Name = "cn.jpush.android.service.AlarmReceiver")] public partial class AlarmReceiver { } }
AlarmReceiver
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Android.App; using Android.Content; using Android.OS; using Android.Runtime; using Android.Views; using Android.Widget; namespace CN.Jpush.Android.Service { [Service(Name = "cn.jpush.android.service.DaemonService", Enabled = true, Exported = true)] [IntentFilter( new string[] { "cn.jpush.android.intent.DaemonService" }, Categories = new string[] { Defines.APP_ID } ) ] public partial class DaemonService { } }
DaemonService
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Android.App; using Android.Content; using Android.OS; using Android.Runtime; using Android.Views; using Android.Widget; namespace CN.Jpush.Android.Service { [Service(Name = "cn.jpush.android.service.DownloadService", Enabled = true, Exported = false)] public partial class DownloadService { } }
DownloadService
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Android.App; using Android.Content; using Android.OS; using Android.Runtime; using Android.Views; using Android.Widget; using Android.Content.PM; namespace CN.Jpush.Android.UI { [Activity(Name = "cn.jpush.android.ui.PushActivity", ConfigurationChanges = ConfigChanges.Orientation | ConfigChanges.KeyboardHidden, Exported = false)] [IntentFilter(new string[] { "cn.jpush.android.ui.PushActivity" }, Categories = new string[]{ "android.intent.category.DEFAULT", Defines.APP_ID })] public partial class PushActivity { public void TTT() { } } }
PushActivity
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Android.App; using Android.Content; using Android.OS; using Android.Runtime; using Android.Views; using Android.Widget; namespace CN.Jpush.Android.Service { [BroadcastReceiver(Name = "cn.jpush.android.service.PushReceiver", Enabled = true)] [IntentFilter(new string[]{ "cn.jpush.android.intent.NOTIFICATION_RECEIVED_PROXY" }, Categories = new string[] { Defines.APP_ID }, Priority = )] [IntentFilter(new string[] { "android.intent.action.USER_PRESENT" , "android.net.conn.CONNECTIVITY_CHANGE" })] [IntentFilter(new string[] { "android.intent.action.PACKAGE_ADDED", "android.intent.action.PACKAGE_REMOVED" }, DataScheme = "package")] public partial class PushReceiver { } }
PushReceiver
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Android.App; using Android.Content; using Android.OS; using Android.Runtime; using Android.Views; using Android.Widget; namespace CN.Jpush.Android.Service { [Service(Name = "cn.jpush.android.service.PushService", Enabled = true, Exported = false)] [IntentFilter(new string[] { "cn.jpush.android.intent.REGISTER", "cn.jpush.android.intent.REPORT", "cn.jpush.android.intent.PushService", "cn.jpush.android.intent.PUSH_TIME" })] public partial class PushService { } }
PushService
以及:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using Android.App; using Android.Content; using Android.OS; using Android.Runtime; using Android.Views; using Android.Widget; namespace CN.Jpush.Android { static class Defines { public const string APP_ID = "notification.Droid"; } }
这个 APP_ID 为 Android 项目的 Package name , 在 DaemonService , PushReceiver 和 PushActivity 中, 做为 IntentFilterAttribute 的 Category 出现.
而且不可缺少, 否则推送会失败....
这一点很蛋疼... 如果这样的话, 这个 Binding Library 就是不可复用的了!
查了一下, 都是说 AndroidManifest.xml 不可动态修改, 当前也没有找到如何动态注册 Receiver / Activity / Service 的方法。
然后在 Android项目下面添加一个 Application.cs 继承自 Android.App.Application:
[assembly: Permission(Name = Notification.Droid.Application.JPUSH_MESSAGE_PERMISSION, ProtectionLevel = Protection.Signature)] [assembly: UsesPermission(Name = Notification.Droid.Application.JPUSH_MESSAGE_PERMISSION)] [assembly: UsesPermission(Name = Android.Manifest.Permission.Internet)] [assembly: UsesPermission(Name = Android.Manifest.Permission.WakeLock)] [assembly: UsesPermission(Name = Android.Manifest.Permission.Vibrate)] [assembly: UsesPermission(Name = Android.Manifest.Permission.ReadPhoneState)] [assembly: UsesPermission(Name = Android.Manifest.Permission.WriteExternalStorage)] [assembly: UsesPermission(Name = Android.Manifest.Permission.ReadExternalStorage)] [assembly: UsesPermission(Name = Android.Manifest.Permission.MountUnmountFilesystems)] [assembly: UsesPermission(Name = Android.Manifest.Permission.AccessNetworkState)] [assembly: UsesPermission(Name = "android.permission.RECEIVE_USER_PRESENT")] [assembly: UsesPermission(Name = Android.Manifest.Permission.WriteSettings)] namespace Notification.Droid { [Application] [MetaData("JPUSH_CHANNEL", Value = "developer-default")] [MetaData("JPUSH_APPKEY", Value = "你的JPush APPKEY")] public class Application : Android.App.Application { //notification.Droid 即 Android 项目的 Package name public const string JPUSH_MESSAGE_PERMISSION = "notification.Droid.permission.JPUSH_MESSAGE"; private SimpleContainer container; public Application(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) { }
做完这一步, 你可以把上面第6步中, 添加到 Proierties/AndroidManifest.xml 中的配置全删了.
编译, xxx\obj\Debug\android\AndroidManifest.xml 中就会自动添加刚刚被你删除的配置了.
只是, 少了一个:
<service android:name="cn.jpush.android.service.DownloadService" android:enabled="true" android:exported="false" > </service>
用反编译工具查看转制成的 dll , 在 CN.Jpush.Android.Service 下, 根本就没有 DownloadService 这个类.
这是为啥呢? 在仔细的看一下 Binding Library 的编译输出, 发现有如下问题:
BINDINGSGENERATOR : warning BG8102: Class CN.Jpush.Android.Service.DownloadService has unknown base type android.app.IntentService.
未知的基类: android.app.IntentService , 导致 DownloadService 没有转制成功. 具体是为什么, 不了解, 搜了一下:
One of the classes internal to the Android project had methods marked as public, so android.app.IntentService was trying to leak out.
解决方法也很简单: 用 Mono 的 IndentService 代替 android.app.intentService.
在 binding Library 项目下的 Transforms/Metadata.xml 中添加:
<attr path="/api/package[@name='cn.jpush.android.service']/class[@name='DownloadService' and @extends='android.app.IntentService']" name="extends">mono.android.app.IntentService</attr>
Metadata.xml 的具体用法请参见:
现在编译 Binding Library , 用反编译工具查看最终的 dll ,这个 DownloadService 就出现了.
然后编译 Android 项目, 那个 cn.jpush.android.service.DownService 也成功的出现在 AndroidManifest.xml 中了!
-------------------分隔线--------------------------
最后, 深入的集成 JPush, 在 Android 项目下添加类: Receiver
[BroadcastReceiver(Enabled = true)] [IntentFilter(new string[] { "cn.jpush.android.intent.REGISTRATION", "cn.jpush.android.intent.UNREGISTRATION" , "cn.jpush.android.intent.MESSAGE_RECEIVED", "cn.jpush.android.intent.NOTIFICATION_RECEIVED", "cn.jpush.android.intent.NOTIFICATION_OPENED", "cn.jpush.android.intent.ACTION_RICHPUSH_CALLBACK", "cn.jpush.android.intent.CONNECTION" }, Categories = new string[] { "notification.Droid" })] public class Receiver : PushReceiver { public async override void OnReceive(Context ctx, Intent intent) { base.OnReceive(ctx, intent); var action = intent.Action; System.Diagnostics.Debug.WriteLine(action); var bundle = intent.Extras; await ReceiverHandler.Handle(intent.Action, bundle); } }
Categories 中的 notification.Droid 还是 Package name
ReceiverHandler 是自定义的处理器,
具体可参考:
http://docs.jpush.io/client/android_api/
--------------------------------------
OK, 完
源码: https://github.com/gruan01/Xamarin-Example/tree/master/Notification
JPush (极光推送) For Xamarin.Android的更多相关文章
- Android消息推送——JPush极光推送
刚看了一篇关于Android消息推送评测总结的博客http://www.cnblogs.com/logan/p/4514635.html: 自己也对原学过的JPush极光进行一下小结,方便后续工作使用 ...
- atitit.web 推送实现方案集合(2)---百度云,jpush 极光推送 ,个推的选型比较.o99
atitit.web 推送实现方案集合(2)---百度云,jpush 极光推送 ,个推的选型比较.o99 1.1. 云推送有推送次数或频率的限制吗? 1 1.2. 推送的消息长度 1 1.3. 离线消 ...
- Springboot项目集成JPush极光推送(Java SDK)
1.由于项目的需求,需要在Android APP上实现消息推送功能,所以引用了极光推送(官网:https://www.jiguang.cn/, 文档:http://docs.jiguang.cn/) ...
- 使用JPush(极光推送)实现远程通知
使用JPush(极光推送)实现远程通知 远程推送是APP 必备的功能, 现在第三方的 SDK 已经做的非常完备了, 在 iOS10.0出来之后, 极光推送也及时更新了他的 SDK, 今天小试了一下效果 ...
- Laravel 集成 JPush 极光推送指北
我是一个 Laravel 小白,我是一个 Laravel 小白,我是一个 Laravel 小白(默念三遍再往下读,如果非小白就不用看了). Laravel 使用 Composer 来管理代码依赖.所以 ...
- Android JPush极光推送应用
JPush纠结了5-6个小时,一直报下面的错误,纠结! [AndroidUtil] AndroidManifest.xml missing required intent filter for Pus ...
- Android开发之第三方推送JPush极光推送知识点详解 学会集成第三方SDK推送
作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985 下面是一些知识点介绍,后期将会带领大家进行代码实战: 一.Android实现推送方式解决方案: 1.推 ...
- 用JPUSH极光推送实现服务端向安装了APP应用的手机推送消息(C#服务端接口)
这次公司要我们做一个功能,就是当用户成功注册以后,他登录以后要收到消息,当然这个消息是安装了我们的手机APP应用的手机咯. 极光推送的网站的网址是:https://www.jpush.cn/ 极光推送 ...
- JPush极光推送 Java调用服务器端API开发
极光推送是:使得开发者可以即时地向其应用程序的用户推送通知或者消息,与用户保持互动,从而有效地提高留存率,提升用户体验.简单的说就是通过JPush后台管理网站进行app消息的推送.可以让用户及时 ...
随机推荐
- 如何正大光明的使用 google 进行搜索
对于程序猿来说,不能使用google,是一大痛所在,今天在使用 百度网盘 搜索时,突然发现 ,他能同时使用 baidu和 google进行搜索,于是想到了这个正大光明的使用google 的方法,不需要 ...
- 烂泥:LVM学习之逻辑卷LV及卷组扩容VG
本文由秀依林枫提供友情赞助,首发于烂泥行天下. 上篇文章中介绍了有关LVM基础的知识,这篇文章我们来介绍如何给LVM的逻辑卷LV及卷组VG扩容. LVM的逻辑卷,我们知道它最后相当于一个分区,既然是一 ...
- CSS纯样式实现箭头、对话框等形状
在使用第三方框架bootstrap的时候,本以为其是图片实现的小箭头,后来使用开发工具查看是用CSS来实现的,现记录如下: 之前都没仔细去观注过其原理,都是拿来使用,在实现小箭头之前需要了解下CSS的 ...
- ES5严格模式(Strict mode)
严格模式(Strict mode)是由ECMA-262规范定义的新兴JavaScript标准,第五版发布于2009年12月.旨在改善错误检查功能并且标识可能不会延续到未来JavaScript版本的脚本 ...
- 排序(qsort sort的使用)
前情:因平常写代码是常将比较函数弄混(写好了排序还要确认一下-.-!),还是写篇博客,方便以后查阅 C语言qsort函数对int类型数组排序: #include "stdio.h" ...
- Linux学习之二——档案与目录的属性和权限
一.属性和权限的基本概念 Linux一般将档案可存取的身份分为三个类别,分别是 owner/group/others,这三种身份各有 read/write/execute 等权限. 所有的系统上的账号 ...
- [译]OpenStack Object Storage Monitoring
注:翻译的不完整,主要是有些地方翻译后反而妨碍理解,有些不知道怎么翻,anyway,需要时拿来用用也是可行的,顺便共享啦.欢迎提意见. 一个OpenStack Object Storage(OSOS) ...
- 原创翻译-值得关注的10个python语言博客
原文链接 原文链接的网页感觉网络不是很好,不容易上.我在这里就给大家做个翻译吧. 大家好,还记得我当时学习python的时候,我一直努力地寻找关于python的博客,但我发现它们的数量很少.这也是我建 ...
- maven - pom.xml 聚合(父)工程 基本内容演示
企业开发中所用到的基本jar包以及插件都已在此 可以自己根据实际情况酌情增减 <project xmlns="http://maven.apache.org/POM/4.0.0&quo ...
- [cb]Unity 项目架构
一.技能机制 二.游戏工程 三.客户端架构