Android code working slower than intended - java

Sorry for my bad english,
I'm doing a project for school where i have to move a vehicle via WiFi with an Android App. I achieved my goal but I got surprised to see that, most of the times when I pressed any button, my vehicle took some time to actually do something. On the other hand, when I press any button a few times quickly, the events stack, and after, even when I'm not pressing any button, the vehicle moves. Needless to say, both are several problems. Is there any way to fix them?.
The Android App conects via REST API to an Arduino Yun. With each touch on a button i'm making an HTTP Request in order to move the vehicle Forward, Backwards, Left or Right.
Here's my code (Despite the spanish, I think it's understandable):
public class MainActivity extends Activity {
private Button boton1,
boton2,
boton3,
boton4;
private String valor;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
configuracion();
}
public void configuracion(){
StrictMode.ThreadPolicy policy = new StrictMode.
ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
establecerBotones();
try{
definir("continua/0/0");
definir("direccion/0/0");
}
catch(Exception e){
Alerta();
}
}
public void establecerBotones(){
boton1= (Button)findViewById(R.id.boton1); //all buttons do something similar
boton1.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch(action){
case MotionEvent.ACTION_DOWN:
v.setPressed(true);
conectar("continua/1/0");
break;
case MotionEvent.ACTION_OUTSIDE:
case MotionEvent.ACTION_UP:
v.setPressed(false);
try {
conectar("continua/0/0");
} catch (Exception e) {
}
break;
}
return true;
}
});
boton2= (Button)findViewById(R.id.boton2);
boton2.setOnTouchListener(new OnTouchListener() {
});
boton3= (Button)findViewById(R.id.boton3);
boton3.setOnTouchListener(new OnTouchListener() {
});
boton4= (Button)findViewById(R.id.boton4);
boton4.setOnTouchListener(new OnTouchListener() {
});
}
private void conectar(final String selector) {
new Thread() {
#Override
public void run() {
try {
// code runs in a thread
runOnUiThread(new Runnable() {
#Override
public void run() {
try {
definir (selector);
} catch (Exception e) {
System.out.println("holi");
}
}
});
} catch (final Exception ex) {
}
}
}.start();
}
public void definir(String selector) throws Exception{
valor = "http://192.168.240.1/arduino/" + selector;
URL url = new URL(valor);
HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
try {
InputStream in = new BufferedInputStream(urlConnection.getInputStream());
leer(in);
}
finally {
urlConnection.disconnect();
}
}
public void leer(InputStream in) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
if ((reader.readLine()) != "1") throw new IOException();
//Arduino is programmed to print "1" when connection succedes
}
public void Alerta(){
Context context = getApplicationContext();
CharSequence text = "Conexion erronea";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
}
PD: I'm kind of a beginner at programming, and I started learning java (and android) purely on my own this year. I'm probably doing some "dumb" errors on my code. I would appreciate any help or advice. Thank you for reading.

You have a problem with the code, as mentioned in comments. Android behaves badly if you do things that take any time on the UI thread. You need to move external connections onto a separate thread and find a way to communicate through it. As a newbie programmer you are going to find this hard, but there are lots of code samples around to help.
But this (and the code you posted) has nothing to do with your real problem. You have an "open loop" control mechanism and you need to close the loop. You need a way for the controlled device to respond with some information about whether it has moved or is moving, and you need to modify your control strategy accordingly.
Alternatively, you can send more complex commands to the vehicle (like "left 3" or "straight ahead") and leave the loop monitoring to the vehicle's on-board processor.
These are all common problems and you will find plenty of general reading material with very little searching. When you have very specific questions, Stack Overflow may be able to help better.

Related

Hanging the UI thread in Android deliberately [duplicate]

