Call a non static method from a different class in Java - java

I'm working on an Android app, but am stuck at a problem I can't seem to find an answer to. I want to call the method "updateTime" in class "MainActivity" from method "run" (thus calling it from a Thread) in class "TaskHandler".
I've Googled for the answer for about an hour now, visited multiple websites and found multiple solutions, of which non worked for me. I have also asked about it in the LinusTechTips and Corsair discord servers.
MainActivity class:
package thedutchmc.net.alarm;
import android.os.Bundle;
import android.widget.EditText;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.time.LocalTime;
public class MainActivity extends AppCompatActivity {
public static String alarmTime;
public static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3);
public static boolean alarmBool = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final TextView mTextView = (TextView) findViewById(R.id.currentTime);
mTextView.setText("Current Time: ");
Main.scheduler.scheduleAtFixedRate(new TaskHandler(), 1, 1, TimeUnit.SECONDS);
}
public void onSubmit(View v){
System.out.println("Submit!");
EditText alarmTimeEditText = (EditText) findViewById(R.id.setAlarmTime);
alarmTime = alarmTimeEditText.getText().toString();
System.out.println("MainActivity (alarmTime): " + alarmTime);
alarmBool = true;
}
public void updateTime() {
TextView seeTime = (TextView) findViewById(R.id.currentTime);
seeTime.setText(LocalTime.now().toString());
}
}
TaskHandler class:
package thedutchmc.net.alarm;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
public class TaskHandler implements Runnable {
final DateTimeFormatter dtf = DateTimeFormatter.ofPattern("HH:mm");
public static boolean isRinging = false;
private String alarmTime;
public static final MainActivity activity = new MainActivity();
#Override
public void run() {
activity.updateTime();
if (checkAlarmBool()) {
System.out.println("Bool true! Alarm!");
Main.alarm.set(false);
Main.alarm.ringAlarm();
}
}
boolean checkAlarmBool() {
if (MainActivity.alarmBool && !isRinging) {
String lTime = LocalTime.now().format(dtf);
System.out.println("TaskHandler alarmTime: " + MainActivity.alarmTime);
System.out.println("TaskHandler LocalTime: " + lTime);
if(lTime.equalsIgnoreCase(MainActivity.alarmTime)) {
isRinging = true;
return true;
} else {
return false;
}
} else {
return false;
}
}
I hope someone can help me :)

Make TaskHandler an inner class inside MainActivity. Then you'll be able to call updateTime(). And drop that static final MainActivity variable, you won't need it if TaskHandler is inside MainActivity. Never create activities with the new operator.
One other thing you'll probably run into, you can't update UI from a background thread, so you'll probably want to use runOnUiThread(Runnable) either when calling updateTime() or inside updateTime().

You can't directly access an Activity's methods outside of the Activity itself. The recommended way to communicate among components is to use Broadcasts.
You do have direct access to your Activity, because you instantiate the TaskHandler from it. You could just pass your Activity in the constructor of TaskHandler (and save it as a global variable in the TaskHandler), but this might lead to crashes if that Activity is finished before TaskHandler executes.
Add a Context to TaskHandler's constructor:
private Context context;
public TaskHandler(Context context) {
this.context = context;
}
and instantiate the TaskHandler with
new TaskHandler(getApplicationContext());
and you'll be able to send a Broadcast that you can then receive in a BroadcastReceiver registered inside your Activity (read the link for details on BroadcastReceivers and such).
Replace Context with MainActivity, and getApplicationContext() with this, and you can just directly call the method you want, but that can cause crashes, and this will only work if TaskHandler is only used inside MainActivity.
If it is only used inside MainActivity, just make it an inner class, and then you can call the method directly, without any reference.
No matter what you do, you can't make a new instance of Activity classes yourself and expect them to work.

Use Broadcasts.
In Your TaskHandler class, from inside "run" method, send broadcast:
Intent i = new Intent("run_method");
sendBroadcast(i);
In Your MainActivity class, in onResume(), register Broadcast Receiver:
private BroadcastReceiver receiver;
if (receiver == null){
receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
updateTime();
}
};
}
registerReceiver(receiver, new IntentFilter("run_method"));

