Skip navigation

Category Archives: android

Just a quick tip: if you accidentally pass a data type to someTextView.setText() that cannot be implicitly coerced into a CharSequence representation (like, say, an int), logcat will display the error ‘fatal error: Resource$NotFoundException’ citing some weird hex String resource id that you probably don’t recognize. I have no idea why the Android framework does this in response to mismatched data types, but there you have it– if you see a contextually nonsensical error about some resource not being found around a TextView’s setText() method, check the data type of the argument you’re passing to setText().
homer-doh

Advertisements

As every Android apps developer should know, an ANR is thrown by the system when the UI thread of your application fails to update itself for 5 seconds; it follows then that long or recurrent tasks should be executed on separate threads, so as to leave the UI thread free to update itself.  This much is simple enough.  Where things can get a bit tricky is when you have a recurrent task that needs to send messages to the UI thread, perhaps like this:

public class MyActivity extends Activity{

        public final int MSG_UPDATE_INFO = 0;

        private CustomHandler mvMainThreadHandler;

        @Override

        protected void onCreate(Bundle savedInstanceState){

        super.onCreate(savedInstanceState);

        mvMainThreadHandler = new CustomHandler(this,Looper.getMainLooper());

        MyRunnable runner = new MyRunnable();

        Thread myThread = new Thread(runner);

        myThread.start();

        }

        private class MyRunnable implements Runnable{
        private boolean bIsUpdating = true;

        public boolean getIsUpdating(){

        return bIsUpdating;

        }

        public void setIsUpdating(boolean b){

        bIsUpdating = b;

        }

        @Override

        public void run(){

        while(bIsUpdating){

        //do something while updating is true

        //send message to main thread to let it know

//something has been done

        mvMainThreadHandler.sendMessage(

mvMainThreadHandler.obtainMessage(MSG_UPDATE_INFO));

        }

        }
        }

        private static class CustomHandler extends Handler{
                
                private WeakReference<MyActivity> whAct;
                
                public CustomHandler(MyActivity act,Looper mainLooper){
                    super(mainLooper);
                    
                    whAct = new WeakReference<MyActivity>(act);
                    
                }

                @Override
                public void handleMessage(Message msg){
                    MyActivity hAct = whAct.get();
                    if(hAct != null){
                        
                        if(msg.what == hAct.MSG_UPDATE_INFO){
                          //stuff
                            
                        }
            
                    }//End if hAct != null
                    else{
                        Log.e(TAG, "hAct, derived from whAct.get(), is null");
                    }
                }
                
            }

        }

This is almost fine… except that the run() method of MyRunnable will send the MSG_UPDATE_INFO message to the UI thread on every CPU clock cycle (or as near to that as it can), which will congest the UI thread and stop it from updating itself possibly for long enough to cause an ANR.  The solution is simple– re-implement MyRunnable::run() like so:

@Override

public void run(){

while(bIsUpdating){

try{

Thread.sleep(1000);

}catch (InterruptedException e) {
                    
       e.printStackTrace();

}

//do something while updating is true

//send message to main thread to let it know

//something has been done

mvMainThreadHandler.sendMessage(mvMainThreadHandler.obtainMessage(MSG_UPDATE_INFO));

}

 

with that small but imperative change the info update thread cannot send messages to the UI thread more frequently than once per 1 second, which should clear up the UI thread’s execution pipeline sufficiently to prevent ANRs.  Note though that the 1 second sleep time was chosen arbitrarily and depending on your recurrent task, your target platform, and the rest of your app, it may need to be a specific value; that said, it worked for me!Image

As of at least ADT plugin version 21 there is a TID column displayed in Eclipse’s logcat viewer when you have display settings on maximum verbosity– this thread id is extremely important to parallel programming (or octopus oriented programming as I prefer to call it). However, the thread id will not be preserved if you simply copy and paste the content of this view from within Eclipse. Instead you’ll see something like [debug level] [timestamp] [invocation] (PID)… but no thread id. I don’t know if there is an easier way to grab the TID data directly from Eclipse, but it can be done using a remote adb shell as follows:

Execute the following from your PC’s command line: adb -s [target_device_serial_number] shell “logcat -v threadtime” > [target_output_file]

your session will appear to hang, but in actuality the device’s logcat output, exactly as shown in eclipse’s logcat view including the TID, is being written in real time to your output file. When you want to view the content, exit the logcat dump process with ctrl+c and open the file on your PC. Other options for logcat filtering etc. can be found here

Logcat

battletac_hud_gunmounted_02
I’ve been experimenting with the proper way to put a 2D UI over a 3D scene for a while and I’ve finally gotten it working (using JPCT-AE as the 3D rendering engine)– behold!

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
am = this.getAssets();

ll = new LinearLayout(this);
hsv = new HorizontalScrollView(this);
img = (ImageButton) findViewById(R.id.tux_view);
img2 = new ImageButton(this);
img2.setImageResource(R.drawable.tux);
Logger.log(“onCreate”);

if (master != null) {
copy(master);
}

ui = new View(getApplication());
button = new Button(getApplication());
button.setText(“Hello!”);
//button.draw(canvas)
mGLView = new GLSurfaceView(getApplication());

mGLView.setEGLConfigChooser(new GLSurfaceView.EGLConfigChooser() {
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
// Ensure that we get a 16bit framebuffer. Otherwise, we’ll fall
// back to Pixelflinger on some device (read: Samsung I7500)
int[] attributes = new int[] { EGL10.EGL_DEPTH_SIZE, 16, EGL10.EGL_NONE };
EGLConfig[] configs = new EGLConfig[1];
int[] result = new int[1];
egl.eglChooseConfig(display, attributes, configs, 1, result);
return configs[0];
}
});