Why i can't force Android ANR with this code?
No log messages or pop up. The application is just launched lazily.
[UPDATE]
I can't get it even sleeping a View.setOnClickListener or BroadcastReceiver.onReceive!
Is there a trick?
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
Log.e("Test", "", e);
}
}
}
I'm using Samsung GT-6200L with stock Android 3.2
Try it in onTouchEvent. In onCreate your activity is not fully running
#Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(TAG,"onTouchEvent");
while(true) {}
}
The ANR-WatchDog project has a test app that produces ANRs in a reliable manner (as reliable as ANRs can be): the app hangs because of a deadlock.
The gist of it:
Prepare a lock object as a private field in your activity:
final Object mutex = new Object();
Have a thread that performs some work in a critical section, and an android.os.Handler that posts work depending on the same lock.
new Thread(new Runnable() {
#Override
public void run() {
synchronized (mutex) {
while (true) {
try {
Thread.sleep(60000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}).start();
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
synchronized (mutex) {
// Shouldn't happen
throw new IllegalStateException();
}
}
}, 1000);
Putting the above code snippet inside a button click handler, for example, should do the trick.
I've been facing the same issue yesterday, and I've found out that using a plain debug build ANR dialogs simply won't show up. (Although the UI thread was completely hanged.)
But after exporting and properly signing the application the dialogs were popped up properly (in every cases mentioned above). However I am still not sure what really prevents to pop up ANR messages, maybe someone else can clarify this later...
Try using:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
int a=0;
while(true) {
a++;
}
}
Your code probably didn't work because it got setup too early, and the Activity probably wasn't fully initialized and created yet. With the above code, launch the activity and touch/swipe on the screen and wait for the ANR dialog to popup.
Make a button in your activity.
public void onBtn1(View v) {
int a = 0;
while(true) {
a++;
}
}
Make the button execute the above code.
Spam click the button with your finger =)
I used this code for force ANR
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void force(View view){
while(true) {}
}
I just created a simple button in the xml file and set android:onClick=force

Settext causing text to flicker irratically

I'm making an app which establishes USB communication between an Arduino UNO R3 and an android tablet. Arduino board is sending data correctly and it is even being received by tablet correctly and when tried to display, the text does get printed but with a rather continuous flicker.
class MyThread extends Thread
{
#Override
public void run()
{
mCallback = new UsbSerialInterface.UsbReadCallback()
{ //Defining a Callback which triggers whenever data is read.
#Override
public void onReceivedData(byte[] arg0) //data received in bytes
{
String data = null;
try
{
data = new String(arg0, "UTF-8");
handler.post(new newthread(data));
}
catch (UnsupportedEncodingException e)
{
e.printStackTrace();
}
}
};
}
}
class newthread implements Runnable
{
String str1;
public newthread(String STR1)
{
str1 = STR1;
}
#Override
public void run()
{
DoseRateDisplay = (TextView) findViewById(R.id.DoseRateDisplay);
if(str1.contains("L"))
{ tv6.append("Health OK"); }
else
{
DoseRateDisplay.settext(str1);
}
}
}
I think the reason for flicker can be that the data is incoming too fast. Using Thread.sleep does not help. What can be the possible solution to this problem ? Also, using append instead of settext doesn't cause any flickering problems, but then data gets appended to textview.
From my comment: try to check if the text received and the text already in the TextView are equal:
if(!DoseRateDisplay.getText().toString().equals(str1)) {
DoseRateDisplay.settext(str1);
}

Android: trying to add a wait in between two animations, getting Object not locked by thread before wait() error

