一、前言
本次实现的功能是地理围栏,在地图上选一个区域作为危险区域,当使用者进入到此区域中时,触发我们的围栏机制,给手机发送通知尽快离开此区域。
在实现此功能前首先了解要服务与广播,服务与广播同属于安卓的四大组件,大半年没碰安卓开发的我显得有些生疏,查了些资料,这里对自己浅显的理解做一下记录。
1.1 Service(服务)
服务跟Activity一样具有生命周期,当他被第一次创建的时候执行 OnCreate() 方法,该方法只执行一次。
服务的启动方式有两种,第一种是bindService(), 该方法将服务与其他应用程序组件(如活动)绑定在一起,该服务随着调用的组件销毁而停止。通过该方法启动服务必须实现服务的 onBind() 方法。
另一种是 startService(),其他组件(如活动)通过调用来该方法启动服务时, 会调用服务的 onStartCommand() 方法,一旦使用这种方法启动,服务可以在后台无限期运行,即使启动它的组件已经被销毁。
本次功能实现用的是上述第二种启动方法,但是这里我的需求是在软件从后台销毁时服务仍然能运行,对用户进行实时定位保障用户安全,这里考虑的一种方案是,在Activity的OnDestory() 发送广播再次拉起应用程序,从而启动服务。
1.2 Broadcast(广播)
广播分为广播发送者与广播接收者(BroadcastReceiver),自定义广播接收器需要继承基类BroadcastReceivre,并实现抽象方法onReceive(context, intent)方法。广播接收器接收到相应广播后,会自动回到onReceive(…)方法,在onReceive中可以通过接收广播的类型实现相应的方法。
BroadcastReceiver总体上可以分为两种注册类型:静态注册和动态注册。静态注册直接在AndroidManifest.xml文件中进行注册。动态注册直接在代码中通过调用Context的registerReceiver函数。
1.3 Notification (通知)
这里不得不提的是我在使用通知栏的时候遇到的坑,由于Android的版本优化,没有设置channel通知渠道的话,就会导致通知无法展示,NotificationChannel是androd8.0新增的特性,指定 Channel 的 id、name 和通知的重要程度。
以下是我实现的通知栏。
二、代码实现
/**
*自定义服务类
*/
package com.example.campusprevention_app.service;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import androidx.annotation.RequiresApi;
import com.amap.api.fence.GeoFence;
import com.amap.api.fence.GeoFenceClient;
import com.amap.api.fence.GeoFenceListener;
import com.amap.api.location.DPoint;
import com.example.campusprevention_app.R;
import java.util.List;
public class GeoFenceService extends Service {
DPoint centerPoint;
//定义接收广播的action字符串
public static final String GEOFENCE_BROADCAST_ACTION = "com.location.apis.geofencedemo.broadcast";
@Override
public void onCreate() {
super.onCreate ();
//地理围栏的中心点
centerPoint = new DPoint ();
centerPoint.setLatitude (34.813956);
centerPoint.setLongitude (113.539805);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//实例化地理围栏客户端
GeoFenceClient mGeoFenceClient = new GeoFenceClient (getApplicationContext ());
mGeoFenceClient.setActivateAction (GeoFenceClient.GEOFENCE_IN | GeoFenceClient.GEOFENCE_OUT);
mGeoFenceClient.addGeoFence (centerPoint, 1000, "1");
GeoFenceListener geoFenceListener = new GeoFenceListener () {
@Override
public void onGeoFenceCreateFinished(List<GeoFence> list, int i, String s) {
if (i == GeoFence.ADDGEOFENCE_SUCCESS) {
// Toast.makeText (getApplicationContext (), "创建围栏成功", Toast.LENGTH_SHORT).show ();
}
}
};
mGeoFenceClient.setGeoFenceListener (geoFenceListener);
//创建并设置PendingIntent
mGeoFenceClient.createPendingIntent (GEOFENCE_BROADCAST_ACTION);
IntentFilter filter = new IntentFilter ();
filter.addAction (GEOFENCE_BROADCAST_ACTION);
registerReceiver (mGeoFenceReceiver, filter);
return super.onStartCommand (intent, flags, startId);
}
private BroadcastReceiver mGeoFenceReceiver = new BroadcastReceiver () {
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
@Override
public void onReceive(Context context, Intent intent) {
//获取Bundle
Bundle bundle = intent.getExtras ();
int statu = bundle.getInt (GeoFence.BUNDLE_KEY_FENCESTATUS);
if (statu == GeoFenceClient.GEOFENCE_IN) {
// Toast.makeText (getApplicationContext (),"你已进入危险区域",Toast.LENGTH_SHORT).show ();
showNotification (context,intent);
Log.e ( "onReceive: ","进入" );
}
else {
Log.e ( "onReceive: ","离开" );
// Toast.makeText (getApplicationContext (),"你已离开危险区域",Toast.LENGTH_LONG).show ();
}
}
};
/**
*显示通知栏
*/
@RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
private void showNotification(Context context,Intent intent){
NotificationManager manager = (NotificationManager) getSystemService (NOTIFICATION_SERVICE);
PendingIntent pendingIntent = PendingIntent.getActivity (context, 0, intent, 0);
Notification notification;
Notification.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder = new Notification.Builder (context, "5996773");
} else {
builder = new Notification.Builder (context);
}
//设置标题与内容
builder.setContentTitle ("风险通知").setContentText ("您正在进入高风险区域,请尽快离开!");
//设置状态栏显示的图标
builder.setSmallIcon (R.mipmap.fence_notification_danger);
// 设置通知灯光(LIGHTS)、铃声(SOUND)、震动(VIBRATE)、(ALL 表示都设置)
builder.setDefaults (Notification.DEFAULT_ALL);
//灯光三个参数,颜色(argb)、亮时间(毫秒)、暗时间(毫秒)
builder.setLights (Color.RED, 200, 200);
// 震动,传入一个 long 型数组,表示 停、震、停、震 ... (毫秒)
builder.setVibrate (new long[]{
0, 200, 200, 200, 200, 200});
// 通知栏点击后自动消失
builder.setAutoCancel (true);
// 简单通知栏设置 Intent
builder.setContentIntent (pendingIntent);
builder.setPriority (Notification.PRIORITY_HIGH);
//设置下拉之后显示的图片
builder.setLargeIcon (BitmapFactory.decodeResource (getResources (), R.mipmap.fence_notification_danger));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel ("5996772", "安卓10a", NotificationManager.IMPORTANCE_DEFAULT);
channel.enableLights (true);//是否在桌面icon右上角展示小红点
channel.setLightColor (Color.RED);//小红点颜色
channel.setShowBadge (true); //是否在久按桌面图标时显示此渠道的通知
manager.createNotificationChannel (channel);
}
notification = builder.build ();
manager.notify (1, notification);
}
@Override
public void onDestroy() {
super.onDestroy ();
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
//开启服务
Intent intent = new Intent (getApplicationContext (), GeoFenceService.class);
this.startService (intent);
三、总结
`以上就是本次记录的内容,虽说知识点很多,但是在这里我记录的内容是十分浅显的,自己也算是对这些知识点有了更加深入的了解吧。再次声明一下,在这里我记录的博客主要是供自己以后学习回顾,如果知识点有什么错误欢迎大家指正!