65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Java 在 Android 上编写控制台应用程序

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.69/5 (10投票s)

2011年5月30日

Apache

5分钟阅读

viewsIcon

111136

downloadIcon

3435

为测试目的在 Android 上编写控制台应用程序。

CmdConsole #:->

logo.png

引言

在开发 Android 应用程序时,有时我想编写一个小的类,并以类似于普通桌面控制台程序的方式测试其功能。

从 'stdin' 读取输入,将输出写入 'stdout'。

然而,Android 实际上要求所有程序都必须是 GUI 程序。为了满足上述需求,我决定在 Android 上编写一个控制台类程序,该程序可以像普通的桌面控制台一样工作:显示一个命令提示符,并让用户发出命令来启动第三方程序执行。

架构概述

arch.png

如图所示,这个 CmdConsole 程序由以下部分组成:

  • 控制台 GUI
    • 将用户输入转发给命令分发器或当前正在运行的自定义控制台应用程序 
    • 打印当前正在运行的自定义控制台应用程序的输出
  • 命令分发器(在自己的线程上运行)
    • 如果用户发出内部命令(例如 'ls'、'cd'、'del' 等),则将其转发给相应的模块执行
    • 如果用户想运行外部控制台应用程序,则启动 ApkRunner 来执行它
  • ApkRunner(在自己的线程上运行)
    • 启动自定义控制台应用程序来运行

控制台 GUI

screenshot1.png

截图 1:运行内置命令的控制台

screenshot2.png

截图 2:运行第三方应用程序的控制台

控制台 UI 非常简单。顶部有一个用于输入命令的编辑框;右上角是一个 'ENTER' enter.png 按钮;最后下面是像普通控制台一样用于输出文本的区域。
输出文本的 TextView 实际上是一个修改过的 ImageView。其 'onDraw' 方法被重写,逐行绘制输出文本。

ApkLoader

它是一个类(在图中未显示),用于加载第三方 apk 并检索 apk 内的 'main' 入点方法以供以后执行。加载 apk 的魔法是通过 'dalvik.system.DexClassLoader' 来实现的,它是 Android SDK 中的一个类。这个类不难使用。只需几次函数调用即可加载并检索您想要的任何类的该方法。检索 'main' 方法的逻辑位于 ApkLoader.loadEntryPoint() 中。然后将 'main' 方法传递给 ApkRunner 执行。

需要注意的一点是:使用 'DexClassLoader' 加载第三方 apk 会生成一个 'dex' 文件用于缓存。这本身没什么不好,但当您想运行第三方应用程序的新版本且原始缓存的 'dex' 文件仍然存在时,有时(并非总是)类加载器会奇怪地失败。因此,CmdConsole 在每次加载 apk 之前都会删除缓存的 'dex' 文件。

编写第三方控制台应用程序

