Java NPE when trying to call method in non-activity class - java

I have googled here and there for two days already, but no solution seems not be working. Issue is within Fitness.getSessionsClient(this, Objects.requireNonNull(GoogleSignIn.getLastSignedInAccount(2ndclass.mContext))) and its passed activity (this) and context (2ndClass.mContext) variables, which I can populate (variables or not null or empty via debugger inspection) by many found approaches already. Still - all the time I get either NPE (java.lang.NullPointerException) or that getApplicationContext() is null. Any ideas?
Screenshot, if it helps:
Crash log line:
E/<SOME TAG>: Error during PUT connection... Data: {"coord":{"lon":-0.13,"lat":51.51},"weather":[{"id":803,"main":"Clouds","description":"broken clouds","icon":"04d"}],"base":"stations","main":{"temp":13.11,"pressure":1033,"humidity":77,"temp_min":10,"temp_max":16.11},"visibility":10000,"wind":{"speed":3.1,"deg":330},"clouds":{"all":69},"dt":1568357213,"sys":{"type":1,"id":1414,"message":0.0113,"country":"GB","sunrise":1568352700,"sunset":1568398909},"timezone":3600,"id":2643743,"name":"London","cod":200} Message: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference
Code pieces:
1st (MainActivity) class:
public class 1stClass extends AppCompatActivity {
public static 1stClass instance;
public static Context mContext;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
instance = this;
mContext = getApplicationContext();
}
public final void 1stmethod(){
2ndClass.2ndmethod();
}
/**
* #return instance
*/
#Contract(pure = true)
public static 1stClass getInstance() { return instance; } // return.getInstance();
public static Context getContext() {
// return instance.getApplicationContext();
return mContext;
}
}
2nd class:
public class 2ndClass extends 1stClass{
static 2ndClass instance;
public final void 2ndmethod() {
instance = this;
3rdClass MFP = new 3rdClass();
MFP.3rdmethod(..);
}
/**
* #return instance
*/
#Contract(pure = true)
public static 2ndClass getInstance() { return instance; } //return.getInstance();
}
Non-activity (3rd) class:
public final class 3rdClass extends 2ndClass {
static 3rdClass instance;
public void 3rdmethod() {
instance = this;
Fitness.getSessionsClient(this, Objects.requireNonNull(GoogleSignIn.getLastSignedInAccount(2ndclass.mContext))) // <---- ERROR HERE
.insertSession(request)
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.i(TAG, "Session insert was successful!");
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.wtf(TAG, "There was a problem inserting the session: " +
e.getLocalizedMessage());
}
});
/**
* #return instance
*/
#Contract(pure = true)
public static MFPalInterface getInstance() {
return instance;
} // return.getInstance();
}

I think I understand what you're doing, even though the code is one great mess.
You are trying to create your own activity in your 2ndClass's method '2ndmethod()' and then call the method on that newly created 3rdClass.
public final void 2ndmethod() {
3rdClass MFP = new 3rdClass();
MFP.3rdmethod(..);
}
Even though you've extended 3rdClass from your AppCompatActivity, you've not asked the system to create the Activity instance and attempted to create it yourself. This means the newly created instance has no context and the system does not know about it.
What You Need to Do
Start the activity by calling the startActivity() method of your current running Activity (in this case 2ndClass I think) and by passing an intent. Like so:
Intent intent = new Intent(this, 3rdClass.class); // this is your current Activity's reference.
currentActivity.startActivity(intent);

Related

Android asks for not to query on main thread, but asyc queries are delaying

