I have a problem with the dependency services for implementing features that depends of the plattorm. I need what my implementation on Android receive a Context object to do the task. How can I do it?
This is my code:
1) On PCL:
public interface ICallService
{
List<string> GetContacts();
}
2) On Android Project:
[assembly: Dependency(typeof(CallService))]
namespace DEMOBLOBS.Droid.DependencyServicesPruebas
{
public class CallService : ICallService
{
public static void Init() { }
public List<string> GetContacts()
{
AT THIS POINT I NEED THE CONTEXT OBJECT!
}
}
}
The constructor of Call Service class does not have any parameter. Maybe I can I pass the Context object like parameter in some way?
Can you help me, please?
you could try answer from https://forums.xamarin.com/discussion/106938/context-is-obsolete-as-of-version-2-5
internal static MainActivity Instance { get; private set; }
protected override void OnCreate(Bundle bundle)
{
Instance = this;
// Forms initialization here...
}
//later where you need it:
var context = MainActivity.Instance;
Related
I'm new to Xamarin and tried some beginner tutorials so far.
Now I wanted to build some custom stuff and need to request the current location.
For this I installed Xamarin.GooglePlayServices.Location in order to have access to the FusedLocationProviderClient class.
In MainActivity I retrieve the FusedLocationProviderClient:
public class MainActivity : FormsAppCompatActivity, ILocationProvider
{
private FusedLocationProviderClient FusedLocationClient { get; set; }
protected override void OnCreate(Bundle savedInstanceState)
{
// snip
FusedLocationClient = LocationServices.GetFusedLocationProviderClient(this);
}
}
The ILocationProvider needs the method TaskCompletionSource<Model.Location> GetPosition() to be implemented:
public TaskCompletionSource<Model.Location> GetPosition()
{
// snip
if (CheckSelfPermission(Manifest.Permission_group.Location) == Permission.Granted)
{
FusedLocationClient.LastLocation.AddOnCompleteListener(new OnLocationRequestCompleteListener(this, tcs));
}
// snip
}
The UI has a button to request the current location.
Whenever the user clicks the button I get the ILocationProvider via DependencyService and execute the GetPosition method:
private void GetPositionClicked(object sender, EventArgs e)
{
var provider = DependencyService.Get<ILocationProvider>();
if(provider != null)
{
var tcs = provider.GetPosition();
// snip
}
}
The problem now is that the application crashes as soon as I try to execute the CheckSelfPermission method in GetPosition().
I set a breakpoint in GetPosition() and noticed that FusedLocationClient is null although it clearly wasn't null after OnCreate was called.
I then inspected this in OnCreate and GetPosition and noticed that they weren't the same instances which leads me to the conclusion that something is clearly wrong here.
To solve this for now I did the following:
public class MainActivity : FormsAppCompatActivity, ILocationProvider
{
internal static MainActivity Instance { get; private set; }
private FusedLocationProviderClient FusedLocationClient { get; set; }
protected override void OnCreate(Bundle savedInstanceState)
{
// snip
Instance = this
// snip
}
// snip
public TaskCompletionSource<Model.Location> GetPosition()
{
if (this != Instance)
{
return Instance.GetPosition();
}
// snip
}
}
At least this works for now but I can't imagine that this is the way to go.
What I learned so far is that DependencyService seems to not get already created instances but create one instead (at least once).
What would be the correct way to call methods in MainActivity from the shared .NET Standard library?
Move those methods out of your Activity class into a standalone class (within your Xamarin.Android application (or a Xamarin.Android library project).
Assuming an interface like:
public interface ILocationProvider
{
Task<Tuple<double, double>> GetPosition();
}
Android Implementation:
public class Location_Android: Java.Lang.Object, ILocationProvider
{
private FusedLocationProviderClient FusedLocationClient { get; set; }
public Location_Android()
{
FusedLocationClient = new FusedLocationProviderClient(Application.Context);
}
public async Task<Tuple<double, double>> GetPosition()
{
var loc = await FusedLocationClient?.GetLastLocationAsync();
return new Tuple<double, double>(loc.Latitude, loc.Longitude);
}
}
public class DriveInteractor implements DriveContract.Interactor {
private final DriveRepository driveRepository;
public DriveInteractor(Context context) {
DriveApplication application = (DriveApplication) context.getApplicationContext();
this.driveRepository = application.getDriveRepository();
}
#Override
public List<Drive> getDrive() {
List<Drive> drives = driveRepository.getDrives();
return drives;
}
}
Following is the code for my Test Class for which I am using Mockito:
#RunWith(PowerMockRunner.class)
public class DriveInteractorTest {
#Mock
private Context context;
#Mock
private DriveRepository driveRepository;
#Mock
private List<Drive> driveList;
#Mock
private DriveApplication driveApplication;
private DriveInteractor driveInteractor;
#Before
public void setUpDriveInterator(){
MockitoAnnotations.initMocks(this);
driveInteractor = new DriveInteractor(context);
}
.....}
I have also written a test method in my Test Class. As soon as I run my test method I keep getting a Null Pointer Exception pointing towards DriveRepository.
I even tried creating an object of my Drive POJO class in setUpDriveInterator method and adding to the arraylist, but it doesn't work:
public class DriveRepository {
private List<Drive> drives;
public DriveRepository(Context context) {
drives = new ArrayList<>();
drives.add(Drive.create(context, "ABC", "Honda"));
.....
}
....}
Which context needs to be passed in DriveInterator for testing? Any help would be greatly appreciated.
stub your context.getApplicationContext(); piece of code in your test method.
something like below Mockito.when(context.getApplicationContext()).thenReturn(driveApplication);
Hope it is useful.
I create a unit test for my Presenter. My Presenter implements Listener callback if successfully load data from API (use Interactor):
PresenterTest.java
public class MainContactPresenterTest {
#Mock LoadContactInteractor loadContactInteractor;
#Mock ApiService apiService;
#Mock LoadContactView loadContactView;
#Mock ContactRepository contactRepository;
#Mock LoadContactInteractor.OnLoadDataFinishedListener listener;
#InjectMocks MainContactPresenterImpl presenter;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void getContactLists() {
// given
// when
presenter.fetchRemoteContacts();
// then
Mockito.verify(loadContactInteractor).onLoadData(listener);
}
}
Here is my Presenter:
public class MainContactPresenterImpl implements MainContactPresenter,
LoadContactInteractor.OnLoadDataFinishedListener {
private LoadContactView loadContactView;
private LoadContactInteractor loadContactInteractor;
private ContactRepository contactRepository;
#Inject
public MainContactPresenterImpl(LoadContactInteractor loadContactInteractor,
#NonNull LoadContactView loadContactView,
ContactRepository contactRepository) {
this.loadContactView = loadContactView;
this.loadContactInteractor = loadContactInteractor;
this.contactRepository = contactRepository;
}
#Override
public void onSuccessLoad(List<Contact> contacts) {
loadContactView.saveDataToLocalStorage(contacts);
}
#Override
public void onErrorLoad() {
loadContactView.dismissProgress();
loadContactView.showErrorMessage();
}
#Override
public void preCheckCacheData() {
if (contactRepository.getContactCount() == 0) {
// Load contacts from Server
fetchRemoteContacts();
} else {
fetchLocalContacts();
}
}
#Override
public void fetchRemoteContacts() {
loadContactView.showProgress();
loadContactInteractor.onLoadData(this);
}
}
But when I ran test, I got the mocking parameter in verify not match.
I got my presenter that have to be an argument. Not the listener.
Argument(s) are different! Wanted:
loadContactInteractor.onLoadData(
listener
);
Actual invocation has different arguments:
loadContactInteractor.onLoadData(
fanjavaid.gojek.com.contacts.presenter.MainContactPresenterImpl#1757cd72
);
How to handle that? Thank you
You are creating a mock...
#Mock LoadContactInteractor.OnLoadDataFinishedListener listener;
...and then you don't use it ever again and act suprised when verify tells you, that it wasn't actually used. Why? Of course it wasn't used, since you never use it anywhere, so how should your classes know to use that mock object?
Your MainContactPresenterImpl does not use an OnLoadDataFinishedListener as an external dependency (then your could perhaps inject it via #InjectMocks), it is itself such a listener and thus mocking another listener makes no sense here.
In other words, MainContactPresenterImpl has no OnLoadDataFinishedListener field, so Mockito is of course not capable of injecting something in this non-existing field. For something like this to work, you would need to add such a field and then use the content of that field when calling your onLoadData method.
The only invocation of your method is here...
loadContactInteractor.onLoadData(this);
And what is this in that context? It's the MainContactPresenterImpl object that contains the method, in other words, your presenter.
So, what will work is...
Mockito.verify(loadContactInteractor).onLoadData(presenter);
Im trying to do a very simple dependency injection in a Android app. I am using dagger 2 as a DI tool.
The issue is no injection is occuring:
here is my code:
//behold Motor.java in all its awe.
public class Motor {
private int rpm;
public Motor(){
this.rpm = 10; //default will be 10
}
public int getRpm(){
return rpm;
}
public void accelerate(int value){
rpm = rpm + value;
}
public void brake(){
rpm = 0;
}
}
and here is the Vehicle.java which utilities the motor class:
import javax.inject.Inject;
public class Vehicle {
private Motor motor;
#Inject
public Vehicle(Motor motor){
this.motor = motor;
}
public void increaseSpeed(int value){
motor.accelerate(value);
}
public void stop(){
motor.brake();
}
public int getSpeed(){
return motor.getRpm();
}
}
I then created a VehicleModule.java class to define my provider:
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
#Module
public class VehicleModule {
#Provides
#Singleton
Motor provideMotor(){
return new Motor();
}
#Provides #Singleton
Vehicle provideVehicle(){
return new Vehicle(new Motor());
}
}
I then i have a interface component annotated, defined like this:
import javax.inject.Singleton;
import Modules.VehicleModule;
import dagger.Component;
#Singleton
#Component(modules = {VehicleModule.class})
public interface VehicleComponent {
Vehicle provideVehicle();
}
and here is my Android mainactivity class that should be injected but its not, can anyone help:
import javax.inject.Inject;
public class MainActivity extends ActionBarActivity {
#Inject
Vehicle vehicle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toast.makeText(this, String.valueOf(vehicle.getSpeed()), Toast.LENGTH_SHORT).show();
}
}
im getting a null pointer exception on vehicle:
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int com.example.uen229.myapplication.Vehicle.getSpeed()' on a null object reference
by the way my gradle dependencies look like this:
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.0'
compile 'com.google.dagger:dagger:2.0'
}
here is my Android mainactivity class that should be injected but its not
You're expecting magical things happen when you annotate something with #Inject. While magical things will happen, it's not that magical. You will need to do that yourself, by instantiating the component implementations that Dagger generated.
You can do this in a couple of ways, I will describe two.
First, in your MainActivity's onCreate:
private Vehicle vehicle; // Note, no #Inject annotation
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
VehicleComponent vehicleComponent = DaggerVehicleComponent.create();
this.vehicle = vehicleComponent.provideVehicle();
Toast.makeText(this, String.valueOf(vehicle.getSpeed()), Toast.LENGTH_SHORT).show();
}
In this case, you create an instance of VehicleComponent, implemented by Dagger, and fetch the Vehicle instance from it. The vehicle field is not annotated by #Inject. This has the advantage that the field can be private, which is a good thing to want.
Secondly, if you do want Dagger to inject your fields, you need to add an inject method to your VehicleComponent:
#Singleton
#Component(modules = {VehicleModule.class})
public interface VehicleComponent {
Vehicle provideVehicle();
void inject(MainActivity mainActivity);
}
In your MainActivity class, you call inject(this), which will fill the vehicle field:
#Inject
Vehicle vehicle; // Note, package-local
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
VehicleComponent vehicleComponent = DaggerVehicleComponent.create();
vehicleComponent.inject(this);
Toast.makeText(this, String.valueOf(vehicle.getSpeed()), Toast.LENGTH_SHORT).show();
}
This brings a bit of extra configuration, but is sometimes necessary.
I like the first method however.
As a final comment, let's have a look at your VehicleModule, and really use the power of Dagger.
Instead of using the module to create the instances yourself, you can make Dagger to that for you. You've already annotated the Vehicle constructor with #Inject, so Dagger will know to use this constructor. However, it needs an instance of Motor, which it doesn't know of. If you add an #Inject annotation to the constructor of Motor as well, and annotate the Motor class with #Singleton, you can get rid of the VehicleModule altogether!
For example:
#Singleton
public class Motor {
private int rpm;
#Inject // Just an annotation to let Dagger know how to retrieve an instance of Motor.
public Motor(){
this.rpm = 10; //default will be 10
}
public int getRpm(){
return rpm;
}
public void accelerate(int value){
rpm = rpm + value;
}
public void brake(){
rpm = 0;
}
}
Your Vehicle class:
#Singleton
public class Vehicle {
private Motor motor;
#Inject
public Vehicle(Motor motor){
this.motor = motor;
}
public void increaseSpeed(int value){
motor.accelerate(value);
}
public void stop(){
motor.brake();
}
public int getSpeed(){
return motor.getRpm();
}
}
You can now safely delete the VehicleModule class, and remove the reference to it in your VehicleComponent.
you are missing the following steps
you have create the module like this in application class
public class D2EApplication extends Applicaiton {
public static D2EComponent component(Context context) {
return ((D2EBaseApplication) context.getApplicationContext()).component;
}
public final static class DaggerComponentInitializer {
public static D2EComponent init(D2EBaseApplication app) {
return DaggerD2EComponent.builder()
.systemServicesModule(new VehicleModule(app))
.build();
}
}
}
and inject it to the activity
D2EApplication.component(this).inject(this);
My base POJO class:
public class BaseDao {
public BaseDao() {
}
// ...
}
My extends POJO class:
public class KelvinDao extends BaseDao {
public KelvinDao () {
super();
}
// ...
}
I want to use KelvinDao in a service like that:
public class HanKelvinHandler extends HttpRequestHandler {
#Inject
private KelvinDao mKelvinDao;
public void treatGet() {
mKelvinDao.blabla(); !!! mKelvinDao is always NULL
}
It's really simple but it doesn't work :(
Thank you guys for your help!
How are you creating HanKelvinHandler? If you're doing it within a subclass of a RoboGuice class, such as RoboActivity, then it should just work. Example:
public class MyActivity extends RoboActivity
{
#Inject
private HanKelvinHandler m_handler;
[...]
}
Otherwise (i.e., you're creating it within another POJO), you're in regular Guice land, and I believe you will need to use the injector to get an instance of it. Example:
public class MyClass
{
public void doSomething()
{
Injector injector = Guice.createInjector( new YourGuiceBindings() );
HanKelvinHandler handler = injector.getInstance( HanKelvinHandler.class );
handler.treatGet(); // mKelvinDao should be good now
}
}
If you haven't seen the use of the injector before, or you don't know what to put for YourGuiceBindings(), then you may need to read the following:
https://github.com/roboguice/roboguice/wiki/Simple-Custom-Binding
https://code.google.com/p/google-guice/wiki/GettingStarted
It seems like there should be a way to do this without using the injector, but I don't know.