2 软件开发

在进行Android程序的开发之前,需要先配置好IDE。一个好的IDE可以极大地提高Android程序的开发效率。

2.1 Java环境搭建、版本选择、安装以及PATH设置

2.1.1 Window安装Java需要的工具

JDK:JDK是Java语言的软件开发工具包,它包含了Java的运行环境、工具集合、基础类库等内容。

Android SDK:Android SDK是谷歌提供的Android开发工具包。在开发Android程序时,我们需要引入该工具包,来使用Android相关的API。

Android Studio:Android Studio是谷歌推出一款官方的IDE工具,由于不再是以插件的形式存在,Android Studio 在开发Android程序时远比Eclipse强大和方便的多。

2.1.2 下载JDK

首先需要下载Java开发工具包JDK,下载地址如下:http://www.oracle.com/technetwork/java/javase/downloads/index.html

点击下图中红圈所示的下载按钮:

在下载页面中选择接受许可,并根据自己的系统选择对应的版本,我们以 Windows 64位系统为例:

下载后JDK的安装根据提示进行。安装JDK的同时也会自动安装JRE,可以一并安装。安装过程中可以自定义安装目录等信息,例如我们选择安装目录为 C:\Program Files (x86)\Java\jdk1.8.0_91。

2.1.3 配置环境变量

1.安装完成后,右击“我的电脑”,点击“属性”,选择“高级系统设置”;

2. 选择“高级”选项卡,点击“环境变量”;

3. 在“系统变量”中设置3项属性,”JAVA_HOME”, “PATH”, “CLASSPATH”(不限制大小写),若已存在则点击“编辑”,不存在则点击“新建”。

变量设置参数如下:

变量名

变量值

JAVA_HOME

C:\Program Files (x86)\Java\jdk1.8.0_91 //根据自己的实际路径配置

PATH

%JAVA_HOME%\bin;%JAVA_HOME%\jre\bin;

CLASSPATH

.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar; //注意前面有个"."

JAVA_HOME 设置

PATH设置

CLASSPATH 设置

这是 Java 的环境配置,配置完成后,启动 Eclipse 来编写代码,它会自动完成java环境的配置。

2.1.4 测试JDK是否安装成功

  1. “开始”->“运行”,键入“cmd”;

  2. 键入命令: java-version、java、javac 几个命令,出现以下信息,说明环境变量配置成功:

2.2 Android Studio版本、安装及环境搭建

2.2.1下载并安装android-studio

进入下载地址http://www.android-studio.org/,在页面点击下载Android Studio。

1.双击下载下来的安装包,双击安装包进行安装,然后点击“Next”,如下图所示:

2.勾选Android Virtual Device,然后点击“Next”,如下图所示:

3. 点击“I Agree”按钮,如下图所示:

4. 选择Android Studio安装位置和Android SDK安装位置,然后点击“Next”,如下图所示:

5. 点击“Install”按钮,如下图所示:

6. 安装过程如下图所示:

7. 直至安装完成,如下图所示:

8. 此时最好使用翻墙工具,可以访问到google,确保可以下载相应的文件,如下图所示:

9. 下载完成,点击”Finish”,如下图所示:

10. 如果需要继续安装其他版本SDK,打开Android Studio,点击Configure,如下图所示:

11. 选择SDK Manager,如下图所示:

12. 在打开的Default Settings中,点击”Launch Standalone SDK Manager“,如下图所示:

13. 在打开了的SDK Manager框下面,可以选择对应的SDK,然后翻墙进行下载,如下图所示:

2.2.2 用Android Studio配置NDK开发环境

Android Studio没有像Eclipse那样的一键add native support,相对来说比较麻烦。下面介绍在Android Studio中如何实现类似于Eclipse的add native support功能(即进行NDK开发的步骤)。

第一步:安装NDK

打开Tools->Android->SDK Manager->SDK Tools选中”LLDB”和”NDK”,点击确认,软件会自动安装NDK。

