In the reference application, RegionBootstrap is initialised in a custom application class on it's onCreate method and of course, the application class is called before any activity is called.
Is there a way to initialise RegionBootstrap inside an activity? I already tried making a static variable of RegionBootstrap so i can call it in a different activity, but unfortunately, it doesn't work.
BeaconApplication.regionBootstrap = new RegionBootstrap((BootstrapNotifier) this.getApplication(), downloadedBeacons);
The Regions I needed to be initialised will come from a server, so initialisation of RegionBootstrap must not come from the application class.
* EDIT *
public class LoginActivity extends ActionBarActivity {
…
/*** short version ***/
#Override
protected void onCreate(Bundle savedInstanceState) {
/*** after successful login ***/
BeaconApplication.beacons = downloadBeaconsFromServer();
}
}
public class BeaconActivity extends ActionBarActivity {
…
#Override
protected void onCreate(Bundle savedInstanceState) {
…
startService(new Intent(this, BeaconService.class));
}
}
This is where I implemented BeaconConsumer
public class BeaconService extends Service implements BeaconConsumer {
private BeaconManager beaconManager;
private BeaconNotifier beaconNotifier;
private RegionBootstrap regionBootstrap;
#Override
public void onCreate() {
super.onCreate();
Log.i(TAG, "onCreate");
beaconManager = 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"));
beaconManager.setBackgroundBetweenScanPeriod(1001);
beaconManager.setBackgroundScanPeriod(101);
beaconManager.setForegroundScanPeriod(101);
beaconManager.setForegroundBetweenScanPeriod(1001);
beaconNotifier = new BeaconNotifier(this);
beaconManager.bind(this);
}
#Override
public void onBeaconServiceConnect() {
beaconManager.setMonitorNotifier(beaconNotifier);
monitorBeacons();
regionBootstrap = new RegionBootstrap(beaconNotifier, BeaconApplication.beacons);
}
private void monitorBeacons() {
for (Region beacon : BeaconApplication.beacons) {
try {
Log.i(TAG, "Monitoring beacon " + beacon.getUniqueId());
beaconManager.startMonitoringBeaconsInRegion(beacon);
} catch (RemoteException e) {
Log.e(TAG, "Monitoring beacon failed");
e.printStackTrace();
}
}
}
}
Implementation of BeaconNotifier
public class BeaconNotifier implements BootstrapNotifier {
private Context context;
public BeaconNotifier(Context context) {
this.context = context;
}
#Override
didEnter.. etc
#Override
public Context getApplicationContext() {
return context;
}
}
You can use:
BeaconManager.setMonitorNotifier(MonitorNotifier);
BeaconManager.startMonitoringBeaconsInRegion(Region);
But do not forget, in order to use BeaconManager methods, you have to wait until BeaconService is connected. Be aware, with this methods, you need to create your own service if you want to monitor beacons even if app is killed.
Btw, I remember, once I have also faced a problem with RegionBootstrap. I used a trick to handle that problem. Can you test following code?
...
BeaconManager.bind(yourConsumer);
...
//wait until BeaconConsumer.onBeaconServiceConnect() is called
//write following code inside of onBeaconServiceConnect
RegionBootstrap dummy = new RegionBootstrap(mBootstrapNotifier, new Region("dummy", null, null, null));
dummy.disable();
//after this point you can create your own RegionBootstrap
There is a key point in here, you need to create your own BootstrapNotifier. If you are doing this in an activity, you can do this:
public class YourActivity extends Activity implements BootstrapNotifier {
...
BootstrapNotifier mBootstrapNotifier = this;
...
Or in an Application class:
public class YourApp extends Application implements BootstrapNotifier {
...
BootstrapNotifier mBootstrapNotifier = this;
...
In my case, I have created an adapter and that adapter requires Contextin its constructor and I have used that adapter as BootstrapNotifier:
public class AltBeaconAdapter implements BootstrapNotifier {
private Context mContext;
...
public AltBeaconAdapter(Context context) {
mContext = context;
...
}
#Override
public Context getApplicationContext() {
return mContext;
}
...
}
Also, you have to implement MonitorNotifier methods since BootstrapNotifier is a sub class of MonitorNotifier.
Yes, this trick is weird and it shows there is an error in the library with initializing RegionBootstrap but I have service so I switched to first method that I proposed to you. If this trick works for you too, let me know so that I can create an issue on the library's GitHub page.
Related
I have 3 classes in my activity, the first is a BLEmanager which handle the bluetooth connection and return some values through a interface.
The second one is an activity which i call splash, it is the LAUNCHER activity.
Splash extends AppCompactActivities and implements the the BLEmanager interface, so that i can call the third activity when the connection has been established.
All the interface method are inside this class now, but i want them to trigger something in the third class too.
The third class is an activity which is called Main and contain the core of the app and all the functionalities. This class extends the Splash one. Thanks to this fact i thought that i could have override the interface method which are already inside Splash.
I don't know why but when i call an interface method from the BLEmanager only the method inside the Splash class are triggered and not the Main method.
The code is quite confusing so i didn't post it, ask me if you need.
thank you a lot.
EDIT:
Here's the code, comments and variable name are in italian
BLEmanager
public class MDPtechBLE {
private int MY_PERMISSION_LOCATION=1;
private Context context;
private Activity activity; //contesto e activity della applicazione
private CallBacksBLE CB_BLE; //Interfaccia di Callback
private BluetoothGatt GATT;
//Variabili Strettamente legate al Bluetooth
private static BluetoothAdapter myBlueAdapt; //Adapter del BLE, rappresenta il modulo HW del BLE
private Intent enableBtIntent;
private ScanCallback myScanCallBack; //NUOVA CALLBACK
private BluetoothAdapter.LeScanCallback myLeScanCallBack; //VECCHIA CALLBACK
public BluetoothDevice DISPOSITIVOCONNESSO;
//variabi utili
private int TempoScansione=1000;
private boolean MitragliatriceState=false;
//Liste
private List<ScanResult> ListaDeiRisultatiTrovatiDallaScansione; //Nuova Lista di device trovati dalla scansione
private List<BluetoothGattService> ListaServizi;
private List<BluetoothGattCharacteristic> ListaTutteLeCaratteristiche;
private List<BluetoothGattCharacteristic> ListaCaratteristicheSCRITTURA;
private List<BluetoothGattCharacteristic> ListaCaratteristicheLETTURA;
List<PacchettoScrittura> Coda;
//Devo tenere in memoria tutte le Caratteristiche che mi serviranno, PARLARNE CON GIACOMO A PROPOSITO, devono essere pubbliche per essere raggiunte anche dai frammenti
public MDPtechBLE(Context context)
{
ListaServizi = new ArrayList<>();
this.context=context;
activity = (Activity)context;
this.CB_BLE = (CallBacksBLE)context;
initVariabiliScansione();
Log.i("BLE","inizializzato");
Start();
}
Inside this class there are all the method's which handle my BLE connection.
**Callback interface **
public interface CallBacksBLE {
//BLUETOOTH
void BluetoothStato(Boolean stato);
void BleError(int ErrorCode);
//GATT
void StatoConnessioneCambiato(int stato);
void ViaLibera(List<BluetoothGattCharacteristic> ListaCaratteristicheSCRITTURA, List<BluetoothGattCharacteristic> ListaCaratteristicheLETTURA);
void RisultatoLettura(byte[] data, BluetoothGattCharacteristic CharLetta);
void Notifica(byte[] data, BluetoothGattCharacteristic CharNotificata);
}
When, for example, the connection state change i call CB_BLE.StatoConnessioneCambiato('current connection state');
SPLASH
public class Splash extends AppCompatActivity implements CallBacksBLE{
private Intent mainIntent;
public static MDPtechBLE MyBLE;
//Variabili Resume Pause
public static boolean CambioActivityEffettuato;
#Override
public void onCreate(Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
setContentView(R.layout.splashscreen_xlink);
startLockTask();
if(MyBLE==null){
Log.i("Splash","Creazione");
MyBLE = new MDPtechBLE(this);
}
mainIntent = new Intent(Splash.this,MasterActivity.class);
}
#Override
public void onResume() {
super.onResume();
Log.e("DEBUG", "onResume of Splash");
}
#Override
public void onPause() {
super.onPause();
Log.e("DEBUG", "OnPause of Splash");
}
Boolean InRicerca=false;
#Override
public void BluetoothStato(Boolean Stato) {
if(Stato && !InRicerca) {
InRicerca=true;
MyBLE.Cerca_e_Connetti();
}
}
#Override
public void BleError(int ErrorCode) {
}
#Override
public void StatoConnessioneCambiato(int stato) {
int i=0;
i++; //BREAKPOINT
}
#Override
public void ViaLibera(List<BluetoothGattCharacteristic> ListaCaratteristicheSCRITTURA, List<BluetoothGattCharacteristic> ListaCaratteristicheLETTURA) {
CambioActivityEffettuato=true;
Splash.this.startActivity(mainIntent);
Splash.this.finish();
MyBLE.SetMitragliatriceState(true);
}
#Override
public void RisultatoLettura(byte[] data, BluetoothGattCharacteristic CharLetta) {
}
#Override
public void Notifica(byte[] data, BluetoothGattCharacteristic CharNotificata) {
}
#Override
public void onBackPressed() {
// nothing to do here
// … really
}
}
Here in splash, the callbacks are triggered once the interface method are called from the BLEmanager
MainMaster
Here in the main class i handle all the fragments and all the rest of the application, to do everything in the right way i need to know the BLE state, to know the BLE state i need to implement in this class the same callbacks which are in the SPLASH. So i thought about Extending SPLASH.
#Override
public void StatoConnessioneCambiato(int stato) {
int k=0;
k++; //BREAKPOINT
}
This is the callback which tell me the BLE connection state.
If i call it from the BLEmanager only the StatoConnessioneCambiato(bool) inside Splash is triggered and not the one inside MasterActivity.
The reason Main is not receiving callbacks is that it has never been setup. `Override' annotation has nothing to do with it.
You are calling this line to receive callbacks from BLE for SPLASH:
MyBLE = new MDPtechBLE(this);
You need to move this call to Main class and pass it's instance as the argument.
I created Service class.
I can run it anywhere where I want, but I always need Context from MainMenuActivity.class.
I tried use getApplicationContext and getBaseContext but they show another class.
Thanks for answer
public class MainActivity extends ActionBarActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
startService(new Intent(this, MyService.class));
}
}
public class MyService extends Service {
private Handler handler = new Handler();
private MyLocationListener mylistener;
public void onCreate() {
handler.postDelayed(new runnable(), 10000);
}
private class runnable implements Runnable {
#Override
public void run() {
mylistener = new MyLocationListener();
}
}
}
public class MyLocationListener implements LocationListener {
#Override
public void onLocationChanged(Location loc) {
SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
}
}
[EDIT]
When I used getApplicationContext() or getBaseContext or MainActivity.this to getDefaultSharedPreferences, always it will be the same?
Solution: 1
In that case you have to use, defaultSharedPreferences. You can access the default shared preferences instance by:
PreferenceManager.getDefaultSharedPreferences(Context context):
Example:
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
This preference is shared across all your Activity and Service classes.
Solution: 2
You can create sharedPreference instance in your application class like:
public class MyApplication extends Application {
public static SharedPreferences preferences;
#Override
public void onCreate() {
super.onCreate();
preferences = getSharedPreferences("Preferences", MODE_PRIVATE);
}
}
And then you can manage your preferences as:
MyApplication.preferences.getString("key", "default");
add context parameter to your service class methods
public void myMethodInsideServiceClass(Context context){
//bluh bluh
}
so that you can call from Activity Class like this
myMethodInsideServiceClass(this);
you can also try
public class MyActivity extends Activity {
public static myActivity;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myActivity=this;
}
}
so that you can use myActivity across the application
(i was not using editor to type code, sorry for syntax error)
I have an abstract class called BaseActivity (extends Activity) which has a function
public abstract void onLocationChanged();
All other activities extends this class. When I call BaseActivity's method onLocationChanged() I would expect, that all other classes that implement this method would call their onLocationChanged() method or at least the activity that is currently on top of the stack, but it seem that only one activity calls the method and it is not the one on top of the stack.
Can someone help?
here are relevant parts of my code:
abstract class BaseActivity extends FragmentActivity {
public static MyLocation location = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (location == null) {
location = new MyLocation(this);
}
}
public abstract void onLocationChanged();
}
public class MyLocation {
BaseActivity parrent;
public MyLocation(BaseActivity act) {
public MyLocation(BaseActivity act) {
this.parrent = act;
}
public void refreshMyLocation() {
/*location get refreshed here*/
this.parrent.onLocationChanged();
}
}
public class MainActivity extends BaseActivity {
#Override
public void onStart() {
BaseActivity.location.refreshMyLocation();
}
public void onLocationChanged() {
/*some stuff*/
}
}
You are calling on location changed on an instance of base activity not all of them at once. Without your code I don't know which one. But simply declaring a method in a base class does not mean it will be called on every class that implements it.
I dont understand why do you want to do that. I think you should need a Serivice that implements onLocationChanged and communicates with activities via Handler or BroadcastReceiver.
I'm trying to track down a new null pointer exception which is appearing in my ACRA logs and which I can't reproduce. Here's the relevant code:
public class MyApplication extends Application {
public void onCreate() {
DataManager.instance().initializeData(this);
}
}
public class DataManager {
private static DataManger instance = new DataManger();
private List<DataModel> dataModels;
private List<I_Callback> callbacks = new ArrayList<I_Callback>();
private boolean isInitialized = false;
private DataManager(){}
public static DataManager instance() {
return instance;
}
public void initializeData(Context context) {
new DataManagerInitializer().execute(context);
}
public void setDataModels(List<DataModel> models) {
dataModels = models;
}
public void synchronized registerInitializeCallbacks(I_Callback callback) {
if (isInitialized) {
callback.executeCallback();
} else {
callbacks.add(callback);
}
}
public void synchronized setInitialized() {
isInitialized = true;
for (I_Callback callback:callbacks) {
callback.executeCallback();
}
callbacks.clear();
}
}
public class DataManagerInitializer extends AsyncTask<Context, Void, Void>{
protected Void doInBackground(Context... contexts){
List<DataModel> dataModels = new ArrayList<DataModel>();
/*various code to create DataModel objects and add to dataModels list*/
DataManager.instance().setDataModels(dataModels);
return null;
}
protected void onPostExecute(Void result) {
DataManager.instance().setInitialized();
}
}
public class ActivityA extends Activity implements I_Callback{
public void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.graphical_layout);
DataManager.instance().registerInitializeCallbacks(this);
}
public void executeCallback() {
/* wire up button to call Activity B */
}
}
public class ActivityB extends Activity {
public void onCreate(Bundle savedInstanceState) {
List<DataModel> dataModels = DataManager.instance().getDataModels();
/* The following line of code throws a null pointer exception
in the stack trace*/
for (int i=0; i < dataModels.size(); i++){
/* do something with the data model */
}
}
}
To break down the above more simply, the application is launched which kicks off the initializion of the data manager singleton. ActivityA, the main activity, launches and waits for the data manager to complete initialization before allowing any actions, wiring up any events, etc. From ActivityA, its not possible to get to ActivityB without the call back method executing and ActivityB is only reachable from ActivityA. The only way for the list of data models to be null in the DataManager is for it to not have been initialized, but I'm struggling to see how this is possible. Any suggestions on how my null pointer may have occurred?
private static DataManger instance = new DataManger();
...
public static DataManager instance() {
return instance;
}
Is where the problem is. So your instance variable is getting garbage collected. As it is instantiated when it is declared, it is not being appropriately re-instantiated. So, try this instead:
private static DataManger instance = null;
...
public static DataManager instance() {
if (instance == null){
instance = new DataManager();
}
return instance;
}
This will ensure the call to instance() (usually called getInstance() but this is only convention), will return a valid single instance of the datamanager. Try to avoid instantiating global variables with their declaration, to avoid this specific problem.
Let's assume that:
you are interacting with the Activity B
press the home button:
start playing with other apps (consuming memory)
at some point the so needs memory and it's gonna start garbage collecting objects, included your "instance".
If that happens when you launch your app the framework will resume the activity B and the npe will happen.
You need to re-create the instance (in the activity B) if it is null.
MAIN ACTIVITY
public class MyActivity() extends Activity
{
onCreate()
{
MyClass myobj=new MyClass();
}
public void Mymethod()
{}
}
//HELPER CLASS IN A SEPARATE FILE
public class MyClass()
{
MyClass(Context context)
{
}
}
I tried to call Mymethod() from an instance of MyClass.
I would really appreciate any help. Thanks.
Why not just pass the activity to the constructor like
public class MyActivity extends Activity {
onCreate(){
MyClass myobj=new MyClass(MyActivity.this);
}
public void myMethod(){
}
}
//HELPER CLASS IN A SEPARATE FILE
public class MyClass{
public MyClass(MyActivity act) {
act.myMethod();
}
}
Make that method as static so you can call without creating the class object
public static void Mymethod()
{}
and call like this way
MainActivity.Mymethod();
This is probably the best way to do it. This is how I'm doing it. It's called a Singleton Design Pattern:
public class MyActivity extends Activity {
private static MainActivity instance;
public static MainActivity getInstance() {
if(instance==null){
setInstance(this);
}
return instance;
}
public static void setInstance(MainActivity instance) {
MainActivity.instance = instance;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setInstance(this);
}
}
If I'm understanding you correctly I believe you can solve your problems using an interface as a callback.
////ACTIVITY/////////////////////////////////
public class MyActivity() extends Activity {
onCreate()
{
MyClass myObj=new MyClass();
//Set the listener on the object. Created as anonymous
myObj.setListener(new MyClass.Listener() {
myMethod();
});
}
}
public void myMethod(){
}
//////Custom Class//////////////////
public class MyClass {
Listener mListener;
public interface Listener {
public void onInterestingEvent();
}
public void setListener(Listener listener) {
mListener = listener;
}
public void someUsefulThingTheClassDoes() {
//Do your code here and when you're ready to call the activity's method do this
mListener.onInterestingEvent();
}
}
I had an inner class that I wanted to pull out into a more general library "Helper" class. I had the same issue you do. I got around it by making the helper class abstract, with a single abstract method. Then in my project package I extended the helper class with a constructor call in the specific class.
public class MyActivity extends Activity {
onCreate() {
MyHelperClass = new MyHelperClass(this, "foobar");
}
public void myMethod() {
// Code...
}
}
// In a different file
public class MyHelperClass extends HelperClass {
private MyActivity mInstance;
public MyHelperClass(MyActivity act, String data) {
super();
this.mInstance = act;
this.mActivity = act; // Useful for calling generic Activity methods in the HelperClass
this.mData = data;
}
protected void callMyActivityMethod() {
mInstance.myMethod();
}
}
// In a different file
public abstract class HelperClass {
protected Activity mActivity;
protected String mData;
public HelperClass() {
// Subclass will set variables
}
protected abstract void callMyActivityMethod();
// More code for all the other stuff the class does
}
In this way, I have a helper class that contains the vast majority of the "work", and all I have to do is make a subclass with the constructor and one method in order to get access to the calling activity's method of interest.
You have to pass instance of MainActivity into another class, then you can call everything public (in MainActivity) from everywhere.
MainActivity.java
public class MainActivity extends AppCompatActivity {
// Instance of AnotherClass for future use
private AnotherClass anotherClass;
#Override
protected void onCreate(Bundle savedInstanceState) {
// Create new instance of AnotherClass and
// pass instance of MainActivity by "this"
anotherClass = new AnotherClass(this);
}
// Method you want to call from another class
public void myMethod(){
...
}
}
AnotherClass.java
public class AnotherClass {
// Main class instance
private MainActivity mainActivity;
// Constructor
public AnotherClass(MainActivity activity) {
// Save instance of main class for future use
mainActivity = activity;
// Call method in MainActivity
mainActivity.myMethod();
}
}
In MainActivity.class file
You have to pass MainActivity context from MainActivity Class. Then in MyClass you have to Get MainActivity context. Remember Context and MyActivity are two different reference.
public class MyActivity extends Activity
{
onCreate(){
MyClass myobj=new MyClass(MyActivity context);
}
public void Mymethod(){}
}
//HELPER CLASS IN A SEPARATE FILE
public class MyClass()
{
MyActivity context;
MyClass(MyActivity context)
{
this.context = context;
this.context.Mymethod();
//Or you can directly use activity context
context.Mymethod();
}
}
I decided to write the HelperClass MyClass as an inner class of MyActivity class. This allows it full access to parent class but the bad thing is now MyClass is restricted to MyActivity class only.
public class MyActivity() extends Activity
{
onCreate()
{
MyClass myobj=new MyClass();
}
public void myMethod()
{
}
}
//INNER CLASS
public class MyClass
{
public MyClass()
{
}
//I can directly access the MyMethod
myMethod();
}