官方教程上讲的是 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 各代表什么意思, 请参考:

https://developer.xamarin.com/guides/android/advanced_topics/java_integration_overview/binding-a-java-library/

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. 

http://forums.xamarin.com/discussion/9118/binding-generator-class-has-unknown-base-type-android-app-intentservice

解决方法也很简单: 用 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 的具体用法请参见:

https://developer.xamarin.com/guides/android/advanced_topics/java_integration_overview/binding-a-java-library/java-bindings-metadata/

现在编译 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的更多相关文章

  1. Android消息推送——JPush极光推送

    刚看了一篇关于Android消息推送评测总结的博客http://www.cnblogs.com/logan/p/4514635.html: 自己也对原学过的JPush极光进行一下小结,方便后续工作使用 ...

  2. atitit.web 推送实现方案集合(2)---百度云,jpush 极光推送 ,个推的选型比较.o99

    atitit.web 推送实现方案集合(2)---百度云,jpush 极光推送 ,个推的选型比较.o99 1.1. 云推送有推送次数或频率的限制吗? 1 1.2. 推送的消息长度 1 1.3. 离线消 ...

  3. Springboot项目集成JPush极光推送(Java SDK)

    1.由于项目的需求,需要在Android APP上实现消息推送功能,所以引用了极光推送(官网:https://www.jiguang.cn/, 文档:http://docs.jiguang.cn/) ...

  4. 使用JPush(极光推送)实现远程通知

    使用JPush(极光推送)实现远程通知 远程推送是APP 必备的功能, 现在第三方的 SDK 已经做的非常完备了, 在 iOS10.0出来之后, 极光推送也及时更新了他的 SDK, 今天小试了一下效果 ...

  5. Laravel 集成 JPush 极光推送指北

    我是一个 Laravel 小白,我是一个 Laravel 小白,我是一个 Laravel 小白(默念三遍再往下读,如果非小白就不用看了). Laravel 使用 Composer 来管理代码依赖.所以 ...

  6. Android JPush极光推送应用

    JPush纠结了5-6个小时,一直报下面的错误,纠结! [AndroidUtil] AndroidManifest.xml missing required intent filter for Pus ...

  7. Android开发之第三方推送JPush极光推送知识点详解 学会集成第三方SDK推送

    作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985 下面是一些知识点介绍,后期将会带领大家进行代码实战: 一.Android实现推送方式解决方案: 1.推 ...

  8. 用JPUSH极光推送实现服务端向安装了APP应用的手机推送消息(C#服务端接口)

    这次公司要我们做一个功能,就是当用户成功注册以后,他登录以后要收到消息,当然这个消息是安装了我们的手机APP应用的手机咯. 极光推送的网站的网址是:https://www.jpush.cn/ 极光推送 ...

  9. JPush极光推送 Java调用服务器端API开发

       极光推送是:使得开发者可以即时地向其应用程序的用户推送通知或者消息,与用户保持互动,从而有效地提高留存率,提升用户体验.简单的说就是通过JPush后台管理网站进行app消息的推送.可以让用户及时 ...

随机推荐

  1. NSTimer定时器的使用

    前言:这是关于NSTimer的学习笔记. 正文内容大纲: 1.关于计时器NSTimer的一个被添加进NSRunLoop的使用细节 2.关于NSTimer常用方法的使用 3.关于NSTimer的类别工具 ...

  2. NABCD模型进行竞争性需求分析

    确定项目:教室管理系统 负责人:李凤娇,高德建 选择比努力更重要.一个项目成功自然离不开组员们的努力.但是,光努力是不够的.还需要用户有需求,能快速实现. 这些东西,看似很虚,却能让我们少走不少弯路. ...

  3. query attr prop区别

    大家都知道有的浏览器只要写disabled,checked就可以了,而有的要写成disabled = "disabled",checked="checked", ...

  4. light oj 1393 - Crazy Calendar 博弈论

    思路:当移到右下角时,就不能移动了.所以与右下角的奇偶性相同的位置,都不能直接到达,先手必败! 只需考虑与右下角奇偶不同的位置,可以看成NIM博弈.最后NIM和不为0的胜,否者败!! 代码如下: #i ...

  5. Xilinx 网站资源导读2

    Xilinx 网站资源导读 ———版权声明———–本文作者 Ricky Suwww.fpganotes.comrickysu.fpga@gmail.com 欢迎转载,转载请保持原样及署名商业使用须得到 ...

  6. 一种使用 sprintf 导致死机的情况

    @2019-02-26 [小记] char temp[10] float money; sprintf(temp, "0.2f", money); 以上使用方法可能导致死机,原因是 ...

  7. Atitit r2017 r6 doc list on home ntpc.docx

    Atitit r2017 r6 doc list on home ntpc.docx 驱动器 D 中的卷是 p2soft 卷的序列号是 9AD0-D3C8 D:\ati\r2017 v4 raf\r2 ...

  8. Flutter 布局(十)- ListBody、ListView、CustomMultiChildLayout详解

    本文主要介绍Flutter布局中的ListBody.ListView.CustomMultiChildLayout控件,详细介绍了其布局行为以及使用场景,并对源码进行了分析. 1. ListBody ...

  9. Spark机器学习(4):朴素贝叶斯算法

    1. 贝叶斯定理 条件概率公式: 这个公式非常简单,就是计算在B发生的情况下,A发生的概率.但是很多时候,我们很容易知道P(A|B),需要计算的是P(B|A),这时就要用到贝叶斯定理: 2. 朴素贝叶 ...

  10. jquery $.ajax $.get $.post的区别?

    $.ajax 是 jQuery 底层 AJAX 实现,$.ajax是一种通用的底层封装,$.ajax()请求数据之后,则需要使用回调函数,有beforeSend.error.dataFilter.su ...