在開發(fā)工程師端的過程中辞色,有一個需求是每隔5分鐘就要定位一次工程師的位置,一旦有訂單來了浮定,就根據(jù)用戶的坐標分配最近的工程師相满。但是這個需求最初的方案是,讓APP長駐后臺桦卒,這個又造成耗電嚴重的問題立美。后來發(fā)現(xiàn)蘋果有個解決長連接的方案,就是PushKit! 方灾。
目前該方法已不可用建蹄。
一.定位服務(wù)
目前定位服務(wù)使用的是百度地圖的定位。
1.根據(jù)官方的說明集成百度地圖
2.除了正常的設(shè)置APP的定位權(quán)限外裕偿,現(xiàn)在有個問題洞慎,那就是用戶在選擇訪問位置權(quán)限的時候有3個參數(shù),'永不','使用應(yīng)用期間','始終'嘿棘。如果用戶選中了非始終的其他兩個選項劲腿,那么后臺就無法定位。
解決方案:
-(void) startLocationService{
if ([CLLocationManager locationServicesEnabled] && ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedAlways ||[CLLocationManager authorizationStatus] == kCLAuthorizationStatusNotDetermined)) {
}else if ([CLLocationManager locationServicesEnabled] && ([CLLocationManager authorizationStatus] == kCLAuthorizationStatusAuthorizedWhenInUse)){//僅在使用APP時定位
UIAlertController *TipAlert = [UIAlertController alertControllerWithTitle:@"警告" message:@"當前位置權(quán)限為\"使用應(yīng)用期間\" \n請修改為\"始終\"" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *goOnAction = [UIAlertAction actionWithTitle:@"設(shè)置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSURL * url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
if (iOS10) {
#ifdef NSFoundationVersionNumber_iOS_10
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
#endif
} else {
[[UIApplication sharedApplication] openURL:url];
}
}
}];
[TipAlert addAction:goOnAction];
[self.mainViewController presentViewController:TipAlert animated:YES completion:^{
}];
}else{
//定位不能用
NSLog(@"定位不能用");
UIAlertController *TipAlert = [UIAlertController alertControllerWithTitle:@"警告" message:@"未獲取位置權(quán)限 \n請設(shè)置位置權(quán)限為 \"始終\"" preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *goOnAction = [UIAlertAction actionWithTitle:@"設(shè)置" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
NSURL * url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
if ([[UIApplication sharedApplication] canOpenURL:url]) {
if (iOS10) {
#ifdef NSFoundationVersionNumber_iOS_10
[[UIApplication sharedApplication] openURL:url options:@{} completionHandler:nil];
#endif
} else {
[[UIApplication sharedApplication] openURL:url];
}
}
}];
[TipAlert addAction:goOnAction];
[self.mainViewController presentViewController:TipAlert animated:YES completion:^{
}];
}
}
這個方法是檢查定位權(quán)限的方法鸟妙,除了在應(yīng)該檢查權(quán)限的地方檢查焦人,在用戶從設(shè)置界面回來的時候挥吵,也應(yīng)該再檢查一遍!
- (void)applicationWillEnterForeground:(UIApplication *)application {
// Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
//用戶可能去設(shè)置定位了花椭,現(xiàn)在回來檢查一下
[self startLocationService];
}
這樣就能保證至少權(quán)限是正常使用的忽匈,在info文件設(shè)置權(quán)限可別忘了-->看這里, 因為已經(jīng)設(shè)置到后臺定位了,這個必須要設(shè)置矿辽!
定位的代碼現(xiàn)在這里脉幢,下面說一下PushKit!
二.PushKit
PushKit是iOS8之后,蘋果引入的新push方式嗦锐,有別于普通的APNs,它不會彈出通知沪曙,而是悄悄的告訴我們的app有推送過來奕污,讓app做出相應(yīng)的處理。我目前的測試環(huán)境最低支持到iOS10液走,不太清楚iOS8和iOS9的穩(wěn)定性碳默,有心的朋友測試了可以給我留言,哈哈哈缘眶。嘱根。。
蘋果開發(fā)PushKit原本是給VoIP應(yīng)用使用的巷懈,所以沒有充分理由该抒,非VoIP想要上AppStore,難于上青天顶燕!現(xiàn)在更好了凑保,VoIP的體驗直逼三大運營商的體驗,所以秉承社會主義涌攻,PushKit即便在VoIP的應(yīng)用上使用欧引,同樣不能上線到大陸的AppStore!
1.制作證書(我去盜的圖 <--臭不要臉!)
后面的步驟恳谎,和制作其他證書的步驟都是一樣的芝此!
把證書下載下來后,雙擊安裝到電腦因痛,我后面要貼java代碼婚苹,所以需要導(dǎo)出p12格式的證書,默認導(dǎo)出的就是p12證書鸵膏。如果要測試租副,推薦使用 Pusher for Mac。
2.開始代碼時間较性!
(1)導(dǎo)入push kit框架#import <PushKit/PushKit.h>
(2)別忘了導(dǎo)包用僧,之前忘了導(dǎo)结胀,一直報錯!
(3)- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions ;
調(diào)用下面的方法設(shè)置pushKit
#pragma mark - creatVoIP
- (void)creatVoIP{
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound) completionHandler:^(BOOL granted, NSError * _Nullable error) {
}];
[UIApplication.sharedApplication registerForRemoteNotifications];
dispatch_queue_t mainQueue = dispatch_get_main_queue();
// Create a push registry object
PKPushRegistry * voipRegistry = [[PKPushRegistry alloc] initWithQueue: mainQueue];
// Set the registry's delegate to self
voipRegistry.delegate = self;
// Set the push type to VoIP
voipRegistry.desiredPushTypes = [NSSet setWithObject:PKPushTypeVoIP];
}
(4)協(xié)議方法 PKPushRegistryDelegate
PushKit的推送token與APNs是不一樣的责循!糟港,下面的協(xié)議方法可以獲取到!
#pragma mark - PKPushRegistryDelegate
- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)pushCredentials forType:(PKPushType)type{
NSString *str = [NSString stringWithFormat:@"%@",pushCredentials.token];
NSString *tokenStr = [[[str stringByReplacingOccurrencesOfString:@"<" withString:@""]
stringByReplacingOccurrencesOfString:@">" withString:@""] stringByReplacingOccurrencesOfString:@" " withString:@""];
NSLog(@"tokenStr獲取到的就是PushKit的token");
NSLog(@"實際生產(chǎn)中院仿,把這個token傳給后端的同學秸抚,我們測試可以拷貝出來,放到pusher里面測試");
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type withCompletionHandler:(void(^)(void))completion{
//收到pushKit通知
[_locationManager requestLocationWithReGeocode:YES withNetworkState:YES completionBlock:^(BMKLocation * _Nullable location, BMKLocationNetworkState state, NSError * _Nullable error) {
if (location) {//得到定位信息歹垫,下面的代碼純粹是為了測試的效果剥汤,實際應(yīng)用中,把位置信息傳給后臺就可以了
if (location.location) {
NSLog(@"LOC = %@",location.location);
}
if (location.rgcData) {
NSLog(@"rgc = %@",[location.rgcData description]);
}
NSString *Str = [NSString stringWithFormat:@"L:%@ R:%@",location.location,[location.rgcData description]];
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
center.delegate = self;
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
content.title = [NSString localizedUserNotificationStringForKey:@"定位信息" arguments:nil];
content.body = [NSString localizedUserNotificationStringForKey:Str arguments:nil];
content.sound = [UNNotificationSound defaultSound];
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"OXNotification" content:content trigger:nil];
[center addNotificationRequest:request withCompletionHandler:^(NSError *_Nullable error) {
NSLog(@"成功添加推送");
}];
}
}];
}
- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type{
NSLog(@"");
}
(5)特別說明排惨!
我在第二個協(xié)議方法里面發(fā)送了一個自定義通知吭敢,為了在殺死APP的情況下進行測試,下面的代碼是為了方便大家的測試寫的暮芭,和本文設(shè)計到的長連接沒有關(guān)系鹿驼!
1.導(dǎo)入頭文件
#ifdef NSFoundationVersionNumber_iOS_9_x_Max
#import <UserNotifications/UserNotifications.h>
#import <UserNotifications/UserNotifications.h>
#import <UserNotificationsUI/UserNotificationsUI.h>
#endif
2.協(xié)議方法
UNUserNotificationCenterDelegate
#pragma mark - ios10才支持的通知!
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{
NSLog(@"");
completionHandler(UNNotificationPresentationOptionAlert);
}
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler{
NSLog(@"");
}
//ios 12才支持
- (void)userNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(nullable UNNotification *)notification{
}
java代碼:
import javapns.back.PushNotificationManager;
import javapns.back.SSLConnectionHelper;
import javapns.data.Device;
import javapns.data.PayLoad;
public class Main {
public static void main(String[] args) throws Exception{
try {
// token為不包含空格和<>的字母數(shù)字組合(字母不區(qū)分大小寫)
String deviceToken = "XXXXXXXXXX";
PushNotificationManager pushManager = PushNotificationManager.getInstance();
pushManager.addDevice("iphone", deviceToken);
// 蘋果推送服務(wù)器
/*
開發(fā)狀態(tài)服務(wù)器地址 gateway.sandbox.push.apple.com 2195
發(fā)布狀態(tài)服務(wù)器地址 gateway.push.apple.com 2195
需要注意:
Xcode打出的Debug安裝包只能選擇開發(fā)服務(wù)器,證書可以選擇開發(fā)推送證書或者發(fā)布推送證書辕宏;
Xcode打出的AdHoc或Release安裝包只能選擇發(fā)布務(wù)器和發(fā)布推送證書畜晰;
*/
String host= "gateway.push.apple.com";
// 端口號
int port = 2195;
// 在mac系統(tǒng)下導(dǎo)出的p12證書(開發(fā)證書對應(yīng)開發(fā)環(huán)境,發(fā)布證書對應(yīng)所有環(huán)境)
String certificatePath = "J:\\QQdir\\2\\JKX_e.p12";
// p12證書密碼
String certificatePassword= "12345678";
// 初始化tcp連接
pushManager.initializeConnection(host, port, certificatePath, certificatePassword, SSLConnectionHelper.KEYSTORE_TYPE_PKCS12);
// Send Push
Device client = pushManager.getDevice("iphone");
// 要推送的消息
PayLoad payLoad = new PayLoad();
payLoad.addAlert("收到消息CALL我");
payLoad.addBadge(1);
payLoad.addSound("default");
// 開始推送
pushManager.sendNotification(client, payLoad);
pushManager.stopConnection();
pushManager.removeDevice("iphone");
System.out.println ("success");
}
catch (Exception e) {
e.printStackTrace();
}
}
}
需要用到的jar包
bcprov-jdk16-145-1.jar
commons-io-2.0.1.jar
commons-lang-2.5.jar
javapns-jdk16-163.jar
log4j-1.2.16.jar
這個代碼和后端的同學測試通過的瑞筐,java沒寫清楚的地方凄鼻,可以看這個!