Android 集成小米推送
前言
- 在Android开发中,消息推送功能的使用非常常见。
为了降低开发成本,使用第三方推送是现今较为流行的解决方案。
今天,我将手把手教大家如何在你的应用里集成小米推送
- 该文档基于小米推送官方Demo,并给出简易推送Demo
- 看该文档前,请先阅读我写的另外两篇文章:
史上最全解析Android消息推送解决方案
Android推送:第三方消息推送平台详细解析
目录
1. 官方Demo解析
首先,我们先对小米官方的推送Demo进行解析。
请先到官网下载官方Demo和SDK说明文档
1.1 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.mipushdemopublic static final String TAG = "com.xiaomi.mipushdemo";private static DemoHandler sHandler = null;private static MainActivity sMainActivity = null;//为了提高推送服务的注册率,官方Demo建议在Application的onCreate中初始化推送服务//你也可以根据需要,在其他地方初始化推送服务@Overridepublic 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() {@Overridepublic void setTag(String tag) {// ignore}@Overridepublic void log(String content, Throwable t) {Log.d(TAG, content, t);}@Overridepublic 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) {//通过比较进程的唯一标识和包名判断进程里是否存在该Appif (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;}@Overridepublic 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
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
总结:
- 步骤1:先判断应用App是否已开启 - 通过判断系统里的进程
- 通过静态方法
public static void registerPush(Context context, String appID, String appKey)
- 1
进行推送服务注册,详细参数如下:
- 为了提高注册率,最好在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.xmlpublic class DemoMessageReceiver extends PushMessageReceiver {private String mRegId;private String mTopic;private String mAlias;private String mAccount;private String mStartTime;private String mEndTime;//透传消息到达客户端时调用//作用:可通过参数message从而获得透传消息,具体请看官方SDK文档@Overridepublic 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文档@Overridepublic 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 其他页面@Overridepublic 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);}//用来接收客户端向服务器发送命令后的响应结果。@Overridepublic 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);}//用于接收客户端向服务器发送注册命令后的响应结果。@Overridepublic 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());}}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
- 212
- 213
- 214
- 215
- 216
总结
根据需要复写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;@Overrideprotected 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void onClick(View v) {new TimeIntervalDialog(MainActivity.this, new TimeIntervalInterface() {@Overridepublic void apply(int startHour, int startMin, int endHour,int endMin) {//调用静态方法进行设置 MiPushClient.setAcceptTime(MainActivity.this, startHour, startMin, endHour, endMin, null);}@Overridepublic void cancel() {//ignore}}).show();}});// 暂停推送findViewById(R.id.pause_push).setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {MiPushClient.pausePush(MainActivity.this, null);}});findViewById(R.id.resume_push).setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {//调用静态方法进行设置 MiPushClient.resumePush(MainActivity.this, null);}});}@Overrideprotected void onResume() {super.onResume();refreshLogInfo();}@Overrideprotected void onDestroy() {super.onDestroy();DemoApplication.setMainActivity(null);}public void refreshLogInfo() {String AllLog = "";for (String log : logList) {AllLog = AllLog + log + "\n\n";}mLogView.setText(AllLog);}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 108
- 109
- 110
- 111
- 112
- 113
- 114
- 115
- 116
- 117
- 118
- 119
- 120
- 121
- 122
- 123
- 124
- 125
- 126
- 127
- 128
- 129
- 130
- 131
- 132
- 133
- 134
- 135
- 136
- 137
- 138
- 139
- 140
- 141
- 142
- 143
- 144
- 145
- 146
- 147
- 148
- 149
- 150
- 151
- 152
- 153
- 154
- 155
- 156
- 157
- 158
- 159
- 160
- 161
- 162
- 163
- 164
- 165
- 166
- 167
- 168
- 169
- 170
- 171
- 172
- 173
- 174
- 175
- 176
- 177
- 178
- 179
- 180
- 181
- 182
- 183
- 184
- 185
- 186
- 187
- 188
- 189
- 190
- 191
- 192
- 193
- 194
- 195
- 196
- 197
- 198
- 199
- 200
- 201
- 202
- 203
- 204
- 205
- 206
- 207
- 208
- 209
- 210
- 211
总结
根据需求对不同用户设置不同的推送标识,如别名、标签等等。
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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
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)
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
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);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
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() {@Overridepublic 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);}@Overrideprotected 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);}@Overridepublic 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();}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
总结
- 使用一个继承了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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
2. 集成小米推送步骤汇总
- 步骤1:在小米推送平台进行相关注册开发者账号,并进行应用的注册:应用包名,AppID和AppKey
- 步骤2:将小米推送的SDK包加入库
- 步骤3:在应用内初始化小米推送服务
- 步骤4:继承PushMessageReceiver,并复写相关推送消息的方法
- 步骤5:在AndroidManifest文件里面配置好权限、注册Service和BroadcastReceiver
在Android6.0里面的权限需要动态获取
- 步骤6:根据需要设置一系列的推送设置,如用户别名、标签等等
接下来,我们来按照上面的步骤,一步步来实现一个简易的小米推送Demo
3. 实例解析
步骤1:在小米推送平台进行相关注册开发者账号,并进行应用的注册:应用包名,AppID和AppKey
注意,填入的包名要跟你的应用App的包名是一致的
步骤2:将小米推送的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中初始化推送服务//你也可以根据需要,在其他地方初始化推送服务@Overridepublic 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) {//通过比较进程的唯一标识和包名判断进程里是否存在该Appif (info.pid == myPid && mainProcessName.equals(info.processName)) {return true;}}return false;}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
注意要在Android.manifest.xml里的application里加入
android:name=".BaseActivity"
- 1
这样在应用初始化时是第一个加载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文档@Overridepublic void onReceivePassThroughMessage(Context context, MiPushMessage message) {//打印消息方便测试System.out.println("透传消息到达了");System.out.println("透传消息是"+message.toString());}//通知消息到达客户端时调用//注:应用在前台时不弹出通知的通知消息到达客户端时也会回调函数//作用:通过参数message从而获得通知消息,具体请看官方SDK文档@Overridepublic void onNotificationMessageArrived(Context context, MiPushMessage message) {//打印消息方便测试System.out.println("通知消息到达了");System.out.println("通知消息是"+message.toString());}//用户手动点击通知栏消息时调用//注:应用在前台时不弹出通知的通知消息到达客户端时也会回调函数//作用:1. 通过参数message从而获得通知消息,具体请看官方SDK文档//2. 设置用户点击消息后打开应用 or 网页 or 其他页面@Overridepublic void onNotificationMessageClicked(Context context, MiPushMessage message) {//打印消息方便测试System.out.println("用户点击了通知消息");System.out.println("通知消息是" + message.toString());System.out.println("点击后,会进入应用" );}//用来接收客户端向服务器发送命令后的响应结果。@Overridepublic 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("注册失败");}}}//用于接收客户端向服务器发送注册命令后的响应结果。@Overridepublic 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());}}}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
具体设置请看官方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>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
步骤6:根据需要设置一系列的推送设置,如用户别名、标签等等
- 此处是简单Demo,所以不作过多的设置
- 更多设置请回看上方官方Demo解析
运行结果
好了,客户端的代码写好后,可以去小米官网测试一下消息推送了
步骤1:在小米官网的消息推送里选择你创建的应用,然后点击“推送工具”
步骤2:设置推送消息的相关信息
可进行的配置非常全面,基本上能满足推送的需求
推送的结果
4. Demo下载地址
Carson的Github:Demo_MiPush
5. 关于对小米推送的思考(问题)
上述说的小米推送看似简单:初始化推送服务 + 相关推送设置。但是,好的代码不仅能在正常情况下工作,还应该充分考虑失败情况。那么,有什么样的失败情况需要我们考虑呢?
- 背景:在这个初始化推送服务的过程中,是需要联系小米推送的服务器来申请reg id(即推送token)。
冲突:初始化过程可能失败:网络问题(没网or网络信号弱)、服务器问题导致初始化失败。那么,当失败以后,该什么时候再次进行初始化呢?
小米推送的Demo里并没有相关措施解决这个问题
解决方案:在初始化失败的情况下提供重试机制,直到初始化成功(可以通过检测是否已经拿到推送token来确定),问题解决的逻辑如下:
- 具体代码在这里就不作过多描述,如果你希望获得含注册重试机制的小米推送源代码,请在评论留下你的邮箱,我将亲自发送到你的邮箱
- 知识点涵盖:网络数据的检测 & 广播接收器
- 具体请看我写的另外两篇文章:
Android:BroadcastReceiver广播接收器最全面解析
Android:检测网络状态&监听网络变化
总结
全面考虑到所有异常问题并恰当地进行处理才能真正体现程序猿的功力,希望大家做撸代码的时候不要只做代码的搬运工,纯粹写代码并不会让你成长,关键在于思考。
6. 总结
- 相信大家已经非常了解如何在你的Android应用中集成小米推送
- 接下来,我会继续介绍具体如何在Android中关于推送的消息,请看
史上最全解析Android消息推送解决方案
Android推送:第三方消息推送平台详细解析 - 接下来,我会继续介绍具体如何在Android应用中集成相应的第三方推送功能(华为推送、极光推送、个推等等),有兴趣可以继续关注Carson_Ho的安卓开发笔记
Android 集成小米推送相关推荐
- Android 集成小米推送功能教程
1.前言 现在好多手机都有APP推送功能,也有好多第三方的SDK,那为什么还要集成只有小米手机好使的"小米推送"呢?因为在小米手机中,小米推送是"系统级通道", ...
- Android集成华为推送教程
前言 目前Android应用在后台杀死进程之后,就收不到通知了,想要接收消息推送,需要集成各个厂商的推送SDK,本次讲解华为集成最新教程 1.配置AppGallery Connect 1)注册成为开发 ...
- Android开发之推送服务(二) 集成小米推送
小米推送主要是用来适配小米手机的.所以,在做的时候,一般会判断当前手机是否是小米手机,如果是小米手机,则去使用小米推送,注册小米推送,如果不是,则默认使用Umeng推送. 在集成小米推送之前,需要先注 ...
- Android消息推送:手把手教你集成小米推送
<script type="text/javascript" src="http://static.blog.csdn.net/scripts/category.j ...
- Android集成极光推送和踩过的坑(一)
转载请标明出处 http://blog.csdn.net/mohan6/article/details/72960346 本文作者:[默寒的博客] 集成步骤以及集成过程遇到的坑: 这部分主要阐述了集成 ...
- Android集成极光推送踩坑(二)升级篇
转载请标明出处 http://blog.csdn.net/mohan6/article/details/74133186 本文作者:[默寒的博客] 前言 前段时间针对集成极光推送写了篇文章( Andr ...
- Android - 集成华为推送
Android ~ 集成华为推送 功能流程如下图所示: 开发流程 一.配置AppGallery Connect 1.注册成为开发者 注册 实名认证 2.创建应用 创建项目 在项目下添加应用 3.生成签 ...
- Android 集成华为推送,集成小米推送,集成OPPO推送,集成vivo推送
最近有时间在我们的应用中集成了华为,小米,OPPO,vivo四大平台的推送,一切顺利. 这里做一个记录, 开撸: 一.华为推送 1.下载HMS Agent套件,解压如下: 找到GetHMSAgent_ ...
- Android集成华为推送功能详细
1.前言 Android集成推送功能也算是有好几个SDK了,包括极光.个推.小米.百度云推送.但是真心感觉华为的文档写的太差,封装的也不好,别的开发文档看一两遍就能按照文档集成成功,而华为仔细看了几遍 ...
最新文章
- 盘点几个值得你借鉴的Java学习方法
- Dataset之WebVision:WebVision数据集简介、下载、使用方法之详细攻略
- Go + Excel 学习 Excelize rows.go
- Spring注解 (更新中)
- 错误内存【读书笔记】C程序中常见的内存操作有关的典型编程错误
- java mybatis 教程_(整理)MyBatis入门教程(一)
- 西密歇根大学计算机科学,西密歇根大学
- TensorFlow开发者证书 中文手册
- 均匀B样条和准均匀B样条
- 硬盘助手写入文件的正确提取
- React 父组件获取子组件的方法/数据(useRef
- android 自定义图片裁剪,Android自定义View实现照片裁剪框与照片裁剪功能
- IDA6.8 爱加密脱壳简单示例
- 单片机常用芯片总结(一)——LCD1602液晶屏
- VUE前端应用部署页面访问404问题
- vue krpano 视角监听
- 社群裂变工具有哪些?怎么用?微信如何引流客源呢?
- 深入Unreal蓝图开发:自定义蓝图节点(下)
- R语言ggplot2 柱状图(条形图)xy轴翻转,及排序
- ArcMap2Sld:一个将MXD中图层配图样式转换为OGC的SLD文件的开源工具