Related

.setText() being called but screen not updateing while in a while(true) loop

I've been running into this bug and I can't seem to figure out how to fix it as I'm fairly new to java and android development so I would really appreciate any help I could get with this!
The bug I'm running into is that when I'm using .setText() to update a TextView element periodically the text displayed on screen never actually changes.
I believe this is due to the while(true) loop in the startCrunching() method I'm using to run the main calculation process as before I start that method the screen updates fine with the test data I'm feeding it.
I also know that when the while loop starts the updateScreen() method is only being called from the while loop and not the repeating handler I have as the handler stops posting logs to the logcat when the method starts but then the while loop logs start being posted.
What I want to achieve is the while(true) loop running as quickly as possible while every so often (as a variable of time and not cycles of the while loop) updating the screen with information regarding the process in the while loop.
I know the while loop is running and the updateScreen() method is being called.
Full source below:
package com.example.android.collatzconjecturepathcruncher;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import java.math.BigInteger;
import java.util.concurrent.TimeUnit;
public class MainActivity extends AppCompatActivity {
TextView longestPathDisplay;
TextView longestPathSeedDisplay;
TextView currentSeedDisplay;
EditText startingNumberDisplay;
BigInteger longestPathSeed= BigInteger.ONE;
int longestPath=0;
BigInteger currentSeed=BigInteger.ZERO;
int currentPath=0;
BigInteger workingSeed=BigInteger.ONE;
boolean run;
int temp =0;
private Handler mHandler;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
longestPathDisplay = findViewById(R.id.longest_path);
longestPathSeedDisplay = findViewById(R.id.longest_path_seed);
currentSeedDisplay = findViewById(R.id.current_seed_display);
startingNumberDisplay = findViewById(R.id.starting_number_display);
longestPathDisplay.setText(getString(R.string.longest_path_display,longestPath));
longestPathSeedDisplay.setText(getString(R.string.longest_path_seed_display,longestPathSeed));
currentSeedDisplay.setText(getString(R.string.current_seed_display,currentSeed));
mHandler = new Handler();
startRepeatingTask();
}
#Override
public void onDestroy(){
super.onDestroy();
stopRepeatingTask();
}
public void startCrunching(View view){
String value = startingNumberDisplay.getText().toString();
currentSeed = new BigInteger(value);
workingSeed=currentSeed;
run=true;
while(run){
if(workingSeed.compareTo(BigInteger.ONE)==0){
if(currentPath>longestPath){
longestPath=currentPath;
longestPathSeed=currentSeed;
}
currentSeed= currentSeed.add(BigInteger.ONE);
workingSeed=currentSeed;
Log.d("end", "startCrunching: Finished "+(currentSeed.subtract(BigInteger.ONE))+" at "+currentPath+". Starting "+currentSeed);
currentPath=0;
updateScreen();
}
if (workingSeed.mod(new BigInteger("2")).compareTo(BigInteger.ZERO)==0){
workingSeed=workingSeed.divide(new BigInteger("2"));
}else{
workingSeed=(workingSeed.multiply(new BigInteger("3"))).add(BigInteger.ONE);
}
currentPath++;
}
}
public void updateScreen() {
//longestPathDisplay.setText(getString(R.string.longest_path_display, longestPath));
//longestPathSeedDisplay.setText(getString(R.string.longest_path_seed_display, longestPathSeed));
//currentSeedDisplay.setText(getString(R.string.current_seed_display, currentSeed));
longestPathDisplay.setText(getString(R.string.longest_path_display, temp));
longestPathSeedDisplay.setText(getString(R.string.longest_path_seed_display, temp));
currentSeedDisplay.setText(getString(R.string.current_seed_display, temp));
Log.d("update","requested screen update. Temp currently: "+temp);
temp++;
}
Runnable mStatusChecker = new Runnable() {
#Override
public void run() {
try{
updateScreen();
Log.d("repeat","Tried Updating Screen");
}finally {
mHandler.postDelayed(mStatusChecker,5000);
}
}
};
void startRepeatingTask(){
mStatusChecker.run();
}
void stopRepeatingTask() {
mHandler.removeCallbacks(mStatusChecker);
}
}
Thanks in advance!
-Michael
Maybe I missed it, but I Don't see where you actual call your crunchnumber method.
startCrunching() this method is never called. I guess you need to change you execution sequence