让我们开始编写一个控制台应用程序,以巩固您对它的经验。在这里,我只列出通过命令行工具构建项目的步骤,但通过 Eclipse 构建项目也是完全可以的。

  1. 使用 android SDK 脚本创建项目
    > android create project \
    --target 3 \
    --name MyHello \
    --path ./MyHello \
    --activity MyHelloActivity \
    --package com.xyz.testhello
  2. Activity 类无用,将其删除即可。
  3. 在 "AndroidManifest.xml" 中注释掉整个 "application" 标签,因为它们也无用。

    <!--
    <application android:label="@string/app_name" android:icon="@drawable/icon">
      <activity android:name="dummy"
                android:label="@string/app_name">
         <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
         </intent-filter>
      </activity>
    </application>
    -->
  4. 创建一个类,例如 'MyHello',其中包含一个 static 方法 "main(HashMap<Integer, Object> args)",并使用 'CmdApp'(源文件中提供的辅助类)来初始化传递的参数。

    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);
          ...
          ..
          .. // write your program here
          . 
       }
    }

    传递的 'args' 是一个哈希表(在调用 'main' 之前由 ApkRunner 构造),其中包含对控制台应用程序有用的环境变量。请参阅附录了解哈希表的内容。

    'CmdApp' 是一个辅助类,在示例控制台应用程序中提供,用于检索 'args' 中的内容,并提供方便的函数(如从 stdin 读取一行)来访问和使用内容。初始化 'CmdApp' 后,只需像往常一样编写控制台程序。

  5. 删除 'res' 目录下的所有不必要的资源文件。

    创建一个文件 "res/raw/entrypoint.txt",其中包含以下单行内容

    com.xyz.testhello.MyHello

    这指定了包含入口函数 "main" 的类。

  6. 在项目根目录的 'build.properties' 文件中,添加此行

    source.dir=src:../_consolehelper_src

    这是 com.sss.consolehelper.CmdApp 所在的根路径,因此,稍后可以找到并构建 CmdApp。(对于 Eclipse 用户,请在“包资源管理器”中右键单击您的项目?“构建路径”?“链接源...”来添加链接。)

  7. 通过以下方式编译项目

    > ant debug 
  8. 将生成的 "apk" 文件放置在模拟器/手机文件系统的任何位置。我通常会将其放在 SD 卡的根目录。要将其放置在那里,只需键入

    > adb push MyHello.apk /sdcard/MyHello.apk
  9. 启动 CmdConsole 并运行 apk,如上面的截图所示

    > run /sdcard/MyHello.apk

    > cd /sdcard
    > run MyHello.apk

附录

控制台菜单

运行第三方应用程序时,目前可以选择 2 个菜单选项

终止运行中的应用程序

实际上,此选项仅向正在运行的应用程序发送一个 java.lang.InterruptedException,因此不能保证运行中的应用程序能够被终止。 'InterruptedException' 仅会中断诸如 'wait'、阻塞的 'read'、'sleep' 和 'join' 等函数。
如果正在运行的应用程序没有挂起在这些函数之一上,或者应用程序有一个捕获所有异常的 try-catch 语句,则此选项无法中断您的第三方应用程序。

终止控制台

这将调用 'android.os.Process.killProcess' 来终止 CmdConsole 本身。

传递给控制台应用程序的参数

控制台应用程序的入口点 'main' 方法应声明为

public static void main(HashMap<Integer, Object> args)

通过 'args' 传递的参数是一个哈希表,其内容如下

args.get(0)  android.app.Application 应用程序上下文
args.get(1)  String[]  命令行参数数组;第 0 个元素是参数的开头(*不是*程序名);如果没有参数,则可能为 null
args.get(2)  java.io.InputStream 充当控制台程序的 stdin
args.get(3)  java.io.PrintStream 充当控制台程序的 stdout
args.get(4)  字符串 指定 stream stdin/stdout)的编码,目前为 "UTF-8"。

建议使用 'CmdApp' 来间接访问内容。

可用的内部命令

CmdConsole 中键入 'help' 可以列出可用的命令,键入 'help [command]' 将打印命令的更详细描述。

目前可用的命令有: 

  • 帮助
  • ls
  • pwd
  • cd
  • clear
  • run
  • 历史记录
  • del
  • mkdir
  • ren 
  • cp
  • cleardex
  • ver
  • sres
  • netinfo
  • fontsize
  • exit

CmdConsole 的 AndroidManifest.xml

由于第三方控制台应用程序实际上是在 CmdConsole 的上下文中运行的,因此第三方应用程序受 CmdConsole 的 'AndroidManifest.xml' 中声明的权限集的限制。目前,CmdConsole 仅声明了 'WRITE_EXTERNAL_STORAGE'、'INTERNET'、'ACCESS_WIFI_STATE' 和 'ACCESS_NETWORK_STATE' 的权限。如果您想编写一个具有更多访问权限的第三方应用程序,您可能需要添加更多权限。

历史

  • 2011 年 5 月 30 日:初次发布
© . All rights reserved.