前言:
由于项目要求在国外上线,所以我使用Google自带的GCM实现推送。GCM即Google Cloud Messaging,可以让开发者在客户端和服务器之间传递消息。GCM需要google service支持,在国内基本不能用,经常会断线,国内采用比较多的是极光、友盟、信鸽等第三方推送,GCM推送相关文章不多,下面简单总结一下我在项目中如何实现GCM前后台推送。
用到的知识点:
- Retrofit实现网络请求
- GCM+NotificationCompat/NotificationChannels实现后台推送
- EventBus+FragmentTabHost实现前台(底部导航)推送
实现的代码:
1.Google官网注册应用
首先去网址:https://console.firebase.google.com/ 去注册自己的应用,并下载google-services.json的文件,把它放到自己项目的app/目录。
2.添加依赖
2.1 Project的build.gradle
classpath 'com.google.gms:google-services:3.1.0'
2.2 Module的build.gradle
dependencies {
//GCM
compile 'com.google.firebase:firebase-core:11.0.4'
compile 'com.google.firebase:firebase-messaging:11.0.4'
}
apply plugin: 'com.google.gms.google-services' //这一句一定要放在最下面,否则无效
3.配置AndroidMenifest.xml文件
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="项目包名">
<!--连接网络权限-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!--保证消息到达的时候,可以得到及时处理-->
<uses-permission android:name="android.permission.WAKE_LOCK" />
<!--声音震动的权限-->
<uses-permission android:name="android.permission.VIBRATE"/>
<application
android:name="uk.co.common.base.MyApp"
android:allowBackup="true"
android:icon="@drawable/app_icon"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".ui.activity.SplashActivity"
android:launchMode="standard"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity>
...
</activity>
<service android:name=".ui.service.MyFirebaseInstanceIDService">
<intent-filter>
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
<service android:name=".ui.service.MyFirebaseMessagingService">
<!--这里的resource的ic_notification要跟接口一致 -->
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_notification" />
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
</application>
</manifest>
4.MyFirebaseInstanceIDService获取token的接口
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
private static final String TAG = "MyFirebaseIIDService";
@Override
public void onTokenRefresh() {
//获取token
String token = FirebaseInstanceId.getInstance().getToken();
SharedPreUtil.saveString(this, "fcm_token", token);//保存token
}
}
5.MyFirebaseMessagingService接收推送
/*
*判断当前的app运行状态,如果app在前台运行的话就把消息推送底部导航,显示未读信息;如果app在后台运行(包括锁屏状态)就Notification通知
*/
public class MyFirebaseMessagingService extends FirebaseMessagingService {
private NotificationManager notificationManager = null;
public void onMessageReceived(RemoteMessage remoteMessage) {
int type = 0;
int count = 0;
if (remoteMessage.getData().size() > 0) {
datatype = Integer.valueOf(remoteMessage.getData().get("type"));
count = Integer.valueOf(remoteMessage.getData().get("count"));
if (datatype == 1 || datatype == 2) { // 1和2都表示不同的类型 EventBus.getDefault().post(new FcmMessageEvent(type, count));
}
}
if (remoteMessage.getNotification() != null) {
//show Notification
Intent intent = new Intent(this, MainActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.putExtra("type",type);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
NotificationCompat.Builder notificationBuilder = (NotificationCompat.Builder) new NotificationCompat.Builder(this)
.setContentTitle(remoteMessage.getNotification().getTitle())
.setContentText(remoteMessage.getNotification().getBody())
.setLargeIcon(bm)
.setSmallIcon(R.drawable.ic_notification)
.setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL)
.setDefaults(Notification.DEFAULT_ALL)
.setAutoCancel(true)
.setContentIntent(pendingIntent);
NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(Global.mContext);
//Android8.0及其以上的适配
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
CharSequence adminChannelName = remoteMessage.getNotification().getTitle();
String adminChannelDescription = remoteMessage.getNotification().getBody();
NotificationChannel adminChannel;
adminChannel = new NotificationChannel("", adminChannelName, NotificationManager.IMPORTANCE_LOW);
adminChannel.setDescription(adminChannelDescription);
adminChannel.setShowBadge(false);
if (notificationManager != null) {
notificationManager.createNotificationChannel(adminChannel);
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(Global.mContext);
builder.setContentIntent(pendingIntent);
}
notificationManagerCompat.notify(0, notificationBuilder.build());
}
}
}
6.MainActivity接收EventBus消息显示底部导航的未读消息
public class MainActivity extends BaseActivity{
...
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);//注册
}
/*
*当有消息推送并且在前台运行的时候,会调用此方法
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(FcmMessageEvent event) {
type = event.getDatatype();
count = event.getCount();
int curtab = tabHost.getCurrentTab();//获取当前的tab
tabHost.clearAllTabs();//清空之前的tab,因为已经初始化过一次了
//update indicator
for (int i = 0; i < texts.length; i++) {
TabHost.TabSpec tabSpec = tabHost.newTabSpec(texts[i]);
if (i == 1) { //Creidit Report
if (count > 0) { //count有数据的话,显示未读条数
View v = getIndicatorView(texts[1], count, imageButton[1]);
tabSpec.setIndicator(v);
tabHost.addTab(tabSpec, fragments[i], null);
} else { //count为0,隐藏未读条数
View v = getIndicatorView(texts[i], 0, imageButton[i]);
tabSpec.setIndicator(v);
tabHost.addTab(tabSpec, fragments[i], null);
}
} else {
View v = getIndicatorView(texts[i], 0, imageButton[i]);
tabSpec.setIndicator(v);
tabHost.addTab(tabSpec, fragments[i], null);
}
}
tabHost.setCurrentTab(2);//这句代码要加上,否则第一个tab会出现白屏
tabHost.setCurrentTab(curtab);//设置当前的tab
}
@Override
protected void onResume() {
//app在后台运行,点击Notification进入的页面
int status = SharedPreUtil.getInt(Global.mContext, "status", 0);
int type2 = SharedPreUtil.getInt(Global.mContext, "type", 0);
if (status == 1 || status == 2) { //status为1是登录进来的状态,status为2是注册进来的状态
if (type2 == 1) { //当type=1进去页面
tabHost.setCurrentTab(1);
CreditReportFragment.position = 2;
tabHost.getTabWidget().getChildAt(1).setBackgroundResource(R.color.colorAccent2);
SharedPreUtil.saveInt(Global.mContext,"type",0);
} else if (type2 == 2) {//当type2=2进去另一个页面
tabHost.setCurrentTab(1);
CreditReportFragment.position = 0;
tabHost.getTabWidget().getChildAt(1).setBackgroundResource(R.color.colorAccent2);
SharedPreUtil.saveInt(Global.mContext,"type",0);
}else {
int currentTab = tabHost.getCurrentTab();
tabHost.setCurrentTab(currentTab);
}
} else {//如果status既不是1,也不是2,要求用户直接登录
startActivity(new Intent(MainActivity.this, LoginActivity.class));
finish();
}
super.onResume();
}
@Override
public void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);//反注册
}
@Override
public void initView() {
tabHost = (FragmentTabHost) findViewById(R.id.tab_host);
tabcontent = (FrameLayout) findViewById(R.id.tabcontent);
tabHost.setup(this, getSupportFragmentManager(), R.id.tabcontent);
//Add indicator
for (int i = 0; i < texts.length; i++) {
TabHost.TabSpec tabSpec = tabHost.newTabSpec(texts[i]);
View v = getIndicatorView(texts[i], 0, imageButton[i]);
tabSpec.setIndicator(v);
tabHost.addTab(tabSpec, fragments[i], null);
}
...
}
@Override
public void initData() {
//请求网络,将token的值发送给服务器,服务器收到后会推送消息
}
/*
*获取底部导航icon和text,显示与隐藏未读条数的方法
*/
private View getIndicatorView(String name, int num, int imgId) {
//R.layout.guide_indicator_item:底部导航的icon和text布局
View indicatorView = View.inflate(this, R.layout.guide_indicator_item, null);
ImageView img = (ImageView) indicatorView.findViewById(R.id.img_indicator);
tv_report_num = (TextView) indicatorView.findViewById(R.id.tv_report_num);//默认GONE
TextView tv = (TextView) indicatorView.findViewById(R.id.tv_indicator);
if (num == 0) {
tv_report_num.setVisibility(View.GONE);
} else {
tv_report_num.setVisibility(View.VISIBLE);
if (num > 99) {//自定义
tv_report_num.setText(String.valueOf(num) + "+");
} else {
tv_report_num.setText(String.valueOf(num));
}
}
tv.setText(name);
img.setBackgroundResource(imgId);
return indicatorView;
}
}
7.FcmMessageEvent的类
public class FcmMessageEvent {
private int datatype;
private int count;
public FcmMessageEvent(int datatype, int count) {
this.datatype = datatype;
this.count = count;
}
public int getDatatype() {
return datatype;
}
public void setDatatype(int datatype) {
this.datatype = datatype;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
8.SettingNotificationsFragment推送的开关,用户可以决定要不要推送
public class SettingNotificationsFragment extends BaseFragment {
...
@Override
public int getLayoutRes() {
return R.layout.setting_notifications;
}
@Override
public void initView() {
tvNotifications = (TextView) findView(R.id.tv_notifications);
switchPush = (Switch) findView(R.id.switch_push);
switchApp = (Switch) findView(R.id.switch_app);
switchPush.setChecked(true);//默认选中
switchApp.setChecked(true);
}
@Override
public void initData() {
//Retrofit请求网络
commonPresenter = new CommonPresenter(this);
}
@Override
public void onClick(View v, int id) {
switchPush.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
if (!TextUtils.isEmpty(uuid)) {
String token = SharedPreUtil.getString(Global.mContext, "fcm_token", "");//取token
//请求推送的网络请求
}
} else {
if (!TextUtils.isEmpty(uuid)) {
// 取消推送的网络请求
}
}
}
});
....
}
....
}
9.总结:
GCM前后台的推送功能已经实现啦,欢迎大家围观如果有什么疑问的话,可以留言联系我哦!