How to call a Main activity method in another non activity class

Below is My Main activity with ColorChange method.I want to call this Colorchange method in ImageColor Class
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void ColorChange() { // <----- Want to call this method in below class
ImageView blue = (ImageView) findViewById(R.id.imageView);
blue.setColorFilter(0xff000000);
}
}
And this is my class where i want to call the ColorChange method of Mainactivity.
public class ImageColor {
public void Imager() {
// Want to call my ColorChange method here
MainActivity obj = new MainActivity();
obj.ColorChange(); //<-------- Using mainactiviy object crashes my app.
}
}
I have already tried using Mainactivity as object it crashes my app.I also cannot declare my ColorChange method static because it uses findViewbyid.Please let me know if there is any way to call Color change method in this Image Color Class.
Try this way. It will help you.
public class ImageColor {
public void Imager(Activity activity) {
// Want to call my ColorChange method here
if(activity instance of MainActivity)
((MainActivity)activity).ColorChange(); //<-------- Using mainactiviy object crashes my app.
}
}
use interface to communicate with activity from non activity class. create colorChange() in interface and get the instance of interface in non activity class and call that method
class MainActivity {
interface mInterface = new interface() {
public void colorChange(){
}
}
}
pass mInterface to non activity class and call colorChange of interface when you want ..
You have to pass activity as a parameter in ImageColor class
Then call your ColorChange() method by refference of Activity.
Like This-
public class ImageColor {
Activity activity;
public ImageColor(Activity activity)
{
this.activity = activity;
}
public void Imager()
{
if(activity instance of MainActivity)
((MainActivity)activity).ColorChange();
}
}
Activity classes are created by Android. So the above method is not correct.
You have 2 ways to access the method in activity.
1 . using a static method
public static void ColorChange() {
ImageView blue = (ImageView) findViewById(R.id.imageView);
blue.setColorFilter(0xff000000);
}
}
Using a callback mechanism
public interface ImageLoadedcallback{
void onColorChanged(int color);
}
And update
public class ImageColor {
public void Imager(ImageLoadedcallback callback) {
callback.onColorChanged(color)
}
}
And In activity
public void ColorChange() {
new ImageLoader().Imager(new ImageLoadedcallback{
#Override
public void onImageLoaded(Color color){
ImageView blue = (ImageView) findViewById(R.id.imageView);
blue.setColorFilter(0xff000000);
});
}
To make it clear, make an Activity as a static variable can lead to Activity leak, so we must avoid doing that.
I suppose if the Activity where you create ImageColor object is MainActivity, you can pass MainActivity directly to achieve what you want.
public class ImageColor {
public void Imager(MainActivity activity) {
activity.ColorChange();
}
}
If you called it from other class(not from MainActivity), you can always passing MainActivity to that other class object to be used for ImageColor object.
PS: Check about java naming convention too, it will help you to write a better code

reverse geocoding getPostalCode on Launch

I’m an experienced AS3 developer and I’ve done quite some stuff with Java for my backends but I’m new to Native Android development so I’m having troubles with some basic Tasks for my first Project.
So hope one of you cracks can help me out here or point me in the right directions, it would be much appreciated and I’ll repay be helping out in the AS3 section. That briefly about me, since it’s my first post. ;)
The task at hand is to get the users postcode on application launch. I’ve been using an AsyncTask for the reverse geocoding and It generally seems to work. But only when I call the ReverseGeocodingTask on a button click, and give it a few seconds before I do so. If I press it immediately it sometimes works and sometimes doesn’t, so obviously when I call it in the onCreate method the app crashes aswell. It also crashes when I turn the internet off on the phone. I reckoned the network provider location should be sufficient and there is no need for the GPS accuracy and the additional permissions.
If the INet is turned off by the user, it should just show a message that the postcode can’t be found and give the user the option to input it manually.
I figured that the currentLocation to pass to the geocoding has not been found yet and is throwing a NullPointerException, so I tried to prevent that by checking it before the call. But that didn’t really help and is no solution for the final version anyways.
Since its always best to show the code so u guys know what’s going on, here goes:
package com.adix.DroidTest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.location.*;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import java.io.IOException;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.atomic.AtomicReference;
import static java.util.Locale.getDefault;
public class MyActivity extends Activity implements View.OnClickListener {
Button getPostCode, confirm;
TextView tvPostcode;
LocationManager locationManager;
Location currentLocation;
double currentLatitude;
double currentLongitude;
private Handler mHandler;
private static final int UPDATE_ADDRESS = 1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
init();
locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
AtomicReference<LocationListener> locationListener = new AtomicReference<LocationListener>(new LocationListener() {
public void onLocationChanged(Location location) {
updateLocation(location);
}
private void updateLocation(Location location) {
currentLocation = location;
currentLatitude = currentLocation.getLatitude();
currentLongitude = currentLocation.getLongitude();
}
public void onStatusChanged(
String provider, int status, Bundle extras) {
}
public void onProviderEnabled(String provider) {
}
public void onProviderDisabled(String provider) {
}
});
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener.get());
//getAddress();
mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE_ADDRESS:
tvPostcode.setText((String) msg.obj);
break;
}
}
};
}
private void init() {
getPostCode = (Button)findViewById(R.id.bGetPostCode);
confirm = (Button)findViewById(R.id.bConfirm);
tvPostcode = (TextView)findViewById(R.id.tvPostcode);
getPostCode.setOnClickListener(this);
confirm.setOnClickListener(this);
}
#Override
public void onClick(View view) {
switch (view.getId()){
case R.id.bGetPostCode:
currentLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
if(currentLocation != null) {
Log.d("TRACE",currentLocation.toString());
Toast.makeText(this, "Suche Postleitzahl", Toast.LENGTH_LONG).show();
(new ReverseGeocodingTask(this)).execute(new Location[]{currentLocation});
}
break;
case R.id.bConfirm:
Intent i = new Intent(MyActivity.this, MainMenu.class);
startActivity(i);
finish();
}
}
private class ReverseGeocodingTask extends AsyncTask<Location, Void, Void> {
Context mContext;
public ReverseGeocodingTask(Context context) {
super();
mContext = context;
}
#Override
protected Void doInBackground(Location... locations) {
try{
Geocoder gcd = new Geocoder(mContext, Locale.getDefault());
List<Address> addresses = gcd.getFromLocation(currentLatitude, currentLongitude,100);
Address address = addresses.get(0);
StringBuilder result = new StringBuilder();
result.append(address.getPostalCode());
// tvPostcode.setText(result.toString());
Message.obtain(mHandler, UPDATE_ADDRESS, result.toString()).sendToTarget();
}
catch(IOException ex){
tvPostcode.setText(ex.getMessage().toString());
Message.obtain(mHandler, UPDATE_ADDRESS, ex.getMessage().toString()).sendToTarget();
}
return null;
}
}
}
I gave this a rest since this post to see if someone sees my mistake. Since I hadn't got an answer, I gave it another shot today. And fortunately found the answer quite quick in the end. Obviously I needed to execute the ReverseGeocodingTask in the onLocationChanged method after updateLocation.

