├── .gitignore ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── chaychan │ │ └── pushdemo │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── chaychan │ │ │ └── pushdemo │ │ │ ├── activity │ │ │ ├── LoginActivity.java │ │ │ ├── MainActivity.java │ │ │ ├── MessageCenterActivity.java │ │ │ ├── OrderDetailActivity.java │ │ │ └── SplashActivity.java │ │ │ ├── global │ │ │ ├── Constants.java │ │ │ └── PushConstants.java │ │ │ ├── receiver │ │ │ └── NotificationReceiver.java │ │ │ ├── service │ │ │ ├── DemoIntentService.java │ │ │ └── DemoPushService.java │ │ │ └── utils │ │ │ ├── NotificationUtils.java │ │ │ ├── PreUtils.java │ │ │ └── SystemUtils.java │ └── res │ │ ├── layout │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ ├── activity_message_center.xml │ │ ├── activity_order_detail.xml │ │ └── activity_splash.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── chaychan │ └── pushdemo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── img ├── 1.gif ├── 2.gif ├── 3.gif └── 4.gif └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | /.idea 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GTPushDemo 2 | ###推送通知的跳转处理和消息提醒 3 |   消息推送功能在App开发中经常用到,用于及时通知用户,推送用户订阅的相关的信息。本篇文章并非详细介绍如何集成和使用推送,关于推送相关知识的介绍,郭神已经在慕课网中有相关的教程,大家如果想要深入研究,可以去观看郭神的相关视频。 4 | 5 | ###效果演示 6 | 一、App处于运行状态下: 7 | 8 | 1.接收到通知,点击通知打开对应activity的演示: 9 | 10 | ![](./img/1.gif) 11 | 12 | 2.接收到通知,点击通知传值并打开对应的activity的演示: 13 | 14 | ![](./img/2.gif) 15 | 16 | 17 | 二、App进程处于销毁状态,但是后台依旧运行着推送常驻的service: 18 | 19 | 1.接收到通知,点击通知打开对应的activity, activity启动流程,SplashActivity -> MainActivity - > 对应的activity 20 | 21 | ![](./img/3.gif) 22 | 23 | 2.接收到通知,点击通知传值并打开对应的activity, activity启动流程,SplashActivity -> MainActivity - > 对应的activity 24 | 25 | ![](./img/4.gif) 26 | 27 | 28 | ###推送的相关流程 29 | 30 | 我所使用的是个推的推送功能,个推在推送这方面,做得还是相对不错的。 31 | 32 | 1. 绑定推送别名,一般在登录完成以后进行别名的绑定。(可以绑定用户名为推送的别名,后台可以通过用户名进行推送)。 33 | 34 | 这是demo的登录操作: 35 | 36 | public void login(View view){ 37 | String username = etUsername.getText().toString().trim(); 38 | String pwd = etPwd.getText().toString().trim(); 39 | 40 | if (TextUtils.isEmpty(username)){ 41 | Toast.makeText(this,"用户名不能为空",Toast.LENGTH_SHORT); 42 | return; 43 | } 44 | if (TextUtils.isEmpty(pwd)){ 45 | Toast.makeText(this,"密码不能为空",Toast.LENGTH_SHORT); 46 | return; 47 | } 48 | 49 | //执行登录逻辑,登录成功后,绑定推送的别名,可以绑定用户名为个推推送的别名 50 | PushManager.getInstance().bindAlias(this, username);//绑定推送别名 51 | 52 | PreUtils.putBoolean(this, Constants.IS_LOGIN,true);//设置为已登录 53 | PreUtils.putString(this,Constants.ACCOUNT,username);//保存账号,关于账号的保存,这里只是保存到sp作为演示,对于用户的信息,最好保存在数据库中 54 | 55 | startActivity(new Intent(this,MainActivity.class)); 56 | finish(); 57 | } 58 | 59 | 60 | 2.解绑推送别名,一般在退出登录以后进行别名的解绑。(解绑后则不会再接收到使用别名类型的推送)。 61 | 62 | 这是demo的退出登录操作: 63 | 64 | public void logout(View view){ 65 | //退出登录 66 | PreUtils.putBoolean(this, Constants.IS_LOGIN,false); 67 | 68 | String username = PreUtils.getString(this, Constants.ACCOUNT, ""); 69 | if (!TextUtils.isEmpty(username)){ 70 | PushManager.getInstance().unBindAlias(this,username,true);//解绑别名 71 | } 72 | 73 | startActivity(new Intent(this,LoginActivity.class)); 74 | finish(); 75 | } 76 | 77 | 3.接收到后台推送的消息: 78 |   个推支持自定义消息的处理,具体请查看个推官方文档,很容易上手的。 自己定义一个类继承 GTIntentService,并在清单文件中注册,当收到透传消息时,onReceiveMessageData()方法会进行回调。虽然个推可以在后台向Android端设备直接推送通知,但由于IOS端仅可接收透传消息,所以这里通过在透传消息中设置一个标识,如果是通知类消息,获取到消息后,自己创建对应的通知进行显示。 79 | 80 | @Override 81 | public void onReceiveMessageData(Context context, GTTransmitMessage msg) { 82 | byte[] payload = msg.getPayload(); 83 | 84 | String message = new String(payload); 85 | Log.i(TAG, "onReceiveMessageData: " + message); 86 | try { 87 | JSONObject jsonObject = new JSONObject(message); 88 | int isNotification = jsonObject.optInt(PushConstants.IS_NOTIFICATION); 89 | if (isNotification == 1) { 90 | //属于通知类的透传消息 91 | showNotification(context, jsonObject); 92 | } else { 93 | //提醒消息 94 | dealNotifyMessage(context, jsonObject); 95 | } 96 | } catch (JSONException e) { 97 | e.printStackTrace(); 98 | } 99 | } 100 | 101 | showNotification()方法是对通知类的透传消息的处理: 102 | 103 | /** 104 | * 根据消息内容弹出通知框 105 | * 106 | * @param context 107 | * @param jsonObject 108 | */ 109 | private void showNotification(Context context, JSONObject jsonObject) { 110 | String title = jsonObject.optString(PushConstants.TITLE); 111 | String content = jsonObject.optString(PushConstants.CONTENT); 112 | 113 | if (TextUtils.isEmpty(title) || TextUtils.isEmpty(content)) { 114 | return; 115 | } 116 | 117 | int pageNum = jsonObject.optInt(PushConstants.PAGE_NUMBER); 118 | String contentId = jsonObject.optString(PushConstants.CONTENT_ID); 119 | 120 | //设置点击通知后是发送广播,传递对应的数据 121 | Intent broadcastIntent = new Intent(context, NotificationReceiver.class); 122 | Bundle bundle = new Bundle(); 123 | bundle.putInt(PushConstants.PAGE_NUMBER, pageNum); 124 | bundle.putString(PushConstants.CONTENT_ID, contentId); 125 | broadcastIntent.putExtras(bundle); 126 | 127 | PendingIntent pendingIntent = PendingIntent. 128 | getBroadcast(context, NotificationUtils.getRandowReqCode(), broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT); 129 | NotificationUtils.showIntentNotification(context, title, content, title, pendingIntent, R.mipmap.ic_launcher, R.mipmap.ic_launcher); 130 | } 131 | 132 | dealNotifyMessage()方法是对提醒类的透传消息的处理: 133 | 134 | /** 135 | * 处理提醒消息 136 | * 137 | * @param context 138 | * @param jsonObject 139 | */ 140 | private void dealNotifyMessage(final Context context, JSONObject jsonObject) { 141 | int notifyType = jsonObject.optInt(PushConstants.NOTIFY_TYPE, PushConstants.MESSAGE_CENTER_NOTIFY); 142 | //判断提醒信息的类型,做相应的UI操作,由于此处处于IntentService中,做UI操作需要进行线程的切换,这里使用了handler的post()方法切换 143 | switch (notifyType) { 144 | case PushConstants.MESSAGE_CENTER_NOTIFY: 145 | //消息中心的提醒 146 | handler.post(new Runnable() { 147 | public void run() { 148 | /* if (MainActivity.instance != null){ 149 | MainActivity.instance.showTabNotify(2);//底部我的tab显示提示点 150 | } 151 | if (MineFragment.ivMessage != null){ 152 | MineFragment.ivMessage.setImageResource(R.mipmap.img_message_unread);//设置消息中心图标为未读的 153 | } 154 | //更新消息表的未读数 155 | UnReadDao.saveOrUpdate(UnReadDao.getUnreadCount() + 1);*/ 156 | 157 | Log.i(TAG,"收到消息提醒,显示小红点"); 158 | } 159 | }); 160 | break; 161 | } 162 | } 163 | 164 | 165 | ###通知的点击处理 166 |   关于通知的点击处理,showNotification()在创建通知的时候,需要设置PendingIntent: 167 | 168 | //设置点击通知后是发送广播,传递对应的数据 169 | Intent broadcastIntent = new Intent(context, NotificationReceiver.class); 170 | Bundle bundle = new Bundle(); 171 | bundle.putInt(PushConstants.PAGE_NUMBER, pageNum);//所要打开的页面的编号 172 | bundle.putString(PushConstants.CONTENT_ID, contentId);//id 173 | broadcastIntent.putExtras(bundle); 174 | 175 | PendingIntent pendingIntent = PendingIntent. 176 | getBroadcast(context, NotificationUtils.getRandowReqCode(), broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT); 177 | 178 |   我们通过PendingIntent.getBroadcast()方法创建PendingIntent,需要传递的数据:pageNum属于页面的编号,定义在PushConstants中,用于判断跳转哪个页面,如果是订单的通知,则contentId则是对应订单的id,用于订单详情页获取订单数据。 179 | 180 |   当点击通知的时候,将会发送广播并传值给NotificationReceiver,具体的跳转操作交由它进行处理,接下来介绍最重要的内容。 181 | 182 |   NotificationReceiver接收到点击通知后发出的广播,在onReceive()方法中回调,获取传过来的数据并进行相应的处理。 183 | 184 | @Override 185 | public void onReceive(Context context, Intent intent) { 186 | Bundle bundle = intent.getExtras(); 187 | int pageNum = bundle.getInt(PushConstants.PAGE_NUMBER, 0); 188 | String contentId = bundle.getString(PushConstants.CONTENT_ID, ""); 189 | 190 | Intent destinationIntent = null;//目标intent 191 | switch (pageNum) { 192 | case PushConstants.PAGE_ORDER_DETAIL: 193 | //订单详情页 194 | destinationIntent = new Intent(context, OrderDetailActivity.class); 195 | destinationIntent.putExtra(OrderDetailActivity.ORDER_ID, contentId);//传订单的id 196 | break; 197 | case PushConstants.PAGE_MESSAGE_CENTER: 198 | //消息中心 199 | destinationIntent = new Intent(context, MessageCenterActivity.class); 200 | break; 201 | } 202 | 203 | if (SystemUtils.isAppAlive(context, context.getPackageName())) { 204 | //如果存活的话,就直接启动目标Activity,但要考虑一种情况,就是app的进程虽然仍然在 205 | //但Task栈已经空了,比如用户点击Back键退出应用,但进程还没有被系统回收,如果直接启动 206 | //目标Activity,再按Back键就不会返回MainActivity了。所以在启动 目标Activity前,要先启动MainActivity。 207 | //将MainAtivity的launchMode设置成SingleTask, 或者在下面flag中加上Intent.FLAG_CLEAR_TOP, 208 | //如果Task栈中有MainActivity的实例,就会把它移到栈顶,把在它之上的Activity都清理出栈, 209 | //如果Task栈不存在MainActivity实例,则在栈顶创建 210 | 211 | Log.i(TAG, "the app process is alive"); 212 | Intent mainIntent = new Intent(context, MainActivity.class); 213 | mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 214 | 215 | if (intentList == null){ 216 | intentList = new ArrayList(); 217 | } 218 | intentList.add(mainIntent); 219 | //如果目标intent不为空,一起打开 220 | if (destinationIntent != null){ 221 | intentList.add(destinationIntent); 222 | } 223 | context.startActivities(intentList.toArray(new Intent[intentList.size()])); 224 | } else { 225 | //如果app进程已经被杀死,先重新启动app,将目标Activity的启动参数传入Intent中,参数经过 226 | //SplashActivity传入MainActivity,此时app的初始化已经完成,在MainActivity中就可以根据传入 227 | // 参数跳转到目标Activity中去了 228 | Log.i(TAG, "the app process is dead"); 229 | Intent launchIntent = context.getPackageManager(). 230 | getLaunchIntentForPackage(context.getPackageName()); 231 | launchIntent.setFlags( 232 | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 233 | launchIntent.putExtras(bundle); 234 | context.startActivity(launchIntent); 235 | } 236 | } 237 | 238 |   获取到传过来的数据,通过pageNum(页面编号)判断要打开哪个activity,如果是订单详情页的activity则需要传递一个contentId(此时则为订单的id),作为详情页获取数据的一个参数。 239 | 240 | 主要分为两种情况考虑: 241 | 242 | App进程存在和App进程不存在两种情况。判断App进程是否存在,可以通过遍历当前手机系统中的所有运行的进程,通过判断运行的进程的包名是否与当前App的包名一致从而知道App进程是否存在。 243 | 244 | `/** 245 | * 判断应用是否已经启动 246 | * @param context 一个context 247 | * @param packageName 要判断应用的包名 248 | * @return boolean 249 | */ 250 | public static boolean isAppAlive(Context context, String packageName){ 251 | ActivityManager activityManager = 252 | (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE); 253 | List processInfos 254 | = activityManager.getRunningAppProcesses(); 255 | for(int i = 0; i < processInfos.size(); i++){ 256 | if(processInfos.get(i).processName.equals(packageName)){ 257 | Log.i("NotificationLaunch", 258 | String.format("the %s is running, isAppAlive return true", packageName)); 259 | return true; 260 | } 261 | } 262 | Log.i("NotificationLaunch", 263 | String.format("the %s is not running, isAppAlive return false", packageName)); 264 | return false; 265 | }` 266 | 267 | 268 |   一、当APP进程存在的时候,在这种情况下,进程虽然存在,但是任务栈可能已经为空,所在无论MainActivity是否已经在栈内,都要打开MainActivity和目标activity,必须注意的是,要将MainActivity的启动模式设置为SingleTask,即栈内复用并且会清除位于其顶部的所有已经开启的activity,只留下MainActivity(一般App中MainActivity位于栈低)和目标activity。 269 | 270 |   二、当App进程已经被杀死,点击通知后启动App,将要传递的数据通过SplashActivity传入MainActivity,在MainActivity根据数据判断要打开的目标activity,从而进行跳转,SplashActivity中,通过判断getIntent().getExtras()是否为空,如果不为空,则传递给MainActivity。 271 | 272 | handler.postDelayed(new Runnable() { 273 | @Override 274 | public void run() { 275 | Intent intent = null; 276 | if (PreUtils.getBoolean(SplashActivity.this, Constants.IS_LOGIN,false)){ 277 | //如果已经处于登录状态,直接进入主界面 278 | intent = new Intent(SplashActivity.this, MainActivity.class); 279 | Bundle bundle = getIntent().getExtras(); 280 | if (bundle != null){ 281 | //如果是App没开启,点击通知打开App,传递的bundle就可以到达MainActivity 282 | intent.putExtras(bundle); 283 | } 284 | }else{ 285 | //未登录,跳转登录界面 286 | intent = new Intent(SplashActivity.this,LoginActivity.class); 287 | } 288 | startActivity(intent); 289 | } 290 | },3000); 291 | 292 | MainActivity中,做了以下处理: 293 | 294 | @Override 295 | protected void onCreate(Bundle savedInstanceState) { 296 | super.onCreate(savedInstanceState); 297 | setContentView(R.layout.activity_main); 298 | 299 | ... 300 | 301 | jumpToOtherPage();//做相应的跳转 302 | } 303 | 304 | 305 | 306 | /** 307 | * 如果有接收到传递过来的消息,则是点击通知打开的,做相应的跳转 308 | */ 309 | private void jumpToOtherPage() { 310 | Bundle bundle = getIntent().getExtras(); 311 | if (bundle != null) { 312 | //如果bundle不为空,则是点击通知栏打开App进来的,获取传过来的数据 313 | int pageNum = bundle.getInt(PushConstants.PAGE_NUMBER, 0); 314 | String id = bundle.getString(PushConstants.CONTENT_ID, ""); 315 | 316 | Intent destinationIntent = null;//目标intent 317 | switch (pageNum) { 318 | case PushConstants.PAGE_ORDER_DETAIL: 319 | //订单详情页 320 | destinationIntent = new Intent(this, OrderDetailActivity.class); 321 | destinationIntent.putExtra(OrderDetailActivity.ORDER_ID, id);//传对应订单的id 322 | break; 323 | case PushConstants.MESSAGE_CENTER_NOTIFY: 324 | //消息中心页面 325 | destinationIntent = new Intent(this, MessageCenterActivity.class); 326 | break; 327 | } 328 | if (destinationIntent != null) { 329 | startActivity(destinationIntent); 330 | } 331 | } 332 | } 333 | 334 |   到此为止,关于App推送通知的处理介绍就结束了,至于数据的定义和相关页面跳转的判断,每个人有各自的规则,我这里是通过在PushConstant中定义json对应的key的常量以及页面编号的常量,后台和App端都定义了相同的常量,同时封装好了对应的推送工具类,适用于安卓和IOS端,需要提醒的是,IOS需要在个推管理后台中配置推送证书。关于演示的App的demo和后台的推送demo,大家可以通过下面的github地址进行clone,希望可以帮助到大家。 335 | 336 | [https://github.com/chaychan/GTPushDemo.git](https://github.com/chaychan/GTPushDemo.git)(android端) 337 | 338 | [https://github.com/chaychan/GTPushDemoJava.git](https://github.com/chaychan/GTPushDemoJava.git)(后台java代码) 339 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion "24.0.3" 6 | 7 | defaultConfig { 8 | applicationId "com.chaychan.getuipushdemo" 9 | minSdkVersion 16 10 | targetSdkVersion 24 11 | versionCode 1 12 | versionName "1.0" 13 | 14 | manifestPlaceholders = [ 15 | GETUI_APP_ID : "0SkbWZcgrV8Rze7NkAbKJ7", 16 | GETUI_APP_KEY : "JrtaNbM2yB8b37vwPOVtD5", 17 | GETUI_APP_SECRET : "XyrfzhBuG7AtIp3liNV2q6" 18 | ] 19 | 20 | ndk { 21 | abiFilters "armeabi", "armeabi-v7a", "x86","x86_64" 22 | } 23 | } 24 | buildTypes { 25 | release { 26 | minifyEnabled false 27 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | } 31 | 32 | dependencies { 33 | compile fileTree(include: ['*.jar'], dir: 'libs') 34 | testCompile 'junit:junit:4.12' 35 | compile 'com.android.support:appcompat-v7:24.2.1' 36 | compile 'com.getui:sdk:2.10.2.0' 37 | compile 'com.android.support:support-v4:+' 38 | } 39 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in G:\SDK\AndroidStudioSDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/chaychan/pushdemo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /app/src/main/java/com/chaychan/pushdemo/activity/LoginActivity.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo.activity; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.text.TextUtils; 7 | import android.view.View; 8 | import android.widget.EditText; 9 | import android.widget.Toast; 10 | 11 | import com.chaychan.pushdemo.R; 12 | import com.chaychan.pushdemo.global.Constants; 13 | import com.chaychan.pushdemo.utils.PreUtils; 14 | import com.igexin.sdk.PushManager; 15 | 16 | public class LoginActivity extends AppCompatActivity { 17 | 18 | private EditText etUsername; 19 | private EditText etPwd; 20 | 21 | @Override 22 | protected void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | setContentView(R.layout.activity_login); 25 | 26 | etUsername = (EditText) findViewById(R.id.et_username); 27 | etPwd = (EditText) findViewById(R.id.et_pwd); 28 | } 29 | 30 | public void login(View view){ 31 | String username = etUsername.getText().toString().trim(); 32 | String pwd = etPwd.getText().toString().trim(); 33 | 34 | if (TextUtils.isEmpty(username)){ 35 | Toast.makeText(this,"用户名不能为空",Toast.LENGTH_SHORT); 36 | return; 37 | } 38 | if (TextUtils.isEmpty(pwd)){ 39 | Toast.makeText(this,"密码不能为空",Toast.LENGTH_SHORT); 40 | return; 41 | } 42 | 43 | //执行登录逻辑,登录成功后,绑定推送的别名,可以绑定用户名为个推推送的别名 44 | PushManager.getInstance().bindAlias(this, username);//绑定推送别名 45 | 46 | PreUtils.putBoolean(this, Constants.IS_LOGIN,true);//设置为已登录 47 | PreUtils.putString(this,Constants.ACCOUNT,username);//保存账号,关于账号的保存,这里只是保存到sp作为演示,对于用户的信息,最好保存在数据库中 48 | 49 | startActivity(new Intent(this,MainActivity.class)); 50 | finish(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/chaychan/pushdemo/activity/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo.activity; 2 | 3 | import android.Manifest; 4 | import android.content.Intent; 5 | import android.content.pm.PackageManager; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.support.v4.app.ActivityCompat; 9 | import android.support.v7.app.AppCompatActivity; 10 | import android.text.TextUtils; 11 | import android.view.View; 12 | 13 | import com.chaychan.pushdemo.R; 14 | import com.chaychan.pushdemo.global.Constants; 15 | import com.chaychan.pushdemo.global.PushConstants; 16 | import com.chaychan.pushdemo.service.DemoIntentService; 17 | import com.chaychan.pushdemo.service.DemoPushService; 18 | import com.chaychan.pushdemo.utils.PreUtils; 19 | import com.igexin.sdk.PushManager; 20 | import com.igexin.sdk.PushService; 21 | 22 | public class MainActivity extends AppCompatActivity { 23 | private static final int REQUEST_PERMISSION = 0; 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_main); 28 | 29 | PackageManager pkgManager = getPackageManager(); 30 | 31 | // 读写 sd card 权限非常重要, android6.0默认禁止的, 建议初始化之前就弹窗让用户赋予该权限 32 | boolean sdCardWritePermission = 33 | pkgManager.checkPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, getPackageName()) == PackageManager.PERMISSION_GRANTED; 34 | 35 | // read phone state用于获取 imei 设备信息 36 | boolean phoneSatePermission = 37 | pkgManager.checkPermission(android.Manifest.permission.READ_PHONE_STATE, getPackageName()) == PackageManager.PERMISSION_GRANTED; 38 | 39 | if (Build.VERSION.SDK_INT >= 23 && !sdCardWritePermission || !phoneSatePermission) { 40 | requestPermission(); 41 | } else { 42 | PushManager.getInstance().initialize(this.getApplicationContext(), DemoPushService.class); 43 | } 44 | 45 | // 注册 intentService 后 PushDemoReceiver 无效, sdk 会使用 DemoIntentService 传递数据, 46 | // AndroidManifest 对应保留一个即可(如果注册 DemoIntentService, 可以去掉 PushDemoReceiver, 如果注册了 47 | // IntentService, 必须在 AndroidManifest 中声明) 48 | PushManager.getInstance().registerPushIntentService(this.getApplicationContext(), DemoIntentService.class); 49 | 50 | 51 | jumpToOtherPage();//做相应的跳转 52 | } 53 | 54 | /** 55 | * 如果有接收到传递过来的消息,则是点击通知打开的,做相应的跳转 56 | */ 57 | private void jumpToOtherPage() { 58 | Bundle bundle = getIntent().getExtras(); 59 | if (bundle != null) { 60 | //如果bundle不为空,则是点击通知栏打开App进来的,获取传过来的数据 61 | int pageNum = bundle.getInt(PushConstants.PAGE_NUMBER, 0); 62 | String id = bundle.getString(PushConstants.CONTENT_ID, ""); 63 | 64 | Intent destinationIntent = null;//目标intent 65 | switch (pageNum) { 66 | case PushConstants.PAGE_ORDER_DETAIL: 67 | //订单详情页 68 | destinationIntent = new Intent(this, OrderDetailActivity.class); 69 | destinationIntent.putExtra(OrderDetailActivity.ORDER_ID, id);//传对应订单的id 70 | break; 71 | case PushConstants.MESSAGE_CENTER_NOTIFY: 72 | //消息中心页面 73 | destinationIntent = new Intent(this, MessageCenterActivity.class); 74 | break; 75 | } 76 | if (destinationIntent != null) { 77 | startActivity(destinationIntent); 78 | } 79 | } 80 | } 81 | 82 | public void logout(View view){ 83 | //退出登录 84 | PreUtils.putBoolean(this, Constants.IS_LOGIN,false); 85 | 86 | String username = PreUtils.getString(this, Constants.ACCOUNT, ""); 87 | if (!TextUtils.isEmpty(username)){ 88 | PushManager.getInstance().unBindAlias(this,username,true);//解绑别名 89 | } 90 | 91 | startActivity(new Intent(this,LoginActivity.class)); 92 | finish(); 93 | } 94 | 95 | 96 | private void requestPermission() { 97 | ActivityCompat.requestPermissions(this, new String[] {android.Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_STATE}, 98 | REQUEST_PERMISSION); 99 | } 100 | 101 | @Override 102 | public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { 103 | if (requestCode == REQUEST_PERMISSION) { 104 | if ((grantResults.length == 2 && grantResults[0] == PackageManager.PERMISSION_GRANTED 105 | && grantResults[1] == PackageManager.PERMISSION_GRANTED)) { 106 | PushManager.getInstance().initialize(this.getApplicationContext(), PushService.class); 107 | } else { 108 | PushManager.getInstance().initialize(this.getApplicationContext(), PushService.class); 109 | } 110 | } else { 111 | super.onRequestPermissionsResult(requestCode, permissions, grantResults); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /app/src/main/java/com/chaychan/pushdemo/activity/MessageCenterActivity.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo.activity; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import com.chaychan.pushdemo.R; 7 | 8 | /** 9 | * 消息中心页面 10 | */ 11 | public class MessageCenterActivity extends AppCompatActivity { 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_message_center); 17 | 18 | //消息中心没有数据传递过来,只是用于简单演示点击通知后打开对应的activity 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/chaychan/pushdemo/activity/OrderDetailActivity.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo.activity; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.widget.TextView; 6 | 7 | import com.chaychan.pushdemo.R; 8 | 9 | /** 10 | * 订单详情页 11 | */ 12 | public class OrderDetailActivity extends AppCompatActivity { 13 | 14 | public static final String ORDER_ID = "orderId"; 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_order_detail); 20 | 21 | TextView tv = (TextView) findViewById(R.id.tv); 22 | 23 | String productId = getIntent().getStringExtra(ORDER_ID); 24 | //订单详情页数据通过获取到对应的订单id访问接口获取,这里只是简单的演示下数据的传递 25 | tv.setText(String.format("订单id为: %s 的详情页",productId)); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/chaychan/pushdemo/activity/SplashActivity.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo.activity; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.support.v7.app.AppCompatActivity; 7 | 8 | import com.chaychan.pushdemo.R; 9 | import com.chaychan.pushdemo.global.Constants; 10 | import com.chaychan.pushdemo.utils.PreUtils; 11 | 12 | public class SplashActivity extends AppCompatActivity { 13 | 14 | private Handler handler; 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_splash); 20 | 21 | handler = new Handler(); 22 | handler.postDelayed(new Runnable() { 23 | @Override 24 | public void run() { 25 | Intent intent = null; 26 | if (PreUtils.getBoolean(SplashActivity.this, Constants.IS_LOGIN,false)){ 27 | //如果已经处于登录状态,直接进入主界面 28 | intent = new Intent(SplashActivity.this, MainActivity.class); 29 | Bundle bundle = getIntent().getExtras(); 30 | if (bundle != null){ 31 | //如果是App没开启,点击通知打开App,传递的bundle就可以到达MainActivity 32 | intent.putExtras(bundle); 33 | } 34 | }else{ 35 | //未登录,跳转登录界面 36 | intent = new Intent(SplashActivity.this,LoginActivity.class); 37 | } 38 | startActivity(intent); 39 | } 40 | },3000); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /app/src/main/java/com/chaychan/pushdemo/global/Constants.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo.global; 2 | 3 | /** 4 | * @author chaychan 5 | * @description: 保存一些常量 6 | * @date 2017/5/21 14:36 7 | */ 8 | public class Constants { 9 | public static final String IS_LOGIN = "isLogin"; 10 | public static final String ACCOUNT = "account"; 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/chaychan/pushdemo/global/PushConstants.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo.global; 2 | 3 | /** 4 | * @author chaychan 5 | * @description: 推送的常量配置 6 | * @date 2017/4/26 11:31 7 | */ 8 | public class PushConstants { 9 | /** 10 | * 属于通知 11 | */ 12 | public static final String IS_NOTIFICATION = "isNotification"; 13 | /** 14 | * 标题 15 | */ 16 | public static final String TITLE = "title"; 17 | /** 18 | * 内容 19 | */ 20 | public static final String CONTENT = "content"; 21 | /** 22 | * 内容的id 23 | */ 24 | public static final String CONTENT_ID = "contentId"; 25 | 26 | /** 27 | * 对应打开页面的编号 28 | */ 29 | public static final String PAGE_NUMBER = "pageNumber"; 30 | 31 | /** 主页面 */ 32 | public static final int PAGE_MAIN = 0; 33 | 34 | /** 35 | * 订单详情页 36 | */ 37 | public static final int PAGE_ORDER_DETAIL = 1000; 38 | 39 | /** 40 | * 消息中心 41 | */ 42 | public static final int PAGE_MESSAGE_CENTER = 1001; 43 | 44 | //******************************推送提醒*********************************************** 45 | 46 | public static final String NOTIFY_TYPE = "notifyType"; 47 | 48 | public static final String UNREAD_COUNT = "unReadCount"; 49 | 50 | /** 51 | * 消息中心的提醒 52 | */ 53 | public static final int MESSAGE_CENTER_NOTIFY = 2000; 54 | 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/chaychan/pushdemo/receiver/NotificationReceiver.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo.receiver; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.util.Log; 8 | 9 | import com.chaychan.pushdemo.activity.MainActivity; 10 | import com.chaychan.pushdemo.activity.MessageCenterActivity; 11 | import com.chaychan.pushdemo.activity.OrderDetailActivity; 12 | import com.chaychan.pushdemo.global.PushConstants; 13 | import com.chaychan.pushdemo.utils.SystemUtils; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | /** 19 | * @author chaychan 20 | * @description: 点击通知栏后接收到消息的广播, 根据消息进行对应页面的跳转 21 | * @date 2017/4/26 15:24 22 | */ 23 | public class NotificationReceiver extends BroadcastReceiver { 24 | 25 | public static final String TAG = NotificationReceiver.class.getSimpleName(); 26 | private List intentList; 27 | 28 | @Override 29 | public void onReceive(Context context, Intent intent) { 30 | Bundle bundle = intent.getExtras(); 31 | int pageNum = bundle.getInt(PushConstants.PAGE_NUMBER, 0); 32 | String contentId = bundle.getString(PushConstants.CONTENT_ID, ""); 33 | 34 | Intent destinationIntent = null;//目标intent 35 | switch (pageNum) { 36 | case PushConstants.PAGE_ORDER_DETAIL: 37 | //订单详情页 38 | destinationIntent = new Intent(context, OrderDetailActivity.class); 39 | destinationIntent.putExtra(OrderDetailActivity.ORDER_ID, contentId);//传订单的id 40 | break; 41 | case PushConstants.PAGE_MESSAGE_CENTER: 42 | //消息中心 43 | destinationIntent = new Intent(context, MessageCenterActivity.class); 44 | break; 45 | } 46 | 47 | if (SystemUtils.isAppAlive(context, context.getPackageName())) { 48 | //如果存活的话,就直接启动目标Activity,但要考虑一种情况,就是app的进程虽然仍然在 49 | //但Task栈已经空了,比如用户点击Back键退出应用,但进程还没有被系统回收,如果直接启动 50 | //目标Activity,再按Back键就不会返回MainActivity了。所以在启动 目标Activity前,要先启动MainActivity。 51 | //将MainAtivity的launchMode设置成SingleTask, 或者在下面flag中加上Intent.FLAG_CLEAR_TOP, 52 | //如果Task栈中有MainActivity的实例,就会把它移到栈顶,把在它之上的Activity都清理出栈, 53 | //如果Task栈不存在MainActivity实例,则在栈顶创建 54 | 55 | Log.i(TAG, "the app process is alive"); 56 | Intent mainIntent = new Intent(context, MainActivity.class); 57 | mainIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 58 | 59 | if (intentList == null){ 60 | intentList = new ArrayList(); 61 | } 62 | intentList.add(mainIntent); 63 | //如果目标intent不为空,一起打开 64 | if (destinationIntent != null){ 65 | intentList.add(destinationIntent); 66 | } 67 | context.startActivities(intentList.toArray(new Intent[intentList.size()])); 68 | } else { 69 | //如果app进程已经被杀死,先重新启动app,将目标Activity的启动参数传入Intent中,参数经过 70 | //SplashActivity传入MainActivity,此时app的初始化已经完成,在MainActivity中就可以根据传入 71 | // 参数跳转到目标Activity中去了 72 | Log.i(TAG, "the app process is dead"); 73 | Intent launchIntent = context.getPackageManager(). 74 | getLaunchIntentForPackage(context.getPackageName()); 75 | launchIntent.setFlags( 76 | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); 77 | launchIntent.putExtras(bundle); 78 | context.startActivity(launchIntent); 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /app/src/main/java/com/chaychan/pushdemo/service/DemoIntentService.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo.service; 2 | 3 | import android.app.PendingIntent; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Bundle; 7 | import android.os.Handler; 8 | import android.os.Looper; 9 | import android.text.TextUtils; 10 | import android.util.Log; 11 | 12 | import com.chaychan.pushdemo.R; 13 | import com.chaychan.pushdemo.global.PushConstants; 14 | import com.chaychan.pushdemo.receiver.NotificationReceiver; 15 | import com.chaychan.pushdemo.utils.NotificationUtils; 16 | import com.igexin.sdk.GTIntentService; 17 | import com.igexin.sdk.message.GTCmdMessage; 18 | import com.igexin.sdk.message.GTTransmitMessage; 19 | 20 | import org.json.JSONException; 21 | import org.json.JSONObject; 22 | 23 | /** 24 | * 继承 GTIntentService 接收来自个推的消息, 所有消息在线程中回调, 如果注册了该服务, 则务必要在 AndroidManifest中声明, 否则无法接受消息
25 | * onReceiveMessageData 处理透传消息
26 | * onReceiveClientId 接收 cid
27 | * onReceiveOnlineState cid 离线上线通知
28 | * onReceiveCommandResult 各种事件处理回执
29 | */ 30 | public class DemoIntentService extends GTIntentService { 31 | 32 | private Handler handler = new Handler(Looper.getMainLooper()); 33 | 34 | public DemoIntentService() { 35 | 36 | } 37 | 38 | @Override 39 | public void onReceiveServicePid(Context context, int pid) { 40 | } 41 | 42 | @Override 43 | public void onReceiveMessageData(Context context, GTTransmitMessage msg) { 44 | byte[] payload = msg.getPayload(); 45 | 46 | String message = new String(payload); 47 | Log.i(TAG, "onReceiveMessageData: " + message); 48 | try { 49 | JSONObject jsonObject = new JSONObject(message); 50 | int isNotification = jsonObject.optInt(PushConstants.IS_NOTIFICATION); 51 | if (isNotification == 1) { 52 | //属于通知类的透传消息 53 | showNotification(context, jsonObject); 54 | } else { 55 | //提醒消息 56 | dealNotifyMessage(context, jsonObject); 57 | } 58 | } catch (JSONException e) { 59 | e.printStackTrace(); 60 | } 61 | } 62 | 63 | /** 64 | * 根据消息内容弹出通知框 65 | * 66 | * @param context 67 | * @param jsonObject 68 | */ 69 | private void showNotification(Context context, JSONObject jsonObject) { 70 | String title = jsonObject.optString(PushConstants.TITLE); 71 | String content = jsonObject.optString(PushConstants.CONTENT); 72 | 73 | if (TextUtils.isEmpty(title) || TextUtils.isEmpty(content)) { 74 | return; 75 | } 76 | 77 | int pageNum = jsonObject.optInt(PushConstants.PAGE_NUMBER); 78 | String contentId = jsonObject.optString(PushConstants.CONTENT_ID); 79 | 80 | //设置点击通知后是发送广播,传递对应的数据 81 | Intent broadcastIntent = new Intent(context, NotificationReceiver.class); 82 | Bundle bundle = new Bundle(); 83 | bundle.putInt(PushConstants.PAGE_NUMBER, pageNum); 84 | bundle.putString(PushConstants.CONTENT_ID, contentId); 85 | broadcastIntent.putExtras(bundle); 86 | 87 | PendingIntent pendingIntent = PendingIntent. 88 | getBroadcast(context, NotificationUtils.getRandowReqCode(), broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT); 89 | NotificationUtils.showIntentNotification(context, title, content, title, pendingIntent, R.mipmap.ic_launcher, R.mipmap.ic_launcher); 90 | } 91 | 92 | /** 93 | * 处理提醒消息 94 | * 95 | * @param context 96 | * @param jsonObject 97 | */ 98 | private void dealNotifyMessage(final Context context, JSONObject jsonObject) { 99 | int notifyType = jsonObject.optInt(PushConstants.NOTIFY_TYPE, PushConstants.MESSAGE_CENTER_NOTIFY); 100 | //判断提醒信息的类型,做相应的UI操作,由于此处处于IntentService中,做UI操作需要进行线程的切换,这里使用了handler的post()方法切换 101 | switch (notifyType) { 102 | case PushConstants.MESSAGE_CENTER_NOTIFY: 103 | //消息中心的提醒 104 | handler.post(new Runnable() { 105 | public void run() { 106 | /* if (MainActivity.instance != null){ 107 | MainActivity.instance.showTabNotify(2);//底部我的tab显示提示点 108 | } 109 | if (MineFragment.ivMessage != null){ 110 | MineFragment.ivMessage.setImageResource(R.mipmap.img_message_unread);//设置消息中心图标为未读的 111 | } 112 | //更新消息表的未读数 113 | UnReadDao.saveOrUpdate(UnReadDao.getUnreadCount() + 1);*/ 114 | 115 | Log.i(TAG,"收到消息提醒,显示小红点"); 116 | } 117 | }); 118 | break; 119 | } 120 | } 121 | 122 | 123 | @Override 124 | public void onReceiveClientId(Context context, String clientid) { 125 | Log.e(TAG, "onReceiveClientId -> " + "clientid = " + clientid); 126 | } 127 | 128 | @Override 129 | public void onReceiveOnlineState(Context context, boolean online) { 130 | } 131 | 132 | @Override 133 | public void onReceiveCommandResult(Context context, GTCmdMessage cmdMessage) { 134 | } 135 | } -------------------------------------------------------------------------------- /app/src/main/java/com/chaychan/pushdemo/service/DemoPushService.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo.service; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.IBinder; 6 | 7 | import com.igexin.sdk.GTServiceManager; 8 | 9 | public class DemoPushService extends Service { 10 | 11 | @Override 12 | public void onCreate() { 13 | super.onCreate(); 14 | GTServiceManager.getInstance().onCreate(this); 15 | } 16 | 17 | @Override 18 | public int onStartCommand(Intent intent, int flags, int startId) { 19 | super.onStartCommand(intent, flags, startId); 20 | return GTServiceManager.getInstance().onStartCommand(this, intent, flags, startId); 21 | } 22 | 23 | @Override 24 | public IBinder onBind(Intent intent) { 25 | return GTServiceManager.getInstance().onBind(intent); 26 | } 27 | 28 | @Override 29 | public void onDestroy() { 30 | super.onDestroy(); 31 | GTServiceManager.getInstance().onDestroy(); 32 | } 33 | 34 | @Override 35 | public void onLowMemory() { 36 | super.onLowMemory(); 37 | GTServiceManager.getInstance().onLowMemory(); 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/com/chaychan/pushdemo/utils/NotificationUtils.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo.utils; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationManager; 5 | import android.app.PendingIntent; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.graphics.BitmapFactory; 9 | import android.os.Build; 10 | import android.support.v7.app.NotificationCompat; 11 | 12 | import com.chaychan.pushdemo.R; 13 | 14 | import java.util.Random; 15 | 16 | /** 17 | * Created by duqian on 16/01/23. 18 | * Android通知栏封装 19 | */ 20 | public class NotificationUtils { 21 | 22 | private static final int SmallIcon = R.mipmap.ic_launcher; 23 | public static final int NotificationNumber = 1; 24 | private static NotificationManager mManager; 25 | private static NotificationCompat.Builder mBuilder; 26 | private static final Random RANDOM = new Random(); 27 | 28 | /** 29 | * 获取Builder 30 | */ 31 | public static NotificationCompat.Builder getBuilder(Context context) { 32 | mBuilder = new NotificationCompat.Builder(context); 33 | mBuilder.setNumber(NotificationNumber) 34 | .setWhen(System.currentTimeMillis()) 35 | .setPriority(Notification.PRIORITY_MAX)//可以让通知显示在最上面 36 | .setAutoCancel(true); 37 | //.setDefaults(Notification.DEFAULT_VIBRATE); 38 | return mBuilder; 39 | } 40 | 41 | /** 42 | * 获取NotificationManager 43 | */ 44 | public static NotificationManager getManager(Context context) { 45 | 46 | if (mManager == null) { 47 | synchronized (NotificationUtils.class) { 48 | if (mManager == null) { 49 | mManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE); 50 | } 51 | } 52 | } 53 | return mManager; 54 | } 55 | 56 | 57 | /** 58 | * 显示普通的通知 59 | */ 60 | public static void showOrdinaryNotification(Context context, String title, String text, String ticker, 61 | int icon, int channel) { 62 | mBuilder = getBuilder(context); 63 | mManager = getManager(context); 64 | mBuilder.setContentTitle(title) 65 | .setContentText(text) 66 | .setContentIntent(getDefalutIntent(context, Notification.FLAG_AUTO_CANCEL)) 67 | .setNumber(NotificationNumber)//显示数量 68 | .setTicker(ticker)//通知首次出现在通知栏,带上升动画效果的,可设置文字,图标 69 | .setWhen(System.currentTimeMillis())//通知产生的时间 70 | .setPriority(Notification.PRIORITY_DEFAULT)//设置该通知优先级 71 | .setAutoCancel(true)//设置让通知将自动取消 72 | .setOngoing(false)//ture,设置他为一个正在进行的通知。如一个文件下载,网络连接。 73 | .setDefaults(Notification.DEFAULT_SOUND)//向通知添加声音、闪灯和振动效果。最简单、最一致的方式是使用当前的用户默认设置,使用defaults属性,可以组合: 74 | //Notification.DEFAULT_ALL Notification.DEFAULT_SOUND 添加声音 //DEFAULT_VIBRATE requires VIBRATE permission 75 | .setSmallIcon(SmallIcon) 76 | ; 77 | Notification mNotification = mBuilder.build(); 78 | mNotification.icon = icon; 79 | mManager.notify(dealWithId(channel), mNotification); 80 | } 81 | 82 | //获取默认的延期意图 83 | public static PendingIntent getDefalutIntent(Context context, int flags) { 84 | PendingIntent pendingIntent = PendingIntent.getActivity(context, 1, new Intent(), flags); 85 | return pendingIntent; 86 | } 87 | 88 | //通知channel ID,唯一标示一个通知 89 | public static int dealWithId(int channel) { 90 | return channel >= 1 && channel <= 100 ? channel : RANDOM.nextInt(Integer.MAX_VALUE - 100) + 101; 91 | } 92 | 93 | //获取系统SDK版本 94 | public static int getSystemVersion() { 95 | int version = Build.VERSION.SDK_INT; 96 | return version; 97 | } 98 | 99 | /** 100 | * 清除所有的通知 101 | * 102 | * @param context 103 | */ 104 | public static void clearAllNotifification(Context context) { 105 | mManager = getManager(context); 106 | mManager.cancelAll(); 107 | } 108 | 109 | /** 110 | * 清除通知 111 | */ 112 | public static void clearNotifificationById(Context context, int channel) { 113 | mManager = getManager(context); 114 | mManager.cancel(dealWithId(channel)); 115 | } 116 | 117 | private static int iconId = 0; 118 | 119 | public void setIconId(int iconId) { 120 | this.iconId = iconId; 121 | } 122 | 123 | /** 124 | * 默认图标 125 | */ 126 | private static int getPushIconId(Context context) { 127 | if (iconId == 0) { 128 | iconId = context.getApplicationInfo().icon; 129 | } 130 | if (iconId < 0) { 131 | iconId = android.R.drawable.sym_def_app_icon; 132 | } 133 | return iconId; 134 | } 135 | 136 | //去通知文本的前15位字符 137 | private static String getSubStringTitle(String desc) { 138 | if (desc != null && !"".equals(desc) && desc.length() > 15) { 139 | return desc.substring(0, 15); 140 | } else { 141 | return desc; 142 | } 143 | } 144 | 145 | /** 146 | * 显示进度的通知栏 147 | */ 148 | public static void showProgressNotification(Context context, String title, String text, String ticker, 149 | Intent resultIntent, boolean indeterminate, int progress, 150 | int icon, int channel) { 151 | mBuilder = getBuilder(context); 152 | mManager = getManager(context); 153 | PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT); 154 | mBuilder.setContentTitle(ticker)//ticker,title 155 | .setContentText(text) 156 | .setTicker(ticker) 157 | .setSmallIcon(SmallIcon) 158 | .setContentIntent(pendingIntent); 159 | if (indeterminate) { 160 | //不确定进度的 设置为true就是不确定的那种进度条效果 161 | mBuilder.setProgress(0, 0, true).setOngoing(false).setAutoCancel(true); 162 | } else { 163 | //确定进度的 164 | mBuilder.setProgress(100, progress, false).setOngoing(true); 165 | } 166 | 167 | Notification progressNotification = mBuilder.build(); 168 | progressNotification.icon = icon; 169 | mManager.notify(dealWithId(channel), progressNotification); 170 | } 171 | 172 | /** 173 | * 显示带意图的通知栏 174 | * @param context 上下文 175 | * @param title 标题 176 | * @param text 内容 177 | * @param ticker 178 | * @param pendingIntent 179 | * @param smallIcon 小图标 180 | * @param largeIcon 大图标 181 | */ 182 | public static void showIntentNotification(Context context, String title, String text, String ticker, 183 | PendingIntent pendingIntent, int smallIcon,int largeIcon) { 184 | mBuilder = getBuilder(context); 185 | mManager = getManager(context); 186 | 187 | mBuilder.setContentTitle(title) 188 | .setContentText(text) 189 | .setTicker(ticker) 190 | .setSmallIcon(smallIcon) 191 | .setLargeIcon(BitmapFactory.decodeResource(context.getResources(),largeIcon)) 192 | .setDefaults(Notification.DEFAULT_SOUND) 193 | .setContentIntent(pendingIntent); 194 | 195 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN){ 196 | //如果当前手机版本大于16 也就是Android4.1以上,可以使用大文本布局 197 | NotificationCompat.BigTextStyle inboxStyle = new NotificationCompat.BigTextStyle(); 198 | inboxStyle.bigText(text); 199 | mBuilder.setStyle(inboxStyle); 200 | } 201 | 202 | mManager.notify(getRandowReqCode(), mBuilder.build()); 203 | } 204 | 205 | public static int getRandowReqCode(){ 206 | return RANDOM.nextInt(Integer.MAX_VALUE); 207 | } 208 | 209 | } -------------------------------------------------------------------------------- /app/src/main/java/com/chaychan/pushdemo/utils/PreUtils.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo.utils; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | public class PreUtils { 7 | 8 | public static final String CONFIG_FILE_NAME = "config"; 9 | 10 | public static void putBoolean(Context context,String key, boolean value){ 11 | SharedPreferences sp = context.getSharedPreferences(CONFIG_FILE_NAME,Context.MODE_PRIVATE); 12 | sp.edit().putBoolean(key,value).commit(); 13 | } 14 | 15 | public static boolean getBoolean(Context context,String key, boolean defValue){ 16 | SharedPreferences sp = context.getSharedPreferences(CONFIG_FILE_NAME, Context.MODE_PRIVATE); 17 | return sp.getBoolean(key,defValue); 18 | } 19 | 20 | public static void putString(Context context,String key, String value){ 21 | SharedPreferences sp = context.getSharedPreferences(CONFIG_FILE_NAME,Context.MODE_PRIVATE); 22 | sp.edit().putString(key,value).commit(); 23 | } 24 | 25 | public static String getString(Context context,String key, String defValue){ 26 | SharedPreferences sp = context.getSharedPreferences(CONFIG_FILE_NAME, Context.MODE_PRIVATE); 27 | return sp.getString(key,defValue); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/chaychan/pushdemo/utils/SystemUtils.java: -------------------------------------------------------------------------------- 1 | package com.chaychan.pushdemo.utils; 2 | 3 | import android.app.ActivityManager; 4 | import android.content.Context; 5 | import android.util.Log; 6 | 7 | import java.util.List; 8 | 9 | public class SystemUtils { 10 | /** 11 | * 判断应用是否已经启动 12 | * @param context 一个context 13 | * @param packageName 要判断应用的包名 14 | * @return boolean 15 | */ 16 | public static boolean isAppAlive(Context context, String packageName){ 17 | ActivityManager activityManager = 18 | (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE); 19 | List processInfos 20 | = activityManager.getRunningAppProcesses(); 21 | for(int i = 0; i < processInfos.size(); i++){ 22 | if(processInfos.get(i).processName.equals(packageName)){ 23 | Log.i("NotificationLaunch", 24 | String.format("the %s is running, isAppAlive return true", packageName)); 25 | return true; 26 | } 27 | } 28 | Log.i("NotificationLaunch", 29 | String.format("the %s is not running, isAppAlive return false", packageName)); 30 | return false; 31 | } 32 | 33 | /** 34 | * 用来判断服务是否运行. 35 | * 36 | * @param mContext 37 | * @param className 使用.class.getCanoicalName获取 38 | * @return true 在运行 false 不在运行 39 | */ 40 | public static boolean isServiceRunning(Context mContext, String className) { 41 | boolean isRunning = false; 42 | ActivityManager activityManager = (ActivityManager) mContext 43 | .getSystemService(Context.ACTIVITY_SERVICE); 44 | List serviceList = activityManager 45 | .getRunningServices(30); 46 | if (!(serviceList.size() > 0)) { 47 | return false; 48 | } 49 | for (int i = 0; i < serviceList.size(); i++) { 50 | if (serviceList.get(i).service.getClassName().equals(className) == true) { 51 | isRunning = true; 52 | break; 53 | } 54 | } 55 | return isRunning; 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 16 | 17 | 24 | 25 |