第二步:将so库导入libs目录下(project结构)

so库下载链接:https://pan.baidu.com/s/13BU8h57yne7aXoWsFdr58g

提取码:pu9y

第三步:同步资源库

2.2.3 Android Assistant下载:

链接:https://pan.baidu.com/s/1m__9Yz1tDAl1Q5aDHyrPPw

提取码:vw92

2.3 串口开发、demo简介、源代码提供链接

串口示例工程下载链接:https://pan.baidu.com/s/1ANmE8ddLMwMXR9YN9BjiNQ

提取码:fq36

2.3.1 串口通信

串口通信(Serial Communications),是指外设和计算机间通过数据信号线、地线、控制线等,按位进行传输数据的一种通讯方式。串口按位(bit)发送和接收字节。尽管比按字节(byte)的并行通信慢,但是串口可以在使用一根线发送数据的同时用另一根线接收数据。它很简单并且能够实现远距离通信。

串口通信最重要的参数是波特率、数据位、停止位和奇偶校验。对于两个进行通信的端口,这些参数必须匹配。

2.3.2 安卓屏和安卓程序间的串口通讯的实现

实现步骤:

1. 串口初始化——创建串口实例、设置串口参数(定义路径和波特率);

2. 获取输入流——读取串口数据;

3. 获取输出流——向串口发送数据;

4. 数据处理与显示;

5. 关闭串口。

2.3.3 串口Demo代码说明

第一步:导入so

1.拷贝相应架构下的so到项目的jinLibs文件夹

2. 在build.gradle的android标签下添加配置:

sourceSets {
main {
jni.srcDirs = []
jniLibs.srcDirs = ['src/main/jniLibs', 'libs']
}
}

第二步:拷贝的android_sericalport_api包下的所有文件到自己项目的java文件夹下,注意路径不能换

第三步:使用串口

1.初始化操作SerialPortManager

SerialPortManager.getInstances().initSerialPort();

2. 打开串口

// 获取串口工具类对象实例(该实例用于打开关闭串口和发送数据等)

SerialPortserialPort = SerialPortManager.getInstances().getSerialPort();

// 初始化读监听器

ReadListenerreadListener= new ReadListener() {
@Override
public void onRead(final String port, final booleanisAscii, final String read) {
Log.d("SerialPortRead", new StringBuffer()
.append("串口号:").append(port)
.append("\n数据格式:").append(isAscii ? "ascii" :"hexString")
.append("\nread:").append(read).toString());
}
};

// 用串口工具类对象实例打开串口传入读监听

serialPort.startSerialPort(SerialPortManager.ttyCOM0, false, readListener);// 打开串口/dev/ttyCOM0,读数据格式是HexString。

3. 修改读数据格式

serialPort.setReadCode(SerialPortManager.ttyCOM0, true);// 修改串口/dev/ttyCOM0读数据格式为Ascii

4. 发送数据(需先打开串口)

serialPort.writeSerialService(SerialPortManager.ttyCOM0, false, "我是要发送的数据");// 向串口/dev/ttyCOM0写数据,数据格式为HexString,数据内容为:"我是要发送的数据"

5. 关闭串口

serialPort.stopSerialPort(SerialPortManager.ttyCOM0);// 关闭串口/dev/ttyCOM0

6. 日志拦截(如需看日志,可设置日志拦截)

//设置日志拦截

SerialPortManager.getInstances().setLogInterceptor(new LogInterceptorSerialPort(){
@Override
public void log(@SerialPortManager.Typefinal String type, final String port, final booleanisAscii, final String log) {
Log.d("SerialPortLog", new StringBuffer()        
   .append("串口号:").append(port)     
   .append("\n数据格式:").append(isAscii ? "ascii" : "hexString")      
   .append("\n操作类型:").append(type)     
   .append("操作消息:").append(log).toString());       
   }
});

7. 销毁SerialPortManager,销毁后在使用串口,需要重新执行初始化操作