Android: Reference to Activity object in another class becomes NULL

What i am trying to do is to pass a reference to the mainactivity to another class and use it.
when i first pass the reference it is not null. but later when i use it in an event handler it becomes null.
here is the code:
public class MainActivity extends MapActivity implements SmsReceivedListener {
SmsReceiver smsreceiver = new SmsReceiver();
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
smsreceiver.setSmsReceivedListener(this);
}
public void drawme() {
// do smth
}
#Override
public void onSmsReceived(String id, String lon, String lat) {
Toast.makeText(this, "SMS delivered", Toast.LENGTH_LONG).show();
}
}
The other class where I obtain the reference:
public class SmsReceiver extends BroadcastReceiver {
private SmsReceivedListener listener;
private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
#SuppressLint("NewApi")
#Override
public void onReceive(final Context context, final Intent intent) {
// Here it is NULL
if(listener!=null)
listener().onSmsReceived(carNumber, lon,lat);
}
public void setSmsReceivedListener(SmsReceivedListener mainActivity) {
//It is no null when I first set the object here
listener = (mainActivity);
}
}
It will only "become" null if you
set it to null
use it in a way which is not thread safe and you are reading in a different thread to setting
you are using the same field, but in a different object.
You are not registering the SmsReceiver, therefor i guess the following :
The SmsReceiver is registered in the Manifest. Therefore, an instance of the receiver is created by the system when the broadcast is detected, and it is not the same instance as the one you create.
Suggestion : register the receiver in the activity, not in the manifest.

