Or, the case of the Android app out-of-order calls
On Android, there is a primary thread which runs all UI stuff. If a GUI operation is ran in a different thread, it just won't work, and may throw an error. If you block this thread with too much processing... well... bad things happen. Due to this design, you have to push all UI operations to this main thread using Looper.run().
Runnables pushed to this thread are always ran in FIFO execution order, which is a useful guarantee for programming.
So I decided to get smart and create the following function to add asynchronous calls that needed to be run on the primary thread. It takes a Runnable and either runs it immediately, if already on the Primary thread, or otherwise adds it to the Primary Thread’s queue.
//Run a function on primary threadpublicstaticvoid RunOnPrimary(RunnableR)
{
LooperL=MainActivity.getMainLooper();
//Start commenting here so that items are always added to the queue, forcing in-order processesingif(Looper.myLooper()==Looper.getMainLooper())
R.run();
else//End commenting herenewHandler(Looper.getMainLooper()).post(R);
}
I was getting weird behaviors though in a part of the project where some actions pulled in from JavaScript were happening before subsequent actions.
After the normal debugging one-by-one steps to figure it out, I realized that MAYBE some of the JavaScript calls were, for some bizarre reason, already running on the primary thread. In this case they would run immediately, before the queued items started coming in. This turned out to be the case, so I ended up having to comment out the first 3 lines after the function’s first comment (if/R.run/else), and it worked great.
I found it kind of irritating that I had to add actions to the queue when it could have been run immediately on the current thread, but oh well, I didn’t really have a choice if I want to make sure everything is always run in order across the system.
Seeing as there are a number of applications on the market that have both a “Free” and “Full” version, you’d think this would be an easy thing to accomplish. Unfortunately, the marketplace uses an application’s package name as its unique identifier, so both versions have to have a different package name, which is again, a bit of a nuisance.
One method of remedying this is just having a recursive string replace through all the files [...]
I spent a bit of time coming up with a solution for this a few months ago for my latest project, [TEMPORARILY DOWN**]. This solution uses a shared package with a shared code base and resources that the different project versions pull from.
**The project that is a great example of how this process works should be uploaded very soon. At that time this message will disappear and appropriate links will be added. You’ll know this has happened when I upload my next project.
The steps for this setup are as follows: (The source for [TEMPORARILY DOWN**] can be used as an example)
First some definitions that will be used below*:
ProjectName: The base name of the project (e.g. “MyAndroidProject”)
VersionName: The name of separate versions (e.g. “Free” and “Full”)
SharedName: The name of the shared section of the code (e.g. “Shared”)
BasePackageName: The base name of the package group (e.g. “com.example.MyAndroidProject”)
BasePackageDirectory: The base path of the package group (e.g. “com/example/MyAndroidProject”)
*Please note these definitions are used in code sections below.
Create the directory structure:
A base directory of ProjectName (e.g. “~/MyAndroidProject”)
A subdirectory under the base directory named SharedName for the shared files (e.g. “~/MyAndroidProject/Shared”). It will hold any shared files in appropriate Android standard directories (e.g. “res”, “src”).
Subdirectories under the base directory named VersionName for each version’s project (e.g. “~/MyAndroidProject/Free”). Each of these will hold its own complete project including the AndroidManifest.
Creating the shared resources: There’s nothing special about shared resources (probably in “SharedName/res”), except I suggest noting at the top of the files that they are shared, for reference sake.
Creating the shared code:
Shared code goes in “SharedName/src/BasePackageDirectory/SharedName” (e.g. “~/MyAndroidProject/Shared/src/com/example/MyAndroidProject/Shared”).
As noted for shared resources, you might want to note at the top of the files that they are shared.
Set the package name for shared code to “BasePackageName.SharedName” (e.g. “package com.example.MyAndroidProject.Shared;”).
Shared code should never directly refer back to a version’s package (non shared) code except through reflection.
Resource IDs are still accessible in this shared package through the “R” class, but when accessed the function or class that does the accessing needs to be proceeded with “@SuppressWarnings({ "static-access" })”. The “R” variable also has a “Version” member that can be used to alter shared code flow depending on the version being used. This will be explained more in detail later.
*BONUS*
If shared code needs to access information from a static member in a non-shared class, reflection can be used, for example:
A static function can be called in a similar manner through reflection:
Class.forName("BasePackageName."+R.Version.name()+".NonSharedClassName").getMethod("StaticMethodName", new Class[0]).invoke(null);
*BONUS* Global Shared Class: I also suggest having a shared class that holds global variables that allows easy interaction from the shared to non shared classes, and holds static information that both use, with members including:
A reference to a Context (or Activity) from the main program
The BasePackageName (needed for reflection, and other stuff like preference storage)
Other useful interfaces like logging
Creating the non-shared code:
Create a separate project for each version in their corresponding subdirectories listed in the third step of the Create the directory structure" section above.
Make the package name for a version as “BasePackageName.VersionName”.
When referencing shared classes form an android manifest, make sure to use their full package name, for example, a shared activity would look like “<activity android:name="BasePackageName.SharedName.ActivityName">”
Import the shared package into all non-shared class files “import BasePackageName.SharedName.*;”
Linking the shared code into each project:
The shared code now needs to get integrated into each project. To do this, all the shared files need to be symbolically (or hard) linked back into their corresponding directories for each version.
First, make sure each project directory also contains the same subdirectories as those found in the shared directory.
The script I have written for this, which needs to be put in the base directory, is as follows: [EDIT ON 2011-01-03 @ 6:28am] See here for a better copy of the script. [/EDIT]
#!/bin/bash
#Run this file to install links to shared files into all branches
LN="ln" #Use hard links, which work well in Windows and require less logic to calculate linking
#if [ `uname || grep -vi 'cygwin'` ]; then #If not windows (NOT YET SUPPORTED)
# LN="ln -s" #Use symbolic links, which take some additional logic that is not yet programmed
#fi
LocalBranches=`find -maxdepth 1 -type d | grep -iPv "^\.(/SharedName|)$"` #Find version names, ignoring "." ".." and the shared directory
#Propagate shared files into different versions
cd Shared
for i in $LocalBranches; do
find -type f -print0 | xargs -0 -i rm -f ../$i/{} #Clear out old links from version just in case the link has been undone
if [ "$1" != "clean" ]; then
find -type f -print0 | xargs -0 -i $LN {} ../$i/{} #Link shared files into directories
fi
done
Tying the resources together:
The resources IDs in the “R” class might need to be accessible by the shared classes. To do this, an “R” class needs to be added into the shared namespace that duplicates the information. Unfortunately, the Android generated resources ID class “R” marks everything as “final” so class duplication requires a bit more work than would be preferable.
Each different version needs its own “R” class, put into the directory “~/MyAndroidProject/VersionName/src/BasePackageDirectory/SharedName” that the shared code reads from.
This class will also contain version information so the shared classes can know which version it is currently interacting with.
The code is as follows:
package BasePackageName.SharedName;
import BasePackageName.VersionName.R.*;
public final class R //Mimic the resources from the (non-shared) parent project
{
//There may be more resource ID groups than these that need to be shared
final static attr attr=null;
final static drawable drawable=null;
final static id id=null;
final static layout layout=null;
final static string string=null;
final static xml xml=null;
final static array array=null;
public enum Versions { Version1, Version2 }; //List versions here
public final static Versions Version=Versions.CurrentVersion;
}
Whenever this “shared” “R” class is accessed, the function or class that does the accessing needs to be proceeded with “@SuppressWarnings({ "static-access" })”. This is due to the hack required to reproduce the “final” class information from the original “R” class into a shared class.
Working with shared projects and code in Eclipse:
When modifying shared code in Eclipse for any of the different versions, other version’s projects need to be refreshed to pick up the new code. I tried many different methods and processes to fix this different package name problem but this described method is still much quicker and easier than any of the others.
Yet another platform/library to learn. It never ends.
Having recently finished my first Android project (and hopefully not last), I decided to supply some notes I took about the process.
While I am going to try and keep impressions to a minimum on the rest of this post, and keep it to tangible notes, I must first comment that trying to find out things for the Android platform was often like pulling teeth. While its typical Java reference online documentation is all there with all the classes and cross-linking, that is about all it is, very dry and virtually useless beyond a base reference. The comments on variable parameters (and many other sections) in the reference are often coarse and not descriptive at all, for example, one parameter named mask has the basic description as “this is a mask”. Some functions don’t even have descriptions at all.
Perhaps I am getting too complacent as a programmer and getting used to excellent documentation like for Python or GTK (I’ve even grown to love Microsoft documentation after having used it for long enough!). After all, most required information is just a Google away, and being a programmer is often just about finding the proper magical incantations to hook into a certain library. Unfortunately, however, even web searches were often yielding less than fruitful results when dealing with Android, as the platform is relatively new.
Some useful tasks and some problems:
Using the virtual (soft) keyboard without a TextView:
Note: “getWindow().getDecorView()” can also be replaced by a View on your screen
Getting the keyboard input: Add the following function to the Activity that opened the keyboard:
@Override public boolean onKeyDown(int keyCode, KeyEvent msg)
Note: This will not work if you’re not using default keyboard input (like if it’s set to enter Japanese or Chinese characters).
Determining the physical dimensions of the screen:
This should be a trivial task using the DisplayMetrics (getWindowManager().getDefaultDisplay()) interface to get dpis and multiply by the screen dimensions getWindowManager().getDefaultDisplay().getWidth() (and .getHeight). However, it doesn’t always work as it should.
The best method to get the DPI would be to use “DisplayMetrics.xdpi” and “DisplayMetrics.ydpi”, but unfortunately, these are misreported by at least the Motorola Droid. I’ve found “DisplayMetrics.density”*160 to be pretty accurate, but if true accuracy is needed, a calibration screen might be required.
Inform user of touch events: Many Android widgets (Views) change their visual state (highlight) when the user presses down on them to let the user know something is going to happen if the user lifts their finger while still on the widget. Unfortunately, there seems to be no text widget or layout view that does this automatic highlighting by itself (ListViews do in groups). The following is some example code to produce this effect.
import android.view.View.OnTouchListener;
public class CLASSNAME extends Activity
{
@Override public void onCreate(Bundle savedInstanceState)
{
View HighlightView=findViewById(R.id.THE_VIEWS_ID);
HighlightView.setOnTouchListener(HighlightState);
}
private OnTouchListener HighlightState = new OnTouchListener() { public boolean onTouch(View v, MotionEvent event)
{
if(event.getAction()==MotionEvent.ACTION_DOWN)
v.setBackgroundColor(0xFF0000FF); //Set background color to blue
else if(event.getAction()==MotionEvent.ACTION_CANCEL || event.getAction()==MotionEvent.ACTION_UP)
v.setBackgroundResource(0); //No background color
return false;
} };
}
Retrieving the names and IDs of all resources in a resource group:
Setting a color matrix on an image: If you have 2 ImageViews that display the same resource image, and either has a color matrix set on it, the will both share one of the color matrices. If this occurs, copy the image the resource, or use a separate image resource. For kicks, here is an example of setting an inverse color matrix on an image.
requestWindowFeature(Window.FEATURE_NO_TITLE); //This must be called before "setContentView", and hides the title bar
getWindow().setFlags(FULLSCREEN ? WindowManager.LayoutParams.FLAG_FULLSCREEN : 0, WindowManager.LayoutParams.FLAG_FULLSCREEN); //Turns on/off the status bar
Starting another local activity: Instead of using Intent(String action) for Context.StartActivity, as the Reference explains, it is much easier to use Intent(Context packageContext, Class<?> cls) like the following: (called from inside an Activity)
Creating a timed event that updates the UI: A function running through java.util.Timer cannot interact with the GUI. One solution to make a timer is with the android.os.Handler interface.
import android.os.Handler;
public class ExampleActivity extends Activity
{
final int InitialDelay, RepeatDelay;
Handler TimedHandler=new Handler();
public void ExampleFunction()
{
TimedHandler.postDelayed(new Runnable() { public void run() {
//Do GUI stuff...
TimedHandler.postDelayed(this, RepeatDelay);
} }, InitialDelay);
}
}
Another solution is to post to a Handler from the Timer function.
When dealing with putting on the market place:
Getting an account to put applications on the Android Market cost $25.
Screenshots shown on the Android Market description page are somewhat buggy, and seemingly randomly either stretch properly or crop. Viewing the full sized screenshots does seem to work properly.
Seeing as there are a number of applications on the market that have both a “Free” and “Full” version, you’d think this would be an easy thing to accomplish. Unfortunately, the marketplace uses an application’s package name as its unique identifier, so both versions have to have a different package name, which is again, a bit of a nuisance.
One method of remedying this is just having a recursive string replace through all the files to change the package names. However, if using eclipse, so you don’t have to reopen it, it’s quicker to update the string first in the manifest, and then renaming the package under the “src” folder by pressing F2 (rename) on it when it is selected.
Also, unfortunately, if you do this, when a person upgrades from the lite to the full version, preferences are not automatically transferred :-\.
The publisher’s market place page is very sparse and leaves a lot to be desired. It also seems to update only once every 24 hours or so (not sure of exact times).
If an application is put up, it WILL get downloads immediately. For example, I put up an application with a description of “This is a test, do not download this” for doing security tests that I took down within like 10 minutes. It already had 2 comments/ratings on it within that time ~.~; .
Google Checkout: Fees. When a copy of your application is purchased, the user has 24 hours to return it. The money is not deposited into your bank account until after this time (when it’s not a weekend). If you want to give your application to someone for free, they need to purchase it through the market, and then you can cancel the purchase transaction before the 24 hours are up. Unfortunately, this has to be done every time they want to update the application. It also seems you cannot buy your own applications, as the purchase server throws an error.
Application Protection:
You can download any Android application by default from your phone to your computer, modify them, and reinstall them back to any phone. An example use for this would be to crack a shareware application where just a single byte probably needs to be changed to make it a full version.
The applications themselves are in an .apk file (which is just a .zip file), and the source code (classes) are encoded as a “Dalvik Executable” file within it (classes.dex), which as I understand it, is optimized Javabytecode. So, AFAIK right now, there is no way to decompile the .dex file back to the original source, like you can with normal Java. However, the Android emulator, part of the Android SDK, includes a tool called dexdump, which allows you to decompile it to bytecode.
The marketplace also has an option to turn on Copy Protection. When this is turned on for an application, the user cannot backup or access the applications package file. I would assume however with a rooted phone you could still grab it from “/data/app-private”, and the rest of the process should be the same. I have not tested this as rooting Android 2.1 is much more of a pain in the butt, ATM, than I want to deal with.