SerialPortManager.getInstances().destroySerialPort();

2.3.4 串口Demo代码

packagecom.hyperlcd.serialport;
importandroid.os.Bundle;
importandroid.support.annotation.IdRes;
import android.support.v7.app.AppCompatActivity;
importandroid.text.TextUtils;
importandroid.util.Log;
importandroid.view.Gravity;
importandroid.view.View;
importandroid.widget.CompoundButton;
importandroid.widget.EditText;
importandroid.widget.RadioButton;
importandroid.widget.RadioGroup;
importandroid.widget.TextView;
importandroid.widget.Toast;
importandroid_serialport_api.hyperlcd.LogInterceptorSerialPort;
importandroid_serialport_api.hyperlcd.ReadListener;
importandroid_serialport_api.hyperlcd.SerialPort;
importandroid_serialport_api.hyperlcd.SerialPortManager;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
privateRadioGroupcodeRG;
privateRadioGroupserialRG;
privateEditTextserialET;
privateEditTextsendET;
privateTextViewreadTV;
privateTextViewlogTV;
privateTextViewserialTitle;
privateTextViewcodeTitle;
private String currentPort;
privateSerialPortserialPort;
privateReadListenerreadListener;
privatebooleanisAscii;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
initData();
}
private void initData() {
SerialPortManager.getInstances().initSerialPort();
SerialPortManager.getInstances().setLogInterceptor(new LogInterceptorSerialPort() {
@Override
public void log(@SerialPortManager.Type final String type, final String port, final booleanisAscii, final String log) {
Log.d("SerialPortLog", new StringBuffer()
.append("串口号:").append(port)
.append("\n数据格式:").append(isAscii ? "ascii" : "hexString")
.append("\n操作类型:").append(type)
.append("操作消息:").append(log).toString());
runOnUiThread(new Runnable() {
@Override
public void run() {
logTV.append(new StringBuffer()
.append(" ").append(port)
.append(" ").append(isAscii ? "ascii" : "hexString")
.append(" ").append(type)
.append(":").append(log)
.append("\n").toString());
}
});
}
});
serialPort = SerialPortManager.getInstances().getSerialPort();
readListener = new ReadListener() {
@Override
public void onRead(final String port, final booleanisAscii, final String read) {
Log.d("SerialPortRead", new StringBuffer()
.append(port).append("/").append(isAscii ? "ascii" : "hex")
.append(" read:").append(read).append("\n").toString());
runOnUiThread(new Runnable() {
@Override
public void run() {
readTV.append(new StringBuffer()
.append(port).append("/").append(isAscii ? "ascii" : "hex")
.append(" read:").append(read).append("\n").toString());
}
});
}
};
}
private void initView() {
codeRG = (RadioGroup) findViewById(R.id.rg_code);
serialRG = (RadioGroup) findViewById(R.id.rg_serial);
serialET = (EditText) findViewById(R.id.et_serial);
sendET = (EditText) findViewById(R.id.et_send);
serialTitle = (TextView) findViewById(R.id.title_serial);
codeTitle = (TextView) findViewById(R.id.title_code);
readTV = (TextView) findViewById(R.id.tv_read);
logTV = (TextView) findViewById(R.id.tv_log);
codeRG.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, @IdResintcheckedId) {
changeCode(checkedId == R.id.rb_ascii);
}
});
serialRG.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, @IdResintcheckedId) {
if (checkedId == R.id.rb_other) {
serialET.requestFocus();
} else {
sendET.requestFocus();
}
}
});
serialET.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, booleanhasFocus) {
if (hasFocus) {
serialRG.check(R.id.rb_other);
}
}
});
findViewById(R.id.btn_open).setOnClickListener(this);
findViewById(R.id.btn_close).setOnClickListener(this);
findViewById(R.id.clear_send).setOnClickListener(this);
findViewById(R.id.btn_send).setOnClickListener(this);
findViewById(R.id.clear_read).setOnClickListener(this);
findViewById(R.id.clear_log).setOnClickListener(this);
codeRG.check(R.id.rb_ascii);
serialRG.check(R.id.rb_com0);
sendET.requestFocus();
}
@Override
protected void onDestroy() {
SerialPortManager.getInstances().destroySerialPort();
super.onDestroy();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
caseR.id.btn_open:
open();
break;
caseR.id.btn_close:
close();
break;
caseR.id.btn_send:
send();
break;
caseR.id.clear_send:
sendET.setText("");
break;
caseR.id.clear_read:
readTV.setText("");
break;
caseR.id.clear_log:
logTV.setText("");
break;
default:
}
}
private void send() {
if (serialPort == null) {
// 串口未初始化
T("串口未初始化");
return;
}
if (TextUtils.isEmpty(currentPort)) {
// 串口未打开
T("串口未打开");
return;
}
String send = sendET.getText().toString().trim();
if (TextUtils.isEmpty(send)) {
// 发送数据为空
T("发送数据为空");
return;
}
// 发送数据
try {
serialPort.writeSerialService(currentPort, isAscii, send);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 打开串口
*/
private void open() {
if (serialPort == null) {
return;
}
String checkPort = getCurrentPort();
if (TextUtils.isEmpty(checkPort)) {
return;
} else if (TextUtils.equals(checkPort, SerialPortManager.other)) {
checkPort = serialET.getText().toString().trim();
if (TextUtils.isEmpty(checkPort)) {
T("请输入串口号");
return;
}
}
if (TextUtils.equals(currentPort, checkPort)) {
return;
}
if (!TextUtils.isEmpty(currentPort)) {
// 关闭currentPort串口
serialPort.stopSerialPort(currentPort);
}
isAscii = codeRG.getCheckedRadioButtonId() == R.id.rb_ascii;
currentPort = checkPort;
// 打开checkPort串口
serialPort.startSerialPort(checkPort, isAscii, readListener);
serialTitle.setText("串口:");
serialTitle.append(currentPort);
codeTitle.setText("数据格式:");
codeTitle.append(isAscii ? "ASCII" : "HexString");
}
/**
* 关闭串口
*/
private void close() {
if (!TextUtils.isEmpty(currentPort)) {
// 关闭currentPort串口
serialPort.stopSerialPort(currentPort);
currentPort = "";
serialTitle.setText("串口");
codeTitle.setText("数据格式");
}
}
/**
* 更改数据格式
*
* @paramisAsciitrue:asciifalse:HexString
*/
private void changeCode(booleanisAscii) {
if (TextUtils.isEmpty(currentPort)) {
return;
}
serialPort.setReadCode(currentPort, isAscii);
codeTitle.setText("数据格式:");
codeTitle.append(isAscii ? "ASCII" : "HexString");
}
/**
* 获取选中的串口号
*
* @return
*/
private String getCurrentPort() {
String checkPort;
switch (serialRG.getCheckedRadioButtonId()) {
case R.id.rb_com0:
checkPort = SerialPortManager.ttyCOM0;
break;
case R.id.rb_com1:
checkPort = SerialPortManager.ttyCOM1;
break;
case R.id.rb_com2:
checkPort = SerialPortManager.ttyCOM2;
break;
case R.id.rb_com3:
checkPort = SerialPortManager.ttyCOM3;
break;
case R.id.rb_s0:
checkPort = SerialPortManager.ttyS0;
break;
case R.id.rb_s1:
checkPort = SerialPortManager.ttyS1;
break;
case R.id.rb_s2:
checkPort = SerialPortManager.ttyS2;
break;
case R.id.rb_s3:
checkPort = SerialPortManager.ttyS3;
break;
caseR.id.rb_other:
checkPort = SerialPortManager.other;
break;
default:
checkPort = "";
}
returncheckPort;
}
private Toast toast;
privateTextViewtextView;
private void T(String message) {
if (toast == null) {
toast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
textView = new TextView(this);
textView.setTextColor(0xffffffff);
textView.setTextSize(30);
textView.setPadding(10, 5, 10, 5);
textView.setBackgroundResource(R.drawable.shape_toast_bg);
toast.setView(textView);
toast.setGravity(Gravity.CENTER, 0, 0);
}
textView.setText(message);
toast.show();
}
}

2.4. 开机自启动介绍

开机自启动一般有两种方式:一种接收开机广播后启动,一种将自己的APK设置成Launcher。前者开机会进入系统桌面后启动APK,后者开机跳过系统桌面直接进入APK。

2.4.1 接收开机广播后启动

当Android启动时,会发出一个系统广播,内容为”ACTION_BOOT_COMPLETED”。它的字符串常量表示为”android.intent.action.BOOT_COMPLETED”。只要在程序中“捕捉”到这个消息,再启动之即可。所以实现的手段就是实现一个BroadcastReceiver。

实现开机自启动主要实现就是修改应用程序具有laucher的权限。

第一步: 自定义广播类 BootReceiver

public class BootReceiver extends BroadcastReceiver {  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        if(intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {     // boot  
            Intent intent2 = new Intent(context, MainActivity.class);  
//          intent2.setAction("android.intent.action.MAIN");  
//          intent2.addCategory("android.intent.category.LAUNCHER");  
            intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
            context.startActivity(intent2);  
        }  
    }  
}  

第二步:清单文件配置

在AndroidManifest.xml中Application节点内,添加自定义的广播类

<receiver android:name="BootReceiver" >  
    <intent-filter>  
        <action android:name="android.intent.action.BOOT_COMPLETED" />  
  <category android:name="android.intent.category.LAUNCHER" />  
    </intent-filter>  
</receiver>  

第三步:在AndroidManifest.xml中manifest节点内,添加开机启动权限

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

2.4.2 将自己的APK设置成Launcher

替换配置文件中的第一个启动的 activity 的<intent-filter>

<activity
android:name="com.imstlife.mydoor.MainActivity"
android:configChanges="screenSize"
android:label="@string/app_name"
android:launchMode="singleTop">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.MONKEY" />
</intent-filter>
</activity>

程序运行后点击Home,弹出Launcher选择框,选中自己开发的APK后点“始终”即可。

2.5 修改系统签名

修改系统签名的步骤如下:

1. 在AndroidManifest.xml配置文件manifest中加入android:sharedUserId="android.uid.system",直接用eclipse编译源代码产生.apk文件(具体代码中的实现请参考demo中的写法)

2. 把.apk文件重命名为.zip格式。用压缩软件打开,删掉META-INF目录下的CERT.SF和CERT.RSA两个文件。

3. 把删除后的文件,扩展名重命名回.apk格式。复制放在 f:/androidsource(此文件随便创建,建议不要有中文) 文件夹下。把附件中platform.pk8、platform.x509.pem、signapk.jar放在同一个文件夹中。

4. 进入cmd命令行,cd 到androidsource目录下再执行命令:

java -jar signapk.jar platform.x509.pem platform.pk8 SetSystemTime.apktest.apk

5. 如下图显示,系统签名修改完成(test.apk是需要输出的apk名称,名字可以自定义)

2.6 APK加密

2.6.1 混淆代码

1.全局混淆选项,修改build/core/package.mk

ifndef LOCAL_PROGUARD_ENABLED
ifneq ($(filter user userdebug, $(TARGET_BUILD_VARIANT)),)
# turn on Proguard by default for user &userdebug build
LOCAL_PROGUARD_ENABLED :=full
Endif

2. 全局flag文件修改,屏蔽-dontobfuscate。修改build/core/proguard.flags

# Don't obfuscate. We only need dead code striping.
#-dontobfuscate

3. 在自己模块下创建proguard.cfg文件,用来配置混淆选项

4. 在Android.mk里每个package类型的LOCAL_MODULE里LOCAL_PACKAGE_NAME下面添加两句指令:

LOCAL_PROGUARD_ENABLED := full #指定当前的应用打开混淆
LOCAL_PROGUARD_FLAG_FILES := proguard.cfg #指定混淆配置文件

5. 编译时设置环境变量使用. ./setenv.sh -bv user

6. 遇到报错需要就需要修改proguard.cfg文件,规则可以找google。

2.6.2 防止二次打包

1.Java代码中加入签名校验(直接修改smali文件)

2.NDK中加入签名校验(ida查看直接hex修改)

3.利用二次打包工具本身的缺陷阻止打包(manifest 欺骗,图片修改后缀等等)

2.6.3 使用第三方加密工具对DEX、RES、SO库的高级加密

1.导出apk(部分第三方加密平台需要导出签名的apk)

2.第三方平台加固(生成多渠道包)

现在比较流行的加密平台有:

http://www.bangcle.com

http://www.ijiami.cn

http://dev.360.cn/protect/welcome

如果项目里面包含了类似百度统计、友盟统计等功能,那么肯定需要了解不同应用商店的下载量,所以生成渠道包就很重要。这些平台都可以帮助生成多个渠道的apk。

3. apk重新签名

加固后的apk是没有签名的,而没有签名的apk是无法安装到手机里面的,因此可以通过使用签名工具重新签名。adt自带的签名工具由于过于麻烦,建议使用第三方的签名工具,例如:爱加密提供的签名工具http://www.ijiami.cn/apply/Sign

2.7 蜂鸣器使用(仅部分屏支持)

蜂鸣器示例下载链接:https://pan.baidu.com/s/19i7cmjNa1S5IIfb9ceNcTw

提取码:ccuy

蜂鸣器是一种一体化结构的电子讯响器,它采用直流电压供电,在电子产品中作发声器件。蜂鸣器由GPIO控制实现发声。其代码如下:

第一步:创建RootShellCmd类

public class RootShellCmd {
/**
* 打开蜂鸣器
*/
public void openBuzzer() {
exec("echo 168 > /sys/class/gpio/export\n");
exec("echo"+" out"+" > /sys/class/gpio/gpio168/direction\n");
exec("echo out > /sys/class/gpio/gpio168/direction\n");
exec("echo 1 > /sys/class/gpio/gpio168/value\n");
}
/**
* 关闭蜂鸣器
*/
public void closeBuzzer() {
exec("echo 0 > /sys/class/gpio/gpio168/value"); }
}

第二步:将libgpioutil.so文件放入lib目录下创建的(armeabi、armeabi-v7a)

第三步:创建GpioUtil类 路径为java文件夹下创建的android_serialport_api目录下

package android_serialport_api;
import android.util.Log;
import java.io.File;
/**
* Created by Administrator on 2015/11/28.
*/
public class GpioUtil {
final String TAG = "GpioUtil";
public final int[] extender_header_gpios = {167};//,169,188,189,190,191,192,193,194,196,197,198,199,202,203,204,205,207,216,217,284,285,286};
static {
// The runtime will add "lib" on the front and ".o" on the end of
// the name supplied to loadLibrary.
System.loadLibrary("gpioutil");
}
public boolean isOccupiedByOther = false;
public boolean isRequested = false;
public int setVal(int pin, int val) {
Log.i(TAG, "gpioctl xx setVal pin " + pin + " value = " + val);
if (false == isRequested) {
int ret = requestGpio(pin);
isRequested = true;
if(ret < 0){
Log.e(TAG,"gpio is occupied by other");
isOccupiedByOther = true;
return -1;
}
}
return setVal_native(pin, val);
}
public int getVal(int pin) {
int value = getVal_native(pin);
Log.i(TAG, "gpioctl getval value = " + value);
return value;
}
public int openDev(String path){
File device = new File(path);
if (!device.canRead() || !device.canWrite()) {
try {
Process su;
su = Runtime.getRuntime().exec("/system/xbin/su");
String cmd = "chmod 777 " + device.getAbsolutePath() + "\n"
+ "exit\n";
su.getOutputStream().write(cmd.getBytes());
if ((su.waitFor() != 0) || !device.canRead()
|| !device.canWrite()) {
//throw new SecurityException();
}
} catch (Exception e) {
e.printStackTrace();
//throw new SecurityException();
return -1;
}
}
Log.i(TAG, "gpioctl openDev_native");
return openDev_native(path);
}
public int closeDev(){
Log.d(TAG, "closeDev");
return closeDev_native();
}
public int releaseGpio(int pin){
Log.d(TAG, "closeDev");
return releaseGpio_native(pin);
}
public int requestGpio(int pin){
Log.d(TAG, "requestGpio");
return requestGpio_native(pin);
}
private static native int setVal_native(int pin, int val);
private static native int getVal_native(int pin);
private static native int openDev_native(String path);
private static native int closeDev_native();
private static native int releaseGpio_native(int pin);
private static native int requestGpio_native(int pin);
}

第四步:在MainActivity中调用控制蜂鸣器发声的方法

RootShellCmdrootShellCmd = new RootShellCmd();
GpioUtil g = new GpioUtil();
int openDev = g.openDev(DEV_NAME);
if (openDev==-1){
Log.e("fmq","Gpio初始化错误");
}
rootShellCmd.openBuzzer();//控制蜂鸣器发声
if (null!=g){
g.setVal(168, 1);
}
Thread.sleep(50);
rootShellCmd.closeBuzzer();//停止发声
if (null!=g){
g.setVal(168, 0);
}

2.8 显示和隐藏导航栏

显示/隐藏导航栏示例下载链接:https://pan.baidu.com/s/1EBIw9w7tzpAK4QaaMNHZGQ

提取码:od6p

在Android程序中创建类RootShellCmd.Android程序向安卓屏发送adb指令,实现隐藏和显示导航栏。

第一步:创建RootShellCmd类

public class RootShellCmd {
private OutputStreamos;
/**
* 执行shell指令
* @paramcmd指令
*/
public final void exec(String cmd) {
try {
if (os == null) {
os = Runtime.getRuntime().exec("su").getOutputStream();
}
os.write(cmd.getBytes());
os.flush();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 后台模拟全局按键
* @paramkeyCode键值
*/
public final void simulateKey(intkeyCode) {
exec("input keyevent " + keyCode + "\n");
}
/**
* 隐藏导航栏
*/
public static void closeBar() {
try {
// REQUIRES ROOT
Build.VERSION_CODESvc = new Build.VERSION_CODES();
Build.VERSIONvr = new Build.VERSION();
String ProcID = "79"; // HONEYCOMB AND OLDER
// v.RELEASE //4.0.3
if (vr.SDK_INT>= vc.ICE_CREAM_SANDWICH) {
ProcID = "42"; // ICS AND NEWER
}
Process proc = Runtime.getRuntime().exec(
new String[] {"su","-c","service call activity " + ProcID+ " s16 com.android.systemui" }); // WAS 79
proc.waitFor();
} catch (Exception ex) {
ex.printStackTrace();
}
}
/**
* 显示导航栏
*/
public static void showBar(){
try {
// REQUIRES ROOT
Build.VERSION_CODESvc = new Build.VERSION_CODES();
Build.VERSIONvr = new Build.VERSION();
String ProcID = "79"; // HONEYCOMB AND OLDER
// v.RELEASE //4.0.3
if (vr.SDK_INT>= vc.ICE_CREAM_SANDWICH) {
ProcID = "42"; // ICS AND NEWER
}
//执行指令
Process proc = Runtime.getRuntime().exec(
new String[] {"su","-c","amstartservice -n com.android.systemui/.SystemUIService " }); // WAS 79
proc.waitFor();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

第二步:在MainActivity中调用隐藏/显示导航栏方法

RootShellCmd.closeBar();//隐藏导航栏
//RootShellCmd.showBar();//显示导航栏

2.9 固件烧录程序

第一步:将屏连接电脑、打开安卓开发工具v2.1

Android开发工具v2.1下载链接:https://pan.baidu.com/s/1tOLK17sFEN77_IMeoPoqFA

提取码:gvnz

第二步:双击进入测试文件,点击升级固件,进入固件选择界面

第三步:连接设备,选择需要烧录的固件

第四步:连接后软件显示(发现一个MSC设备)表示已经连接识别

第五步:点击切换(显示切换MSC成功)

注:如无法加载固件,请安装驱动。

第六步:点击升级,开始自动加载

2.10 开机配置

进行开机配置需要安装固件工厂工具。

固件工厂工具下载链接:https://pan.baidu.com/s/1kASqTYBU5djB5hGX8aX8NQ

提取码:aagy

2.10.1 画面配置

画面配置可以用来查看替换内核图片,Android开机动画,默认壁纸和充电动画。

2.10.2 开机Logo修改

开机Logo可以通过如下步骤进行更改:

2.10.2.1 内核LOGO 的生成

可以使用bin\目录下FirmwareToPPM.exe来转换成所需的224 ppm的图片,支持图片的格式ppm,bmp,jpg,png,转换出来图片可以放到内核中参加编译。

2.10.2.2 开机logo的修改

点击“Replace boot logo”按钮,选择需要替换的开机logo,工具会根据你固件中的logo 图片格式自动过滤出符合内核logo 的图片,可以根据文件类型选择符合要求的图片,支持的图片格式ppm, bmp, jpg, png.

注:内核配置为Bmp Logo 的固件可支持多分辨率的替换,替换logo的最大分辨率和显示logo的分辨率如下

2.10.3 修改开机动画

2.10.3.1 制作开机动画

开机动画的文件是bootanimation.zip,该文件中包含part0 和desc.txt 两部分:

  1. part0:该文件夹下放置开机显示的动画图片,图片分辨率必须和机器分辨率一致,否则显示会有问题。

  2. desc.txt:此文件要在Linux 系统下生成。文件参数如下(以分辨率800*480 的屏为例):

参数

说明

800 480 10

800 480 -- 屏幕分辨率(宽*高)。

p 0 0 part0

p -- 播放; 0 -- 0表示循环播放;1表示单次播放; 0 -- 延时时间; Part0 -- 存储动画文件的文件夹。

修改完毕并替换part0下的开机动画之后,按如下方式压缩文件(必须选择zip 和存储的压缩方式)。压缩完成后要双击打开压缩文件查看是否有多余文件,若有则将其删掉,否则开机没有动画。

2.10.3.2 替换开机动画

1.打开固件工厂工具

2. 点击固件、打开需要修改的固件

注:固件需要时可提供

3. 解包固件

4. 固件里没有包含开机动画、点击确定

5. 点击替换开机动画按钮、将2.10.3.1制作好的开机动画导入

6. 可预览开机动画的效果

7. 修改完成后点击保存

注意:保存的文件命名格式以.img结尾

2.11 其他

Demo 下载链接:https://pan.baidu.com/s/1q8d05_g7o69_jDwEur3aMQ

提取码:zteg

第一步:导入RootShellCmd.java工具类

链接:https://pan.baidu.com/s/1jA7JQvXe7LDEG3R-Nnmuew

提取码:xdn3

第二步:在主类中调用

2.11.1 调节屏幕亮度

RootShellCmd.getInstance().setBrightness(0);

2.11.2 Home键

RootShellCmd.getInstance().home();

2.11.3 Back键

RootShellCmd.getInstance().back();

2.11.4 动态关机

RootShellCmd.getInstance().shutDown();

2.11.5 动态重启

RootShellCmd.getInstance().reboot();