I'm new to Android and Java programming. I created an app for a Coursera class. It has two animations that occur repeatedly. I want them to animate like a drum beat - 1,2,1,2,1,2,1,2,1,2.
I wrote two recursive methods that run until the user clicks a Reset button. I tried adding a wait in between the method calls so the second would kick off right after the first one.
This is the part of my code that has the wait.
mHandler = new Handler(); // .os package class when importing
mLeftfoot = findViewById(R.id.leftfoot);
mRightfoot = findViewById(R.id.rightfoot);
mFootAnim = AnimationUtils.loadAnimation(this, R.anim.foot); //this looks to the foot.xml file for the animations
leftstepRecursive();
try {
wait(600);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
rightstepRecursive();
This part of my code shows the recursive methods, which could probably be written better I know. A lot of DRY...
private void leftstepRecursive() {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mLeftfoot.startAnimation(mFootAnim);
if (!pleaseStop)
leftstepRecursive();
}
}, mIntervalleft);}
private void rightstepRecursive() {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mRightfoot.startAnimation(mFootAnim);
if (!pleaseStop)
rightstepRecursive();
}
}, mIntervalright);
Any better ideas to implement this left, right, left, right, left, right, left, right, left, right, idea?
Or, what I can do to correct the wait?
I also tried this:
private void rightstepRecursive() {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
Object testobj = mRightfoot.startAnimation(mFootAnim);
synchronized (testobj) {
testobj.wait(600);
}
if (!pleaseStop)
rightstepRecursive();
}
}, mIntervalright);
#LuigiMendoza. I tried thread(sleep). No errors, but both animations move simultaneously not sure why...
private void rightstepRecursive() {
mHandler.postDelayed(new Runnable() {
#Override
public void run() {
mRightfoot.startAnimation(mFootAnim);
if (!pleaseStop)
rightstepRecursive();
}
}, mIntervalright);
try
{
Thread.sleep(500);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
I also get this message repeating in Logcat.
02-11 12:15:32.261: I/Choreographer(30480): Skipped 302 frames! The application may be doing too much work on its main thread.
You should really utilize the Animations framework and some of the classes it provides such as AnimationListeners and AnimatorSets. The Animations framework provides a lot of functionality that would really simplify what you are trying to do i.e. repeating animations, adding delays, timing animations, etc.
Here's a really bare bones example to give the basic idea:
AnimatorSet animations;
//Play the animation
private void playAnimation(){
//If they haven't stopped the animation, loop it.
ObjectAnimator anim1 = ObjectAnimator.ofFloat(target, propertyName, values);
ObjectAnimator anim2 = ObjectAnimator.ofFloat(target, propertyName, values);
animations = new AnimatorSet();
animations.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
playAnimation();
}
});
animations.play(anim1).before(anim2);
animations.start();
}
//Called when cancel is selected
private void stopAnimation(){
animations.end();
}

Providing delay between events in UiAutomator Android

