I have an android application that is connected to the computer via USB cable. I use a TCPServer Class to send messages and listen. For example:
When I send a message like: request:x
I get the response: response:x:55
I need to make changes on my activity according to the response I get. At the moment I temporarily solved the problem by passing activity and activity class object to the TCPServer's constructor
public TCPServer(int portNum, Activity activity, IntroActivity ia) {
super();
port = portNum;
this.activity = activity;
this.ia = ia;
}
Then after I receive the response:
void updateButton(final int color, final String txt) {
activity.runOnUiThread(new Runnable() {
public void run() {
ia.getConnectionButton().setBackgroundColor(color);
ia.getConnectionButton().setText(txt);
}
});
}
As you see, this is not effective at all. I need to somehow notify the activity whenever a relevant variable is received. I use a Class for GlobalVariables and change those static variables after listen(), however I am having troubles notifying the activity.
First of all, it is almost always bad practice to pass Activity instances around. This is a time when it's bad.
Define an interface and use a callback to let the activity know that a response has been received.
public interface ResponseReceivedListener {
void onResponseReceived(int arg1, string arg2); // <- add arguments you want to pass back
}
In your TCPServer class:
ArrayList<ResponseReceivedListener> listeners = new ArrayList<>();
// ...
public void setResponseReceivedListener(ResponseReceivedListener listener) {
if (!listeners.contains(listener) {
listeners.add(listener);
}
}
public void removeResponseReceivedListener(ResponseReceivedListener listener) {
if (listeners.contains(listener) {
listeners.remove(listener);
}
}
When you receive a response:
for (ResponseReceivedListener listener : listeners) {
listener.onResponseReceived(arg1, arg2);
}
In your Activity:
public class MainActivity extends Activity implements ResponseReceivedListener {
// ...
#Override
public void onCreate(Bundle savedInstanceState) {
// ...
tcpServer.setResponseReceivedListener(this);
// ...
}
public void onResponseReceived(int arg1, string arg2) {
// do whatever you need to do
}
// ...
}
All from memory so please excuse typos.
This approach decouples the classes. The TCP Server has no knowledge of the activities. It simply calls back to any listeners registered. Those listeners might be Activities, they might be services. They might be instances of MySparklyUnicorn. The server neither knows nor cares. It simply says "if anyone's interested, I've received a response and here are the details".
Related
Is there a canonical solution using Espresso to wait for a specific Activity to finish or start?
I have a SplashActivity that appears for a few seconds, then a MainActivity. I want Espresso to interact with the MainActivity, not the SplashActivity, but I can't seem to find any information about waiting for such a condition.
The closest thing I can find is a mention of idle resources but its not clear to me how I would use that here to wait for the Activity.
I guess your splash activity is performing some initialization.
If this is the case, my suggestion is to define some sort of listener pattern in order to be able to get a callback when the initialization is done. Then, you can make Espresso wait for the initialization with an IdlingResource.
NB: The following is NOT complete code, but it is meant to give you a hint in how to do so:
public class SplashIdlingResource implements IdlingResource, YourApplicationInitListener {
// volatile because can be set by a different
// thread than the test runner: the one calling back
private volatile boolean mIsInitialized;
private ResourceCallback mCallback;
public SplashIdlingResource() {
YourApplication application = // retrieve your Application object
mIsInitialized = application.isInitialized();
if (!mIsInitialized) {
application.addInitListener(this);
}
}
#Override
public String getName() {
return SplashIdlingResource.class.getName();
}
#Override
public boolean isIdleNow() {
return mIsInitialized;
}
#Override
public void registerIdleTransitionCallback(ResourceCallback callback) {
mCallback = callback;
}
#Override
public void onApplicationInitCompleted() {
m_isInitialized = true;
if (m_callback != null) {
m_callback.onTransitionToIdle();
}
}
}
Where onApplicationInitCompleted() is the callback you defined and which must be called when the Splash Activity, and so the initialization, is done.
Finally, register this new IdlingResource with Espresso by calling Espresso.registerIdlingResource in test setup.
I want to implement AsyncTaskLoader for my custom data source:
public class DataSource {
public interface DataSourceObserver {
void onDataChanged();
}
...
}
DataSource will keep list of registered observers and will notify them about changes. CustomLoader will implement DataSourceObserver. The question is how to properly notify CustomLoader since Loader.onContentChanged() must be called from UI thread but in my case DataSource operations (and calls to DataSourceObserver.onDataChanged()) will be done from background threads.
Updated with idea from Selvin tip:
public class CustomLoader extends AsyncTaskLoader<...> implements DataSource.DataSourceObserver {
private final Handler observerHandler;
public CustomLoader(Context context) {
super(context);
observerHandler = new Handler()
}
#Override
public void onDataChanged() {
observerHandler.post(new Runnable() {
#Override
public void run() {
onContentChanged();
}
});
}
}
I've had a lot of success using Local Broadcasts in a case that's very similar to yours. The method involves an AsyncTaskLoader implementation that will register a BroadcastReceiver listening for a particular String that describes what's changed. This BroadcastReceiver keeps a reference to the Loader and calls onContentChanged. When the data needs a refresh, make the Local Broadcast with the aforementioned String and the BroadcastReceiver will hear it and trigger the load. Here's some example code, it may not work perfectly if you drop it in, I've generalized some class names, but hopefully you'll get the idea:
Broadcast Receiver to be used in your Loader Implmentation:
public class LoaderBroadcastReceiver extends BroadcastReceiver
{
private Loader loader;
public LoaderBroadcastReceiver(Loader loader)
{
this.loader = loader;
}
#Override
public void onReceive(Context context, Intent intent)
{
loader.onContentChanged();
}
}
Loader Implementation registers the Receiver in onStartLoading()
private LoaderBroadcastReceiver loaderBroadcastReceiver = null;
#Override
protected void onStartLoading()
{
//... some code here
if(loaderBroadcastReceiver == null)
{
loaderBroadcastReceiver = new LoaderBroadcastReceiver(this);
LocalBroadcastManager.getInstance(getContext()).registerReceiver(loaderBroadcastReceiver, new IntentFilter("NEWDATASTRING"));
}
//... some more code here
}
Finally, here's how onDataChanged in DataSource will make the Broadcast. It'll need a Context to help send the Broadcast. Since this can be called from an arbitrary Thread, I'd use your ApplicationContext, since an Context from an Activity could cause problems if the Activity is destroyed.
public class DataSource
{
public interface DataSourceObserver
{
void onDataChanged(Context applicationContext)
{
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("NEWDATASTRING"));
}
}
...
}
You'll probably want to play with it a bit to see how it works for you. You can use different Strings to differentiate different data that needs loading. You'll also want to unregister the Receiver at some point, perhaps in onReset(). Let me know if any of this in unclear in the comments, I'll try my best to clarify.
I always have this problem of java.lang.IllegalStateException:Could not execute method of the activity. I was planning to perform an android component event (ex. Button event - indicating the number of times this button was clicked). Here's the code snippet for this problem:
interface Selection {
public void clicked();
}
public class ParentClass extends FragmentActivity {
// fTabs : FragmentTabHost
// tabs : Map<String, Selection>
private void initialize() {
// fistFrag : FirstChildClass = new FirstChildClass()
// secondFrag : SecondChildClass = new SecondChildClass()
tabs.put("first", firstFrag);
tabs.put("second", secondFrag);
fTabs.add(fTab.newTabSpec("first").setTitle("First"), firstFrag.getClass(), null)
fTabs.add(fTab.newTabSpec("second").setTitle("Second"), secondFrag.getClass(), null)
}
#Override
public void onBackPressed() {
tabs.get(fTabHost.getCurrentTabTag()).clicked();
}
}
public class FirstChildClass extends Fragment implements Selection {
// data : TextView
// hit : int = 0
#Override
public void clicked() {
data.setText(String.format("Hit Count: %d", ++hit));
}
}
public class SecondChildClass extends Fragment implements Selection {
// data : TextView
// hit : int = 0
#Override
public void clicked() {
data.setText(String.format("Hit Count: %d", ++hit));
}
}
I've tried to assure of clicked() works view interfacing approach by invoking a message on Logcat and it worked but when I used Button the error above always prompts me. I've checked if data is null and it returned true. I am a little bit confused, I've tried to check nullity of data from the Activity methods is returns false but when I access any method override by an interface it always return true. Is there a way to solve this?
Here's a way my friend told me to solve this problem. Using getSupportFragmentManager. He told me also that creating an Activity or Fragment using its constructor isn't applicable on the Android platform. So I switched by to the conventional way of adding tabs to FragmentTabHost.
#Override
public void onBackPressed() {
//tabs.get(fTabHost.getCurrentTabTag()).clicked();
((Selection) getSupportFragmentManager().findByFragmentByTag(fTabHost.getCurrentTabTag()).clicked();
}
I want to change a TextView in the onReceive method of my BroadcastReceiver, but I can't access it with findViewById because it's not an activity.
I don't want to create a private class for the BroadcastReceiver in my activity.
How can I get access?
Define an interface and use a callback to let the activity know that a broadcast event has been received.
public Interface BroadcastReceiverListener {
void onReceive(int arg1, String arg2); ..<----add arguments you want to pass back
}
In your BroadcastReceiver class
ArrayList<BroadcastReceiveListener > listeners = new ArrayList<BroadcastReceiveListener >();
...
public void addBroadcastReceiveListener (BroadcastReceiveListener listener){
if(!listeners.contains(listener)){
listeners.add(listener);
}
}
public void removeBroadcastReceiveListener (BroadcastReceiveListener listener){
if(listeners.contains(listener)){
listeners.remove(listener);
}
}
In your OnReceive
for (BroadcastReceiveListener listener:listeners){
listener.onReceive(arg1, arg2);
}
In your Activity:
public class MyActivity extends Activity implements BroadcastReceiveListener {
...
broadcastReceiver.addBroadcastReceiveListener(this); <---- the instance of your receiver
...
}
public void onReceive(int arg1, String arg2){
// do whatever you need to do
}
Note. Because you use an interface, any class (not just an Activity) can implement it so you can update anywhere in your app. The BroadcastReceiver class doesn't know or care. It just calls the listeners, if any are registered.
Now, you don't need to access R, or anything to do with the UI since your Activity is the only class that knows about, and can change, your UI - which is the Android way!
[EDIT]
The arguments are whatever you need them to be.
Think of the Interface as a contract. It says that anyone who implements it, must implement the onReceive() method and that the method will be called with an integer and a String. It's up to you what arguments you need, if any.
BroadcastReceiver.onReceive() calls the onReceive callback of the interface and passes in the int and String as arguments.
You could change the Interface definition to pass a bool for example.
public Interface BroadcastReceiverListener {
void onReceive(boolean arg1); ..<----add arguments you want to pass back
}
Then your caller looks like this:
for (BroadcastReceiveListener listener:listeners){
listener.onReceive(someBooleanValue);
}
And your callback looks like this:
public void onReceive(boolean theCallerIsReady){
if(theCallerIsReady){
// do interesting stuff
}
}
During the most recent Google IO, there was a presentation about implementing restful client applications. Unfortunately, it was only a high level discussion with no source code of the implementation.
In this diagram, on the return path there are various different callbacks to other methods.
How do I declare what these methods are?
I understand the idea of a callback - a piece of code that gets called after a certain event has happened, but I don't know how to implement it. The only way I've implemented callbacks so far have been overriding various methods (onActivityResult for example).
I feel like I have a basic understanding of the design pattern, but I keep on getting tripped up on how to handle the return path.
In many cases, you have an interface and pass along an object that implements it. Dialogs for example have the OnClickListener.
Just as a random example:
// The callback interface
interface MyCallback {
void callbackCall();
}
// The class that takes the callback
class Worker {
MyCallback callback;
void onEvent() {
callback.callbackCall();
}
}
// Option 1:
class Callback implements MyCallback {
void callbackCall() {
// callback code goes here
}
}
worker.callback = new Callback();
// Option 2:
worker.callback = new MyCallback() {
void callbackCall() {
// callback code goes here
}
};
I probably messed up the syntax in option 2. It's early.
When something happens in my view I fire off an event that my activity is listening for:
// DECLARED IN (CUSTOM) VIEW
private OnScoreSavedListener onScoreSavedListener;
public interface OnScoreSavedListener {
public void onScoreSaved();
}
// ALLOWS YOU TO SET LISTENER && INVOKE THE OVERIDING METHOD
// FROM WITHIN ACTIVITY
public void setOnScoreSavedListener(OnScoreSavedListener listener) {
onScoreSavedListener = listener;
}
// DECLARED IN ACTIVITY
MyCustomView slider = (MyCustomView) view.findViewById(R.id.slider)
slider.setOnScoreSavedListener(new OnScoreSavedListener() {
#Override
public void onScoreSaved() {
Log.v("","EVENT FIRED");
}
});
If you want to know more about communication (callbacks) between fragments see here:
http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
No need to define a new interface when you can use an existing one: android.os.Handler.Callback. Pass an object of type Callback, and invoke callback's handleMessage(Message msg).
Example to implement callback method using interface.
Define the interface, NewInterface.java.
package javaapplication1;
public interface NewInterface {
void callback();
}
Create a new class, NewClass.java. It will call the callback method in main class.
package javaapplication1;
public class NewClass {
private NewInterface mainClass;
public NewClass(NewInterface mClass){
mainClass = mClass;
}
public void calledFromMain(){
//Do somthing...
//call back main
mainClass.callback();
}
}
The main class, JavaApplication1.java, to implement the interface NewInterface - callback() method. It will create and call NewClass object. Then, the NewClass object will callback it's callback() method in turn.
package javaapplication1;
public class JavaApplication1 implements NewInterface{
NewClass newClass;
public static void main(String[] args) {
System.out.println("test...");
JavaApplication1 myApplication = new JavaApplication1();
myApplication.doSomething();
}
private void doSomething(){
newClass = new NewClass(this);
newClass.calledFromMain();
}
#Override
public void callback() {
System.out.println("callback");
}
}
to clarify a bit on dragon's answer (since it took me a while to figure out what to do with Handler.Callback):
Handler can be used to execute callbacks in the current or another thread, by passing it Messages. the Message holds data to be used from the callback. a Handler.Callback can be passed to the constructor of Handler in order to avoid extending Handler directly. thus, to execute some code via callback from the current thread:
Message message = new Message();
<set data to be passed to callback - eg message.obj, message.arg1 etc - here>
Callback callback = new Callback() {
public boolean handleMessage(Message msg) {
<code to be executed during callback>
}
};
Handler handler = new Handler(callback);
handler.sendMessage(message);
EDIT: just realized there's a better way to get the same result (minus control of exactly when to execute the callback):
post(new Runnable() {
#Override
public void run() {
<code to be executed during callback>
}
});
You can also use LocalBroadcast for this purpose. Here is a quick guide
Create a broadcast receiver:
LocalBroadcastManager.getInstance(this).registerReceiver(
mMessageReceiver, new IntentFilter("speedExceeded"));
private BroadcastReceiver mMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Double currentSpeed = intent.getDoubleExtra("currentSpeed", 20);
Double currentLatitude = intent.getDoubleExtra("latitude", 0);
Double currentLongitude = intent.getDoubleExtra("longitude", 0);
// ... react to local broadcast message
}
This is how you can trigger it
Intent intent = new Intent("speedExceeded");
intent.putExtra("currentSpeed", currentSpeed);
intent.putExtra("latitude", latitude);
intent.putExtra("longitude", longitude);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
unRegister receiver in onPause:
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(mMessageReceiver);
}
asume the main function is the activity that is triggering the event:
fun main() {
val worker = Worker()
worker.setOnCallListener(
object: OnCallListener {
override fun onCall() {
// here we define what should happen
// when the event accures
print("event happend")
}
}
)
// most events will be called from Android system itself
// but in our case we have to call it manually
worker.listener.onCall()
}
the Worker class has an instance of Type OnCallListener interface
and a method to set its value:
class Worker() {
lateinit var listener: OnCallListener
fun setOnCallListener(listener: OnCallListener) {
this.listener = listener
}
}
and the OnCallListener interface looks like this:
interface OnCallListener {
fun onCall()
}