精华内容
下载资源
问答
  • Android消息推送

    千次下载 热门讨论 2013-04-17 12:02:48
    Android消息推送 支持离线消息 Android消息推送 支持离线消息
  • 前言 在Android开发中,消息推送功能的使用非常常见。为了降低开发成本,使用第三方推送是现今较为流行的解决方案。 今天,我将手把手教大家如何在你的... Android推送:第三方消息推送平台详细解析目录1. 官方Demo解

    前言

    • 在Android开发中,消息推送功能的使用非常常见。

    推送消息截图

    • 为了降低开发成本,使用第三方推送是现今较为流行的解决方案。

    • 今天,我将手把手教大家如何在你的应用里集成小米推送

    1. 该文档基于小米推送官方Demo,并给出简易推送Demo
    2. 看该文档前,请先阅读我写的另外两篇文章:
      史上最全解析Android消息推送解决方案
      Android推送:第三方消息推送平台详细解析

    目录

    目录


    1. 官方Demo解析

    首先,我们先对小米官方的推送Demo进行解析。

    请先到官网下载官方DemoSDK说明文档

    1.1 Demo概况

    Demo目录

    目录说明:

    • DemoApplication类
      继承自Application类,其作用主要是:设置App的ID & Key、注册推送服务

    • DemoMessageReceiver类
      继承自BroadcastReceiver,用于接收推送消息并对这些消息进行处理

    • MainActivity
      实现界面按钮处理 & 设置本地推送方案

    • TimeIntervalDialog
      设置推送的时间间段

    接下来,我将对每个类进行详细分析

    1.2 详细分析

    1.2.1 DemoApplication类

    继承自Application类,其作用主要是:

    • 设置App的ID & Key
    • 注册推送服务

    接下来我们通过代码来看下这两个功能如何实现:

    DemoApplication.java

    package com.xiaomi.mipushdemo;
    
    import android.app.ActivityManager;
    import android.app.ActivityManager.RunningAppProcessInfo;
    import android.app.Application;
    import android.content.Context;
    import android.os.Handler;
    import android.os.Message;
    import android.os.Process;
    import android.text.TextUtils;
    import android.util.Log;
    import android.widget.Toast;
    
    import com.xiaomi.channel.commonutils.logger.LoggerInterface;
    import com.xiaomi.mipush.sdk.Logger;
    import com.xiaomi.mipush.sdk.MiPushClient;
    
    import java.util.List;
    
    
    public class DemoApplication extends Application {
    
        // 使用自己APP的ID(官网注册的)
        private static final String APP_ID = "1000270";
        // 使用自己APP的KEY(官网注册的)
        private static final String APP_KEY = "670100056270";
    
        // 此TAG在adb logcat中检索自己所需要的信息, 只需在命令行终端输入 adb logcat | grep
        // com.xiaomi.mipushdemo
        public static final String TAG = "com.xiaomi.mipushdemo";
    
        private static DemoHandler sHandler = null;
        private static MainActivity sMainActivity = null;
    
        //为了提高推送服务的注册率,官方Demo建议在Application的onCreate中初始化推送服务
        //你也可以根据需要,在其他地方初始化推送服务
        
        @Override
        public void onCreate() {
    
            super.onCreate();
            
            //判断用户是否已经打开App,详细见下面方法定义
            if (shouldInit()) {
            //注册推送服务
            //注册成功后会向DemoMessageReceiver发送广播
            // 可以从DemoMessageReceiver的onCommandResult方法中MiPushCommandMessage对象参数中获取注册信息
                MiPushClient.registerPush(this, APP_ID, APP_KEY);
             //参数说明
            //context:Android平台上app的上下文,建议传入当前app的application context
            //appID:在开发者网站上注册时生成的,MiPush推送服务颁发给app的唯一认证标识
           //appKey:在开发者网站上注册时生成的,与appID相对应,用于验证appID是否合法
            }
    
    
    		//下面是与测试相关的日志设置
            LoggerInterface newLogger = new LoggerInterface() {
    
                @Override
                public void setTag(String tag) {
                    // ignore
                }
    
                @Override
                public void log(String content, Throwable t) {
                    Log.d(TAG, content, t);
                }
    
                @Override
                public void log(String content) {
                    Log.d(TAG, content);
                }
            };
            Logger.setLogger(this, newLogger);
            if (sHandler == null) {
                sHandler = new DemoHandler(getApplicationContext());
            }
        }
    
    
    //通过判断手机里的所有进程是否有这个App的进程
    //从而判断该App是否有打开
        private boolean shouldInit() {
    //通过ActivityManager我们可以获得系统里正在运行的activities
    //包括进程(Process)等、应用程序/包、服务(Service)、任务(Task)信息。
            ActivityManager am = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE));
            List<RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
            String mainProcessName = getPackageName();
            
           //获取本App的唯一标识
            int myPid = Process.myPid();
            //利用一个增强for循环取出手机里的所有进程
            for (RunningAppProcessInfo info : processInfos) {
                //通过比较进程的唯一标识和包名判断进程里是否存在该App
                if (info.pid == myPid && mainProcessName.equals(info.processName)) {
                    return true;
                }
            }
            return false;
        }
    
        public static DemoHandler getHandler() {
            return sHandler;
        }
    
        public static void setMainActivity(MainActivity activity) {
            sMainActivity = activity;
        }
    
    
    //通过设置Handler来设置提示文案
        public static class DemoHandler extends Handler {
    
            private Context context;
    
            public DemoHandler(Context context) {
                this.context = context;
            }
    
            @Override
            public void handleMessage(Message msg) {
                String s = (String) msg.obj;
                if (sMainActivity != null) {
                    sMainActivity.refreshLogInfo();
                }
                if (!TextUtils.isEmpty(s)) {
                    Toast.makeText(context, s, Toast.LENGTH_LONG).show();
                }
            }
        }
    }
    

    总结:

    • 步骤1:先判断应用App是否已开启 - 通过判断系统里的进程
    • 通过静态方法
    public static void registerPush(Context context, String appID, String appKey)
    

    进行推送服务注册,详细参数如下:

    • 为了提高注册率,最好在Application的onCreate中初始化推送服务

    你也可以根据需要,在其他地方初始化推送服务

    1.2.2 DemoMessageReceiver类

    继承自PushMessageReceiver(抽象类,继承自BroadcastReceiver),其作用主要是:

    • 接收推送消息
    • 对推送消息进行处理

    DemoMessageReceiver.java

    package com.xiaomi.mipushdemo;
    
    import android.annotation.SuppressLint;
    import android.content.Context;
    import android.os.Message;
    import android.text.TextUtils;
    import android.util.Log;
    
    import com.xiaomi.mipush.sdk.ErrorCode;
    import com.xiaomi.mipush.sdk.MiPushClient;
    import com.xiaomi.mipush.sdk.MiPushCommandMessage;
    import com.xiaomi.mipush.sdk.MiPushMessage;
    import com.xiaomi.mipush.sdk.PushMessageReceiver;
    
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.List;
    
    /**
     * 1、PushMessageReceiver 是个抽象类,该类继承了 BroadcastReceiver。
     * 2、需要将自定义的 DemoMessageReceiver 注册在 AndroidManifest.xml
    
    
    
    public class DemoMessageReceiver extends PushMessageReceiver {
    
        private String mRegId;
        private String mTopic;
        private String mAlias;
        private String mAccount;
        private String mStartTime;
        private String mEndTime;
    
    
        //透传消息到达客户端时调用
        //作用:可通过参数message从而获得透传消息,具体请看官方SDK文档
        @Override
        public void onReceivePassThroughMessage(Context context, MiPushMessage message) {
            Log.v(DemoApplication.TAG,
                    "onReceivePassThroughMessage is called. " + message.toString());
            String log = context.getString(R.string.recv_passthrough_message, message.getContent());
            MainActivity.logList.add(0, getSimpleDate() + " " + log);
    
            if (!TextUtils.isEmpty(message.getTopic())) {
                mTopic = message.getTopic();
            } else if (!TextUtils.isEmpty(message.getAlias())) {
                mAlias = message.getAlias();
            }
    
            Message msg = Message.obtain();
            msg.obj = log;
            DemoApplication.getHandler().sendMessage(msg);
        }
    
    
    //通知消息到达客户端时调用
     	 //注:应用在前台时不弹出通知的通知消息到达客户端时也会回调函数
        //作用:通过参数message从而获得通知消息,具体请看官方SDK文档
       
        @Override
        public void onNotificationMessageArrived(Context context, MiPushMessage message) {
            Log.v(DemoApplication.TAG,
                    "onNotificationMessageArrived is called. " + message.toString());
            String log = context.getString(R.string.arrive_notification_message, message.getContent());
            MainActivity.logList.add(0, getSimpleDate() + " " + log);
    
            if (!TextUtils.isEmpty(message.getTopic())) {
                mTopic = message.getTopic();
            } else if (!TextUtils.isEmpty(message.getAlias())) {
                mAlias = message.getAlias();
            }
    
            Message msg = Message.obtain();
            msg.obj = log;
            DemoApplication.getHandler().sendMessage(msg);
        }
        
     	//用户手动点击通知栏消息时调用
     	 //注:应用在前台时不弹出通知的通知消息到达客户端时也会回调函数
        //作用:1. 通过参数message从而获得通知消息,具体请看官方SDK文档
        //2. 设置用户点击消息后打开应用 or 网页 or 其他页面
    
        @Override
        public void onNotificationMessageClicked(Context context, MiPushMessage message) {
            Log.v(DemoApplication.TAG,
                    "onNotificationMessageClicked is called. " + message.toString());
            String log = context.getString(R.string.click_notification_message, message.getContent());
            MainActivity.logList.add(0, getSimpleDate() + " " + log);
    
            if (!TextUtils.isEmpty(message.getTopic())) {
                mTopic = message.getTopic();
            } else if (!TextUtils.isEmpty(message.getAlias())) {
                mAlias = message.getAlias();
            }
    
            Message msg = Message.obtain();
            if (message.isNotified()) {
                msg.obj = log;
            }
            DemoApplication.getHandler().sendMessage(msg);
        }
    
    
     	
    	//用来接收客户端向服务器发送命令后的响应结果。
        @Override
        public void onCommandResult(Context context, MiPushCommandMessage message) {
            Log.v(DemoApplication.TAG,
                    "onCommandResult is called. " + message.toString());
            String command = message.getCommand();
            List<String> arguments = message.getCommandArguments();
            String cmdArg1 = ((arguments != null && arguments.size() > 0) ? arguments.get(0) : null);
            String cmdArg2 = ((arguments != null && arguments.size() > 1) ? arguments.get(1) : null);
            String log;
            if (MiPushClient.COMMAND_REGISTER.equals(command)) {
                if (message.getResultCode() == ErrorCode.SUCCESS) {
                    mRegId = cmdArg1;
                    log = context.getString(R.string.register_success);
    
                } else {
                    log = context.getString(R.string.register_fail);
                }
            } else if (MiPushClient.COMMAND_SET_ALIAS.equals(command)) {
                if (message.getResultCode() == ErrorCode.SUCCESS) {
                    mAlias = cmdArg1;
                    log = context.getString(R.string.set_alias_success, mAlias);
                } else {
                    log = context.getString(R.string.set_alias_fail, message.getReason());
                }
            } else if (MiPushClient.COMMAND_UNSET_ALIAS.equals(command)) {
                if (message.getResultCode() == ErrorCode.SUCCESS) {
                    mAlias = cmdArg1;
                    log = context.getString(R.string.unset_alias_success, mAlias);
                } else {
                    log = context.getString(R.string.unset_alias_fail, message.getReason());
                }
            } else if (MiPushClient.COMMAND_SET_ACCOUNT.equals(command)) {
                if (message.getResultCode() == ErrorCode.SUCCESS) {
                    mAccount = cmdArg1;
                    log = context.getString(R.string.set_account_success, mAccount);
                } else {
                    log = context.getString(R.string.set_account_fail, message.getReason());
                }
            } else if (MiPushClient.COMMAND_UNSET_ACCOUNT.equals(command)) {
                if (message.getResultCode() == ErrorCode.SUCCESS) {
                    mAccount = cmdArg1;
                    log = context.getString(R.string.unset_account_success, mAccount);
                } else {
                    log = context.getString(R.string.unset_account_fail, message.getReason());
                }
            } else if (MiPushClient.COMMAND_SUBSCRIBE_TOPIC.equals(command)) {
                if (message.getResultCode() == ErrorCode.SUCCESS) {
                    mTopic = cmdArg1;
                    log = context.getString(R.string.subscribe_topic_success, mTopic);
                } else {
                    log = context.getString(R.string.subscribe_topic_fail, message.getReason());
                }
            } else if (MiPushClient.COMMAND_UNSUBSCRIBE_TOPIC.equals(command)) {
                if (message.getResultCode() == ErrorCode.SUCCESS) {
                    mTopic = cmdArg1;
                    log = context.getString(R.string.unsubscribe_topic_success, mTopic);
                } else {
                    log = context.getString(R.string.unsubscribe_topic_fail, message.getReason());
                }
            } else if (MiPushClient.COMMAND_SET_ACCEPT_TIME.equals(command)) {
                if (message.getResultCode() == ErrorCode.SUCCESS) {
                    mStartTime = cmdArg1;
                    mEndTime = cmdArg2;
                    log = context.getString(R.string.set_accept_time_success, mStartTime, mEndTime);
                } else {
                    log = context.getString(R.string.set_accept_time_fail, message.getReason());
                }
            } else {
                log = message.getReason();
            }
            MainActivity.logList.add(0, getSimpleDate() + "    " + log);
    
            Message msg = Message.obtain();
            msg.obj = log;
            DemoApplication.getHandler().sendMessage(msg);
        }
    
    
     	//用于接收客户端向服务器发送注册命令后的响应结果。
        @Override
        public void onReceiveRegisterResult(Context context, MiPushCommandMessage message) {
            Log.v(DemoApplication.TAG,
                    "onReceiveRegisterResult is called. " + message.toString());
            String command = message.getCommand();
            List<String> arguments = message.getCommandArguments();
            String cmdArg1 = ((arguments != null && arguments.size() > 0) ? arguments.get(0) : null);
            String log;
            if (MiPushClient.COMMAND_REGISTER.equals(command)) {
                if (message.getResultCode() == ErrorCode.SUCCESS) {
                    mRegId = cmdArg1;
                    //打印日志:注册成功
                    log = context.getString(R.string.register_success);
                } else {
                		  //打印日志:注册失败
                    log = context.getString(R.string.register_fail);
                }
            } else {
                log = message.getReason();
            }
    
            Message msg = Message.obtain();
            msg.obj = log;
            DemoApplication.getHandler().sendMessage(msg);
        }
    
        @SuppressLint("SimpleDateFormat")
        private static String getSimpleDate() {
            return new SimpleDateFormat("MM-dd hh:mm:ss").format(new Date());
        }
    
    }
    

    总结

    • 根据需要复写PushMessageReceiver里对消息的相关处理方法,以下是相关方法的详情:
      相关方法详情

    • 关于onCommandResult(Context context,MiPushCommandMessage message)
      a. 作用:当客户端向服务器发送注册push、设置alias、取消注册alias、订阅topic、取消订阅topic等等命令后,从服务器返回结果。
      b. 参数说明
      参数说明

    1.2.3 MainActivity

    用于给用户设置标识,如别名、标签、账号等等

    MainActivity.java

    public class MainActivity extends Activity {
    
        public static List<String> logList = new CopyOnWriteArrayList<String>();
    
        private TextView mLogView = null;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            DemoApplication.setMainActivity(this);
    
            mLogView = (TextView) findViewById(R.id.log);
            
            // 设置别名
            findViewById(R.id.set_alias).setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    final EditText editText = new EditText(MainActivity.this);
                    new AlertDialog.Builder(MainActivity.this)
                            .setTitle(R.string.set_alias)
                            .setView(editText)
                            .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
    
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    String alias = editText.getText().toString();
    //调用静态方法进行设置                                MiPushClient.setAlias(MainActivity.this, alias, null);
                                }
    
                            })
                            .setNegativeButton(R.string.cancel, null)
                            .show();
                }
            });
            // 撤销别名
            findViewById(R.id.unset_alias).setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    final EditText editText = new EditText(MainActivity.this);
                    new AlertDialog.Builder(MainActivity.this)
                            .setTitle(R.string.unset_alias)
                            .setView(editText)
                            .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
    
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    String alias = editText.getText().toString();
    //调用静态方法进行设置                                  MiPushClient.unsetAlias(MainActivity.this, alias, null);
                                }
    
                            })
                            .setNegativeButton(R.string.cancel, null)
                            .show();
    
                }
            });
            // 设置帐号
            findViewById(R.id.set_account).setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    final EditText editText = new EditText(MainActivity.this);
                    new AlertDialog.Builder(MainActivity.this)
                            .setTitle(R.string.set_account)
                            .setView(editText)
                            .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
    
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    String account = editText.getText().toString();
    //调用静态方法进行设置                                  MiPushClient.setUserAccount(MainActivity.this, account, null);
                                }
    
                            })
                            .setNegativeButton(R.string.cancel, null)
                            .show();
    
                }
            });
            // 撤销帐号
            findViewById(R.id.unset_account).setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    final EditText editText = new EditText(MainActivity.this);
                    new AlertDialog.Builder(MainActivity.this)
                            .setTitle(R.string.unset_account)
                            .setView(editText)
                            .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
    
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    String account = editText.getText().toString();
    //调用静态方法进行设置                                  MiPushClient.unsetUserAccount(MainActivity.this, account, null);
                                }
    
                            })
                            .setNegativeButton(R.string.cancel, null)
                            .show();
                }
            });
            // 设置标签
            findViewById(R.id.subscribe_topic).setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    final EditText editText = new EditText(MainActivity.this);
                    new AlertDialog.Builder(MainActivity.this)
                            .setTitle(R.string.subscribe_topic)
                            .setView(editText)
                            .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
    
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    String topic = editText.getText().toString();
    //调用静态方法进行设置                                  MiPushClient.subscribe(MainActivity.this, topic, null);
                                }
    
                            })
                            .setNegativeButton(R.string.cancel, null)
                            .show();
                }
            });
            // 撤销标签
            findViewById(R.id.unsubscribe_topic).setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    final EditText editText = new EditText(MainActivity.this);
                    new AlertDialog.Builder(MainActivity.this)
                            .setTitle(R.string.unsubscribe_topic)
                            .setView(editText)
                            .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
    
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    String topic = editText.getText().toString();
    //调用静态方法进行设置                                  MiPushClient.unsubscribe(MainActivity.this, topic, null);
                                }
    
                            })
                            .setNegativeButton(R.string.cancel, null)
                            .show();
                }
            });
            // 设置接收消息时间
            findViewById(R.id.set_accept_time).setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    new TimeIntervalDialog(MainActivity.this, new TimeIntervalInterface() {
    
                        @Override
                        public void apply(int startHour, int startMin, int endHour,
                                          int endMin) {
                            //调用静态方法进行设置  
                            MiPushClient.setAcceptTime(MainActivity.this, startHour, startMin, endHour, endMin, null);
                        }
    
                        @Override
                        public void cancel() {
                            //ignore
                        }
    
                    })
                            .show();
                }
            });
            // 暂停推送
            findViewById(R.id.pause_push).setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                    MiPushClient.pausePush(MainActivity.this, null);
                }
            });
    
            findViewById(R.id.resume_push).setOnClickListener(new OnClickListener() {
    
                @Override
                public void onClick(View v) {
                //调用静态方法进行设置  
                    MiPushClient.resumePush(MainActivity.this, null);
                }
            });
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            refreshLogInfo();
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            DemoApplication.setMainActivity(null);
        }
    
        public void refreshLogInfo() {
            String AllLog = "";
            for (String log : logList) {
                AllLog = AllLog + log + "\n\n";
            }
            mLogView.setText(AllLog);
        }
    }
    

    总结

    根据需求对不同用户设置不同的推送标识,如别名、标签等等。

    a. 别名(Alias)

    • 开发者可以为指定用户设置别名,然后给这个别名推送消息,

    效果等同于给RegId推送消息,Alias是除Regid(自动生成的)和UserAccount之外的第三个用户标识

    • 开发者可以取消指定用户的某个别名,服务器就不会给这个别名推送消息了。
    //设置别名
    MiPushClient.setAlias(Context context, String alias, String category);
    
    //撤销别名
    MiPushClient.unsetAlias(Context context, String alias, String category);
    //参数说明
    //context:Android平台上app的上下文,建议传入当前app的application context
    //alias:为指定用户设置别名 / 为指定用户取消别名
    //category:扩展参数,暂时没有用途,直接填null
    
    //获取该客户端所有的别名
    public static List<String> getAllAlias(final Context context)
    

    b. 用户账号(UserAccoun)

    • 开发者可以为指定用户设置userAccount
    • 开发者可以取消指定用户的某个userAccount,服务器就不会给这个userAccount推送消息了
    //设置
    MiPushClient.setUserAccount(final Context context, final String userAccount, String
    category)
    
    //撤销
    MiPushClient.unsetUserAccount(final Context context, final String userAccount, String
    category)
    //参数说明
    //context:Android平台上app的上下文,建议传入当前app的application context
    //userAccount:为指定用户设置userAccount / 为指定用户取消userAccount
    //category:扩展参数,暂时没有用途,直接填null
    
    //获取该客户端所有设置的账号
    public static List<String> getAllUserAccount(final Context context)
    

    c. 标签(Topic)

    • 开发者可以结合自己的业务特征,给用户打上不同的标签。
    • 消息推送时,开发者可以结合每条消息的内容和目标用户,为每条消息选择对应的标签,为开发者可以根据订阅的主题实现分组群发,从而进行消息的精准推送
    //设置标签
    MiPushClient.subscribe(Context context, String topic, String category);
    //撤销标签
    MiPushClient.unsubscribe(Context context, String topic, String category);
    //参数说明
    //context:Android平台上app的上下文,建议传入当前app的application context
    //topic:为指定用户设置设置订阅的主题 / 为指定用户取消订阅的主题
    //category:扩展参数,暂时没有用途,直接填null
    
    //获取该客户端所有的标签
    public static List<String> getAllTopic(final Context context);
    
    

    TimeIntervalDialog

    作用:用于设置推送的时间-开始时间+暂停时间

    package com.xiaomi.mipushdemo;
    
    import android.app.Dialog;
    import android.content.Context;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TimePicker;
    import android.widget.TimePicker.OnTimeChangedListener;
    
    //继承OnTimeChangedListener接口
    public class TimeIntervalDialog extends Dialog implements OnTimeChangedListener {
    
    	
        private TimeIntervalInterface mTimeIntervalInterface;
        private Context mContext;
        private TimePicker mStartTimePicker, mEndTimePicker;
        private int mStartHour, mStartMinute, mEndHour, mEndMinute;
    
        private Button.OnClickListener clickListener = new Button.OnClickListener() {
    
            @Override
            public void onClick(View v) {
                switch (v.getId()) {
                    case R.id.apply:
                        dismiss();
                        //设置时间参数
                        mTimeIntervalInterface.apply(mStartHour, mStartMinute, mEndHour, mEndMinute);
                        break;
                    case R.id.cancel:
                        dismiss();
                        mTimeIntervalInterface.cancel();
                        break;
                    default:
                        break;
                }
            }
        };
    
        public TimeIntervalDialog(Context context, TimeIntervalInterface timeIntervalInterface,
                                  int startHour, int startMinute, int endHour, int endMinute) {
            super(context);
            mContext = context;
            this.mTimeIntervalInterface = timeIntervalInterface;
            this.mStartHour = startHour;
            this.mStartMinute = startMinute;
            this.mEndHour = endHour;
            this.mEndMinute = endMinute;
        }
    
        public TimeIntervalDialog(Context context, TimeIntervalInterface timeIntervalInterface) {
            this(context, timeIntervalInterface, 0, 0, 23, 59);
        }
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.set_time_dialog);
            setCancelable(true);
            setTitle(mContext.getString(R.string.set_accept_time));
            mStartTimePicker = (TimePicker) findViewById(R.id.startTimePicker);
            mStartTimePicker.setIs24HourView(true);
            mStartTimePicker.setCurrentHour(mStartHour);
            mStartTimePicker.setCurrentMinute(mStartMinute);
            mStartTimePicker.setOnTimeChangedListener(this);
            mEndTimePicker = (TimePicker) findViewById(R.id.endTimePicker);
            mEndTimePicker.setIs24HourView(true);
            mEndTimePicker.setCurrentHour(mEndHour);
            mEndTimePicker.setCurrentMinute(mEndMinute);
            mEndTimePicker.setOnTimeChangedListener(this);
            Button applyBtn = (Button) findViewById(R.id.apply);
            applyBtn.setOnClickListener(clickListener);
            Button cancelBtn = (Button) findViewById(R.id.cancel);
            cancelBtn.setOnClickListener(clickListener);
        }
    
        @Override
        public void onTimeChanged(TimePicker view, int hourOfDay, int minute) {
            if (view == mStartTimePicker) {
                mStartHour = hourOfDay;
                mStartMinute = minute;
            } else if (view == mEndTimePicker) {
                mEndHour = hourOfDay;
                mEndMinute = minute;
            }
        }
    
        interface TimeIntervalInterface {
            void apply(int startHour, int startMin, int endHour, int endMin);
    
            void cancel();
        }
    }
    
    

    总结

    • 使用一个继承了Dialog类的TimeIntervalDialog类进行推送时间的配置
    • 可进行的配置:设置推送时间(开始 & 结束)、暂停推送时间、恢复推送时间
    //设置推送时间(开始 & 结束)
    MiPushClient.setAcceptTime(Context context, int startHour, int startMin, int endHour,
    int endMin, String category)
    //设置暂停推送时间、恢复推送时间
    pausePush(Context context, String category)`和`resumePush(Context context, String category)
    //参数说明
    //context:Android平台上app的上下文,建议传入当前app的application context
    //startHour:接收时段开始时间的小时
    //startMin	:接收时段开始时间的分钟
    //endHour:接收时段结束时间的小时
    //endMin:接收时段结束时间的分钟
    //category:扩展参数,暂时没有用途,直接填null
    
    

    AndroidManifest文件的配置

    //小米推送支持最低的Android版本是2.2
    <uses-sdk  android:minSdkVersion="8"/>
    
    //设置一系列权限
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
        <uses-permission android:name="android.permission.READ_PHONE_STATE" />
        <uses-permission android:name="android.permission.GET_TASKS" />
        <uses-permission android:name="android.permission.VIBRATE" />
    
    //这里com.xiaomi.mipushdemo改成自身app的包名
    	<permission android:name="com.xiaomi.mipushdemo.permission.MIPUSH_RECEIVE" android:protectionLevel="signature" />
    
    //这里com.xiaomi.mipushdemo改成自身app的包名
    	<uses-permission android:name="com.xiaomi.mipushdemo.permission.MIPUSH_RECEIVE" />
    
    
    //注册广播BroadcastReceiver & Service
    //都是静态注册,因为要长期处在后台运行
    //注:共是3个广播接收器和4个服务,其中包括继承了PushMessageReceiver的DemoMessageReceiver
                    
            //4个后台服务
    		<service
    		  android:enabled="true"
    		  android:process=":pushservice"
    		  android:name="com.xiaomi.push.service.XMPushService"/>
    
            //此service必须在3.0.1版本以后(包括3.0.1版本)加入
    		<service
    		  android:name="com.xiaomi.push.service.XMJobService"
    		  android:enabled="true"
    		  android:exported="false"
    		  android:permission="android.permission.BIND_JOB_SERVICE"
    		  android:process=":pushservice" />
    		
            //此service必须在2.2.5版本以后(包括2.2.5版本)加入
    		<service
    		  android:enabled="true"
    		  android:exported="true"
    		  android:name="com.xiaomi.mipush.sdk.PushMessageHandler" /> 
    
    		<service android:enabled="true"
    		  android:name="com.xiaomi.mipush.sdk.MessageHandleService" /> 
    		
    
            //3个广播
    		<receiver
    		  android:exported="true"
    		  android:name="com.xiaomi.push.service.receivers.NetworkStatusReceiver" >
    		  <intent-filter>
    		    <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    		    <category android:name="android.intent.category.DEFAULT" />
    		  </intent-filter>
    		</receiver>
    
    		<receiver
    		  android:exported="false"
    		  android:process=":pushservice"
    		  android:name="com.xiaomi.push.service.receivers.PingReceiver" >
    		  <intent-filter>
    		    <action android:name="com.xiaomi.push.PING_TIMER" />
    		  </intent-filter>
    		</receiver>
    
    //继承了PushMessageReceiver的DemoMessageReceiver的广播注册
    		<receiver
                android:name="com.xiaomi.mipushdemo.DemoMessageReceiver"
                android:exported="true">
                <intent-filter>
                    <action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" />
                </intent-filter>
                <intent-filter>
                    <action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED" />
                </intent-filter>
                <intent-filter>
                    <action android:name="com.xiaomi.mipush.ERROR" />
                </intent-filter>
            </receiver>
    

    2. 集成小米推送步骤汇总

    • 步骤1:在小米推送平台进行相关注册开发者账号,并进行应用的注册:应用包名,AppID和AppKey
    • 步骤2:将小米推送的SDK包加入库
    • 步骤3:在应用内初始化小米推送服务
    • 步骤4:继承PushMessageReceiver,并复写相关推送消息的方法
    • 步骤5:在AndroidManifest文件里面配置好权限、注册Service和BroadcastReceiver

    在Android6.0里面的权限需要动态获取

    • 步骤6:根据需要设置一系列的推送设置,如用户别名、标签等等

    接下来,我们来按照上面的步骤,一步步来实现一个简易的小米推送Demo

    3. 实例解析

    步骤1:在小米推送平台进行相关注册开发者账号,并进行应用的注册:应用包名,AppID和AppKey

    注意,填入的包名要跟你的应用App的包名是一致的
    创建应用

    AppID和Key

    步骤2:将小米推送的SDK包加入到你应用的库里

    点击此处进行下载

    小米推送SDK

    步骤3:在应用内初始化小米推送服务

    为了提高推送服务的注册率,我选择在Application的onCreate中初始化推送服务

    BaseActivity.java

    package scut.carson_ho.demo_mipush;
    
    import android.app.ActivityManager;
    import android.app.Application;
    import android.content.Context;
    import android.os.Process;
    
    import com.xiaomi.mipush.sdk.MiPushClient;
    
    import java.util.List;
    
    /**
     * Created by Carson_Ho on 16/10/26.
     */
    
        //主要要继承Application
    public class BaseActivity extends Application {
        // 使用自己APP的ID(官网注册的)
        private static final String APP_ID = "2882303761517520369";
        // 使用自己APP的Key(官网注册的)
        private static final String APP_KEY = "5401752085369";
    
    
        //为了提高推送服务的注册率,我建议在Application的onCreate中初始化推送服务
        //你也可以根据需要,在其他地方初始化推送服务
        @Override
        public void onCreate() {
            super.onCreate();
    
    
            if (shouldInit()) {
                //注册推送服务
                //注册成功后会向DemoMessageReceiver发送广播
                // 可以从DemoMessageReceiver的onCommandResult方法中MiPushCommandMessage对象参数中获取注册信息
                MiPushClient.registerPush(this, APP_ID, APP_KEY);
            }
        }
    
        //通过判断手机里的所有进程是否有这个App的进程
        //从而判断该App是否有打开
        private boolean shouldInit() {
    
        //通过ActivityManager我们可以获得系统里正在运行的activities
        //包括进程(Process)等、应用程序/包、服务(Service)、任务(Task)信息。
            ActivityManager am = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE));
            List<ActivityManager.RunningAppProcessInfo> processInfos = am.getRunningAppProcesses();
            String mainProcessName = getPackageName();
    
            //获取本App的唯一标识
            int myPid = Process.myPid();
            //利用一个增强for循环取出手机里的所有进程
            for (ActivityManager.RunningAppProcessInfo info : processInfos) {
                //通过比较进程的唯一标识和包名判断进程里是否存在该App
                if (info.pid == myPid && mainProcessName.equals(info.processName)) {
                    return true;
                }
            }
            return false;
        }
    }
    

    注意要在Android.manifest.xml里的application里加入

    android:name=".BaseActivity"
    

    这样在应用初始化时是第一个加载BaseActivity.java类文件的
    如下图:
    示意图

    步骤4:设置子类继承PushMessageReceiver,并复写相关推送消息的方法

    Mipush_Broadcast.java

    package scut.carson_ho.demo_mipush;
    
    import android.content.Context;
    
    import com.xiaomi.mipush.sdk.ErrorCode;
    import com.xiaomi.mipush.sdk.MiPushClient;
    import com.xiaomi.mipush.sdk.MiPushCommandMessage;
    import com.xiaomi.mipush.sdk.MiPushMessage;
    import com.xiaomi.mipush.sdk.PushMessageReceiver;
    
    /**
     * Created by Carson_Ho on 16/10/26.
     */
    
    public class Mipush_Broadcast extends PushMessageReceiver {
    
        //透传消息到达客户端时调用
        //作用:可通过参数message从而获得透传消息,具体请看官方SDK文档
        @Override
        public void onReceivePassThroughMessage(Context context, MiPushMessage message) {
    
            //打印消息方便测试
            System.out.println("透传消息到达了");
            System.out.println("透传消息是"+message.toString());
    
        }
    
    
    //通知消息到达客户端时调用
        //注:应用在前台时不弹出通知的通知消息到达客户端时也会回调函数
        //作用:通过参数message从而获得通知消息,具体请看官方SDK文档
    
        @Override
        public void onNotificationMessageArrived(Context context, MiPushMessage message) {
            //打印消息方便测试
            System.out.println("通知消息到达了");
            System.out.println("通知消息是"+message.toString());
        }
    
        //用户手动点击通知栏消息时调用
        //注:应用在前台时不弹出通知的通知消息到达客户端时也会回调函数
        //作用:1. 通过参数message从而获得通知消息,具体请看官方SDK文档
        //2. 设置用户点击消息后打开应用 or 网页 or 其他页面
    
        @Override
        public void onNotificationMessageClicked(Context context, MiPushMessage message) {
    
            //打印消息方便测试
            System.out.println("用户点击了通知消息");
            System.out.println("通知消息是" + message.toString());
            System.out.println("点击后,会进入应用" );
    
        }
    
        //用来接收客户端向服务器发送命令后的响应结果。
        @Override
        public void onCommandResult(Context context, MiPushCommandMessage message) {
    
            String command = message.getCommand();
            System.out.println(command );
            
    
            if (MiPushClient.COMMAND_REGISTER.equals(command)) {
                if (message.getResultCode() == ErrorCode.SUCCESS) {
                    
                    //打印信息便于测试注册成功与否
                    System.out.println("注册成功");
    
                } else {
                    System.out.println("注册失败");
                }
            }
        }
    
        //用于接收客户端向服务器发送注册命令后的响应结果。
        @Override
        public void onReceiveRegisterResult(Context context, MiPushCommandMessage message) {
    
            String command = message.getCommand();
            System.out.println(command );
        
            if (MiPushClient.COMMAND_REGISTER.equals(command)) {
                if (message.getResultCode() == ErrorCode.SUCCESS) {
                    
                    //打印日志:注册成功
                    System.out.println("注册成功");
                } else {
                    //打印日志:注册失败
                    System.out.println("注册失败");
                }
            } else {
                System.out.println("其他情况"+message.getReason());
            }
        }
    
    }
    

    具体设置请看官方SDK文档,这里只给出最简单Demo,不作过多描述

    步骤5:在AndroidManifest文件里面配置好权限、注册Service和BroadcastReceiver

    AndroidManifest.xml

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="scut.carson_ho.demo_mipush">
    
        //相关权限
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
        <uses-permission android:name="android.permission.READ_PHONE_STATE" />
        <uses-permission android:name="android.permission.GET_TASKS" />
        <uses-permission android:name="android.permission.VIBRATE" />
    
    
        //注意这里.permission.MIPUSH_RECEIVE是自身app的包名
        <permission android:name="scut.carson_ho.demo_mipush.permission.MIPUSH_RECEIVE" android:protectionLevel="signature" />
    
        //注意这里.permission.MIPUSH_RECEIVE是自身app的包名
        <uses-permission android:name="scut.carson_ho.demo_mipush.permission.MIPUSH_RECEIVE" />
    
    //注意要初始化BaseActivity.java类
        <application
            android:name=".BaseActivity"
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
    
    
    
        //注册广播BroadcastReceiver和Service
        //都是静态注册,因为要长期处在后台运行
        //注:共是3个广播接收器和4个服务,其中包括继承了PushMessageReceiver的DemoMessageReceiver
    
        //4个后台服务
        <service
            android:enabled="true"
            android:process=":pushservice"
            android:name="com.xiaomi.push.service.XMPushService"/>
    
        //此service必须在3.0.1版本以后(包括3.0.1版本)加入
        <service
            android:name="com.xiaomi.push.service.XMJobService"
            android:enabled="true"
            android:exported="false"
            android:permission="android.permission.BIND_JOB_SERVICE"
            android:process=":pushservice" />
    
        //此service必须在2.2.5版本以后(包括2.2.5版本)加入
        <service
            android:enabled="true"
            android:exported="true"
            android:name="com.xiaomi.mipush.sdk.PushMessageHandler" />
    
        <service android:enabled="true"
            android:name="com.xiaomi.mipush.sdk.MessageHandleService" />
    
    
        //3个广播
        <receiver
            android:exported="true"
            android:name="com.xiaomi.push.service.receivers.NetworkStatusReceiver" >
            <intent-filter>
                <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>
    
        <receiver
            android:exported="false"
            android:process=":pushservice"
            android:name="com.xiaomi.push.service.receivers.PingReceiver" >
            <intent-filter>
                <action android:name="com.xiaomi.push.PING_TIMER" />
            </intent-filter>
        </receiver>
    
        //继承了PushMessageReceiver的DemoMessageReceiver的广播注册
        <receiver
            android:name=".Mipush_Broadcast"
            android:exported="true">
            <intent-filter>
                <action android:name="com.xiaomi.mipush.RECEIVE_MESSAGE" />
            </intent-filter>
            <intent-filter>
                <action android:name="com.xiaomi.mipush.MESSAGE_ARRIVED" />
            </intent-filter>
            <intent-filter>
                <action android:name="com.xiaomi.mipush.ERROR" />
            </intent-filter>
        </receiver>
    
    
        </application>
    </manifest>
    
    

    步骤6:根据需要设置一系列的推送设置,如用户别名、标签等等

    • 此处是简单Demo,所以不作过多的设置
    • 更多设置请回看上方官方Demo解析

    运行结果

    测试成功结果

    好了,客户端的代码写好后,可以去小米官网测试一下消息推送了

    步骤1:在小米官网的消息推送里选择你创建的应用,然后点击“推送工具”

    点击推送工具

    步骤2:设置推送消息的相关信息

    可进行的配置非常全面,基本上能满足推送的需求

    设置推送消息
    设置推送消息

    推送的结果

    消息到达客户端

    测试结果
    测试结果:收到的信息

    点击通知栏消息后

    4. Demo下载地址

    Carson的Github:Demo_MiPush

    5. 关于对小米推送的思考(问题)

    上述说的小米推送看似简单:初始化推送服务 + 相关推送设置。但是,好的代码不仅能在正常情况下工作,还应该充分考虑失败情况。那么,有什么样的失败情况需要我们考虑呢?

    • 背景:在这个初始化推送服务的过程中,是需要联系小米推送的服务器来申请reg id(即推送token)。
    • 冲突:初始化过程可能失败:网络问题(没网or网络信号弱)、服务器问题导致初始化失败。那么,当失败以后,该什么时候再次进行初始化呢?

    小米推送的Demo里并没有相关措施解决这个问题

    • 解决方案:在初始化失败的情况下提供重试机制,直到初始化成功(可以通过检测是否已经拿到推送token来确定),问题解决的逻辑如下:

    解决逻辑

    • 具体代码在这里就不作过多描述,如果你希望获得含注册重试机制的小米推送源代码,请在评论留下你的邮箱,我将亲自发送到你的邮箱
    1. 知识点涵盖:网络数据的检测 & 广播接收器
    2. 具体请看我写的另外两篇文章:
      Android:BroadcastReceiver广播接收器最全面解析
      Android:检测网络状态&监听网络变化

    总结

    全面考虑到所有异常问题并恰当地进行处理才能真正体现程序猿的功力,希望大家做撸代码的时候不要只做代码的搬运工,纯粹写代码并不会让你成长,关键在于思考

    6. 总结


    请帮顶!因为你的鼓励是我写作的最大动力!

    展开全文
  • Android 极光推送

    千次阅读 2017-04-05 21:23:21
    Android 极光推送 效果图 Android SDK 概述 Android 常见问题Android 客户端 SDK 下载 JPush Android 开发者集成 JPush Android SDK 到其应用里,JPush Android SDK 创建到 JPush Cloud 的长连接,为 App ...
    Android 极光推送
    效果图

    极光推送步骤:
    1.首先需要打开极光推送官网:点击打开链接 注册账号并登陆
    2.在主页选择SDK下载(如果嫌麻烦可以参考下文中的jcenter 自动集成步骤)
    3.在主类MainActivity中onCreate方法中添加
    //如果是正式上线的APP则改为True
    JPushInterface.setDebugMode(true);
    //初始化极光推送
    JPushInterface.init(this);

    Android SDK 概述

    JPush Android

    jpush_android

    开发者集成 JPush Android SDK 到其应用里,JPush Android SDK 创建到 JPush Cloud 的长连接,为 App 提供永远在线的能力。
    当开发者想要及时地推送消息到达 App 时,只需要调用 JPush API 推送,或者使用其他方便的智能推送工具,即可轻松与用户交流。

    图中红色部分,是 JPush 与 App 开发者的接触点。手机客户端侧,App 需要集成 JPush SDK;服务器端部分,开发者调用 JPush REST API 来进行推送。

    Android SDK 服务

    JPush Android SDK 是作为 Android Service 长期运行在后台的,从而创建并保持长连接,保持永远在线的能力。

    多平台支持

    JPush Android SDK 除了 jar 包,还有一个 .so文件,.so文件需要与CPU平台适配,需要支持哪个平台的 CPU,就需要包含这个平台相应的 .so 编译文件。

    除支持默认的 ARM CPU 平台之外,JPush SDK 还提供 x86 与 MIPs 平台的 CPU 版本 SDK。请单独到 资源下载 页下载。

    电量与流量

    JPush Android SDK 由于使用自定义协议,协议体做得极致地小,流量消耗非常地小。

    电量方面,JPush Android SDK 经过持续地优化,尽可能减少不必要的代码执行;并且,长期的版本升级迭代,不断地调优,在保证一定的网络连接稳定性的要求小,减少电量消耗。

    压缩包说明

    供下载的 JPush Android SDK 压缩包,一般包含以下几个部分:

    • .jar 文件
    • .so 文件
    • AndroidManifest.xml 配置示例

    其中 .jar, .so 文件有版本号后缀,需要互相匹配。请升级时一定记得检查版本号,并删除旧版本。

    AndroidManifest.xml 配置示例可能在版本升级时,会有变更。请留意版本发布说明。

    Android SDK 集成

    请参考以下文档与教程,来集成 Android SDK。

    集成到其他平台

    客户端集成插件

    Android SDK 集成指南

    使用提示

    本文是 JPush Android SDK 标准的集成指南文档。用以指导 SDK 的使用方法,默认读者已经熟悉IDE(Eclipse 或者 Android Studio)的基本使用方法,以及具有一定的 Android 编程知识基础。

    本篇指南匹配的 JPush Android SDK 版本为:v3.0.0 及以后版本。

    • 3 分钟快速 Demo(Android):如果您想要快速地测试、感受下极光推送的效果,请参考本文在几分钟内跑通Demo。
    • 极光推送文档网站上,有极光推送相关的所有指南、API、教程等全部的文档。包括本文档的更新版本,都会及时地发布到该网站上。
    • 极光社区网站:大家除了文档之外,还有问题与疑问,会到这里来提问题,以及时地得到解答。
    • 如果您看到本文档,但还未下载Android SDK,请访问SDK下载页面下载。

    产品功能说明

    极光推送(JPush)是一个端到端的推送服务,使得服务器端消息能够及时地推送到终端用户手机上,让开发者积极地保持与用户的连接,从而提高用户活跃度、提高应用的留存率。极光推送客户端支持 Android, iOS 两个平台。

    本 Android SDK 方便开发者基于 JPush 来快捷地为 Android App 增加推送功能。

    主要功能

    • 保持与服务器的长连接,以便消息能够即时推送到达客户端
    • 接收通知与自定义消息,并向开发者App 传递相关信息

    主要特点

    • 客户端维持连接占用资源少、耗电低
    • SDK丰富的接口,可定制通知栏提示样式
    • 服务器大容量、稳定

    jpush-android-release-3.x.y.zip 集成压缩包内容

    • AndroidManifest.xml
      • 客户端嵌入SDK参考的配置文件
    • libs/jcore-android.v1.x.y.jar
      • 极光开发者服务的核心包。
    • libs/jpush-android_v3.x.y.jar
      • JPush SDK 开发包。
    • libs/(cpu-type)/libjcore1xy.so
      • 各种CPU类型的native开发包。
    • res
      • 集成SDK必须添加的资源文件
    • example
      • 是一个完整的 Android 项目,通过这个演示了 JPush SDK 的基本用法,可以用来做参考。

    Android SDK 版本

    目前SDK只支持Android 2.3或以上版本的手机系统。富媒体信息流功能则需Android3.0或以上版本的系统。

    jcenter 自动集成步骤

    说明 : 使用jcenter自动集成的开发者,不需要在项目中添加jar和so,jcenter会自动完成依赖;在AndroidManifest.xml中不需要添加任何JPush SDK 相关的配置,jcenter会自动导入。

    • 如果开发者需要修改组件属性,可以在本地的 AndroidManifest 中定义同名的组件并配置想要的属性,然后用 xmlns:tools 来控制本地组件覆盖 jcenter 上的组件。示例:

      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.android.tests.flavorlib.app"
          xmlns:tools="http://schemas.android.com/tools">
      
          <application
              android:icon="@drawable/icon"
              android:name="com.example.jpushdemo.ExampleApplication"
              android:label="@string/app_name" >
      
              <service android:name="cn.jpush.android.service.PushService"
                  android:process=":multiprocess"
                  tools:node="replace" >
      
                  ……
              </service>
      
          ……
        </application>
      
        ……
      </manifest>
      
    • 确认android studio的 Project 根目录的主 gradle 中配置了jcenter支持。(新建project默认配置就支持)

      buildscript {
          repositories {
              jcenter()
          }
          ......
      }
      
      allprojects {
          repositories {
              jcenter()
          }
      }
      
    • 在 module 的 gradle 中添加依赖和AndroidManifest的替换变量。

      android {
          ......
          defaultConfig {
              applicationId "com.xxx.xxx" //JPush上注册的包名.
              ......
      
              ndk {
                  //选择要添加的对应cpu类型的.so库。 
                  abiFilters 'armeabi', 'armeabi-v7a', 'armeabi-v8a' 
                  // 还可以添加 'x86', 'x86_64', 'mips', 'mips64'
              }
      
              manifestPlaceholders = [
                  JPUSH_PKGNAME : applicationId,
                  JPUSH_APPKEY : "你的appkey", //JPush上注册的包名对应的appkey.
                  JPUSH_CHANNEL : "developer-default", //暂时填写默认值即可.
              ]
              ......
          }
          ......
      }
      
      dependencies {
          ......
      
          compile 'cn.jiguang.sdk:jpush:3.0.3'  // 此处以JPush 3.0.3 版本为例。
          compile 'cn.jiguang.sdk:jcore:1.1.1'  // 此处以JCore 1.1.1 版本为例。
          ......
      }
      

     : 如果在添加以上 abiFilter 配置之后android Studio出现以下提示:

        NDK integration is deprecated in the current plugin. Consider trying the new experimental plugin.
    

    则在 Project 根目录的gradle.properties文件中添加:

        android.useDeprecatedNdk=true
    

    说明:若没有res/drawable-xxxx/jpush_notification_icon这个资源默认使用应用图标作为通知icon,在5.0以上系统将应用图标作为statusbar icon可能显示不正常,用户可定义没有阴影和渐变色的icon替换这个文件,文件名不要变。

    手动集成步骤

    • 解压缩 jpush-android-release-3.x.y.zip 集成压缩包。
    • 复制 libs/jcore-android_v1.x.y.jar 到工程 libs/ 目录下。
    • 复制 libs/jpush-android_v3.x.y.jar 到工程 libs/ 目录下。
    • 复制 libs/(cpu-type)/libjcore1xy.so 到你的工程中存放对应cpu类型的目录下。
    • 复制 res/ 中drawable-hdpi, layout, values文件夹中的资源文件到你的工程中 res/ 对应同名的目录下。

    说明 1:若没有res/drawable-xxxx/jpush_notification_icon这个资源默认使用应用图标作为通知icon,在5.0以上系统将应用图标作为statusbar icon可能显示不正常,用户可定义没有阴影和渐变色的icon替换这个文件,文件名不要变。

    说明 2:使用android studio的开发者,如果使用jniLibs文件夹导入so文件,则仅需将所有cpu类型的文件夹拷进去;如果将so文件添加在module的libs文件夹下,注意在module的gradle配置中添加一下配置:

        android {
            ......
            sourceSets {
                main {
                    jniLibs.srcDirs = ['libs']
                    ......
                }
                ......
            }
            ......
        }
    

    配置 AndroidManifest.xml

    根据 SDK 压缩包里的 AndroidManifest.xml 样例文件,来配置应用程序项目的 AndroidManifest.xml 。

    主要步骤为:

    • 复制备注为 "Required" 的部分
    • 将标注为“您应用的包名”的部分,替换为当前应用程序的包名
    • 将标注为“您应用的Appkey”的部分,替换为在Portal上注册该应用的的Key,例如:9fed5bcb7b9b87413678c407

    小帖士

    如果使用android studio, 可在AndroidManifest中引用applicationId的值,在build.gradle配置中 defaultConfig节点下配置,如:

    defaultConfig {
          applicationId "cn.jpush.example" // <--您应用的包名
          ……
     }
    
    

    在AndroidManifest中使用 ${applicationId} 引用gradle中定义的包名

    AndroidManifest 示例

    
    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="您应用的包名"
        android:versionCode="303"
        android:versionName="3.0.3"
        >
        <uses-sdk android:minSdkVersion="9" android:targetSdkVersion="23" />
    
        <!-- Required -->
        <permission 
            android:name="您应用的包名.permission.JPUSH_MESSAGE"  
            android:protectionLevel="signature" />
    
        <!-- Required -->
        <uses-permission android:name="您应用的包名.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.WAKE_LOCK" />
        <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.VIBRATE" />
        <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" />
    
        <application
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:name="Your Application Name">
    
            <!-- 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 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="您应用的包名"/>
                 </intent-filter>
             </service>
    
            <!-- Required SDK核心功能-->
            <receiver
                android:name="cn.jpush.android.service.PushReceiver"
                android:enabled="true" >
              <intent-filter android:priority="1000"> 
                    <action android:name="cn.jpush.android.intent.NOTIFICATION_RECEIVED_PROXY" /> 
                    <category android:name="您应用的包名"/> 
                </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核心功能-->
            <activity
                android:name="cn.jpush.android.ui.PushActivity"
                android:configChanges="orientation|keyboardHidden"
                android:theme="@android:style/Theme.NoTitleBar"
                android:exported="false" >
                <intent-filter>
                    <action android:name="cn.jpush.android.ui.PushActivity" />
                    <category android:name="android.intent.category.DEFAULT" />
                    <category android:name="您应用的包名" />
                </intent-filter>
            </activity>
            <!-- SDK核心功能-->
            <activity
                android:name="cn.jpush.android.ui.PopWinActivity"
                android:configChanges="orientation|keyboardHidden"
                android:exported="false"
                android:theme="@style/MyDialogStyle">
                <intent-filter>
                    <category android:name="android.intent.category.DEFAULT" />
                    <category android:name="您应用的包名" />
                </intent-filter>
            </activity>
    
            <!-- Required SDK核心功能-->
            <service
                android:name="cn.jpush.android.service.DownloadService"
                android:enabled="true"
                android:exported="false" >
            </service>
    
            <!-- Required SDK核心功能-->
            <receiver android:name="cn.jpush.android.service.AlarmReceiver" />
    
            <!-- User defined. 用户自定义的广播接收器-->
             <receiver
                 android:name="您自己定义的Receiver"
                 android:enabled="true">
                 <intent-filter>
                     <!--Required 用户注册SDK的intent-->
                     <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 用户打开自定义通知栏的intent-->
                     <action android:name="cn.jpush.android.intent.NOTIFICATION_OPENED" /> 
                     <!-- 接收网络变化 连接/断开 since 1.6.3 -->
                     <action android:name="cn.jpush.android.intent.CONNECTION" />
                     <category android:name="您应用的包名" />
                 </intent-filter>
             </receiver>
    
            <!-- Required. For publish channel feature -->
            <!-- JPUSH_CHANNEL 是为了方便开发者统计APK分发渠道。-->
            <!-- 例如: -->
            <!-- 发到 Google Play 的APK可以设置为 google-play; -->
            <!-- 发到其他市场的 APK 可以设置为 xxx-market。 -->
            <!-- 目前这个渠道统计功能的报表还未开放。-->
            <meta-data android:name="JPUSH_CHANNEL" android:value="developer-default"/>
            <!-- Required. AppKey copied from Portal -->
            <meta-data android:name="JPUSH_APPKEY" android:value="您应用的Appkey"/> 
        </application>
    </manifest>
    

    配置和代码说明

    必须权限说明

    权限 用途
    You Package.permission.JPUSH_MESSAGE 官方定义的权限,允许应用接收JPUSH内部代码发送的广播消息。
    RECEIVE_USER_PRESENT 允许应用可以接收点亮屏幕或解锁广播。
    INTERNET 允许应用可以访问网络。
    WAKE_LOCK 允许应用在手机屏幕关闭后后台进程仍然运行
    READ_PHONE_STATE 允许应用访问手机状态。
    WRITE_EXTERNAL_STORAGE 允许应用写入外部存储。
    READ_EXTERNAL_STORAGE 允许应用读取外部存储。
    WRITE_SETTINGS 允许应用读写系统设置项。
    VIBRATE 允许应用震动。
    MOUNT_UNMOUNT_FILESYSTEMS 允许应用挂载/卸载 外部文件系统。
    ACCESS_NETWORK_STATE 允许应用获取网络信息状态,如当前的网络连接是否有效。

    集成 JPush Android SDK 的混淆

    • 请下载4.x及以上版本的proguard.jar, 并替换你Android Sdk "tools\proguard\lib\proguard.jar"

    • 请在工程的混淆文件中添加以下配置:

      -dontoptimize
      -dontpreverify
      
      -dontwarn cn.jpush.**
      -keep class cn.jpush.** { *; }
      
      -dontwarn cn.jiguang.**
      -keep class cn.jiguang.** { *; }
      
    • v2.0.5 ~ v2.1.7 版本有引入 gson 和 protobuf ,增加排除混淆的配置。(2.1.8版本不需配置)

      #==================gson && protobuf==========================
      -dontwarn com.google.**
      -keep class com.google.gson.** {*;}
      -keep class com.google.protobuf.** {*;}
      

    添加代码

    JPush SDK 提供的 API 接口,都主要集中在 cn.jpush.android.api.JPushInterface 类里。

    基础API

    • init 初始化SDK

      public static void init(Context context)
      
    • setDebugMode 设置调试模式

    注:该接口需在init接口之前调用,避免出现部分日志没打印的情况。多进程情况下建议在自定义的Application中onCreate中调用。

        // You can enable debug mode in developing state. You should close debug mode when release.
        public static void setDebugMode(boolean debugEnalbed)
    

    添加统计代码

    调用示例代码(参考 example 项目)

    • init 只需要在应用程序启动时调用一次该 API 即可。

    • 以下代码定制一个本应用程序 Application 类。需要在 AndoridManifest.xml 里配置。请参考上面 AndroidManifest.xml 片断,或者 example 项目。

      public class ExampleApplication extends Application {
      @Override
          public void onCreate() {
              super.onCreate();
              JPushInterface.setDebugMode(true);
              JPushInterface.init(this);
          }
      }
      

    测试确认

    • 确认所需的权限都已经添加。如果必须的权限未添加,日志会提示错误。
    • 确认 AppKey(在Portal上生成的)已经正确的写入 Androidmanifest.xml 。
    • 确认在程序启动时候调用了init(context) 接口
    • 确认测试手机(或者模拟器)已成功连入网络 + 客户端调用 init 后不久,如果一切正常,应有登录成功的日志信息
    • 启动应用程序,在 Portal 上向应用程序发送自定义消息或者通知栏提示。详情请参考管理Portal
      • 在几秒内,客户端应可收到下发的通知或者正定义消息,如果 SDK 工作正常,则日志信息会如下:
    [JPushInterface] action:init
    
    .......
    
    [PushService] Login succeed!
    

    如图所示,客户端启动分为 4 步:

    • 检查 metadata 的 appKey 和 channel ,如果不存在,则启动失败
    • 初始化 JPush SDK,检查 JNI 等库文件的有效性,如果库文件无效,则启动失败
    • 检查 Androidmanifest.xml,如果有 Required 的权限不存在,则启动失败
    • 连接服务器登录,如果存在网络问题,则登陆失败,或者前面三步有问题,不会启动JPush SDK

    高级功能

    请参考:

    API: Android




    展开全文
  • Android消息推送:第三方消息推送平台详细解析

    万次阅读 多人点赞 2016-10-19 19:33:50
    消息推送Android开发中应用的场景是越来越多了,比如说电商产品进行活动宣传、资讯类产品进行新闻推送等等,如下图: 本文将介绍Android中实现消息推送的第三方推送的详细解决方案 阅读本文前,建议先阅读我的写...

    前言

    • 消息推送在Android开发中应用的场景是越来越多了,比如说电商产品进行活动宣传、资讯类产品进行新闻推送等等,如下图:

    推送消息截图

    • 本文将介绍Android中实现消息推送的第三方推送的详细解决方案

    阅读本文前,建议先阅读我写的文章:史上最全解析Android消息推送解决方案


    目录

    目录


    1. 主流的第三方推送平台分类

    • 手机厂商类:小米推送、华为推送。
    • 第三方平台类:友盟推送、极光推送、云巴(基于MQTT)
    • BAT大厂的平台推送:阿里云移动推送、腾讯信鸽推送、百度云推送

    2. 对比其他推送方式的特点

    其他推送方式还有:C2DM、轮询、SMS、MQTT协议、XMPP协议等等,相对于这些推送方式,第三方推送方式的特点分别是:

    • 优点:

      1. 成本低
        上述的推送大多数是免费的,假如自己实现则消耗过多资源(开发成本和后台管理、统计成本)
      2. 消息到达率高
        如果一个手机里有多个App使用了同一家推送服务,那么这些App将共用一条消息通道,即使你家的App推送服务被杀死了,那么只要用户打开了其他集成该推送服务的App,你家的推送就能到达用户
    • 缺点

      1. 安全性低
        使用别人的服务器,所以你懂的。
      2. 服务会被杀死
        由于Android系统的机制,后台推送 Service 会被各种主动的或是被动的行为给杀死,而服务一旦被杀死,意味着就接收不到推送消息。

    3. 第三方推送服务方式的特点

    • 第三方服务基本都具备免费、和到达率高的特点
    • 那么应该如何选择呢?我们来分别看一下第三方推送各种方式的优点:

    3.1 手机厂商推送

    请记住一个潜规则:操作系统是不会杀死属于自己品牌的推送服务。

    1. 手机厂商的推送服务在自家的手机上属于系统级别的服务,这意味着系统不会杀死自家的推送服务
    2. 比如说,Android原生系统是不会杀死C2DM消息推送服务,MIUI系统是不会杀死小米的推送服务。

    当今市场上的Android手机系统份额最高是MIUI系统,即小米(具体排名请看http://www.umindex.com/)

    因为:免费、到达率高且在Android系统市场份额第一的MIUI系统上不被杀死。所以,如果要选择手机厂商的推送服务,请选择小米推送作为第三方平台实现推送服务

    下面一些应用可以从侧面来证明我的推断:

    • 腾讯新闻使用的小米推送,没有使用自己家的信鸽推送
    • 淘宝使用了自家的阿里云推送,同时还集成了小米推送
    • 百度视频爱奇艺使用的是小米推送,没有用自家的百度推送
    • 官网截图 - 集成应用:

    集成应用

    1. 如果希望进一步提高推送的效果,其实可以集成多个手机厂商的推送服务
    2. 比如小米渠道用小米推送,华为渠道用华为推送,但这样的实现成本会大一些

    3.2 第三方平台类

    请记住一个规则:推送系统会共享一条推送渠道

    1. 这意味着假设你接入了友盟推送,而恰好今日头条也接入了友盟。
    2. 有一天你的App被杀死了,但这时用户启动了今日头条,那么推送系统也就会通过共享的推送通道顺便把你推送消息送达到手机上,然后还可能把你的进程也唤醒(被“保活”了)。

    所以说,关于如何选择第三方平台类的推送,推送平台的规模效应就很重要了。
    那如何得知他们的规模和市场份额呢?按个人经验,主要看两点:

    • 问内部的朋友。
    • 看推送平台的合作客户里有哪些大的app - 参考对应官网的合作案例

    3.3 BAT大厂的推送

    BAT大厂其实并没有什么优势,同时谨记:

    • 不要以为用了腾讯信鸽推送,就能占上微信的光保证你的App永远内部被杀死。
    • 说个题外话,手机淘宝除了自家的阿里云的移动推送,同时也使用其它的第三方推送平台啊(比如友盟推送)。

    4. 如何选择第三方平台推送服务?

    • 选择因素
      用户群体属性、实现成本、渠道

    • 具体选择方案

    示意图

    所以,大家可根据自己的使用场景来进行消息推送平台的选择。


    5. 推送消息类别的选择

    5.1 推送消息的类别

    通常第三方推送平台都支持两种推送消息类型:通知栏消息和透传消息。

    • 通知栏消息:该类消息在被送达用户的设备后,直接以系统通知栏的形式展示给用户

    不会继续被传递到App

    • 透传消息:该类消息在被送达用户的设备后,还会继续传递到App
    1. 通过回调App的某个BroadcastReceiver的形式将消息传递到App内部。然后由App决定如何处理和显示这个消息。
    2. 所以透传消息不一定会以系统通知栏的形式进行推送,由程序猿自定义

    5.2 消息类别的区别与特点

    • 二者的区别在于:透传消息在整个消息传递过程中比通知栏消息多了一步-传递到App
    • 通知栏消息的优点:送达率高
      因为透传消息在整个消息传递过程中比通知栏消息多了一步-传递到App,因此透传消息就增加一些被系统限制的概率,给系统杀死的概率就高一些,所以说,通知栏消息比透传消息应该能提供更好的送达率。

    我们来看下小米推送的官方文档描述:

    1. 在一些 Android 系统(如 MIUI)中,受到系统自启动管理设置的限制,应用不能在后台自启动
    2. 在这类系统中,如果在发送消息的时候对应的应用没有被启动,透传类消息将不能顺利送达。
    3. 因此,对于对送达率要求很高的消息,建议尽量采用通知栏提醒的方式推送消息
    • 透传消息的优点:对消息操作程度高 & 自定义程度高
    1. 提供了对消息数据的更灵活的操纵能力。

    App如果仅仅通过通知栏消息,是无法接触到消息数据本身的。

    1. 可自定义通知提醒的样式(包括提示样式、提示形式如声音等等)

    所以大家可以根据不同的使用场景来对推送消息类别进行选择了。


    6. 总结


    请帮顶 / 评论点赞!因为你的鼓励是我写作的最大动力!

    展开全文
  • Android:最全面解析Android消息推送解决方案

    万次阅读 多人点赞 2016-09-28 19:46:05
    鉴于现在运营需求的增强,消息推送Android开发中应用的场景是十分常见 如电商的活动宣传、资讯类产品进行新闻推送等等 今天,我将全面介绍Android中实现消息推送的7种主流解决方案 目录 1....

    前言

    • 鉴于现在运营需求的增强,消息推送在Android开发中应用的场景是十分常见

    如电商的活动宣传、资讯类产品进行新闻推送等等

    推送消息截图

    • 今天,我将全面介绍Android中实现消息推送的7种主流解决方案

    目录

    示意图


    1. 本质

    应用App 主动向用户推送服务器最新消息

    实际上,是当服务器有新消息需推送给用户时,先发送给应用App,应用App再发送给用户

    示意图


    2. 作用

    • 产品角度:功能需要,如:资讯类产品的新闻推送、工具类产品的公告推送等等
    • 运营角度:活动运营需要,如:电商类产品的促销活动;召回用户 / 提高活跃度等等

    作为开发者,不要有需求就接,应该多思考、多理解用户 / 功能的使用场景,有助于我们更好地去选择合适的开发方式


    3. 原理

    • 推送的原理主要是2种方式:Push 和 Pull
    • 具体如下

    示意图


    4. 解决方案

    • Android中,实现消息推送的主流方案有7种
    • 下面,我将详细解析每个解决方案

    注:

    1. 每个操作系统有自身的消息推送功能,属于 系统级别,即 任何时候都可以推送给用户,且不会被系统杀死
    2. Android的消息推送服务称为:C2DM(Cloudto Device Messaging),将在下面详细描述

    4.1 C2DM

    Cloud to Device Messaging,云端推送,是Android系统级别的消息推送服务(Google出品)

    • 原理
      基于Push方式
    • 具体描述
      C2DM服务负责处理诸如消息排队等事务,并向运行于目标设备上的应用程序分发这些消息。如下图:

    C2DM原理

    • 优点
      C2DM提供了一个简单的、轻量级的机制,允许服务器可以通知移动应用程序直接与服务器进行通信,以便于从服务器获取应用程序更新和用户数据。
    • 缺点
      1. 依赖于Google官方提供的C2DM服务器,但在国内使用Google服务需要翻墙,成本较大;
      2. 需要用户手机安装Google服务。但由于Android机型、系统的碎片化 & 国内环境,国内的Android系统都自动去除Google服务,假如要使用C2DM服务,这意味着用户还得去安装Google服务,成本较大。

    4.2 轮询

    • 原理
      基于Pull方式
    • 具体描述
      应用程序隔固定时间主动与服务器进行连接并查询是否有新的消息
    • 优点
      实时性好
    • 缺点
      1. 成本大,需要自己实现与服务器之间的通信,例如消息排队等;
      2. 到达率不确定,考虑轮询的频率:太低可能导致消息的延迟;太高,更费客户端的资源(CPU资源、网络流量、系统电量)和服务器资源(网络带宽)

    ####4.3 SMS信令推送

    • 原理
      基于Push方式
    • 具体描述
      服务器有新消息时,发送1条类似短信的信令给客户端,客户端通过拦截信令,解析消息内容 / 向服务器获取信息
    • 优点
      可实现完全的实时操作
    • 缺点
      成本高(主要是短信资费的支出)

    4.4 MQTT协议

    • 定义
      轻量级的消息发布/订阅协议
    • 原理
      基于Push方式,wmqtt.jar 是IBM提供的MQTT协议的实现,原理如下图:

    MQTT协议原理

    更多关于MQTT协议:

    1. 项目实例源
    2. 一个采用PHP书写的服务器端
    3. Jar包下载地址,并加入自己的Android应用程序中。
    4. 拓展:RSMB是从MQTT协议引申出来的另外一种解决方案:简单的MQTT代理,详情请点击

    4.5 XMPP协议

    • 定义
      Extensible Messageing and Presence Protocol,可扩展消息与存在协议,是基于可扩展标记语言(XML)的协议,是目前主流的四种IM协议之一

    其他三种:

    • 即时信息和空间协议(IMPP)

    • 空间和即时信息协议(PRIM)

    • 即时通讯和空间平衡扩充的进程开始协议SIP(SIMPLE)

    • 原理
      XMPP中定义了三个角色,分别是客户端、服务器和网关
      客户端

      1. 通过 TCP/IP与XMPP 服务器连接,然后在之上传输与即时通讯相关的指令(XML);
      2. 解析组织好的 XML 信息包;
      3. 理解消息数据类型。
    • XMPP的核心:XML流传输协议(在网络上分片断发送XML的流协议),也是即时通讯指令的传递基础,即XMPP用TCP传的是XML流
    1. 与即时通讯相关的指令,在以前要么用2进制的形式发送(比如QQ),要么用纯文本指令加空格加参数加换行符的方式发送(比如MSN)。
    2. XMPP传输的即时通讯指令的逻辑与以往相仿,只是协议的形式变成了XML格式的纯文本。

    服务器

    1. 监听客户端连接,并直接与客户端应用程序通信(客户端信息记录)
    2. 与其他 XMPP 服务器通信;

    网关:与异构即时通信系统进行通信

    异构系统包括SMS(短信),MSN,ICQ等

    通信能够在这三者的任意两个之间双向发生。

    原理流程

    原理流程

    • 优点

      1. 开源:可通过修改其源代码来适应我们的应用程序。
      2. 简单:XML易于解析和阅读;将复杂性从客户端转移到了服务器端
      3. 可拓展性强:继承了在XML环境中灵活的发展性,可进一步对协议进行扩展,实现更为完善的功能。

      GTalk、QQ、IM等都用这个协议

    • 缺点
      如果将消息从服务器上推送出去,则不管消息是否成功到达客户端手机上。

    • 源码实例:有一个很棒的基于XMPP协议的java开源Android push notification:Androidpn项目地址,大家有兴趣可以去看看

    更多关于XMPP协议更加详细请点击

    4.6 使用第三方平台

    现今主流的推送平台分为

    1. 手机厂商类:小米推送、华为推送。
    2. 第三方平台类:友盟推送、极光推送、云巴(基于MQTT)
    3. BAT大厂的平台推送:阿里云移动推送、腾讯信鸽推送、百度云推送

    具体各推送平台的优缺点请看我写的文章:Android推送:第三方消息推送平台详细解析

    4.7 自己搭建

    如果你的产品对于消息推送具备较高的功能和性能要求,同时对安全性要求非常高的话,自己搭建可能是最好的方式,但这种方式无疑成本是最高的。

    至此,关于Android推送的解决方案讲解完毕。


    5. Android推送 与 iOS推送的区别

    示意图


    6. 总结

    • 经过上面的详细讲解,相信大家现在对Android中消息推送的解决方案已经非常了解,总结如下
      示意图

    • 解决方案没有优劣,要具具体使用场景而定。但一般来说,个人建议使用第三方平台推送,成本低+抵达率高

    • 关于Android消息推送的其他知识,具体请看:
      Android消息推送:手把手教你集成小米推送
      Android推送:第三方消息推送平台详细解析

    • 接下来,我会继续讲解Android开发中关于消息推送的其他知识,感兴趣的同学可以继续关注carson_ho的微信公众号
      示意图
      示意图


    请帮顶 / 评论点赞!因为你的鼓励是我写作的最大动力!

    展开全文
  • 1、现在的Android推送功能使用的越来越广泛,许多app都想加一个推送功能,也出现了好多第三方推送服务如:极光、百度云推送、个推、友盟、网易云推送等。我本人是使用过百度云推送、个推和极光推送。百度云推送感觉...
  • Android谷歌推送

    千次阅读 2017-09-02 15:04:47
    Android谷歌推送GCM(全称Google Cloud Messaging),谷歌云通讯,它可以实现消息从服务器端推送android系统相关的应用上,从而实现服务器主动推送消息到android终端的功能。FCM(FireBaseCloud Messaging)是谷歌在...
  • Android 消息推送:个推

    万次阅读 2018-01-30 13:49:09
    之前一直在使用极光推送,这次项目打算使用个推试试!个推:个推官方文档集成失败原因:1、检查自己的APPID等信息2、检查AndroidManifest.xml&lt;!-- 自定义权限 自定义权限解释:部分手机型号不能正常运行个推...
  • 前段时间公司需要 Android 端的手机群功能,我们就通过 MQTT 来实现了该功能。 MQTT 的官网如下http://mqtt.org/ 在 MQTT 官网中 http://mqtt.org/software 列举出了实现了该协议的开源客户端和服务器工程和库。...
  • android消息推送

    千次阅读 2014-05-27 10:08:10
     所谓的消息推送就是从服务器端向移动终端发送连接,传输一定的信息。比如一些新闻客户端,每隔一段时间收到一条或者多条通知,这就是从服务器端传来的推送消息;还比如常用的一些IM软件如微信、GTalk等,都具有...
  • Android推送 百度云推送 入门篇

    万次阅读 多人点赞 2014-05-27 20:52:27
    现在app基本都有推送的功能,于是看了下百度云的推送,官方文档和Demo都很到位,记录下使用过程,目标是利用百度云推送最为服务器写个及时通讯的例子~当然了,这是第一篇入门~ 1、第一步就是在百度开发者服务管理中...
  • iPhone上已经有了比较简单的和完美的推送通知解决方案,可是Android平台上实现起来却相对比较麻烦,最近由于项目需要,利用十几天的时间对Android推送通知服务进行了初步的研究。    Android Cloud to Device ...
  • 本来是想实现通知栏可以多行显示通知内容的,但是我实现了之后,收到了两条推送,下面是代码,求大神解答。 ![图片说明](https://img-ask.csdn.net/upload/201702/12/1486872060_223676.png) ``` public class ...
  • Android消息推送总结

    千次阅读 2018-12-24 21:38:57
    由于国内防火墙以及各手机厂商系统定制的原因,Android系统内置的GCM(已升级到FCM)推送服务在国内基本不能用,大家只能各自发挥,有的像QQ、微信一样自己搭建推送服务,有的集成像极光、个推等专门做推送服务的第三...
  • android推送(本地推送远程推送

    千次阅读 2016-05-16 10:11:41
    在现在的app中推送技术很普遍,推送可以提高用户活跃度,也可以进行一些活动推送,今天就跟大家聊一下推送 推送分为远程推送和本地推送,都可以通过推送消息跳转到一些应用或者活动界面,本地推送几行代码就可以...
  • 现在接别人的活,做的项目中,集成了激光推送服务,但是有些用户要不然不来推送,来就了就很多,而且只有在应用程序开启的时候,也就是系统进程运行这这个应用才有推送,如何做到应用没开启也能收到推送通知呢?...
  • Android信鸽推送全解

    千次阅读 2017-07-26 17:25:37
    Android开发中集成信鸽推送还是比较简单的,但是遇到了坑就麻烦先介绍一下
  • 最近公司需要 Android 的消息推送,在网上找了很多消息推送的例子,都是无法运行. 经过一段时间的研究,终于把例子运行起来了。现在共享出来,共同学习。 在 androidpn 的官网下载...
  • Android百度推送使用详解

    千次阅读 2015-12-16 14:42:09
    Android百度推送使用详解 ONE Goal ,ONE Passion! 现在个项目都希望有推送功能,各种推送平台也是很多.不过用起来都是大同小异.公司项目使用的百度推送,去百度官网快速集成demo可以搞定.不过使用自己集成是就出现...
  • Android平台的信息推送实现

    千次阅读 2013-09-03 09:12:31
    通过该项目对Socket通信、长连接有了更深一步的理解,于是就想试试将Mina用到Android平台的信息推送上。经过了解发现,目前已经有不少成型的框架能够实现Android平台的信息推送,特别是AndroidPn。  平时我们...
  • android后台信息推送调研

    千次阅读 2016-07-15 11:15:06
    android后台信息推送调研 前言 我们已经开发了一个应用,这里称为A应用,类似于天气weather那种。现在的任务就是如果这些A应用有新版本了,或者天气出现比较恶劣的状况,要及时在手机上进行消息的推送,提示有新的...
  • Android 友盟推送踩坑

    千次阅读 2017-11-15 20:10:02
    最近项目使用推送是友盟推送,实际上推送原理相同,但是友盟推送和极光推送Android端的代码编写还是有很大差别的。极光推送我记得是定义广播,然后在广播中发送通知,在设置跳转以一类的,但是在友盟推送中,友盟...
  • Android 本地推送

    千次阅读 2017-04-03 22:56:00
    今天研究了一下本地推送,大体的思路采用定时器,发送广播从而达到推送的目的.
  • Android本地推送

    千次阅读 2018-08-28 18:23:55
    推送基本接口 与Alarm服务结合,定时推送消息 多消息推送主要事项 推送基本接口 //获取app图标 int iconResId = 0; if(applicationInfo!=null){ iconResId = applicationInfo.icon; } if(iconResId == ...
  • Android华为推送集成与测试

    千次阅读 2019-03-14 20:39:11
    Android推送一直是个大坑,好在统一联盟的的推送在测试了,希望早点一统推送,挖去开发者的心病吧。在此之前,该踩坑还是踩坑,按官方文档一步步踩吧。 集成 官方的开发准备 先集成必备套件 HMS SDK Agent ...
  • Android 消息推送以及推送原理

    千次阅读 2015-07-23 11:19:45
    消息推送,就是在互联网上通过定期传送用户需要的信息来减少信息过载的一项新技术。推送技术通过自动传送信息给用户,来减少用于网络上搜索的时间。它根据用户的兴趣来搜索、过滤信息,并将其定期推给用户,帮助用户...
  • 工信部统一 Android 消息推送标准

    千次阅读 2019-06-25 14:54:45
    统一推送联盟成立于2017年10月,挂靠单位是电信终端产业协会(TAF),接受工业和信息化部业务指导,住所设在北京。联盟核心任务是:探索推送行业创新,促进终端生产厂商、应用开发厂商和第三方服务提供商等进行深入...
  • Android推送服务——百度云推送

    千次阅读 2013-12-06 11:44:09
    Android开发中,这里的发起方我们把它叫做推送服务器(Push Server),接收方叫做客户端(Client)。相比通过轮询来获取新消息或通知,推送无论是在对客户端的资源消耗还是设备耗电量来说都比轮询要好,所以,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 70,624
精华内容 28,249
关键字:

android信息推送