最近在思索着Android命令行下可以执行C语言写的命令行程序(Android是基于Linux的,那么Linux支持的,在Android上也是支持的),那么Android上层基于JAVA的Dalvik虚拟机是否也可以让JAVA程序在命令行下执行,答案是肯定的,我们发现在Android源码目录下的frameworks/base/cmds/目录下有相应的程序,可将普通的JAVA程序打包成JAR,让其运行于Dalvik虚拟机上,而不是标准的JVM上。为了对其研究实践一番,编写了如下例子:
Android4.0状态栏控制命令:基于Android4.0.4系统,需要在源码环境下编译(即需要有Android源码和编译环境),实现功能有:
1、控制整个状态栏的显示、隐藏;
2、控制BACK、HOME、RECENT这三个按键的显示、隐藏;
首先,我们在Android源码目录下的frameworks/base/cmds目录下创建sbstate目录(StatusBarState),该目录下包含Android.mk、sbstate两文件和src目录,其中src目录下包含了整个JAVA包的层级目录及类源码,其路径结构为src/com/cmds/xinu/Sbstate.java。
Sbstate.java源码如下:
package com.cmds.xinu;
import android.content.Context;
import android.os.ServiceManager;
import android.os.RemoteException;
import android.view.View;
import com.android.internal.statusbar.IStatusBarService;
public class Sbstate {
public static void main(String[] args) {
(new Sbstate()).run(args);
}
private void run(String[] args){
if (args.length < 1){
showUsage();
return;
}
String command = args[0];
IStatusBarService mBarService;
mBarService = IStatusBarService.Stub.asInterface(
ServiceManager.getService(Context.STATUS_BAR_SERVICE));
if (command.equals("sbvis")){
try {
mBarService.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);//View.STATUS_BAR_VISIBLE
} catch (RemoteException ex) {
}
System.out.println("Status Bar Visible!");
} else if (command.equals("sbdis")){
try {
mBarService.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);//View.STATUS_BAR_HIDDEN
} catch (RemoteException ex) {
}
System.out.println("Status Bar Disable/Unvisible!");
} else if (command.equals("bvis")){
try{
mBarService.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE & ~View.STATUS_BAR_DISABLE_BACK);
} catch (RemoteException ex) {
}
System.out.println("Back button visible!");
} else if (command.equals("bdis")){
try{
mBarService.setSystemUiVisibility(View.STATUS_BAR_DISABLE_BACK);
} catch (RemoteException ex) {
}
System.out.println("Back button Disable/Unvisible!");
} else if (command.equals("hvis")){
try{
mBarService.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE & ~View.STATUS_BAR_DISABLE_HOME);
} catch (RemoteException ex) {
}
System.out.println("Home button visible!");
} else if (command.equals("hdis")){
try{
mBarService.setSystemUiVisibility(View.STATUS_BAR_DISABLE_HOME);
} catch (RemoteException ex) {
}
System.out.println("Home button Disable/Unvisible!");
} else if (command.equals("rvis")){
try{
mBarService.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE & ~View.STATUS_BAR_DISABLE_RECENT);
} catch (RemoteException ex) {
}
System.out.println("Recent button visible!");
} else if (command.equals("rdis")){
try{
mBarService.setSystemUiVisibility(View.STATUS_BAR_DISABLE_RECENT);
} catch (RemoteException ex) {
}
System.out.println("Recent button Disable/Unvisible!");
}
}
private void showUsage(){
System.err.println("Usage:sbstate [sbvis|sbdis|bvis|bdis|hvis|hdis|rvis|rdis]");
System.err.println(" sbvis:statusbar visible");
System.err.println(" sbdis:statusbar disable");
System.err.println(" bvis:back button visible");
System.err.println(" bdis:back button disable");
System.err.println(" hvis:home button visible");
System.err.println(" hdis:home button disable");
System.err.println(" rvis:recent button visible");
System.err.println(" rdis:recent button disable");
}
}
该部分源码都是调用Android的API,之前一直犯难的是在如何获取StatusBar相关对象,后来查看了cmds目录下的其他文件,发现基本上是通过调用Interface来获取实例,其他都是标准的JAVA语言代码。
sbstate文件内容如下:
base=/system
export CLASSPATH=$base/framework/sbstate.jar
exec app_process $base/bin com.cmds.xinu.Sbstate $*
该部分内容主要是设置CLASSPATH环境变量为我们编译出来的JAR文件,即指定了我们的程序位置,Android源码编译完后会放至/system/framework目录下,接下来使用Linux命令下的exec命令来调用app_process命令执行我们的JAVA程序,其中app_process命令位于源码目录下的frameworks/base/cmds/app_process目录下,其命令的使用方法如下:
Usage: app_process [java-options] cmd-dir start-class-name [options]
其中我们文件里的$base/bin对应的是命令所在的目录,com.cmds.xinu.Sbstate为我们的JAVA代码的入口类,即main函数所在的类名,而$*是将命令行输入的任何参数传递作为main函数的参数列表。
Android.mk文件内容:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES := $(call all-subdir-java-files)
LOCAL_MODULE := sbstate
include $(BUILD_JAVA_LIBRARY)
include $(CLEAR_VARS)
ALL_PREBUILT += $(TARGET_OUT)/bin/sbstate
$(TARGET_OUT)/bin/sbstate : $(LOCAL_PATH)/sbstate | $(ACP)
$(transform-prebuilt-to-target)
其中文件是参考其他同目录下的Android.mk文件,其功能主要是将JAVA文件编译成JAVA库JAR文件,并把sbstate文件复制到目标目录的system/bin目录下,我这边测试时是手动把sbstate文件复制到/system/bin目录下,此时需要注意该文件的权限,不然会有Permission Deny之类的提示。
由于我们放在源码里编译,如果直接这样mm一下,是会出错的,我们还需要修改Android源码的编译配置,涉及到如下文件:
1、build/core/legacy_prebuilts.mk
对应于Android.mk里要用到的prebuilt功能,主要是把sbstate文件复制到/system/bin目录下,不加的话会提示预编译失败;
在文件中修改GRANDFATHERED_ALL_PREBUILT配置项,添加上sbstate。
2、build/core/user_tags.mk
对应于Android.mk里要用到的编译为JAR功能,在文件中修改GRANDFATHERED_USER_MODULES配置项,添加上sbstate。
至此,整个编译配置及源码就实现好了,接下来只需要mm一下,复制或者烧录一下,就可以在命令行(串口或者ADB)下大胆的敲上sbstate命令,会有如下提示:
Usage:sbstate [sbvis|sbdis|bvis|bdis|hvis|hdis|rvis|rdis]
sbvis:statusbar visible
sbdis:statusbar disable
bvis:back button visible
bdis:back button disable
hvis:home button visible
hdis:home button disable
rvis:recent button visible
rdis:recent button disable
此时,我们可以输入如下形式的命令来执行相应的功能:
1、显示状态栏:sbstate sbvis
2、隐藏状态栏:sbstate sbdis
3、显示BACK按钮图标:sbstate bvis
4、隐藏BACK按钮图标:sbstate bdis
5、显示HOME按钮图标:sbstate hvis
6、隐藏HOME按钮图标:sbstate hdis
7、显示RECENT按钮图标:sbstate rvis
8、隐藏RECENT按钮图标:sbstate rdis
好了,亲手尝试一下吧,见证状态栏的风起云涌,变幻莫测吧。
网友评论