使用 Android AltBeacon 库获取连续的 iBeacons 信息
Get continuous iBeacons info with Android AltBeacon Library
我正在使用 RadiusNetworks 的 AltBeacon 库开发一个 Android 应用程序。我正在使用导航抽屉(启动活动,而不是片段)来显示不同的部分。因此,我有一个主要的 activity 用于抽屉,它通过意图启动不同的活动。
我想在应用程序的任何地方都提供附近的 iBeacons 信息(ID 和距离)。如何解决这个问题?
我尝试了以下方法(如果我们将我的应用程序结构与 RadiusNetworks 提供的参考结构进行映射):
- BeaconReferenceApp:与参考应用相同。启动 MainActivity。
- MainActivity:我合并了 Monitoring 和 Ranging 活动。此 activity 还管理导航抽屉。
- 第 1 部分:这部分扩展了 MainActivity 并根据 iBeacons 收集的数据显示一些信息。
在 MainActivity 中,我执行以下操作:
public void onBeaconServiceConnect() {
beaconManager.setRangeNotifier(new RangeNotifier() {
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
for (Beacon beacon : beacons) {
Log.i("TAG","Beacon detected with id1: "+beacon.getId1()+" id2:"+beacon.getId2()+" id3: "+beacon.getId3()+" distance: "+beacon.getDistance());
}
}
});
try {
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
} catch (RemoteException e) { }
}
我可以在日志中看到 iBeacon 信息,但只有一次(第一次检测)。但是,我想在每次看到 iBeacon 时持续获取此信息(就像在参考应用程序中发生的那样),以便我可以将其传递给我的其他活动。
我该如何解决这个问题?我应该使用不同的应用程序结构(因为导航抽屉)还是我以错误的方式使用库?
谢谢。
更新:MainActivity 和 BeaconReferenceApp 的代码。
BeaconReferenceApp:
public class BeaconReferenceApp extends Application implements BootstrapNotifier {
private static final String TAG = "BeaconApp";
private RegionBootstrap regionBootstrap;
private BackgroundPowerSaver backgroundPowerSaver;
private boolean haveDetectedBeaconsSinceBoot = false;
private MainActivity mainActivity = null;
public void onCreate() {
super.onCreate();
BeaconManager beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
beaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));
Log.d(TAG, "setting up background monitoring for beacons and power saving");
// wake up the app when a beacon is seen
Region region = new Region("backgroundRegion", null, null, null);
regionBootstrap = new RegionBootstrap(this, region);
backgroundPowerSaver = new BackgroundPowerSaver(this);
}
public void didEnterRegion(Region arg0) {
Log.d(TAG, "did enter region.");
// regionBootstrap.disable();
if (!haveDetectedBeaconsSinceBoot) {
Log.d(TAG, "auto launching MainActivity");
// The very first time since boot that we detect an beacon, we launch the MainActivity
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
haveDetectedBeaconsSinceBoot = true;
} else {
sendNotification();
}
}
public void didExitRegion(Region region) {}
public void didDetermineStateForRegion(int state, Region region) {}
public void setMainActivity(MainActivity activity) {this.mainActivity = activity;}
主要活动:
public class MainActivity extends ActionBarActivity implements BeaconConsumer {
protected FrameLayout frameLayout;
protected ListView mDrawerList;
protected String[] listArray = { "Item1", "Item2", "Item3", "Item4" };
protected static int position;
private static boolean isLaunch = true;
private DrawerLayout mDrawerLayout;
private ActionBarDrawerToggle actionBarDrawerToggle;
private BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
verifyBluetooth();
beaconManager.bind(this);
setContentView(R.layout.navigation_drawer_base_layout);
frameLayout = (FrameLayout)findViewById(R.id.content_frame);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
// set up the drawer's list view with items and click listener
mDrawerList.setAdapter(new ArrayAdapter<String>(this, R.layout.drawer_list_item, listArray));
mDrawerList.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
openActivity(position);
}
});
// enable ActionBar app icon to behave as action to toggle nav drawer
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
// ActionBarDrawerToggle ties together the proper interactions between the sliding drawer and the action bar app icon
actionBarDrawerToggle = new ActionBarDrawerToggle(
this, /* host Activity */
mDrawerLayout, /* DrawerLayout object */
R.string.open_drawer, /* "open drawer" description for accessibility */
R.string.close_drawer) /* "close drawer" description for accessibility */
{
public void onDrawerClosed(View drawerView) {
getSupportActionBar().setTitle(listArray[position]);
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
super.onDrawerClosed(drawerView);
}
public void onDrawerOpened(View drawerView) {
getSupportActionBar().setTitle(getString(R.string.app_name));
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
super.onDrawerOpened(drawerView);
}
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, slideOffset);
}
public void onDrawerStateChanged(int newState) {
super.onDrawerStateChanged(newState);
}
};
actionBarDrawerToggle.syncState();
mDrawerLayout.setDrawerListener(actionBarDrawerToggle);
/**
* MainActivity is intended just to add navigation drawer in the app.
* Open some activity with layout on launch. Check checking if this
* MainActivity is called first time when we are
* opening our first activity.
* */
if(isLaunch){
/**
*Setting this flag false so that next time it will not open
* first activity.
* Use this flag because we are using MainActivity
* as parent activity to our other activities.
* In this case this base activity will always be call when any
* child activity will launch.
*/
isLaunch = false;
openActivity(0);
}
}
/**
* Launching activity when any list item is clicked.
*/
protected void openActivity(int position) {
mDrawerLayout.closeDrawer(mDrawerList);
MainActivity.position = position; //Setting currently selected position in this field so that it will be available in child activities.
switch (position) {
case 0:
startActivity(new Intent(this, Item1.class));
break;
case 1:
startActivity(new Intent(this, Item2.class));
break;
case 2:
startActivity(new Intent(this, Item3.class));
break;
case 3:
startActivity(new Intent(this, Item4.class));
break;
default:
break;
}
}
public void onDestroy() {
super.onDestroy();
beaconManager.unbind(this);
}
public void onResume() {
super.onResume();
((BeaconReferenceApp) this.getApplicationContext()).setMainActivity(this);
if (beaconManager.isBound(this)) beaconManager.setBackgroundMode(false);
}
public void onPause() {
super.onPause();
((BeaconReferenceApp) this.getApplicationContext()).setMainActivity(null);
if (beaconManager.isBound(this)) beaconManager.setBackgroundMode(true);
}
private void verifyBluetooth() {}
public void onBeaconServiceConnect() {
beaconManager.setRangeNotifier(new RangeNotifier() {
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
for (Beacon beacon : beacons) {
Log.i("TAG", "Beacon detected with id1: " + beacon.getId1() + " id2:" + beacon.getId2() + " id3: " + beacon.getId3() + " distance: " + beacon.getDistance());
}
}
});
try {
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
} catch (RemoteException e) { }
}
}
最简单的方法是在自定义 android.app.Application
class 中进行测距。每个 Android 应用程序都有其中之一,这是放置您希望对整个应用程序通用的代码的好地方。要定义你自己的 class,你可以在你的清单中通过添加一个属性来声明它:
<application
...
android:name="MyApplication">
然后你可以定义一个具有相同名称的 class 来进行这样的测距:
public class MyApplication extends Application implements BeaconConsumer {
private BeaconManager mBeaconManager;
public void onCreate() {
super.onCreate();
mbeaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
mBeaconManager.bind(this);
}
public void onBeaconServiceConnect() {
mBeaconManager.setRangeNotifier(new RangeNotifier() {
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
for (Beacon beacon : beacons) {
Log.i("TAG","Beacon detected with id1: "+beacon.getId1()+" id2:"+beacon.getId2()+" id3: "+beacon.getId3()+" distance: "+beacon.getDistance());
}
}
});
try {
mBeaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
} catch (RemoteException e) { }
}
如果您可以执行 Application
class 中的所有逻辑,这就是您需要做的全部。但是,您可能需要将测距数据转发给各个活动或片段。如果是这样,最简单的方法是从片段或 activity 中获取对中央 Application
class 的引用,如下所示:
MyApplication myApplication = ((MyApplication)this.getApplication());
然后您可以从 Application
class 访问集中存储的数据,或者告诉 Application
class 将 didRangeBeaconsInRegion
回调转发给您的activity 或片段。
我已经解决了修改 Beaconing 的问题 class(我只显示更改):
public class Beaconing extends Application implements BootstrapNotifier, BeaconConsumer {
....
private BeaconManager beaconManager = null;
....
public void onCreate() {
super.onCreate();
beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
....
}
....
public void onBeaconServiceConnect() {
beaconManager.setBackgroundMode(true);
}
所以我实现了 BeaconConsumer,将 beaconManager 设置为 null 并在 onCreate() 中获取它,然后我添加了 onBeaconServiceConnect()。
这种方式对我有用,我实际上得到了连续测距。
然而我不知道为什么它以前不起作用。如果有人知道并解释它,我会很高兴。
感谢@davidgyoung (https://whosebug.com/users/1461050/davidgyoung) 的帮助! :)
我正在使用 RadiusNetworks 的 AltBeacon 库开发一个 Android 应用程序。我正在使用导航抽屉(启动活动,而不是片段)来显示不同的部分。因此,我有一个主要的 activity 用于抽屉,它通过意图启动不同的活动。
我想在应用程序的任何地方都提供附近的 iBeacons 信息(ID 和距离)。如何解决这个问题?
我尝试了以下方法(如果我们将我的应用程序结构与 RadiusNetworks 提供的参考结构进行映射):
- BeaconReferenceApp:与参考应用相同。启动 MainActivity。
- MainActivity:我合并了 Monitoring 和 Ranging 活动。此 activity 还管理导航抽屉。
- 第 1 部分:这部分扩展了 MainActivity 并根据 iBeacons 收集的数据显示一些信息。
在 MainActivity 中,我执行以下操作:
public void onBeaconServiceConnect() {
beaconManager.setRangeNotifier(new RangeNotifier() {
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
for (Beacon beacon : beacons) {
Log.i("TAG","Beacon detected with id1: "+beacon.getId1()+" id2:"+beacon.getId2()+" id3: "+beacon.getId3()+" distance: "+beacon.getDistance());
}
}
});
try {
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
} catch (RemoteException e) { }
}
我可以在日志中看到 iBeacon 信息,但只有一次(第一次检测)。但是,我想在每次看到 iBeacon 时持续获取此信息(就像在参考应用程序中发生的那样),以便我可以将其传递给我的其他活动。
我该如何解决这个问题?我应该使用不同的应用程序结构(因为导航抽屉)还是我以错误的方式使用库?
谢谢。
更新:MainActivity 和 BeaconReferenceApp 的代码。
BeaconReferenceApp:
public class BeaconReferenceApp extends Application implements BootstrapNotifier {
private static final String TAG = "BeaconApp";
private RegionBootstrap regionBootstrap;
private BackgroundPowerSaver backgroundPowerSaver;
private boolean haveDetectedBeaconsSinceBoot = false;
private MainActivity mainActivity = null;
public void onCreate() {
super.onCreate();
BeaconManager beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
beaconManager.getBeaconParsers().add(new BeaconParser().
setBeaconLayout("m:2-3=0215,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));
Log.d(TAG, "setting up background monitoring for beacons and power saving");
// wake up the app when a beacon is seen
Region region = new Region("backgroundRegion", null, null, null);
regionBootstrap = new RegionBootstrap(this, region);
backgroundPowerSaver = new BackgroundPowerSaver(this);
}
public void didEnterRegion(Region arg0) {
Log.d(TAG, "did enter region.");
// regionBootstrap.disable();
if (!haveDetectedBeaconsSinceBoot) {
Log.d(TAG, "auto launching MainActivity");
// The very first time since boot that we detect an beacon, we launch the MainActivity
Intent intent = new Intent(this, MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.startActivity(intent);
haveDetectedBeaconsSinceBoot = true;
} else {
sendNotification();
}
}
public void didExitRegion(Region region) {}
public void didDetermineStateForRegion(int state, Region region) {}
public void setMainActivity(MainActivity activity) {this.mainActivity = activity;}
主要活动:
public class MainActivity extends ActionBarActivity implements BeaconConsumer {
protected FrameLayout frameLayout;
protected ListView mDrawerList;
protected String[] listArray = { "Item1", "Item2", "Item3", "Item4" };
protected static int position;
private static boolean isLaunch = true;
private DrawerLayout mDrawerLayout;
private ActionBarDrawerToggle actionBarDrawerToggle;
private BeaconManager beaconManager = BeaconManager.getInstanceForApplication(this);
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
verifyBluetooth();
beaconManager.bind(this);
setContentView(R.layout.navigation_drawer_base_layout);
frameLayout = (FrameLayout)findViewById(R.id.content_frame);
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
mDrawerList = (ListView) findViewById(R.id.left_drawer);
// set up the drawer's list view with items and click listener
mDrawerList.setAdapter(new ArrayAdapter<String>(this, R.layout.drawer_list_item, listArray));
mDrawerList.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
openActivity(position);
}
});
// enable ActionBar app icon to behave as action to toggle nav drawer
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setHomeButtonEnabled(true);
// ActionBarDrawerToggle ties together the proper interactions between the sliding drawer and the action bar app icon
actionBarDrawerToggle = new ActionBarDrawerToggle(
this, /* host Activity */
mDrawerLayout, /* DrawerLayout object */
R.string.open_drawer, /* "open drawer" description for accessibility */
R.string.close_drawer) /* "close drawer" description for accessibility */
{
public void onDrawerClosed(View drawerView) {
getSupportActionBar().setTitle(listArray[position]);
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
super.onDrawerClosed(drawerView);
}
public void onDrawerOpened(View drawerView) {
getSupportActionBar().setTitle(getString(R.string.app_name));
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
super.onDrawerOpened(drawerView);
}
public void onDrawerSlide(View drawerView, float slideOffset) {
super.onDrawerSlide(drawerView, slideOffset);
}
public void onDrawerStateChanged(int newState) {
super.onDrawerStateChanged(newState);
}
};
actionBarDrawerToggle.syncState();
mDrawerLayout.setDrawerListener(actionBarDrawerToggle);
/**
* MainActivity is intended just to add navigation drawer in the app.
* Open some activity with layout on launch. Check checking if this
* MainActivity is called first time when we are
* opening our first activity.
* */
if(isLaunch){
/**
*Setting this flag false so that next time it will not open
* first activity.
* Use this flag because we are using MainActivity
* as parent activity to our other activities.
* In this case this base activity will always be call when any
* child activity will launch.
*/
isLaunch = false;
openActivity(0);
}
}
/**
* Launching activity when any list item is clicked.
*/
protected void openActivity(int position) {
mDrawerLayout.closeDrawer(mDrawerList);
MainActivity.position = position; //Setting currently selected position in this field so that it will be available in child activities.
switch (position) {
case 0:
startActivity(new Intent(this, Item1.class));
break;
case 1:
startActivity(new Intent(this, Item2.class));
break;
case 2:
startActivity(new Intent(this, Item3.class));
break;
case 3:
startActivity(new Intent(this, Item4.class));
break;
default:
break;
}
}
public void onDestroy() {
super.onDestroy();
beaconManager.unbind(this);
}
public void onResume() {
super.onResume();
((BeaconReferenceApp) this.getApplicationContext()).setMainActivity(this);
if (beaconManager.isBound(this)) beaconManager.setBackgroundMode(false);
}
public void onPause() {
super.onPause();
((BeaconReferenceApp) this.getApplicationContext()).setMainActivity(null);
if (beaconManager.isBound(this)) beaconManager.setBackgroundMode(true);
}
private void verifyBluetooth() {}
public void onBeaconServiceConnect() {
beaconManager.setRangeNotifier(new RangeNotifier() {
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
for (Beacon beacon : beacons) {
Log.i("TAG", "Beacon detected with id1: " + beacon.getId1() + " id2:" + beacon.getId2() + " id3: " + beacon.getId3() + " distance: " + beacon.getDistance());
}
}
});
try {
beaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
} catch (RemoteException e) { }
}
}
最简单的方法是在自定义 android.app.Application
class 中进行测距。每个 Android 应用程序都有其中之一,这是放置您希望对整个应用程序通用的代码的好地方。要定义你自己的 class,你可以在你的清单中通过添加一个属性来声明它:
<application
...
android:name="MyApplication">
然后你可以定义一个具有相同名称的 class 来进行这样的测距:
public class MyApplication extends Application implements BeaconConsumer {
private BeaconManager mBeaconManager;
public void onCreate() {
super.onCreate();
mbeaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
mBeaconManager.bind(this);
}
public void onBeaconServiceConnect() {
mBeaconManager.setRangeNotifier(new RangeNotifier() {
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
for (Beacon beacon : beacons) {
Log.i("TAG","Beacon detected with id1: "+beacon.getId1()+" id2:"+beacon.getId2()+" id3: "+beacon.getId3()+" distance: "+beacon.getDistance());
}
}
});
try {
mBeaconManager.startRangingBeaconsInRegion(new Region("myRangingUniqueId", null, null, null));
} catch (RemoteException e) { }
}
如果您可以执行 Application
class 中的所有逻辑,这就是您需要做的全部。但是,您可能需要将测距数据转发给各个活动或片段。如果是这样,最简单的方法是从片段或 activity 中获取对中央 Application
class 的引用,如下所示:
MyApplication myApplication = ((MyApplication)this.getApplication());
然后您可以从 Application
class 访问集中存储的数据,或者告诉 Application
class 将 didRangeBeaconsInRegion
回调转发给您的activity 或片段。
我已经解决了修改 Beaconing 的问题 class(我只显示更改):
public class Beaconing extends Application implements BootstrapNotifier, BeaconConsumer {
....
private BeaconManager beaconManager = null;
....
public void onCreate() {
super.onCreate();
beaconManager = org.altbeacon.beacon.BeaconManager.getInstanceForApplication(this);
....
}
....
public void onBeaconServiceConnect() {
beaconManager.setBackgroundMode(true);
}
所以我实现了 BeaconConsumer,将 beaconManager 设置为 null 并在 onCreate() 中获取它,然后我添加了 onBeaconServiceConnect()。
这种方式对我有用,我实际上得到了连续测距。
然而我不知道为什么它以前不起作用。如果有人知道并解释它,我会很高兴。
感谢@davidgyoung (https://whosebug.com/users/1461050/davidgyoung) 的帮助! :)