我们正在编写一个客户端服务器应用程序,该应用程序与服务器通信以通知其BLE位置,并从服务器获取更新以执行任何请求的操作。此应用程序使用ALT信标库检测BLE接近度,并在UI上向设备用户显示接近度范围。该应用程序具有服务和活动,并且当BLE运行时,这两项都会使多个异步任务非常频繁地(约每30秒)与服务器通信。我们已经意识到,当启用BLE接近度(开始测距和监视)时,异步任务线程执行得很晚,这导致与服务器通信的延迟。我想了解我使用ALT信标库的方式是否会引起关注,它将创建更多线程或泄漏?
伪代码如下:
public class BeaconScanner extends AppCompatActivity implements View.OnClickListener, ServerCallBack, BeaconConsumer {
protected static final String TAG = "BeaconScanner";
private static BeaconManager beaconManager;
private static Handler timerHandler = new Handler();
private Runnable timerRunnable = new Runnable() {
@Override
public void run() {
refreshInfo();
timerHandler.removeCallbacks(timerRunnable);
timerHandler.postDelayed(this, 30 * 1000);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_ranging);
mContext = this;
//....
enableBluetooth();
myctx = getApplicationContext();
if (!createNewRegion()){
finish();
}
beaconManager = BeaconManager.getInstanceForApplication(myctx);
//BeaconManager.setRssiFilterImplClass(ArmaRssiFilter.class);
beaconManager.getBeaconParsers().add(new BeaconParser()
.setBeaconLayout("m:2-3=beac,i:4-19,i:20-21,i:22-23,p:24-24,d:25-25"));
beaconManager.bind(this);
//...
//timerHandler.postDelayed(timerRunnable, 0);
logToDisplay();
}
private boolean createNewRegion() {
if ((strBeacon == null) || strBeacon.isEmpty()) {
return false;
}
try {
Identifier id1 = Identifier.parse(strBeacon.substring(0, 8) + "-" +
strBeacon.substring(8, 12) + "-" +
strBeacon.substring(12, 16) + "-" +
strBeacon.substring(16, 20) + "-" +
strBeacon.substring(20, 32));
Identifier id2 = Identifier.parse(strBeacon.substring(32, 36));
Identifier id3 = Identifier.parse(strBeacon.substring(36, 40));
regionZebra = new Region(getRandomString(10), id1, id2, id3);
return true;
} catch (Exception e) {
Log.e(TAG, e.getMessage());
return false;
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_BACK:
showExitDialog(getString(R.string.exit_msg));
return true;
}
return super.onKeyDown(keyCode, event);
}
private static final String ALLOWED_CHARACTERS = "0123456789qwertyuiopasdfghjklzxcvbnm";
private static String getRandomString(final int sizeOfRandomString) {
final Random random = new Random();
final StringBuilder sb = new StringBuilder(sizeOfRandomString);
for (int i = 0; i < sizeOfRandomString; ++i)
sb.append(ALLOWED_CHARACTERS.charAt(random.nextInt(ALLOWED_CHARACTERS.length())));
return sb.toString();
}
@Override
protected void onDestroy() {
super.onDestroy();
restoreFindingDeviceState(0);
if(null != beaconManager) {
try {
beaconManager.stopRangingBeaconsInRegion(regionZebra);
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
beaconManager.unbind(this);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case android.R.id.home:
showExitDialog(getString(R.string.exit_msg));
break;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onPause() {
super.onPause();
timerHandler.removeCallbacks(timerRunnable);
if (beaconManager.isBound(this))
beaconManager.setBackgroundMode(true);
//restoreFindingDeviceState(0);
}
@Override
protected void onResume() {
super.onResume();
timerHandler.postDelayed(timerRunnable, 0);
restoreFindingDeviceState(1);
if (beaconManager.isBound(this))
beaconManager.setBackgroundMode(false);
}
@Override
public void onClick(View v) {
v.startAnimation(buttonClick);
String appendData = "";
String uri = "";
ServerConnect serverConnect = null;
switch (v.getId()) {
case R.id.playsound:
logMessage(TAG, "### Play sound button clicked" + uri);
//Creates async task to communicate with server
break;
case R.id.found:
//Show error to user
//Creates async task to communicate with server
break;
case R.id.notfound:
//Show error to user
//Creates async task to communicate with server
break;
}
}
@Override
public void onServerResponse(String response, int requestCode, String data) {
//Process server response
}
public void refreshInfo() {
if ((System.currentTimeMillis() - findBeaconTime) > 40 * 1000) {
//displayState = Constants.BEACON_FINDING;
logToDisplay();
}
//Creates async task to communicate with server to pull the latest info
}
private String changeFormatofBeaconID(String id) {
String retString = "";
if (id != null) {
for (int i = 0; i < 4 - id.length(); i++) {
retString = "0" + retString;
}
}
return retString + id;
}
@Override
public void onBeaconServiceConnect() {
beaconManager.removeAllRangeNotifiers();
beaconManager.addRangeNotifier(new RangeNotifier() {
@Override
public void didRangeBeaconsInRegion(Collection<Beacon> beacons, Region region) {
if (beacons.size() > 0) {
for (Iterator<Beacon> iterator = beacons.iterator(); iterator.hasNext(); ) {
String id2 = "";
String id3 = "";
Beacon findBeacon = iterator.next();
if ((strBeacon == null) || (findBeacon == null)) {
continue;
}
try {
//....
if (true == uuidReceived.startsWith(uuidToSearch)) {
displayBeacon = findBeacon;
displayState = Constants.BEACON_FOUND;
logToDisplay();
}
} catch (Exception e) {
Log.e(TAG, "### Exception when compare Beacon, UUID is null");
}
}
}
}
});
beaconManager.removeAllMonitorNotifiers();
beaconManager.addMonitorNotifier(new MonitorNotifier() {
// If the phone enters a Beacon region
@Override
public void didEnterRegion(org.altbeacon.beacon.Region region) {
try {
findBeaconTime = System.currentTimeMillis();
} catch (Exception e) {
e.printStackTrace();
}
}
// If the phone leaves a Beacon region
@Override
public void didExitRegion(org.altbeacon.beacon.Region region) {
try {
displayState = Constants.BEACON_FINDING;
if ((System.currentTimeMillis() - findBeaconTime) > 5 * 1000) {
logToDisplay();
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void didDetermineStateForRegion(int i, org.altbeacon.beacon.Region region) {
}
});
try {
beaconManager.startMonitoringBeaconsInRegion(regionZebra);
} catch (RemoteException e) {
Log.e(TAG, e.getMessage());
}
try {
beaconManager.startRangingBeaconsInRegion(regionZebra);
} catch (RemoteException e) {
Log.e(TAG, e.getMessage());
}
}
private void showRanging(int mode) {
webViewRanging = (WebView) findViewById(R.id.webViewRanging);
webViewRanging.setBackgroundColor(Color.TRANSPARENT); //for gif without background
String gifName;
if (mode == IMMEDIATE_RANGE)
gifName = "immediate.gif";
else if (mode == NEAR_RANGE)
gifName = "near.gif";
else if (mode == FAR_RANGE)
gifName = "far.gif";
else
gifName = "none.gif";
//Update the UI
}
private void logToDisplay() {
runOnUiThread(new Runnable() {
public void run() {
TextView txtShowDistance = (TextView) findViewById(R.id.txtRanging);
switch (displayState) {
case Constants.BEACON_FOUND:
txtShowDistance.setText("");
if (displayBeacon.getDistance() <= 3) {
showRanging(IMMEDIATE_RANGE);
} else if (3 < displayBeacon.getDistance() && displayBeacon.getDistance() <= 8) {
showRanging(NEAR_RANGE);
} else if (displayBeacon.getDistance() > 8) {
showRanging(FAR_RANGE);
}
break;
case Constants.BEACON_FINDING:
if (bleStatus)
txtShowDistance.setText(R.string.out_of_bt_range);
else
txtShowDistance.setText(R.string.ble_off_msg);
txtShowDistance.setTextColor(Color.BLACK);
showRanging(NOT_IN_RANGE);
break;
}
}
});
}
private static boolean enableBluetooth() {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
boolean isEnabled = bluetoothAdapter.isEnabled();
if (!isEnabled) {
return bluetoothAdapter.enable();
}
return true;
}
public void showFoundDialog(String msg) {
//Show alert to the user clicks on found button when the device is found
//Creates async task to communicate with the server
}
public void showNotFoundDialog(String msg) {
//Show alert to the user clicks on not found button when the device is found
//Creates async task to communicate with the server
}
private void restoreFindingDeviceState(int mode) {
//Creates async task to communicate with server
}
public void showExitDialog(String msg) {
//Show application exit dialog
//Creates async task to communicate with the server
}
public void showStatusChangeDialog() {
//Show server changed the status changed dialog
}
public void showSnackBarInfo(String info, int mode) {
final Snackbar snackbar = Snackbar.make(scrollLayout, info, Snackbar.LENGTH_LONG);
snackbar.setActionTextColor(Color.WHITE);
if (mode == 1) {
View sbView = snackbar.getView();
sbView.setBackgroundColor(Color.RED);
snackbar.setDuration(15*1000);
}
snackbar.setAction("DISMISS", new View.OnClickListener() {
@Override
public void onClick(View view) {
snackbar.dismiss();
}
});
snackbar.show();
}
private void handleSearchStatusError(int errorCode) {
Log.e(TAG, "handleSearchStatusError called");
switch (errorCode) {
case Constants.WIFI_NOT_AVAILABLE:
Log.e(TAG, "wifi not available");
showErrorDialog(getString(R.string.details_wifi_not_connected), getString(R.string.wifi_error_desc));
break;
case Constants.ZDS_NOT_ENABLED:
showErrorDialog(getString(R.string.zds_disabled), getString(R.string.zds_error_desc));
break;
default:
break;
}
}
private void showErrorDialog(String title, String message) {
AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
builder.setTitle(title).
setMessage(message).setCancelable(false)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mDialog.dismiss();
mDialog = null;
}
});
mDialog = builder.create();
mDialog.show();
}
}
}
在下面记录消息
01-30 10:43:21.582 30185-30492/com.company.myapp D/ServerConnect: ServerTask started:
01-30 10:43:21.583 30185-30492/com.company.myapp D/ServerConnect: ServerTask doInBackground ():/locationInfo/setDeviceCommand?<<deleted>>
ServerTask doInBackground ():
01-30 10:43:21.587 30185-30492/com.company.myapp D/ServerConnect: url = <<deleted>>
01-30 10:43:23.436 30185-30578/com.company.myapp D/BeaconScanner: Find the Beaconid1: bd8a9fb8-613b-437f-f8a0-0dc8c7aedf06 id2: 0 id3: 71 type altbeacon
01-30 10:43:24.553 30185-30579/com.company.myapp D/BeaconScanner: Find the Beaconid1: bd8a9fb8-613b-437f-f8a0-0dc8c7aedf06 id2: 0 id3: 71 type altbeacon
01-30 10:43:25.674 30185-30580/com.company.myapp D/BeaconScanner: Find the Beaconid1: bd8a9fb8-613b-437f-f8a0-0dc8c7aedf06 id2: 0 id3: 71 type altbeacon
01-30 10:43:26.790 30185-30581/com.company.myapp D/BeaconScanner: Find the Beaconid1: bd8a9fb8-613b-437f-f8a0-0dc8c7aedf06 id2: 0 id3: 71 type altbeacon
01-30 10:43:27.902 30185-30583/com.company.myapp D/BeaconScanner: Find the Beaconid1: bd8a9fb8-613b-437f-f8a0-0dc8c7aedf06 id2: 0 id3: 71 type altbeacon
01-30 10:43:29.023 30185-30584/com.company.myapp D/BeaconScanner: Find the Beaconid1: bd8a9fb8-613b-437f-f8a0-0dc8c7aedf06 id2: 0 id3: 71 type altbeacon
01-30 10:43:30.135 30185-30585/com.company.myapp D/BeaconScanner: Find the Beaconid1: bd8a9fb8-613b-437f-f8a0-0dc8c7aedf06 id2: 0 id3: 71 type altbeacon
01-30 10:43:31.263 30185-30586/com.company.myapp D/BeaconScanner: Find the Beaconid1: bd8a9fb8-613b-437f-f8a0-0dc8c7aedf06 id2: 0 id3: 71 type altbeacon
01-30 10:43:31.817 30185-30492/com.company.myapp I/ServerConnect: Server Response code : 200
Server Response : {<<deleted>>}
01-30 10:43:31.818 30185-30492/com.company.myapp D/ServerConnect: ServerTask started:
01-30 10:43:31.818 30185-30185/com.company.myapp D/ServerConnect: ServerTask: onPostExecute started
答案 0 :(得分:0)
与库耗尽线程有关的主要原因是,如果您在库的回调方法didEnterRegion
中或更长时间didRangeBeaconsInRegion
中进行长时间运行的操作。由于显示的代码并不表示在那里有任何长期运行的操作,因此这应该不是问题。
该库使用内部线程池来解析从蓝牙堆栈传入的信标检测。此线程池的初始化如下:
mExecutor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1);
在四核处理器上,这将创建五个线程来解析信标数据包。这些中的大多数通常都是空闲的,除非存在大量的蓝牙LE流量。对于您描述的用例,我会怀疑这些线程是否会减慢您的进程。
您可能会发现所描述的问题与您设置的AsyncTask有关,而与信标扫描无关。一种检查方法是不启动信标测距,而是使用某种计时器来模拟信标事件。如果问题仍然存在,则表明它不是特定于扫描的。
这是我写的sample app,它执行类似的操作,将检测结果发送到服务器。它为检测到的每个信标向服务器发送一个请求,即使附近有大量信标,我也没有看到这种速度下降。您可以看到AsyncTask实现here。