renderer = new MyRenderer();
mGLView.setRenderer(renderer);
ll.addView(button);
ll.addView(img2);
hsv.addView(ll);
setContentView(mGLView);
addContentView(hsv, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

}

This overlays 2D button and 2D imagebutton widgets over a 3D GLSurfaceView… awesome!

android_awesome

Okay, finally had some time to revisit this issue and I believe I’ve found the answer: First, before the xml layout or its components can be addressed they need to be inflated. I knew this, but I wasn’t sure when exactly they were inflated. It turns out that setContextView (and probably addContextView) trigger xml inflations. In order to have completely modular activity/view classes, I needed to do something like the following:

Activity Class–

package com.ai.ultimap;

import com.ai.ultimap.views.HomeView;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup.LayoutParams;

public class UltiMapActivity extends Activity {
private View hv;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
hv = new HomeView(this);
}
}

Custom View Class-

package com.ai.ultimap.views;

import com.ai.ultimap.R;
import android.app.Activity;
import android.os.Bundle;
import android.view.*;
import android.widget.*;
import android.view.View.OnClickListener;

public class HomeView extends View implements OnClickListener{

private RadioButton twodRB;
private RadioButton threedRB;
private TextView locTV;
private EditText editlocET;

public HomeView(Activity hAct) {
super(hAct);
//THE FOLLOWING LINE INFLATES– IT (or another function which calls xml inflation)
//MUST COME BEFORE ANY JAVA ADDRESSING OF WIDGETS IN
//THE XML LAYOUT
//Also note that even though you could invoke findViewById from a class extending
//View, in this case you must use hAct.findViewById. I believe this is due to the
//fact that the activity referenced by hAct is the object responsible for inflating
//the xml and thus the widgets need to be instantiated from it.
hAct.setContentView(R.layout.ultimap);
twodRB = (RadioButton) hAct.findViewById(R.id.twodRBV);
threedRB = (RadioButton) hAct.findViewById(R.id.threedRBV);
locTV = (TextView) hAct.findViewById(R.id.locationTV);
editlocET = (EditText) hAct.findViewById(R.id.locationETV);
//After instantiation however they can be freely accessed from java in
//non-activity classes, which is the point; see the next line…
twodRB.setOnClickListener(this);

}

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
locTV.setText(“yo”);
}

}

This code works properly to load up the pre-defined xml view ultimap.xml and then address the widgets dynamically from Java (completely outside the activity class), changing the text of the location text view from ‘Location’ to ‘yo’ when the twodRB radiobutton is clicked!  To get a direct handle to the layout inflater service, invoke the following:

LayoutInflater instantiates a layout XML file into its corresponding View objects. It is never used directly. Instead, use an Activity object’s getLayoutInflater() or getSystemService(String) method to retrieve a standard LayoutInflater instance that is already hooked up to the current context and correctly configured for the device you are running on. For example:

LayoutInflater inflater = (LayoutInflater)context.getSystemService
      (Context.LAYOUT_INFLATER_SERVICE);

To create a new LayoutInflater with an additional LayoutInflater.Factory for your own views, you can use cloneInContext(Context) to clone an existing ViewFactory, and then call setFactory(LayoutInflater.Factory) on it to include your Factory.

(ref: http://developer.android.com/reference/android/view/LayoutInflater.html)

Hope this helps some googlers :)

linux_android_iphone1

Ever been vexed by defining layout parameters in android views via the “new LayoutParams(LayoutParams.whatever,LayoutParams.whatever)” syntax you see all over the place? Well I certainly am, and it’s also bad practice for runtime performance since you use the ‘new’ operator repeatedly for essentially the same task.

Below follows a convenience class for defining simple layout parameters:


import android.view.ViewGroup.LayoutParams;

public class DefineLayoutParams {
//the controls
private static final int MATCHMATCH = 1;
private static final int WRAPMATCH = 2;
private static final int MATCHWRAP = 3;
private static final int WRAPWRAP = 4;

//the ONE layoutparams object
private static LayoutParams floatParams = new LayoutParams(0,0);

public DefineLayoutParams(){

}

public static LayoutParams getParams(int type){
if (type == MATCHMATCH){
floatParams.width = LayoutParams.MATCH_PARENT;
floatParams.height = LayoutParams.MATCH_PARENT;
}
else if(type == WRAPWRAP){
floatParams.width = LayoutParams.WRAP_CONTENT;
floatParams.height = LayoutParams.WRAP_CONTENT;
}
else if (type == WRAPMATCH){
floatParams.width = LayoutParams.WRAP_CONTENT;
floatParams.height = LayoutParams.MATCH_PARENT;
}
else if (type == MATCHWRAP){
floatParams.width = LayoutParams.MATCH_PARENT;
floatParams.height = LayoutParams.WRAP_CONTENT;
}
return floatParams;
}

public static LayoutParams getCustomParams(int w, int h){
floatParams.width = w;
floatParams.height = h;
return floatParams;
}

public static int getMM(){
return MATCHMATCH;
}
public static int getWW(){
return WRAPWRAP;
}
public static int getWM(){
return WRAPMATCH;
}
public static int getMW(){
return MATCHWRAP;
}

}

Once this class is added to your project’s src directory, use it like so–
1. [predefined] hActivity.addContentView(hView,DefineLayoutParams.getParams(DefineLayoutParams.getWW()));
2.[custom] hActivity.addContentView(hView,DefineLayoutParams.getCustomParams(150,250));

For JVM/DVM Java memory usage, the Eclipse Memory Analyzer Tool (MAT)

For native memory debugging (allocations in C/C++ etc.), Valgrind is awesome and supports quite a few platforms!

st-george-dragonThe Chivalrous Valgrind Logo!