How can I provide delay between event clicks in UiAutomator Android.
First event is entering a url in EditText :
new UiObject(new UiSelector().text("Search here")).setText("abc.com");
getUiDevice.waitForIdle(15000);
Here I am loading a webpage inside a webview. So when url loading finishes , then I need to check for second event.
Second event is checking content description of a object :
UiObject curClass = new UiObject(new UiSelector().className("android.widget.RelativeLayout"));
UiObject yCur = curClass.getChild(new UiSelector().description("ye"));
getCurCol();
public void getCurCol() throws UiObjectNotFoundException {
try {
if (yCur.getContentDescription() != null)
report.append("Success");
} catch (Exception e) {
System.err.println("\nCaught Exception: " + e.getMessage());
}
But this doesn't seem to be working.
I just want the app to wait for some time before checking for the second event.
I know these three methods are provided for delay in UI Automator -
public void waitForIdle(long timeout)
public void waitForIdle()
public boolean waitForWindowUpdate(String packageName, long timeout)
But I don't know how to use these methods.
Please suggest me a example how to use these.
Any help would be appreciated.
Thanks!
Try just
sleep(5000);
Let me know whether it works. Or you can try WaitForIdle( ) method.
If you need a wait after pressing a button
UiObject settingsButton = mDevice.findObject(new UiSelector().text("Settings"));
settingsButton.clickAndWaitForNewWindow();
or
settingsButton.waitUntilGone(1000);
In case of launching new activity from intent this way is the easiest I could find to wait for a settings package appear:
mDevice.wait(Until.hasObject(By.pkg("com.android.settings").depth(0)), 2000);
UiObject settingsValidation = mDevice.findObject(new UiSelector().packageName("com.android.settings").text("Settings"));
assertTrue("Unable to detect Settings", settingsValidation.waitForExists(4000));
public boolean waitForWindowUpdate(null, long timeout)
Works good for me. I think it's possible to bring any 'not existing' String packagaName, for ex. "blablabla", because you can get null instead of String packageName.
I've create a simply method to make a delay:
public void waitTime(int time) {
getUiDevice().waitForWindowUpdate(null, time);
}
Hope this is helpful.
I am no expert in UIautomator, but this can be done with Thread.sleep().
UiDevice provides a few methods that may work for you:
public void waitForIdle(long timeout)
public void waitForIdle()
public boolean waitForWindowUpdate(String packageName, long timeout)
i think TimeUnit.SECONDS.sleep(val); should be helpful here
for instance
import java.util.concurrent.TimeUnit;
private void waitFor(long val)
{
try
{
TimeUnit.SECONDS.sleep(val);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
This should work
I had a case where I had to wait for the Smart Lock dialog and find a way to dismiss it.
I ended up doing the following
final UiDevice mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
// Wait for the Smart Lock dialog to popup
if(mDevice.waitForWindowUpdate(null, 5000)) {
if("com.google.android.gms".equals(mDevice.getCurrentPackageName())) {
// Dismiss dialog
mDevice.pressBack();
}
}
This will wait for the dialog to appear and if it does it gets dismissed.
webView.loadUrl("http://abc.com");
webView.setWebChromeClient(new WebChromeClient());
webView.setWebViewClient(new WebViewClient() {
#Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
// TODO Auto-generated method stub
super.onPageStarted(view, url, favicon);
}
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
//Handle your code here
//Toast.makeText(TableContentsWithDisplay.this, "Width " + view.getWidth() +" *** " + "Height " + view.getHeight(), Toast.LENGTH_SHORT).show();
}
#Override
public void onReceivedSslError(WebView view,
SslErrorHandler handler, SslError error) {
// TODO Auto-generated method stub
super.onReceivedSslError(view, handler, error);
//Toast.makeText(TableContentsWithDisplay.this, "error "+error, Toast.LENGTH_SHORT).show();
}
});
Load your url then there is method onPageFinished() where you can handle your code.
The only way I was able to add delay in UIAutomator code was in a similar way to slott 's answer:
I basically lunched a small app (the basic app from android examples) and pressed back to close it. This allows time for all elements to load properly.
Context context = getApplicationContext();
final Intent intent = context.getPackageManager()
.getLaunchIntentForPackage(BASIC_SAMPLE_PACKAGE);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // Clear out any previous instances
context.startActivity(intent);
// Wait for the app to appear
mDevice.wait(Until.hasObject(By.pkg(BASIC_SAMPLE_PACKAGE).depth(0)), LAUNCH_TIMEOUT);
//then close this small app
mDevice.pressBack();

Android: Updating gridview multiple times after x seconds

I am having a problem updating the view in android every x seconds.
To get to know how android works I am writing a small game.
It has a GameController which holds the game loop. Inside the loop, the logic is executed and afterwards the gui will be informed about the changes.
The problem is that the changes cannot be seen in the view. The view stays the same until the game loop finishes and updates then only once.
Here is my code (the important parts):
GameController.java:
IGui gui;
public void play() {
while (playing) {
getAndProcessInput();
updateGui();
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
}
}
}
private void updateGui() {
gui.setPlayer(x, y);
// ...
}
GameActivity.java:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
GridView gridview = (GridView) findViewById(R.id.GridView1);
TextAdapter = new TextAdapter(this);
gridview.setAdapter(textAdapter);
GameController c = new GameController();
// here, the game loop shoud start.
// what i tried:
// new Thread(c).start(); <-- causes crash
// c.play(); <-- causes view to frease until game loop is done
this.runOnUiThread(c); <-- causes view to frease until game loop is done
}
TextAdapter.java:
public class TextAdapter extends BaseAdapter implements IGui {
private final Context context;
private final String[] texts;
public TextAdapter(Context context) {
this.context = context;
texts = new String[height * width];
for (int i = 0; i < texts.length; i++) {
texts[i] = " ";
}
}
public int getCount() {
return height * width;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
TextView tv;
if (convertView == null) {
tv = new TextView(context);
tv.setLayoutParams(new GridView.LayoutParams(25, 25));
} else {
tv = (TextView) convertView;
}
tv.setText(texts[position]);
return tv; // <-- this happens only when game loop is done, not everytime something changed
}
#Override
public void setPlayer(int x, int y) {
texts[width * y + x] = "X";
// this.notifyDataSetChanged(); <-- this does not help (view still freases)
// gridview.invalidateViews(); does not help either
}
}
I googled a lot and tried a lot as well (and I do know that similar questions where asked here already, but they did not help me either), but somehow it just does not work.
I cannot get it do work that the view and logic run on different theads in android, and if they run on the same thread the logic blocks the view.
Any help would be greatly appreciated.
// edit:
If I try new Thread(c).start(); LogCat sais:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
And if I add Looper.prepare(); :
java.lang.RuntimeException: Unable to start activity ComponentInfo{GameActivity}: java.lang.RuntimeException: Only one Looper may be created per thread
If I try this.runOnUiThread(c); there are no errors.
First, this doesn't seems like the way to crate a game, you will need to use SurfaceView, or GLSurfaceView to better do what you want.
You can also look for Cocos2d for android, it's a 2D platform (that was ported from iPhone) that makes you life easier:
http://code.google.com/p/cocos2d-android/
I muse warn you though, I tried it a couple months back, and it was not production grade yet, it did crash from time to time.
Anyway, if you still want to continue heading your way I'll try answering your question:
I think you are messing too much with the way these stuff should work. try understanding first how handlers work with threads.
Do anything you want on your thread:
new Thread(new Runnable()
{
#Override
public void run()
{
try
{
calculateGameChangesHere();
handler.sendEmptyMessage(SUCCESS);
}
catch (Exception e)
{
handler.sendEmptyMessage(FAILURE);
}
}
}).start();
When your data is ready, tell the handler to put it in a view and show it:
protected Handler handler = new Handler()
{
#Override
public void handleMessage(Message msg)
{
if (msg.what == SUCCESS)
{
setCalculatedDataToaView(); // the data you calculated from your thread can now be shown in one of your views.
}
else if (msg.what == FAILURE)
{
errorHandlerHere();//could be your toasts or any other error handling...
}
}
};
This goes to everything that requires heavy processing/networking that shouldn't block your UI thread.
Hope this helps.
Not sure what you are trying to achieve by refreshing a listView every few seconds. Anyways if you are writing some 2d games, you have to use SurfaceView , which is especially meant for continuous refreshing.
Had the same problem and solved it using a pretty simple code.
Tips:
GridView must be refreshed by the UI thread
To display every change you must keep the loop and the Sleep method away from the UI thread
Solution:
Method to update the grid (put on your Activity that you build your GridView at the first place or wherever)
public void UpdateGrid(){
//your logic
//your way to change grid values (there are tones of other ways)
CustomGridAdapter cga = new CustomGridAdapter(this, values);
gridView.setAdapter(cga);
gridView.invalidateViews();
}
Build the non UI Thread that does the work
public class DataReceiver implements Runnable {
private MainActivity mainActivity;
public DataReceiver(MainActivity ma) {
mainActivity = ma;
}
#Override
public void run() {
for (int i = 0; i < 100; i++) {
mainActivity.runOnUiThread(new Runnable() {
public void run() {
//update the grid here
mainActivity.UpdateGrid();
}
});
//sleep here
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}

Categories

Resources