产品为了提升推送送达率,提了一个需求:在 APP 推送关闭的状况下显示一个小 TIP,点击 TIP 跳转到 APP 消息设置界面。javascript
咱们的 APP 是基于 React Native 开发的,这些功能 Facebook 官方没有提供,须要咱们开发对应的原生模块。java
由于开发原生模块属于比较深刻的内容了,写这篇文章时我就默认阅读者已经具备必定的 Objective-C
和 Java
开发能力,下面就直接贴代码说思路了。react
开发一个原生模块的基础知识能够直接看官方文档,写的很详细,我这里就很少重复了。android
React Native 开发 Android 原生模块ios
下面开始分析实现。github
这里我主要参考极光推送。由于公司内部有统一的推送 SDK
(主要整合了市面上多家推送服务公司和手机厂商的推送服务),一些极光推送很方便的功能暂时用不了,只能本身参考实现。objective-c
Promise
的形式进行封装(极光推送是基于 callback
的)getSystemNoticeStatus()
这个函数,在 APP 推送开启的状况下返回 true
,未开启状况返回 false
。react-native
iOS 代码以下:promise
RCT_EXPORT_METHOD( getSystemNoticeStatus: (RCTPromiseResolveBlock) resolve
rejecter: (RCTPromiseRejectBlock) reject )
{
dispatch_async( dispatch_get_main_queue(), ^{
float systemVersion = [[UIDevice currentDevice].systemVersion floatValue];
if ( systemVersion >= 8.0 )
{
UIUserNotificationSettings *settings = [[UIApplication sharedApplication] currentUserNotificationSettings];
UIUserNotificationType type = settings.types;
if ( type == UIUserNotificationTypeNone )
{
return(resolve (@NO) );
}else {
return(resolve (@YES) );
}
}else if ( systemVersion >= 10.0 )
{
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler: ^ (UNNotificationSettings * _Nonnull settings) {
switch ( settings.authorizationStatus )
{
case UNAuthorizationStatusDenied:
case UNAuthorizationStatusNotDetermined:
return(resolve (@NO) );
break;
case UNAuthorizationStatusAuthorized:
return(resolve (@YES) );
break;
}
}];
}
} );
}
复制代码
Android 代码以下:
参考连接:
/** * 获取 APP 系统通知状态 * * */
@ReactMethod
public void getSystemNoticeStatus(Promise promise) {
promise.resolve(hasPermission("OP_POST_NOTIFICATION"));
}
private boolean hasPermission(String appOpsServiceId) {
Context context = getReactApplicationContext();
if (Build.VERSION.SDK_INT >= 24) {
NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(
Context.NOTIFICATION_SERVICE);
return mNotificationManager.areNotificationsEnabled();
}else if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
ApplicationInfo appInfo = context.getApplicationInfo();
String pkg = context.getPackageName();
int uid = appInfo.uid;
Class appOpsClazz;
try {
appOpsClazz = Class.forName(AppOpsManager.class.getName());
Method checkOpNoThrowMethod = appOpsClazz.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE,
String.class);
Field opValue = appOpsClazz.getDeclaredField(appOpsServiceId);
int value = opValue.getInt(Integer.class);
Object result = checkOpNoThrowMethod.invoke(mAppOps, value, uid, pkg);
return Integer.parseInt(result.toString()) == AppOpsManager.MODE_ALLOWED;
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
return false;
}
复制代码
而后咱们在 JavaScript侧直接引用就可
import {
Platform,
NativeModules,
} from 'react-native';
function getSystemNoticeStatus() {
NativeModules.appName.getSystemNoticeStatus().then((isOpen) => {
console.log('getSystemNotice', isOpen) //
}).catch((e) => {
console.log('getSystemNoticeStatus error', e)
});
}
复制代码
跳转到 APP 设置界面也要考虑不一样系统版本的兼容。好比说 iOS11+ 如今只容许跳转到系统设置首页/该应用的设置界面,Android 还要考虑不一样厂商对 APP 设置页面的魔改,非常头疼。
首先 iOS 适配,咱们直接跳转到该应用的设置首页,就是下图:
这个开发比较简单,直接在 React Native 中引用 Linking.openURL('app-settings:')
就行;
Android 就要多些一些代码了,具体的适配能够看注释:
/** * * 跳转到系统通知设置界面 * this.appContext 表示文件/应用的上下文环境 * */
@ReactMethod
public void openSystemNoticeView(){
try {
// 跳转到通知设置界面
Intent intent = new Intent();
intent.setAction(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
//这种方案适用于 API 26, 即8.0(含8.0)以上能够用
intent.putExtra(EXTRA_APP_PACKAGE, this.appContext.getPackageName());
intent.putExtra(EXTRA_CHANNEL_ID, this.appContext.getApplicationInfo().uid);
//这种方案适用于 API21——25,即 5.0——7.1 之间的版本可使用
intent.putExtra("app_package", this.appContext.getPackageName());
intent.putExtra("app_uid", this.appContext.getApplicationInfo().uid);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.appContext.startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
// 出现异常则跳转到应用设置界面:锤子
Intent intent = new Intent();
//下面这种方案是直接跳转到当前应用的设置界面。
//https://blog.csdn.net/ysy950803/article/details/71910806
intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
Uri uri = Uri.fromParts("package", this.appContext.getPackageName(), null);
intent.setData(uri);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.appContext.startActivity(intent);
}
}
复制代码
而后咱们在 JavaScript 侧作一些兼容处理:
import {
Linking,
Platform,
} from 'react-native';
/** * 跳转到 APP 消息设置页面 * */
export function openSystemNoticeSetting() {
if (Platform.OS === "android") {
NativeModules.appName.openSystemNoticeView();
} else {
Linking.openURL('app-settings:')
.catch(err => console.log('openSystemSetting error', err));
}
}
复制代码
上面就是开发中遇到的两个难点,若是此篇文章你认为对你有用,能够点个赞表示对个人鼓励,谢谢。