As the title says, android needs queries out of main thread since it will trhow java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time otherwise. So I managed to make async queries as many tutorials explain, but it doesn't make so much sense (so far) as I could achieve.
public class NewDetalleDiarioActivity extends AppCompatActivity {
#Override
protected void onStart() {
super.onStart();
db = Room.databaseBuilder(getApplicationContext(), AppDatabase.class, "database").build();
findPeriodo();
findDiario();
}
private void findPeriodo() {
periodo = Diarios.getPeriodo(db);
if (periodo == null) {
Intent intent = new Intent(NewDetalleDiarioActivity.this, NewPeriodoActivity.class);
startActivity(intent);
}
}
PROBLEM/ERROR:
If periodo is null, another activity is started, otherwise this one continues its thread.
The problem is that, when I debug it (which slows proceses, of course) periodo returns an instance from the database, but when I run the code without debugging, periodo is null.
public class Diarios {
public static Periodo getPeriodo(AppDatabase db) {
return Factory.getIntPeriodo().getPeriodo(db);
}
}
.
public class Factory {
private static IntPeriodo intPeriodo;
public static IntPeriodo getIntPeriodo() {
return (intPeriodo == null) ? intPeriodo = new BusPeriodo() : intPeriodo;
}
}
.
public class BusPeriodo implements IntPeriodo {
// I don't think it's necessary to post the interface...
#Override
public Periodo getPeriodo(final AppDatabase db) {
final Periodo[] periodo = new Periodo[1];
AsyncTask.execute(new Runnable() {
#Override
public void run() { //the async query that is driving me mad.
periodo[0] = db.periodoDao().getPeriodo(new Date());
}
});
return periodo[0];
}
}
What's the proper way to make select queries without getting them delayed?
The select query is indeed working, I don't think is necessary to post it (because it is returning an unique result when I debug), but it returns null when I run the code without debugging!! Please help.
SOLUTION:
As #user7041125 suggested, but instead I made a new class with an interface to call methods back to the activity, like this:
public class PeriodoBridge extends AsyncTask<Void, Void, Periodo> implements IntPeriodoBridge {
private WeakReference<Activity> weakActivity;
private IntPeriodoBridge caller; //implement this interface in the activity which needs to query
private AppDatabase db;
private Periodo periodo;
public PeriodoBridge(Activity activity, IntPeriodoBridge caller, AppDatabase db) {
weakActivity = new WeakReference<>(activity);
this.caller = caller; // assign activity instance to the local interface instance
this.db = db;
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
#Override
protected Periodo doInBackground(Void... voids) {
periodo = Diarios.getPeriodo(db);
return periodo;
}
#Override
protected void onPostExecute(Periodo periodo) {
Activity activity = weakActivity.get();
if (activity == null) {
return;
}
if (periodo == null) {
Intent intent = new Intent(activity, NewPeriodoActivity.class);
activity.startActivity(intent);
} else {
setPeriodo(periodo);
}
}
#Override //this is an interface method (IntPeriodoBridge)
public void setPeriodo(Periodo periodo) {
caller.setPeriodo(periodo); //I can set the query result back to the activity class with this
}
Call the init method of this class. The activity implements IntPeriodoBridge and in that way I can set the query result object to the activity class.

Fix activity leak when listener keeps implicit reference

In the MessageFeedActivity onCreate method it load feeds by calling getMessageTypes method of CTFeedAPI class.
public class MessageFeedActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting the listener
CTFeedAPI ctFeedAPI = new CTFeedAPI(new CTFeedAPI.CTFeedAPIListener() {
#Override
public void feedAPISuccessListener(Object object) {
// Handle Success
}
#Override
public void feedAPIErrorListener(int error) {
// Handle Error
}
});
ctFeedAPI.getMessageTypes();
}
#Override
protected void onDestroy() {
super.onDestroy();
}
}
and wait for CTFeedAPIListener response. And CTFeedAPI class make network request by calling performRequest method of NetworkRequest class as
public class CTFeedAPI implements NetworkListener {
private CTFeedAPIListener apiListener;
public CTFeedAPI(CTFeedAPIListener feedAPIListener) {
apiListener = feedAPIListener;
}
public void getMessageTypes() {
Map < String, String > params = new HashMap < > ();
params.put("f", "GetMessageTypes");
NetworkRequest networkRequest = new NetworkRequest(this);
networkRequest.performRequest();
}
public interface CTFeedAPIListener {
void feedAPISuccessListener(Object object);
void feedAPIErrorListener(int error);
}
}
and wait for NetworkListener response
public class NetworkRequest {
private NetworkListener mListener;
public interface NetworkListener {
void networkReqSuccessListener(String cacheKey, String tag, String response);
void networkReqErrorListener(String tag, int error);
}
public NetworkRequest(NetworkListener listener) {
this.mListener = listener;
}
public void performRequest(
// Perform Network Requests and respond as
if (mListener != null) {
if (success) {
mListener.networkReqSuccessListener(getUrl(), getTag(), response);
} else {
mListener.networkReqErrorListener(getTag(), err_msg);
}
}
}
When users press back key, before destroy the MessageFeedActivity, the system call 'onDestroy' method. And Unfortunately, because the background thread (performRequest method in NetworkRequest class) is still keep a reference to it, leak occurs.
So how to implement CTFeedAPIListener reference in MessageFeedActivity to remove leak.
In this design not only you will leak memory but also your code would be highly coupled and very hard to test; prone to bugs that are hard to detect. I would suggest you implement MVP or similar architecture. Your activity should never know anything about your network layer. Add a presenter layer that is responsible to request something on behalf of your activity and use interface to update your activity. Your presenter should access a business entity that is mapped from the response of repository layer, that is responsible for network or Db access and return values to the client presenter. This way your presenter and business logic layers would be decoupled and easy to test independently. In the future if business requirements change, your changes don't affect other layers. Please see this article for more information on the subject.
Weak reference objects, which do not prevent their referents from
being made finalizable, finalized, and then reclaimed. Weak references
are most often used to implement canonicalizing mappings.
Suppose that the garbage collector determines at a certain point in
time that an object is weakly reachable. At that time it will
atomically clear all weak references to that object and all weak
references to any other weakly-reachable objects from which that
object is reachable through a chain of strong and soft references. At
the same time it will declare all of the formerly weakly-reachable
objects to be finalizable. At the same time or at some later time it
will enqueue those newly-cleared weak references that are registered
with reference queues.
You can use Weak Reference:
import java.lang.ref.WeakReference;
public class NetworkRequest {
public interface NetworkListener {
void networkReqSuccessListener(String cacheKey, String tag, String response);
void networkReqErrorListener(String tag, int error);
}
private WeakReference<NetworkListener> mListener;
public NetworkRequest(NetworkListener listener) {
this.mListener = new WeakReference<NetworkListener>(listener);
}
public void performRequest(){
// Perform Network Requests and respond as
NetworkListener listener = mListener.get();
if (listener != null) {
if (success) listener.networkReqSuccessListener(getUrl(), getTag(), response);
else listener.networkReqErrorListener(getTag(), err_msg);
}
}
}
public class CTFeedAPI implements NetworkListener {
private WeakReference<CTFeedAPIListener> apiListener;
public CTFeedAPI(CTFeedAPIListener feedAPIListener) {
apiListener = new WeakReference<>(feedAPIListener);
}
public void getMessageTypes() {
Map < String, String > params = new HashMap < > ();
params.put("f", "GetMessageTypes");
NetworkRequest networkRequest = new NetworkRequest(this);
networkRequest.performRequest();
}
public interface CTFeedAPIListener {
void feedAPISuccessListener(Object object);
void feedAPIErrorListener(int error);
}
}
save CTFeedAPI and CTFeedAPIListener as instance variable of MessageFeedActivity to prevent GC collecting them when activity is presented:
public class MessageFeedActivity extends AppCompatActivity {
private CTFeedAPI ctFeedAPI = null;// keeping a reference to CTFeedAPI
private CTFeedAPIListener listener = null;// keeping a reference to listener
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Setting the listener
listener = new CTFeedAPI.CTFeedAPIListener() {
#Override
public void feedAPISuccessListener(Object object) {
// Handle Success
}
#Override
public void feedAPIErrorListener(int error) {
// Handle Error
}
});
ctFeedAPI = new CTFeedAPI(listener);
ctFeedAPI.getMessageTypes();
}

Multiple Activities Implementing Same Listener

I know there are similar questions but it still doesn't answer my question in the manner I need for my current situation.
I have three activity presenters that each need to call a certain data remotely which will therefore call the activity presenter back when data arrives. To create this data listener I created an interface listener and since all three Presenters ask for the same data and retrieve it, all three presenters implement this interface listener.
Interface listener:
interface ListenerInterface {
onGotData();
}
Presenter one:
class PresenterOne implements ListenerInterface{
public void getData() {
DataManager dataManager = new DataManager();
dataManager.getData(this);
}
#Override
public void onGotData(Data data) {
//Do something with data
}
}
Presenter two very similar to presenter one:
class PresenterTwo implements ListenerInterface{
public void getData() {
DataManager dataManager = new DataManager();
dataManager.getData(this);
}
#Override
public void onGotData(Data data) {
//Do something with data
}
}
Assume Presenter three is exactly the same as the previous. The data manager class:
class DataManager {
public void getData(final ListenerInterface listener) {
//Gets data
addOnCompleteListener(new OnCompleteListener<Data data > () {
#Override
public void onComplete (#NonNull DataCall < Data > dataCall) {
listener.onGotData(dataCall.getResults());
}
});
}
}
Would doing so someone call all three presenters since the interface is the one doing the calling or only call the presenter that is passed? Is there anything I should worry about if I followed way? If anyone who knows the Android framework well could provide a detailed answer so I could learn from it more that would be great.
The reason I want to do this is I want to communicate through interfaces between classes.
Sorry if this question is simple for some people but I am still learning.
Thank you very much in advance.
you can use RxBus implementation to make global event (e.g. your onGotData).
First you have to create RxBus class.
public class RxBus {
private static RxBus instance;
private PublishSubject<Event> subject = PublishSubject.create();
public static synchronized RxBus getInstance() {
if (instance == null) {
instance = new RxBus();
}
return instance;
}
private RxBus(){}
public void postEvent(Event event){
subject.onNext(event);
}
public Observable<Event> getEvents(){
return subject;
}
}
And now, you should subscribe to it in BaseActivity or something like this (depends or your project structure).
private RxBus rxbus;
private Subscription rxBusSubscription;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rxBus = RxBus.getInstance();
}
#Override
protected void onResume() {
super.onResume();
if (shouldSubscribeRxBus()) {
rxBusSubscription = rxBus.getEvents()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(event -> {
if (event.getEventType() == Event.EventType.DATA_RECEIVED) {
onGotData(data);
}
});
}
}
Now implement you onGotData as you want.
When you catch data received call:
class DataManager {
public void getData(final ListenerInterface listener) {
//Gets data
addOnCompleteListener(new OnCompleteListener<Data data > () {
#Override
public void onComplete (#NonNull DataCall < Data > dataCall) {
RxBus.getInstance().postEvent(new GotDataEvent(dataCall.getResults()));
}
});
}
}
You can create your Event classes structure as you want.