How to run a background application in countdowntimer using threading concept?

I want to make a background application in countdowntimer, that is if i am starting
the timer and comes out of that application, and going to another application then
coming back to that same countdowntimer application. i want that timer to be
running until i stop. I know about the methods involved in it, but i am not sure
about the threading concepts used in it.
//MyActivity.class
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MyappActivity extends Activity
{
private static final String Tag = "Background_Timer";
private Button start;
private Button stop;
private TextView tv;
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
start = (Button) findViewById(R.id.button);
stop = (Button) findViewById(R.id.button1);
tv = (TextView) findViewById(R.id.text1);
this.runOnUiThread(new Runnable()
{
public void run()
{
tv.setText(MyAppService.seconds + " Seconds Left");
}
});
}
public void onClick(View src)
{
switch (src.getId())
{
case R.id.button:
Log.e(Tag, "onClick: starting service");
startService(new Intent(this, MyAppService.class));
break;
case R.id.button1:
Log.e(Tag, "onClick: stopping service");
stopService(new Intent(this, MyAppService.class));
break;
}
}
}
//MyService.class
import android.app.Service;
import android.content.Intent;
import android.os.CountDownTimer;
import android.os.IBinder;
import android.util.Log;
import android.widget.TextView;
public class MyAppService extends Service
{
private static final String TAG = "My Service";
private static boolean state;
private static TextView TextTimer;
public static String seconds;
MyThread mt = new MyThread();
#Override
public void onCreate()
{
CountDownTimer Myapp = new CountDownTimer(50000, 1000)
{
public void onTick(long millisUntilFinished)
{
TextTimer.setText("Seconds left: " + (millisUntilFinished)
/ 1000);
}
public void onFinish()
{
TextTimer.setText("Finished!");
}
};
}
public void onStart(Intent intent, int startid)
{
Log.d(TAG, "on-Start");
mt.start();
}
public void onDestroy()
{
Log.d(TAG, "on-Stop");
mt.stop();
}
public static class MyThread extends Thread
{
public void run()
{
try
{
while (state = true)
{
MyThread.sleep(500);
}
}
catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
};
}
}
#Override
public IBinder onBind(Intent intent)
{
// TODO Auto-generated method stub
return null;
}
}
Have a static Handler in Your Activity which receives your Message of Tick and Finish
In the Service have a Thread which start the CountDown So that your CountDown will be working at your Thread not in the Main Thread.
You can use TimerTask Class for such purpose. It is same as thread , but allows you to perform a task based on Time Interval. you can also perform repeatable task in its run method.
Please check simple example here.
First off: You shouldn't run the CountDownTimer in the applications UI thread, since that could cause ANRs (Application not responding) issues.
You can use the SheduledThreadPoolExecutor with your custom Runnable or use a Bound Service. To call back the actual activity (if it's running, you could use different methods like subscribing via the Observer Pattern or send around Intents using the LocalBroadcastManager and then update the UI in it's own thread.
You may also use TimerTask as Lucifer has pointed out, but make sure the Runnable calls runOnUiThread when updating views.

Categories

Resources