I have a OSGi DS component with properties defined and which is referencing another bundles service. In the setService method I get the reference of the other's bundle service and start another thread. I'm dependent on properties defined in component.xml. Thus the first time I'm able to read the properties from the component.xml file is after the bundle gets the activated method called and getting the reference of the ComponentContext. Now it seems I have serious timing problems, as the setService is executed before the activate is executed.
How can that be? How can the bundle get the required service references before it is even activated?
How can I access the properties defined in the component.xml when starting the thread in the setService method?
The concrete example:
private String publishingUrl = "http://0.0.0.0:11023/ws"; // default address
protected synchronized void activate(ComponentContext context) {
this.ctx = context;
if (ctx != null) {
String url = String.valueOf(ctx.getProperties().get("publishingUrl"));
if (url != null) publishingUrl = url;
}
logger.info("Activated and got the publishingUrl: "+ publishingUrl);
}
public void setService(AnotherService service) {
synchronized (this) {
if (this.anotherService == service) {
logger.info("anotherService was already set.");
return;
} else {
this.anotherService = service;
logger.info("Got anotherService. Thank you DS!");
}
}
startWebserviceThread(publishingUrl);
}
In the console ouput I see the logger message from the setService, then from activate. The method startWebserviceThread is always called with the default value of publishingUrl not with the one got from the ComponentContext property file.
Also it doesn't make a difference if I set immediate="true" or immediate="false" in component.xml
Runtime: Java 1.6, eclipse equinox
The setService() method is used to inject dependencies into your DS.
Then activate() is called, and it is the place where the worker thread should start.
You have to move startWebserviceThread(publishingUrl); at the end of activate() method.
This is also suggested by your logic. You get the publishing url setting from your context, and then you can start your web service. To use another service, you need its reference before starting, so this is why setService() is called before activate().
As noted in this tutorial, a service should not be used inside set/unset methods.
Related
Context
I'm building a Flutter Plugin above the DJK SDK. For that, I have to implement the communication with the aircraft on the native side, and I'm doing it with Java. I'm also doing it only for Android.
One of the methods of the API is boolean connectToAircraft(), which must return if the connection with the aircraft succeeded.
Expected/current behavior
After I call connectToAircraft() - which invokes the DJISDKManager.getInstance().startConnectionToProduct() method, I expected to be able to use anything related to aircraft immediately, but this doesn't happen. I have to wait a few seconds before I can retrieve data from the aircraft.
Some code
public class UavApi implements IUavApi, DJISDKManager.SDKManagerCallback {
...
private final CountDownLatch onConnectToUavFinishedSignal = new CountDownLatch(1);
...
public boolean connectToUav() throws InterruptedException {
Logger.v("connectToUav()");
DJISDKManager.getInstance().startConnectionToProduct();
synchronized (onConnectToUavFinishedSignal) {
onConnectToUavFinishedSignal.await();
}
return DJISDKManager.getInstance().getProduct() instanceof Aircraft;
}
...
#Override
public void onProductConnect(#Nullable final BaseProduct baseProduct) {
Logger.v(MessageFormat.format("onProductConnect(product: {0})", baseProduct));
if (baseProduct != null) {
handleProductConnected(baseProduct);
}
}
#Override
public void onProductChanged(#Nullable final BaseProduct baseProduct) {
Logger.v(MessageFormat.format("onProductChanged(product: {0})", baseProduct));
if (baseProduct != null) {
handleProductConnected(baseProduct);
}
}
...
private void handleProductConnected(#NonNull final BaseProduct baseProduct) {
Logger.d(MessageFormat.format("Is null? {0}", baseProduct == null ? "Yes" : "No"));
Logger.d(MessageFormat.format("Type: {0}", baseProduct.getClass().getSimpleName()));
onConnectToUavFinishedSignal.countDown();
}
...
}
Problem
The code above is what I tried to do, but it's not working and guess it's because I'm misunderstanding the use of the onProductChange() and onProductConnect() methods.
The DJISDKManager.getInstance().getProduct() is always returning null.
OBS: It's always returning null immediately after the onConnectToUavFinishedSignal.await() call finishes. After a few seconds, I get a valid instance of the aircraft.
Something I've also noticed is that sometimes the onProductChange() is called with some value that the log outputs as Unknwoun and None. What are those and how can I test for them? Like if (baseProduct == ???) doSomething()
Environment
Android 9
MSDK 4.13.1
Phantom 4 Pro
Difference
According to the SDK Docs onProductChanged is primarily used to detect when the connection status changes from only remote controller connected to a full connection between the aircraft and the SDK running on your device.
Keep in mind that when the aircraft is disconnected, this method will be called with an instance of an aircraft, but this instance will come with property isConnected as false. If you print the aircraft object to the console you will notice that if isConnected is true, it will print the aircraft name, otherwise, it will print "None".
As long for the onProductConnect, it will be called always after DJISDKManager.getInstance().registerApp() succeeded or after you manually connect to the aircraft with success using DJISDKManager.getInstance().startConnectionToProduct(). In my tests, even though the app registration succeeds, the method will return false, so you might need to check if the SDKManagerCallback::onRegister results in DJISDKError.REGISTRATION_SUCCESS.
Solution
You need to listen to component change events. Unfortunately just because the product is connected it does not mean that the individual components, such as the flight controller, camera etc are connected. You will need to implement onComponentChange and add a listener to detect when a component is connected. These don't always connect in the same order and may start to connect before or after the product is connected.
#Override
public void onComponentChange(
BaseProduct.ComponentKey componentKey,
BaseComponent oldBaseComponent,
BaseComponent newBaseComponent
) {
newBaseComponent.setComponentListener(isConnected -> {
// check if component connected and access data
if (isConnected) {
if(componentKey == ComponentKey.FLIGHT_CONTROLLER) {
// DJISDKManager.getInstance().getProduct() should no longer be null
DJISDKManager.getInstance().getProduct().getModel();
}
}
})
}
I'm testing out Realm for database storage.
I'm using a singleton for fetching and managing common data that needs to be refreshed fairly often. But it seems that the Realm defaultInstance that get in my singleton is not in the same scope as if I get it in my Activity. So when I fetch remote data via my singleton, then save to realm, I can't retrieve that data from an Activity (get an empty result set).
I have attempted to pass in the Realm instance I defined in the Activity to the singleton (and close it there as well), but I still cannot retrieve saved results via the Activity instance.
I'm setting the default configuration in my Application class if that makes a difference.
Any help would be appreciated in clearing this up.
**Edit
Here's some more code. I'm using retrofit and gson, and my relevant services are in a Utility class (which may be causing the issue).
private void fetchMyObjects(Context context) {
// Fetch the myObjects
UtilityServices utilityServices = new UtilityServices(context);
utilityServices.getMyObjects(new UtilityServices.MyObjectsListener() {
#Override
public void gotMyObjects(final ArrayList<MyObject> myObjects, Exception e) {
if(e == null) {
Realm realm = null;
try {
realm = Realm.getDefaultInstance();
realm.executeTransaction(new Realm.Transaction() {
#Override
public void execute(Realm realm) {
realm.delete(MyObject.class);
realm.copyToRealm(myObjects);
Log.v("qwer", "LocalDataFragment fetchMyObjects: " + realm.where(MyObject.class).findAll().size());
}
});
} finally {
if(realm != null) {
realm.close();
}
}
} else {
// TODO: Handle a myObject error.
e.printStackTrace();
}
}
});
}
There is only one way that the results of a Realm transaction would not be visible after a transaction is executed and that is that the transaction takes place on a different thread.
It seems quite likely that this is the case, in your code, since, if getMyObjects ran on the UI thread, you would be getting the "no network activity on the UI thread exception"
how to add a binding context to a Dialog in Eclipse RCP4.
I have defined multiple Binding Contexts to listen for FKeys. The Parts react to the keys pressed but i have no ideas how to add a binding to a Dialog.
I create my Dialog with
ImportDialog dialog = ContextInjectionFactory.make(ImportDialog.class, context);
dialog.open();
Therefore i have access to every Service, what to do to make it send key events to the Binding Contexts?
In your application.e4xmi define a 'Binding Context' for your dialog, make this a child of the 'In Dialogs' Binding Context.
Add a new 'BindingTable' with the context id set to your new Binding Context. Add the key bindings you want to use in the dialog to this table.
In your dialog inject the MApplication:
#Inject
private MApplication _app;
do not try to inject other services as you will not get the correct instance of the service.
Add this method to get the correct Eclipse Context for the dialog:
IEclipseContext getEclipseContext()
{
// Must use active leaf from the Application context to get the correct context for key bindings and contexts in dialogs
return _app.getContext().getActiveLeaf();
}
Override the dialog getShellListener to return a shell listener because we need to wait for the shell to activate before setting up contexts:
#Override
protected ShellListener getShellListener()
{
return new ActivateShellListener();
}
private final class ActivateShellListener extends ShellAdapter
{
#Override
public void shellActivated(final ShellEvent e)
{
doShellActivated();
}
}
void doShellActivated()
{
IEclipseContext context = getEclipseContext();
EContextService contextService = context.get(EContextService.class);
contextService.activateContext(ID_CONTEXT);
EHandlerService handlerService = context.get(EHandlerService.class);
handlerService.activateHandler(ID_COMMAND, handler);
}
where 'ID_CONTEXT' is the binding context id and 'ID_COMMAND' is a command you want to activate a handler for.
Override close to clean up:
#Override
public boolean close()
{
IEclipseContext context = getEclipseContext();
EHandlerService handlerService = context.get(EHandlerService.class);
handlerService.deactivateHandler(ID_COMMAND, handler);
EContextService contextService = context.get(EContextService.class);
contextService.deactivateContext(ID_CONTEXT);
return super.close();
}
I have a custom CursorAdapter that is using multiple AsyncTasks in its bindView method to load images into a grid.
When bindView runs my AsyncTasks get launched multiple times. This has the effect of pushing up my heap size and can cause Out of Memory errors.
What is the best approach to take, to get AsyncTasks to run just once?
You can cache the results of the Asynctask. There is a very useful project on Github you might want to look into.https://github.com/nostra13/Android-Universal-Image-Loader
I think you are looking for AsyncTaskLoader or its support package implementation. It handles everything for you.
An example of how to use it:
public SampleLoader extends AsyncTaskLoader<List<SampleItem>> {
// We hold a reference to the Loader’s data here.
private List<SampleItem> mData;
public SampleLoader(Context ctx) {
// Loaders may be used across multiple Activitys (assuming they aren't
// bound to the LoaderManager), so NEVER hold a reference to the context
// directly. Doing so will cause you to leak an entire Activity's context.
// The superclass constructor will store a reference to the Application
// Context instead, and can be retrieved with a call to getContext().
super(ctx);
}
/****************************************************/
/** (1) A task that performs the asynchronous load **/
/****************************************************/
#Override
public List<SampleItem> loadInBackground() {
// This method is called on a background thread and should generate a
// new set of data to be delivered back to the client.
List<SampleItem> data = new ArrayList<SampleItem>();
// TODO: Perform the query here and add the results to 'data'.
return data;
}
/********************************************************/
/** (2) Deliver the results to the registered listener **/
/********************************************************/
#Override
public void deliverResult(List<SampleItem> data) {
if (isReset()) {
// The Loader has been reset; ignore the result and invalidate the data.
releaseResources(data);
return;
}
// Hold a reference to the old data so it doesn't get garbag ecollected.
// We must protect it until the new data has been delivered.
List<SampleItem> oldData = mData;
mData = data;
if (isStarted()) {
// If the Loader is in a started state, deliver the results to the
// client. The superclass method does this for us.
super.deliverResult(data);
}
// Invalidate the old data as we don't need it any more.
if (oldData != null && oldData != data) {
releaseResources(oldData);
}
}
/*********************************************************/
/** (3) Implement the Loader’s state-dependent behavior **/
/*********************************************************/
#Override
protected void onStartLoading() {
if (mData != null) {
// Deliver any previously loaded data immediately.
deliverResult(mData);
}
// Begin monitoring the underlying data source.
if (mObserver == null) {
mObserver = new SampleObserver();
// TODO: register the observer
}
if (takeContentChanged() || mData == null) {
// When the observer detects a change, it should call onContentChanged()
// on the Loader, which will cause the next call to takeContentChanged()
// to return true. If this is ever the case (or if the current data is
// null), we force a new load.
forceLoad();
}
}
#Override
protected void onStopLoading() {
// The Loader is in a stopped state, so we should attempt to cancel the
// current load (if there is one).
cancelLoad();
// Note that we leave the observer as is. Loaders in a stopped state
// should still monitor the data source for changes so that the Loader
// will know to force a new load if it is ever started again.
}
#Override
protected void onReset() {
// Ensure the loader has been stopped.
onStopLoading();
// At this point we can release the resources associated with 'mData'.
if (mData != null) {
releaseResources(mData);
mData = null;
}
// The Loader is being reset, so we should stop monitoring for changes.
if (mObserver != null) {
// TODO: unregister the observer
mObserver = null;
}
}
#Override
public void onCanceled(List<SampleItem> data) {
// Attempt to cancel the current asynchronous load.
super.onCanceled(data);
// The load has been canceled, so we should release the resources
// associated with 'data'.
releaseResources(data);
}
private void releaseResources(List<SampleItem> data) {
// For a simple List, there is nothing to do. For something like a Cursor, we
// would close it in this method. All resources associated with the Loader
// should be released here.
}
/*********************************************************************/
/** (4) Observer which receives notifications when the data changes **/
/*********************************************************************/
// NOTE: Implementing an observer is outside the scope of this post (this example
// uses a made-up "SampleObserver" to illustrate when/where the observer should
// be initialized).
// The observer could be anything so long as it is able to detect content changes
// and report them to the loader with a call to onContentChanged(). For example,
// if you were writing a Loader which loads a list of all installed applications
// on the device, the observer could be a BroadcastReceiver that listens for the
// ACTION_PACKAGE_ADDED intent, and calls onContentChanged() on the particular
// Loader whenever the receiver detects that a new application has been installed.
// Please don’t hesitate to leave a comment if you still find this confusing! :)
private SampleObserver mObserver;
}
The source: androiddesignpatterns.com - Implementing Loaders (part 3)
The onCreate of my userDatabase that extends ContentProvider is not properly called
Here is some of my userBatabase code:
public class userDatabase extends ContentProvider {
private MainDatabaseHelper mOpenHelper;
public userDatabase(){}
public static final class MainDatabaseHelper extends SQLiteOpenHelper{...}
#Override
public boolean onCreate() {
mOpenHelper = new MainDatabaseHelper(getContext());
return true;
}
#Override
public Uri insert(Uri uri, ContentValues values) {
long id = mOpenHelper.getWritableDatabase().insert("Users", null, values);
return Uri.withAppendedPath(CONTENT_URI, "" + id);
}
...
}
In my main activity I call:
userDatabase cpDatabase = new userDatabase();
But when I try to call cpDatabase.insert(userDatabase.CONTENT_URI, values);
Everything crashes inside insert when mOpenHelper.getWritableDatabase().insert("Users", null, values); is called.
I found out that mOpenHelper.getWritableDatabase() was the issue, as it wont run even by itself, and then I found out this was because mOpenHelper was null.
I instantiate mOpenHelper in the constructor, so I figure its not running. A few log messages confirm this, when I call userDatabase cpDatabase = new userDatabase(); my log messages showed that the userDatabase() constructor ran normally, but the onCreate never ran, so the mOpenHelper never got instantiated.
(Note: with these log messages, I noticed that the constructor and the onCreate for my userDatabase got called when my app started. I have no idea why or where. I dont understand why this was run before i tried to create an instance. and even though it was run, mOpenHelper still wasn't initialized, and when i created an instance, the constructor ran but the onCreate didnt.)
What could possibly be happening, and how can I make my onCreate run?
Since you are using content providers, according to the documentation
This method is called for all registered content providers on the
application main thread at application launch time
And the way you try to use the content provider is seems wrong and,
You don't need to manually instantiate the content provider, once you made the request via the ContentResolver by passing the URI, the system inspects the authority of the given URI and passes the request to the content provider registered with the authority.
for example
getContentResolver().delete(uri, null, null);
Where the uri is, the full URI to query.
This tutorial will guide you in right direction