Need to use SQLiteOpenHelper in a Jersey REST service but I cannot pass required Android context

I am trying to replace in-memory data with data from a database, which will be the data that is used in a REST API. The API is a service running on an Android box. In order to use SQLiteOpenHelper I must pass a Context to the class that extends it. Unfortunately, the thread that handles the REST API does not instantiate a class to which I can pass down Context, rather it configures the API as such:
public static void createServer(){
new Thread(new Runnable() {
#Override
public void run() {
URI baseUri = UriBuilder.fromUri("http://localhost/").port(8080).build();
ResourceConfig config = new ResourceConfig(ThingResource.class);
config.register(GsonMessageBodyHandler.class);
config.register(AndroidFriendlyFeature.class);
JettyHttpContainerFactory.createServer(baseUri, config);
}
}).start();
}
Here's the class that requires Context for me to use:
public class DbHelper extends SQLiteOpenHelper {
private final String DB_NAME = DatabaseContract.DATABASE_NAME;
private SQLiteDatabase db;
public DbHelper(Context context) {
super(context, DatabaseContract.DATABASE_NAME, null, DatabaseContract.DATABASE_VERSION);
}
//rest of class handles db methods
}
Here is where createServer() is called:
public class RoomStatus extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
JerseyBootstrap.createServer();
//rest of method/class set up main screen
}
}
How can I either pass Context to the REST service so I can use DbHelper or use some other method to get data from SQLite database?
Why dont you try this:
public static void createServer(Context context){
new Thread(new Runnable() {
#Override
public void run() {
URI baseUri = UriBuilder.fromUri("http://localhost/").port(8080).build();
ResourceConfig config = new ResourceConfig(ThingResource.class);
config.register(GsonMessageBodyHandler.class);
config.register(AndroidFriendlyFeature.class);
JettyHttpContainerFactory.createServer(baseUri, config);
}
}).start();
}
And call it this way:
public class RoomStatus extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
JerseyBootstrap.createServer(this);
//rest of method/class set up main screen
}
}
Since ThingResource is a static class, I just set the context from my main activity.

