CmdConsole #:-)>
Introduction
During development of Android application, sometimes I want to write a small class and test its functions in a fashion similar to a normal desktop console program:
Read input from 'stdin
' and write output to 'stdout
'.
However, Android virtually requires all programs to be a GUI program. In order to fulfill the above needs, I decided to write a console-like program on Android which acts like normal desktop console: display a command prompt and let user issue commands to bring up a third-party program to execute.
Architecture Overview
As shown in the diagram, this CmdConsole
program consists of:
- Console GUI
- to forward user input to command dispatcher or the currently running custom console app
- to print output from currently running custom console app
- Command dispatcher (Run on its own thread)
- if user issues an internal command (e.g. 'ls', 'cd', 'del', etc), forward it to appropriate module to execute
- if user wants to run an external console app, ApkRunner is launched to execute it
- ApkRunner (Run on its own thread)
- brings up the custom console app to run
Console GUI
Screenshot 1: Console running built-in commands
Screenshot 2: Console running a 3rd party app
Console UI is very simple. On the top, you got an edit box to input command; on the top right is an 'ENTER' button; and finally below is the area like normal console to output text.
The text view which outputs text is actually a modified ImageView
. Its 'onDraw
' method is overridden to draw the output text line by line.
ApkLoader
It is a class (not shown in the diagram) to load a third party apk and retrieve the 'main
' entry point method inside the apk for later execution. The magic to load the apk is performed by 'dalvik.system.DexClassLoader
', which is a class inside the Android SDK. This class isn't difficult to use. Only a few function calls can load and retrieve the method of any class you want. The logic to retrieve the 'main
' method resides in ApkLoader.loadEntryPoint()
. The 'main
' method is then passed to ApkRunner
to execute.
One point to note here is: loading a third party apk by 'DexClassLoader
' will generate a 'dex' file for caching purpose. It is not bad but when you want to run a newer version of the third party app and the original cached 'dex' file still exists, sometimes, not always, the class loader failed strangely. Because of this, CmdConsole
will remove the cached 'dex' file every time before the apk is loaded.
Writing a 3rd Party Console App
Let's start writing a console app so as to consolidate your experience with it. Here, I only list the steps to build a project by command line tools but it is perfectly OK to build a project by Eclipse.
- Create a project using android SDK script:
> android create project \
--target 3 \
--name MyHello \
--path ./MyHello \
--activity MyHelloActivity \
--package com.xyz.testhello
- The
Activity
class is useless. Just delete it. Comment out the whole "application
" tag in "AndroidManifest.xml" as they are useless, too:
Create a class, say 'MyHello
', with a static
method "main(HashMap<Integer, Object> args)
" and use the 'CmdApp
' (a helper class in the source attached) to initialize the arguments passed in:
package com.xyz.testhello;
import java.util.HashMap;
import com.sss.consolehelper.CmdApp;
public class MyHello
{
public static void main(HashMap<Integer, Object> args)
{
CmdApp cmdApp = new CmdApp(args);
...
..
..
.
}
}
The 'args
' passed in is a hash table (constructed inside ApkRunner
before calling 'main
') containing some environment variables useful to a console app. Please see appendix for the content of the hash table.
The 'CmdApp
' is a helper class, provided in the sample console apps, to retrieve the content inside 'args
' and provide convenient functions (like reading a line from stdin
) to access and use the content. After initializing 'CmdApp
', just write your console program as usual.
Delete all unnecessary resource files inside 'res'.
Create a file "res/raw/entrypoint.txt" with the following single-line content:
com.xyz.testhello.MyHello
This states the class which has the entry point function "main
".
Inside 'build.properties
' at the project root, add this line:
source.dir=src:../_consolehelper_src
It is the root path where com.sss.consolehelper.CmdApp
resides, thus, CmdApp
can be found and built later on. (For Eclipse users, right click your project in 'Package Explorer' ? 'Build Path' ? 'Link Source ...' to add the linking.)
Compile the project by:
> ant debug
Put the resultant "apk" file to anywhere of the file system in the emulator/phone. I usually happen to put it at the root of the SD card. To put it there, just type:
> adb push MyHello.apk /sdcard/MyHello.apk
Launch CmdConsole
and run the apk as shown in the screenshot above:
> run /sdcard/MyHello.apk
or
> cd /sdcard
> run MyHello.apk
Appendix
Console Menu
When running a 3rd party app, currently, 2 menu options can be selected:
Kill Running App
Actually, this option only sends a java.lang.InterruptedException
to the running app and so this does not ensure the running app can be killed. The 'InterruptedException
' only breaks out functions like 'wait
', blocking 'read
', 'sleep
' and 'join
'.
If the running app does not hang on one of these functions or the app has a try
-catch
statement that catches all exceptions, this option can't break your 3rd party app.
Kill Console
This one will call 'android.os.Process.killProcess
' to kill CmdConsole
itself.
Arguments Passed to the Console App
The entry point 'main
' method of a console app should be declared as:
public static void main(HashMap<Integer, Object> args)
The arguments passed in by 'args
' is a hash table with the following content:
args.get(0) | android.app.Application | application context |
args.get(1) | String[] | array of command line arguments; the 0th element is the start of arguments (which is *NOT* the program name); may be null if no arguments |
args.get(2) | java.io.InputStream | act as stdin for the console program |
args.get(3) | java.io.PrintStream | act as stdout for the console program |
args.get(4) | String | specifies the stream (stdin /stdout ) encoding, which is "UTF-8" at present |
It is recommended to use 'CmdApp
' to indirectly access the content.
Available Internal Commands
Typing 'help
' in CmdConsole
can list the available commands and typing 'help [command]
' will print a more detailed description of the command.
Currently available commands are:
- help
- ls
- pwd
- cd
- clear
- run
- history
- del
- mkdir
- ren
- cp
- cleardex
- ver
- sres
- netinfo
- fontsize
- exit
AndroidManifest.xml of CmdConsole
Since the 3rd party console app is actually running in the context of CmdConsole
, thus, the 3rd party app is restricted by the permission set inside 'AndroidManifest.xml' of CmdConsole
. Currently, CmdConsole
only states the permissions of 'WRITE_EXTERNAL_STORAGE
', 'INTERNET
', 'ACCESS_WIFI_STATE
' and 'ACCESS_NETWORK_STATE
'. You may want to add more if you want to write a 3rd party app with more access rights.
History
- 30th May, 2011: Initial post