My Android Eclipse project uses two external jars: Flurry and Admob. They work fine until I upgraded Android SDK to 4.0.3. Then there is forced close due to java.lang.NoClassDefFoundError: com.flurry.android.FlurryAgent.
The fix is to check these two jars in the "Order and Export" tab.
Friday, 20 April 2012
Saturday, 14 April 2012
Android: proguard cannot find symbols in Flurry
When exporting signed signed application package in Eclipse, proguard emits warnings about not being able to find Flurry symbols, then aborts.
The fix is to add
The fix is to add
-libraryjars /path/to/FlurryAgent.jar
to proguard.cfg
Friday, 13 April 2012
Unity: How to test iOS Unity Apps without using Xcode
Xcode is slow when building Unity iOS apps. It is not a good option for testing Unity iOS apps since it could take several minutes to build the app.
You cannot use the Game panel in Unity to test iOS apps which handles touch events because the Game panel does not emulate touch event with mouse. If you use the Game panel, Input.TouchCount is always zero since there is no touch events.
You need to use Unity Remote to test Unity iOS apps. It is a iOS app which can be downloaded from Apple App Store freely and installed on your iOS devices. Before testing your app, connect your iOS device to your Mac, then run Unity Remote on your iOS device and select your Mac machine. In Unity, run your app, the Game panel will stream video display to Unity Remote and Unity Remote will pass touch events from your iOS device to the Game panel. This way you can test your Unity iOS app with all the iOS events.
Wednesday, 11 April 2012
Unity: Problem with invalid signature on iOS
If you build Unity project for iOS and see this error when running the app:
The application does not have a valid signature
And your development provisional profile are OK. It can be caused by having more than one development provisional profiles. You can use organizer to remove extra development provisional profiles and only keep the generic one. This should fix the problem.
The application does not have a valid signature
And your development provisional profile are OK. It can be caused by having more than one development provisional profiles. You can use organizer to remove extra development provisional profiles and only keep the generic one. This should fix the problem.
Unity: problem with Xcode 4.3.2
Currently Unity basic edition does not work with Xcode 4.3.1 and 4.3.2. The error is:
You are using Unity iPhone Basic. You are not allowed to remove the Unity splash screen from your game
The workaround is generate your Xcode project and build it with Xcode 4.3 or older version.
Until now there is no fix yet. Unity is aware of this issue and promised a fix in the next release.
http://forum.unity3d.com/threads/126820-Unity-3.5-and-iOS-5.1
You are using Unity iPhone Basic. You are not allowed to remove the Unity splash screen from your game
The workaround is generate your Xcode project and build it with Xcode 4.3 or older version.
Until now there is no fix yet. Unity is aware of this issue and promised a fix in the next release.
http://forum.unity3d.com/threads/126820-Unity-3.5-and-iOS-5.1
Saturday, 7 April 2012
Android: cannot use Admob due to String types not allowed at 'configChanges'
I was trying to use Admob in my app and added this to my AndroidManifest.xml:
<activity
<activity
android:name="com.google.ads.AdActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize" >
</activity>
then there is a build error:
Sting types not allowed at 'configChanges' with value 'keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize'.
Turns out to use Admob you need to change build target to be at least 13. You can still let your app run on lower Android versions even your build target is 13. build target is only for build. Your minimum required OS version is specified by uses-sdk minSdkVersion.
http://stackoverflow.com/questions/7902121/admob-cant-display-ads-because-of-configchanges
Android: Project build target vs minSdkVersion
Project build target is the SDK version you build your app with. minSdkVersion is the minimum SDK version you want your app to run on.
You want to build your app with latest SDK since it may contain bug fixes not available in older SDK, but you can still run your app on an older SDK. For example, you can set project build target to be 15 (Android 4.0) but set minSdkVersion to be 8 (Android 2.2).
You set project build target by right click your project and choose "properties", then go to "Android" and select "Project build target".
You set minSdkVersion by setting <uses-sdk android:minSdkVersion="x" /> in AndroidManifest.xml, before application node.
You want to build your app with latest SDK since it may contain bug fixes not available in older SDK, but you can still run your app on an older SDK. For example, you can set project build target to be 15 (Android 4.0) but set minSdkVersion to be 8 (Android 2.2).
You set project build target by right click your project and choose "properties", then go to "Android" and select "Project build target".
You set minSdkVersion by setting <uses-sdk android:minSdkVersion="x" /> in AndroidManifest.xml, before application node.
Android: Eclipse no output in LogCat
For unknown reason my eclipse has no output in LogCat. This is very bad since the output at exception goes to LogCat, without which it is impossible to know what is wrong when app is forced to close.
Luckily found a solution on Stackoverflow: switch to DDMS perspective and click the device running the app, then switch back to Java perspective.
http://stackoverflow.com/questions/2250112/why-doesnt-logcat-show-anything-in-my-android
Luckily found a solution on Stackoverflow: switch to DDMS perspective and click the device running the app, then switch back to Java perspective.
http://stackoverflow.com/questions/2250112/why-doesnt-logcat-show-anything-in-my-android
Monday, 26 March 2012
Alternative to iOS UDID
Apple has started to reject apps using UDID
Things to do:
1. update Flurry
2. update Admob
https://groups.google.com/forum/?fromgroups#!topic/google-admob-ads-sdk/dz7cep43THA
3. update AdWhirl
Replace UDID with
http://stackoverflow.com/questions/9858850/an-alternative-to-the-device-udid-preparing-ourselves
Things to do:
1. update Flurry
2. update Admob
https://groups.google.com/forum/?fromgroups#!topic/google-admob-ads-sdk/dz7cep43THA
3. update AdWhirl
Replace UDID with
CFUUIDCreate
http://stackoverflow.com/questions/9858850/an-alternative-to-the-device-udid-preparing-ourselves
Sunday, 25 March 2012
Error: iPhone Distribution: No profiles currently match
Saw this error when building for distribution.
solution at stackoverflow:
http://stackoverflow.com/questions/3608851/iphone-distribution-no-profiles-currently-match
Basically
1. in organizer under library/certificates refresh to get new developer and distribution certificates
2. go to provisioning portal, under provisioning/distribution, create a new provisioning profile, download and double click.
solution at stackoverflow:
http://stackoverflow.com/questions/3608851/iphone-distribution-no-profiles-currently-match
Basically
1. in organizer under library/certificates refresh to get new developer and distribution certificates
2. go to provisioning portal, under provisioning/distribution, create a new provisioning profile, download and double click.
Error: application executable is missing a required architecture armv6
Saw this error when submitting iOS app to itunes connect.
http://stackoverflow.com/questions/7053466/application-executable-is-missing-a-required-architecture-armv6
Basically change architecture from "armv7" to "armv6 armv7".
Monday, 19 March 2012
Give your function a bad name and ruins it all
I do not know about other compilers, but I happen to use a compiler which does not protect system function names, that is, if you define a function read(), it overrides some system function read() and messed up all kinds of stuff. If you rename it as readxxx(), then everything works.
This sounds really stupid, but be careful when you name your functions. Adding a prefix maybe a good idea.
This sounds really stupid, but be careful when you name your functions. Adding a prefix maybe a good idea.
Tuesday, 6 March 2012
Android: things to do before publishing your app
Assuming Eclipse is used to develop Android app. After the development work is done, before publishing your app in Google Play or Amazon Android Store, there are still several things need to be done:
Add proguard.config=proguard.cfg to project.properties. Although proguard.config is automatically generated, it is not turned on by default. You need to manually add it to project.properties to enable it in your building process, otherwise your apk file contains symbols which are easier to be reverse engineered.
Disable debug in AndroidManifest.xml, then export the app. Check proguard directory is created under your project directory. mapping.txt contains mapped symbols whereas seeds.txt contains unmapped symbols. Send the apk file to your email and open it on your Android device, then you can install it on your Android device and test it. You can also put it on a private web server and download it on your Android device to install it.
Create an EULA and a dialog to show EULA to the user the first time the app is run. Unlike iPhone App Store, Google Play does not have a general EULA for Android apps, so you had better to have one by yourself to provide at least some shelter.
Add proguard.config=proguard.cfg to project.properties. Although proguard.config is automatically generated, it is not turned on by default. You need to manually add it to project.properties to enable it in your building process, otherwise your apk file contains symbols which are easier to be reverse engineered.
Disable debug in AndroidManifest.xml, then export the app. Check proguard directory is created under your project directory. mapping.txt contains mapped symbols whereas seeds.txt contains unmapped symbols. Send the apk file to your email and open it on your Android device, then you can install it on your Android device and test it. You can also put it on a private web server and download it on your Android device to install it.
Create an EULA and a dialog to show EULA to the user the first time the app is run. Unlike iPhone App Store, Google Play does not have a general EULA for Android apps, so you had better to have one by yourself to provide at least some shelter.
Android: write string to a file
FileOutputStream os = new FileOutputStream(fileName);
OutputStreamWriter osw = new OutputStreamWriter(os);
osw.write(str);
osw.flush();
osw.close();
Do not use
DataOutputStream os = new DataOutputStream(new FileOutputStream(file));
os.writeUTF(str); // this will write a weird char at the beginning of the file
os.writeUTF writes the string with a modified UTF-8 encoding. If the file is opened by WebView as HTML, a weird char shows up at the beginning of the file.
Sunday, 26 February 2012
Android: ProgressDialog does not show up
Android ProgressDialog can be used like iOS ActivityIndicator to show the app is blocked on some time-consuming work.
You cannot just show a ProgressDialog and then busy waiting a flag. If so the dialog will not show up. You need to busy wait in another thread.
Example:
You cannot just show a ProgressDialog and then busy waiting a flag. If so the dialog will not show up. You need to busy wait in another thread.
Example:
final ProgressDialog dialog = ProgressDialog.show(
MyActivity.this, "",
"Loading. Please wait...", true);
new Thread(new Runnable() {
public void run() {
while (!isWorkDone)
Thread.yield();
dialog.dismiss();
}
}).start();
Android: optimize app loading time
No one likes to wait for an app to launch for more than several seconds. It is critical to optimize the app loading time.
My app loads a large 3D model at launching. It is a custom binary format consisting of vertex array and index array. The vertex format is interleaved since it is from an iOS app. Each vertex is 8 floats: 3 for position, 3 for normal, and 2 for texture coordinates.
It is not suitable for Android if you use Java instead of NDK. So I write a small Java program to convert the binary file so that there is a position array, a normal array and a texture coordinates array. When I load the mesh, I can read all positions to a byte array, wrap it as a byte buffer, use it as a float buffer, then copy it to a direct float buffer, which could be used by OpenGL ES. Same for normals and tex coords. This optimization speeds up the file I/O by 50%. Although it is still kind of slow (4 secs). Most of time is spent copying from the byte array wrapped as float buffer to the direct float buffer.
Since 4 seconds is still slow for launching an app, I ran the mesh loading code in another thread, and add a flag for mesh ready. If mesh is not ready, the render code will skip rendering the mesh. With this change, the app is very fast to load.
My app loads a large 3D model at launching. It is a custom binary format consisting of vertex array and index array. The vertex format is interleaved since it is from an iOS app. Each vertex is 8 floats: 3 for position, 3 for normal, and 2 for texture coordinates.
It is not suitable for Android if you use Java instead of NDK. So I write a small Java program to convert the binary file so that there is a position array, a normal array and a texture coordinates array. When I load the mesh, I can read all positions to a byte array, wrap it as a byte buffer, use it as a float buffer, then copy it to a direct float buffer, which could be used by OpenGL ES. Same for normals and tex coords. This optimization speeds up the file I/O by 50%. Although it is still kind of slow (4 secs). Most of time is spent copying from the byte array wrapped as float buffer to the direct float buffer.
Since 4 seconds is still slow for launching an app, I ran the mesh loading code in another thread, and add a flag for mesh ready. If mesh is not ready, the render code will skip rendering the mesh. With this change, the app is very fast to load.
Android: run time-consuming work in another thread
Many times you want to run time-consuming work in another thread instead of main thread because UI will stop response if you run it in main thread, and the user will frustrate.
It is easy to implement:
Just wrap up the time-consuming code with
It is easy to implement:
Just wrap up the time-consuming code with
new Thread(new Runnable() {
public void run() {
//... time-consuming work
}
}).start();
Friday, 24 February 2012
Android: Using Flurry to gather information
Flurry is a mobile app analytics service for many platforms including iOS and Android.
It is very easy to use. Just register an account, then create a new app. It will generate an app id for your new app, then you can download its SDK.
For Android, you just need to add an external Jar to your Eclipse project, then add severa lines of code to init and end Flurry sessions. You can then log events in your app.
Some usages:
It is very easy to use. Just register an account, then create a new app. It will generate an app id for your new app, then you can download its SDK.
For Android, you just need to add an external Jar to your Eclipse project, then add severa lines of code to init and end Flurry sessions. You can then log events in your app.
Some usages:
- log screen size and dpi, so that you will know distribution of screen sizes, and prioritize your layout effort
- log time spent in time-consuming procedures, to prioritize optimization effort
- log frame rate for OpenGL ES, so that you may exclude some slow device from your app
- log supported OpenGL ES version. If OpenGL ES 2.0 is supported by 90% devices, you may start working on it
- log pages viewed, to rank the pages
Java: inline initializer for Map
Flurry has a function
FlurryAgent.logEvent(String event, Map<String,String> parameters)
It can be called with an inline initializer like this
FlurryAgent.logEvent("event", new HashMap<String,String>() {{
put("key1","val1");
put("key2","val2");
}});
FlurryAgent.logEvent(String event, Map<String,String> parameters)
It can be called with an inline initializer like this
FlurryAgent.logEvent("event", new HashMap<String,String>() {{
put("key1","val1");
put("key2","val2");
}});
Timing in Java
Timing in Java is easy.
long start = System.nanoTime();
long time = System.nanoTime()-start;
The unit is nanosecond. The long type is enough for 300 years.
long start = System.nanoTime();
long time = System.nanoTime()-start;
The unit is nanosecond. The long type is enough for 300 years.
Wednesday, 22 February 2012
Eclipse CDT: index not working
Eclipse CDT indexing of source codes is very important for large projects. Without it you cannot highlight a class or function and right click "open declaration". Manually go through the source tree to find a file is a nightmare.
The index of my C++ project was suddenly broken. I cannot highlight a class member and right click "open declaration". Also when I edit a header file, the outline panel does not show the class members.
I find this link
http://wiki.eclipse.org/CDT/User/FAQ#Why_does_Open_Declaration_.28F3.29_not_work.3F_.28also_applies_to_other_functions_using_the_indexer.29
but it does not work for me.
Then it occurs to me Eclipse no longer treated my project as a C++ project. Finally I got a solution:
Choose menu File/New/Convert to a C/C++ project. It took a while to index the source code. After that, everything works like a charm.
The index of my C++ project was suddenly broken. I cannot highlight a class member and right click "open declaration". Also when I edit a header file, the outline panel does not show the class members.
I find this link
http://wiki.eclipse.org/CDT/User/FAQ#Why_does_Open_Declaration_.28F3.29_not_work.3F_.28also_applies_to_other_functions_using_the_indexer.29
but it does not work for me.
Then it occurs to me Eclipse no longer treated my project as a C++ project. Finally I got a solution:
Choose menu File/New/Convert to a C/C++ project. It took a while to index the source code. After that, everything works like a charm.
Sunday, 19 February 2012
Android: taking screenshots
In eclipse, choose DDMS perspective, then click the button for screenshot.
Android: null pointer exception after changing view id in xml
Sometimes after you modify view id in layout xml files, you get null pointer exception in seemingly irrelevant code due to findViewById returns null when finding irrelevant views.
This is due to modifying view id messed up the view id definitions of all views. A simple solution is to clean up your project and rebuild it (usually it should be auto rebuilt after cleaning up).
This is due to modifying view id messed up the view id definitions of all views. A simple solution is to clean up your project and rebuild it (usually it should be auto rebuilt after cleaning up).
Wednesday, 15 February 2012
OpenGL: objects with color are displayed as black or white
When displaying with OpenGL, if the normals are not normalized, the objects may become black if the normals are too small and white if the normals are too big.
two constants affects normalization
GL_NORMALIZE: normalize normals. it is slower
GL_RESCALE_NORMAL: only scale normals based on scaling factor in modelview matrix. faster than GL_NORMALIZE. If the normals are not normalized from beginning, this won't fix them.
two constants affects normalization
GL_NORMALIZE: normalize normals. it is slower
GL_RESCALE_NORMAL: only scale normals based on scaling factor in modelview matrix. faster than GL_NORMALIZE. If the normals are not normalized from beginning, this won't fix them.
Android: artifacts in OpenGL ES display when dithering disabled
Android shows artifacts in OpenGL ES display when dithering is disabled. This is because by default the OpenGL view uses 16 bit color for performance reasons. Not all Android device supports 32 bit color. For better display dithering should not be disabled.
Tuesday, 14 February 2012
Android: exception when downloading web page without network connection
URL url = new URL("http://blah");
InputStream is = null;
is = url.openStream();
When there is no network connection, is is null. Using it will cause null pointer exception.
Check to make sure is is not null before using it.
Monday, 13 February 2012
Android: App name and icon not updated
Sometimes after you modify app name and icon through AndroidManifest.xml and rebuild it and install it, but the app name and icon on your device do not change. You may need to uninstall it and install again to see the change.
Android: How to delete development apps
Development apps installed through eclipse cannot be deleted by Android Market.
At home screen, press menu button, select Uninstall, then click the apps you want to install. Press back button once it is done.
At home screen, press menu button, select Uninstall, then click the apps you want to install. Press back button once it is done.
Android: OpenGL ES: segfault displaying modified vertex array
I fixed a bug causing segfault when displaying a modified vertex array with OpenGL ES in Android.
Problem: I have an activity which contains an OpenGL view and a list view on top of it. The OpenGL view displays a vertex array by glDrawElements. Each time the user tap a list item in the list view, the onClick event handler will update the vertex array used by the OpenGL view. Segmentation fault will happen if I click the list items enough number of times (usually 4-10 times).
The vertex array is a FloatBuffer. When I modify it I call position() to move the position to the end of the buffer, then call put(value) in a loop to add new vertices. After that, I call position(0) to move the position to the beginning of the buffer.
I found that instead of using put(value), if I use put(position, value) to add new vertices, I won't get segfault. Then I noticed some weird triangles were displayed shortly when I click the list view.
Then I realized the event handler of the list view uses different threads than the thread displaying the vertex array in the OpenGL view. Therefore if I change the position attribute of the FloatBuffer, it could be used by the drawing thread and cause segfault.
Lessons learned:
1. make sure vertex array is not modified by other threads when displaying it
2. when debugging this kind of problem, try to removing problematic codes part by part until narrow down to one line of code
Problem: I have an activity which contains an OpenGL view and a list view on top of it. The OpenGL view displays a vertex array by glDrawElements. Each time the user tap a list item in the list view, the onClick event handler will update the vertex array used by the OpenGL view. Segmentation fault will happen if I click the list items enough number of times (usually 4-10 times).
The vertex array is a FloatBuffer. When I modify it I call position() to move the position to the end of the buffer, then call put(value) in a loop to add new vertices. After that, I call position(0) to move the position to the beginning of the buffer.
I found that instead of using put(value), if I use put(position, value) to add new vertices, I won't get segfault. Then I noticed some weird triangles were displayed shortly when I click the list view.
Then I realized the event handler of the list view uses different threads than the thread displaying the vertex array in the OpenGL view. Therefore if I change the position attribute of the FloatBuffer, it could be used by the drawing thread and cause segfault.
Lessons learned:
1. make sure vertex array is not modified by other threads when displaying it
2. when debugging this kind of problem, try to removing problematic codes part by part until narrow down to one line of code
Sunday, 29 January 2012
Endianness issues in Java
Java is big endian. C endianness depends on CPU. In iphone simulator on x86 based Mac, C is little endian. Jave has problem loading a binary file saved by an iphone app run in iphone simulator. When saving the binary file to be opened by a Java program, each variable needs to reverse the byte order.
Android: cannot load files larger than 1MB
Android cannot load files larger than 1MB under assets or res/raw.
This is a known issue:
http://stackoverflow.com/questions/2860157/load-files-bigger-than-1m-from-assets-folder
The workaround is to rename the file as .png so that android won't zip it.
This is a known issue:
http://stackoverflow.com/questions/2860157/load-files-bigger-than-1m-from-assets-folder
The workaround is to rename the file as .png so that android won't zip it.
Monday, 16 January 2012
Android problem with ArrayList of String
I was trying to read a text file to an ArrayList of String. The code is like this:
public class SymptomCategory {private ArrayList<String> mCategory;public SymptomCategory (InputStream is) {String str="";StringBuffer buf = new StringBuffer();BufferedReader reader = new BufferedReader(new InputStreamReader(is));if (is!=null) {try {while ((str = reader.readLine()) != null) {System.out.println(str);mCategory.add(new String(str));buf.append(str + "\n" );}} catch (IOException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}public ArrayList<String> getCategory() {return mCategory;}}
The problem was that I can only read the first line of the file.
I fixed the problem by adding the following at the beginning of the constructor:
mCategory = new ArrayList<String>();
Sunday, 15 January 2012
Android problem: cannot find text file under assets
I put symp.txt under assets and use the following code to load it:
AssetManager am = context.getAssets();
InputStream is;
try {
is = am.open("symp.txt");
mCategory = new SymptomCategory(is);
} catch (IOException e) {
e.printStackTrace();
}
However I got exception when opening the file.
The problem was fixed by cleaning up the project and let it auto recompile.
Subscribe to:
Posts (Atom)