How to call Activity method from Presenter in Clean architecture?

I am developing an app where I am using clean architecture. In presenter, when something comes to method onCompleted then I must call function from Main activity.
this is my Presenter:
public class CheckInPresenter implements Presenter {
UseCase postCheckInUseCase;
Context context;
#Inject
CheckInPresenter(#Named("putCheckIn") UseCase postCheckInUseCase){
this.postCheckInUseCase = postCheckInUseCase;
}
public void initialize(){this.initializeCheckIn();}
public void initializeCheckIn(){this.putCheckIn();}
public void putCheckIn(){
this.postCheckInUseCase.execute(new CheckInSubscriber());
}
#Override
public void resume() {
}
#Override
public void pause() {
}
#Override
public void destroy() {
}
private final class CheckInSubscriber extends DefaultSubscriber<EventEntity>{
#Override
public void onCompleted() {
Log.d("onCompleted", "OnCompleted");
}
#Override
public void onError(Throwable e) {
Log.d("onError", "OnError: " + e.getMessage());
}
#Override
public void onNext(EventEntity eventEntity) {
Log.d("onNext", "OnNext");
}
}
}
And this is my function from MainActivity that I have to call:
public void getDataForToolbar() {
SharedPreferences sharedPreferences = getSharedPreferences(getResources().getString(R.string.Account_json), Context.MODE_PRIVATE);
final String account = sharedPreferences.getString(getResources().getString(R.string.account_json), null);
if (account != null) {
Gson gson = new Gson();
mAccount = gson.fromJson(account, AccountModel.class);
for (CompanyModel com : mAccount.getCompanies()) {
String name = com.getName();
company_name.setText(name);
logo_url = com.getLogo_url();
}
if (logo_url == null || logo_url.isEmpty()) {
Picasso
.with(this)
.load(R.drawable.default_company)
.resize(70, 58)
.transform(new RoundedTransformation(8, 0))
.into(toolbarImage);
} else {
picassoLoader(this, toolbarImage, logo_url);
}
String username = mAccount.getUsername();
if(mAccount.getStatus()){
aUsername.setText(username + "/" + getResources().getString(R.string.on_duty));
aUsername.setBackgroundColor(ContextCompat.getColor(mContext, R.color.colorGreen));
}else{
aUsername.setText(username + "/" + getResources().getString(R.string.off_duty));
aUsername.setBackgroundColor(ContextCompat.getColor(mContext, R.color.colorWhite));
}
}
}
Could someone helps me how to call this function into my onCompleted method in Presenter? Thanks in advance.
If you want to call some Activity's function from another object, you'll have to pass Activity's reference to that object. This means that you need to add Activity parameter to presenter's constructor.
Side note
I know that what you're implementing is called a "clean architecture" in many places (including the official MVP tutorials by Google), but you might want to read my posts on the subject in order to get an alternative view on what "clean" on Android should look like.
Why activities in Android are not UI elements
MVC and MVP architectural patterns in Android
Create interface IView and make your Activity to implement it.
In IView create method void getDataForToolbar();
I see #Inject annotation in your CheckInPresenter, so if you are using Dagger 2, update you Dagger module's constructor with IView, create a provider method for it and provide it for CheckInPresenter in this module.
Place IView in constructor of CheckInPresenter and update provider method for CheckInPresenter in Dagger module and Dagger component initialization in your Activity.
For example, it might look like this:
public class YourActivity implements IView {
...
}
#Module
public class YourModule {
private IView view;
public YourModule(IView view) {
this.view = view;
}
#Provides
public IView provideView() {
return view;
}
#Provides
#YourScope
public Presenter providePresenter() {
return new YourPresenter(view);
}
}
Just complete your existing Presenter and Module with IView.
After that call in your onCompleted method view.getDataForToolbar().

Categories

Resources