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. 1.
    “开始”->“运行”,键入“cmd”;
  2. 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结构)
提取码:pu9y
第三步:同步资源库

2.2.3 Android Assistant下载:

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标签下添加配置:
1
sourceSets {
2
main {
3
jni.srcDirs = []
4
jniLibs.srcDirs = ['src/main/jniLibs', 'libs']
5
}
6
}
Copied!
第二步:拷贝的android_sericalport_api包下的所有文件到自己项目的java文件夹下,注意路径不能换
第三步:使用串口
1.初始化操作SerialPortManager
1
SerialPortManager.getInstances().initSerialPort();
Copied!
2. 打开串口
// 获取串口工具类对象实例(该实例用于打开关闭串口和发送数据等)
1
SerialPortserialPort = SerialPortManager.getInstances().getSerialPort();
Copied!
// 初始化读监听器
1
ReadListenerreadListener= new ReadListener() {
2
@Override
3
public void onRead(final String port, final booleanisAscii, final String read) {
4
Log.d("SerialPortRead", new StringBuffer()
5
.append("串口号:").append(port)
6
.append("\n数据格式:").append(isAscii ? "ascii" :"hexString")
7
.append("\nread:").append(read).toString());
8
}
9
};
Copied!
// 用串口工具类对象实例打开串口传入读监听
1
serialPort.startSerialPort(SerialPortManager.ttyCOM0, false, readListener);// 打开串口/dev/ttyCOM0,读数据格式是HexString。
Copied!
3. 修改读数据格式
1
serialPort.setReadCode(SerialPortManager.ttyCOM0, true);// 修改串口/dev/ttyCOM0读数据格式为Ascii
Copied!
4. 发送数据(需先打开串口)
1
serialPort.writeSerialService(SerialPortManager.ttyCOM0, false, "我是要发送的数据");// 向串口/dev/ttyCOM0写数据,数据格式为HexString,数据内容为:"我是要发送的数据"
Copied!
5. 关闭串口
1
serialPort.stopSerialPort(SerialPortManager.ttyCOM0);// 关闭串口/dev/ttyCOM0
Copied!
6. 日志拦截(如需看日志,可设置日志拦截)
//设置日志拦截
1
SerialPortManager.getInstances().setLogInterceptor(new LogInterceptorSerialPort(){
2
@Override
3
public void log(@SerialPortManager.Typefinal String type, final String port, final booleanisAscii, final String log) {
4
Log.d("SerialPortLog", new StringBuffer()        
5
   .append("串口号:").append(port)     
6
   .append("\n数据格式:").append(isAscii ? "ascii" : "hexString")      
7
   .append("\n操作类型:").append(type)     
8
   .append("操作消息:").append(log).toString());       
9
   }
10
});
Copied!
7. 销毁SerialPortManager,销毁后在使用串口,需要重新执行初始化操作
1
SerialPortManager.getInstances().destroySerialPort();
Copied!

2.3.4 串口Demo代码

1
packagecom.hyperlcd.serialport;
2
importandroid.os.Bundle;
3
importandroid.support.annotation.IdRes;
4
import android.support.v7.app.AppCompatActivity;
5
importandroid.text.TextUtils;
6
importandroid.util.Log;
7
importandroid.view.Gravity;
8
importandroid.view.View;
9
importandroid.widget.CompoundButton;
10
importandroid.widget.EditText;
11
importandroid.widget.RadioButton;
12
importandroid.widget.RadioGroup;
13
importandroid.widget.TextView;
14
importandroid.widget.Toast;
15
16
importandroid_serialport_api.hyperlcd.LogInterceptorSerialPort;
17
importandroid_serialport_api.hyperlcd.ReadListener;
18
importandroid_serialport_api.hyperlcd.SerialPort;
19
importandroid_serialport_api.hyperlcd.SerialPortManager;
20
21
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
22
23
privateRadioGroupcodeRG;
24
privateRadioGroupserialRG;
25
privateEditTextserialET;
26
privateEditTextsendET;
27
privateTextViewreadTV;
28
privateTextViewlogTV;
29
privateTextViewserialTitle;
30
privateTextViewcodeTitle;
31
32
private String currentPort;
33
privateSerialPortserialPort;
34
privateReadListenerreadListener;
35
privatebooleanisAscii;
36
37
@Override
38
protected void onCreate(Bundle savedInstanceState) {
39
super.onCreate(savedInstanceState);
40
setContentView(R.layout.activity_main);
41
42
initView();
43
44
initData();
45
}
46
47
private void initData() {
48
SerialPortManager.getInstances().initSerialPort();
49
SerialPortManager.getInstances().setLogInterceptor(new LogInterceptorSerialPort() {
50
@Override
51
public void log(@SerialPortManager.Type final String type, final String port, final booleanisAscii, final String log) {
52
Log.d("SerialPortLog", new StringBuffer()
53
.append("串口号:").append(port)
54
.append("\n数据格式:").append(isAscii ? "ascii" : "hexString")
55
.append("\n操作类型:").append(type)
56
.append("操作消息:").append(log).toString());
57
runOnUiThread(new Runnable() {
58
@Override
59
public void run() {
60
logTV.append(new StringBuffer()
61
.append(" ").append(port)
62
.append(" ").append(isAscii ? "ascii" : "hexString")
63
.append(" ").append(type)
64
.append(":").append(log)
65
.append("\n").toString());
66
}
67
});
68
}
69
});
70
71
serialPort = SerialPortManager.getInstances().getSerialPort();
72
readListener = new ReadListener() {
73
@Override
74
public void onRead(final String port, final booleanisAscii, final String read) {
75
Log.d("SerialPortRead", new StringBuffer()
76
.append(port).append("/").append(isAscii ? "ascii" : "hex")
77
.append(" read:").append(read).append("\n").toString());
78
runOnUiThread(new Runnable() {
79
@Override
80
public void run() {
81
readTV.append(new StringBuffer()
82
.append(port).append("/").append(isAscii ? "ascii" : "hex")
83
.append(" read:").append(read).append("\n").toString());
84
}
85
});
86
}
87
};
88
}
89
90
91
private void initView() {
92
codeRG = (RadioGroup) findViewById(R.id.rg_code);
93
serialRG = (RadioGroup) findViewById(R.id.rg_serial);
94
95
serialET = (EditText) findViewById(R.id.et_serial);
96
97
sendET = (EditText) findViewById(R.id.et_send);
98
serialTitle = (TextView) findViewById(R.id.title_serial);
99
codeTitle = (TextView) findViewById(R.id.title_code);
100
readTV = (TextView) findViewById(R.id.tv_read);
101
logTV = (TextView) findViewById(R.id.tv_log);
102
103
codeRG.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
104
@Override
105
public void onCheckedChanged(RadioGroup group, @IdResintcheckedId) {
106
changeCode(checkedId == R.id.rb_ascii);
107
}
108
});
109
110
serialRG.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
111
@Override
112
public void onCheckedChanged(RadioGroup group, @IdResintcheckedId) {
113
if (checkedId == R.id.rb_other) {
114
serialET.requestFocus();
115
} else {
116
sendET.requestFocus();
117
}
118
}
119
});
120
121
serialET.setOnFocusChangeListener(new View.OnFocusChangeListener() {
122
@Override
123
public void onFocusChange(View v, booleanhasFocus) {
124
if (hasFocus) {
125
serialRG.check(R.id.rb_other);
126
}
127
}
128
});
129
130
findViewById(R.id.btn_open).setOnClickListener(this);
131
findViewById(R.id.btn_close).setOnClickListener(this);
132
findViewById(R.id.clear_send).setOnClickListener(this);
133
findViewById(R.id.btn_send).setOnClickListener(this);
134
findViewById(R.id.clear_read).setOnClickListener(this);
135
findViewById(R.id.clear_log).setOnClickListener(this);
136
137
codeRG.check(R.id.rb_ascii);
138
serialRG.check(R.id.rb_com0);
139
sendET.requestFocus();
140
}
141
142
@Override
143
protected void onDestroy() {
144
SerialPortManager.getInstances().destroySerialPort();
145
super.onDestroy();
146
}
147
148
@Override
149
public void onClick(View v) {
150
switch (v.getId()) {
151
caseR.id.btn_open:
152
open();
153
break;
154
caseR.id.btn_close:
155
close();
156
break;
157
caseR.id.btn_send:
158
send();
159
break;
160
caseR.id.clear_send:
161
sendET.setText("");
162
break;
163
caseR.id.clear_read:
164
readTV.setText("");
165
break;
166
caseR.id.clear_log:
167
logTV.setText("");
168
break;
169
default:
170
}
171
}
172
173
private void send() {
174
if (serialPort == null) {
175
// 串口未初始化
176
T("串口未初始化");
177
return;
178
}
179
180
if (TextUtils.isEmpty(currentPort)) {
181
// 串口未打开
182
T("串口未打开");
183
return;
184
}
185
186
String send = sendET.getText().toString().trim();
187
if (TextUtils.isEmpty(send)) {
188
// 发送数据为空
189
T("发送数据为空");
190
return;
191
}
192
// 发送数据
193
try {
194
serialPort.writeSerialService(currentPort, isAscii, send);
195
} catch (Exception e) {
196
e.printStackTrace();
197
}
198
}
199
200
/**
201
* 打开串口
202
*/
203
private void open() {
204
if (serialPort == null) {
205
return;
206
}
207
String checkPort = getCurrentPort();
208
if (TextUtils.isEmpty(checkPort)) {
209
return;
210
} else if (TextUtils.equals(checkPort, SerialPortManager.other)) {
211
checkPort = serialET.getText().toString().trim();
212
if (TextUtils.isEmpty(checkPort)) {
213
T("请输入串口号");
214
return;
215
}
216
}
217
218
if (TextUtils.equals(currentPort, checkPort)) {
219
return;
220
}
221
222
if (!TextUtils.isEmpty(currentPort)) {
223
// 关闭currentPort串口
224
serialPort.stopSerialPort(currentPort);
225
}
226
227
isAscii = codeRG.getCheckedRadioButtonId() == R.id.rb_ascii;
228
currentPort = checkPort;
229
// 打开checkPort串口
230
serialPort.startSerialPort(checkPort, isAscii, readListener);
231
232
serialTitle.setText("串口:");
233
serialTitle.append(currentPort);
234
codeTitle.setText("数据格式:");
235
codeTitle.append(isAscii ? "ASCII" : "HexString");
236
}
237
238
/**
239
* 关闭串口
240
*/
241
private void close() {
242
if (!TextUtils.isEmpty(currentPort)) {
243
// 关闭currentPort串口
244
serialPort.stopSerialPort(currentPort);
245
currentPort = "";
246
serialTitle.setText("串口");
247
codeTitle.setText("数据格式");
248
}
249
}
250
251
/**
252
* 更改数据格式
253
*
254
* @paramisAsciitrue:asciifalse:HexString
255
*/
256
private void changeCode(booleanisAscii) {
257
if (TextUtils.isEmpty(currentPort)) {
258
return;
259
}
260
serialPort.setReadCode(currentPort, isAscii);
261
codeTitle.setText("数据格式:");
262
codeTitle.append(isAscii ? "ASCII" : "HexString");
263
}
264
265
/**
266
* 获取选中的串口号
267
*
268
* @return
269
*/
270
private String getCurrentPort() {
271
String checkPort;
272
switch (serialRG.getCheckedRadioButtonId()) {
273
case R.id.rb_com0:
274
checkPort = SerialPortManager.ttyCOM0;
275
break;
276
case R.id.rb_com1:
277
checkPort = SerialPortManager.ttyCOM1;
278
break;
279
case R.id.rb_com2:
280
checkPort = SerialPortManager.ttyCOM2;
281
break;
282
case R.id.rb_com3:
283
checkPort = SerialPortManager.ttyCOM3;
284
break;
285
case R.id.rb_s0:
286
checkPort = SerialPortManager.ttyS0;
287
break;
288
case R.id.rb_s1:
289
checkPort = SerialPortManager.ttyS1;
290
break;
291
case R.id.rb_s2:
292
checkPort = SerialPortManager.ttyS2;
293
break;
294
case R.id.rb_s3:
295
checkPort = SerialPortManager.ttyS3;
296
break;
297
caseR.id.rb_other:
298
checkPort = SerialPortManager.other;
299
break;
300
default:
301
checkPort = "";
302
}
303
returncheckPort;
304
}
305
306
private Toast toast;
307
privateTextViewtextView;
308
309
private void T(String message) {
310
if (toast == null) {
311
toast = Toast.makeText(this, "", Toast.LENGTH_SHORT);
312
textView = new TextView(this);
313
textView.setTextColor(0xffffffff);
314
textView.setTextSize(30);
315
textView.setPadding(10, 5, 10, 5);
316
textView.setBackgroundResource(R.drawable.shape_toast_bg);
317
toast.setView(textView);
318
toast.setGravity(Gravity.CENTER, 0, 0);
319
}
320
textView.setText(message);
321
toast.show();
322
}
323
}
324
Copied!

2.4. 开机自启动介绍

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

2.4.1 接收开机广播后启动

当Android启动时,会发出一个系统广播,内容为”ACTION_BOOT_COMPLETED”。它的字符串常量表示为”android.intent.action.BOOT_COMPLETED”。只要在程序中“捕捉”到这个消息,再启动之即可。所以实现的手段就是实现一个BroadcastReceiver。
实现开机自启动主要实现就是修改应用程序具有laucher的权限。
第一步: 自定义广播类 BootReceiver
1
public class BootReceiver extends BroadcastReceiver {  
2
    @Override  
3
    public void onReceive(Context context, Intent intent) {  
4
        if(intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {     // boot  
5
            Intent intent2 = new Intent(context, MainActivity.class);  
6
//          intent2.setAction("android.intent.action.MAIN");  
7
//          intent2.addCategory("android.intent.category.LAUNCHER");  
8
            intent2.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
9
            context.startActivity(intent2);  
10
        }  
11
    }  
12
}  
Copied!
第二步:清单文件配置
在AndroidManifest.xml中Application节点内,添加自定义的广播类
1
<receiver android:name="BootReceiver" >  
2
    <intent-filter>  
3
        <action android:name="android.intent.action.BOOT_COMPLETED" />  
4
  <category android:name="android.intent.category.LAUNCHER" />  
5
    </intent-filter>  
6
</receiver>  
Copied!
第三步:在AndroidManifest.xml中manifest节点内,添加开机启动权限
1
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Copied!

2.4.2 将自己的APK设置成Launcher

替换配置文件中的第一个启动的 activity 的<intent-filter>
1
<activity
2
android:name="com.imstlife.mydoor.MainActivity"
3
android:configChanges="screenSize"
4
android:label="@string/app_name"
5
android:launchMode="singleTop">
6
<intent-filter>
7
<action android:name="android.intent.action.MAIN" />
8
9
<category android:name="android.intent.category.LAUNCHER" />
10
<category android:name="android.intent.category.HOME" />
11
<category android:name="android.intent.category.DEFAULT" />
12
<category android:name="android.intent.category.MONKEY" />
13
14
</intent-filter>
15
</activity>
Copied!
程序运行后点击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目录下再执行命令:
1
java -jar signapk.jar platform.x509.pem platform.pk8 SetSystemTime.apktest.apk
Copied!
5. 如下图显示,系统签名修改完成(test.apk是需要输出的apk名称,名字可以自定义)

2.6 APK加密

2.6.1 混淆代码

1.全局混淆选项,修改build/core/package.mk
1
ifndef LOCAL_PROGUARD_ENABLED
2
ifneq ($(filter user userdebug, $(TARGET_BUILD_VARIANT)),)
3
# turn on Proguard by default for user &userdebug build
4
LOCAL_PROGUARD_ENABLED :=full
5
Endif
Copied!
2. 全局flag文件修改,屏蔽-dontobfuscate。修改build/core/proguard.flags
1
# Don't obfuscate. We only need dead code striping.
2
#-dontobfuscate
Copied!
3. 在自己模块下创建proguard.cfg文件,用来配置混淆选项
4. 在Android.mk里每个package类型的LOCAL_MODULE里LOCAL_PACKAGE_NAME下面添加两句指令:
1
LOCAL_PROGUARD_ENABLED := full #指定当前的应用打开混淆
2
LOCAL_PROGUARD_FLAG_FILES := proguard.cfg #指定混淆配置文件
Copied!
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.第三方平台加固(生成多渠道包)
现在比较流行的加密平台有:
如果项目里面包含了类似百度统计、友盟统计等功能,那么肯定需要了解不同应用商店的下载量,所以生成渠道包就很重要。这些平台都可以帮助生成多个渠道的apk。
3. apk重新签名
加固后的apk是没有签名的,而没有签名的apk是无法安装到手机里面的,因此可以通过使用签名工具重新签名。adt自带的签名工具由于过于麻烦,建议使用第三方的签名工具,例如:爱加密提供的签名工具http://www.ijiami.cn/apply/Sign

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

蜂鸣器示例下载链接:https://pan.baidu.com/s/19i7cmjNa1S5IIfb9ceNcTw
提取码:ccuy
蜂鸣器是一种一体化结构的电子讯响器,它采用直流电压供电,在电子产品中作发声器件。蜂鸣器由GPIO控制实现发声。其代码如下:
第一步:创建RootShellCmd类
1
public class RootShellCmd {
2
3
/**
4
* 打开蜂鸣器
5
*/
6
public void openBuzzer() {
7
exec("echo 168 > /sys/class/gpio/export\n");
8
exec("echo"+" out"+" > /sys/class/gpio/gpio168/direction\n");
9
exec("echo out > /sys/class/gpio/gpio168/direction\n");
10
exec("echo 1 > /sys/class/gpio/gpio168/value\n");
11
}
12
/**
13
* 关闭蜂鸣器
14
*/
15
public void closeBuzzer() {
16
exec("echo 0 > /sys/class/gpio/gpio168/value"); }
17
}
Copied!
第二步:将libgpioutil.so文件放入lib目录下创建的(armeabi、armeabi-v7a)
第三步:创建GpioUtil类 路径为java文件夹下创建的android_serialport_api目录下
1
package android_serialport_api;
2
3
import android.util.Log;
4
5
import java.io.File;
6
7
/**
8
* Created by Administrator on 2015/11/28.
9
*/
10
public class GpioUtil {
11
final String TAG = "GpioUtil";
12
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};
13
14
static {
15
// The runtime will add "lib" on the front and ".o" on the end of
16
// the name supplied to loadLibrary.
17
System.loadLibrary("gpioutil");
18
}
19
20
public boolean isOccupiedByOther = false;
21
public boolean isRequested = false;
22
23
public int setVal(int pin, int val) {
24
Log.i(TAG, "gpioctl xx setVal pin " + pin + " value = " + val);
25
26
if (false == isRequested) {
27
int ret = requestGpio(pin);
28
isRequested = true;
29
if(ret < 0){
30
Log.e(TAG,"gpio is occupied by other");
31
isOccupiedByOther = true;
32
return -1;
33
}
34
}
35
36
return setVal_native(pin, val);
37
}
38
39
public int getVal(int pin) {
40
int value = getVal_native(pin);
41
Log.i(TAG, "gpioctl getval value = " + value);
42
return value;
43
}
44
45
public int openDev(String path){
46
File device = new File(path);
47
if (!device.canRead() || !device.canWrite()) {
48
try {
49
Process su;
50
su = Runtime.getRuntime().exec("/system/xbin/su");
51
String cmd = "chmod 777 " + device.getAbsolutePath() + "\n"
52
+ "exit\n";
53
54
su.getOutputStream().write(cmd.getBytes());
55
if ((su.waitFor() != 0) || !device.canRead()
56
|| !device.canWrite()) {
57
//throw new SecurityException();
58
}
59
} catch (Exception e) {
60
e.printStackTrace();
61
//throw new SecurityException();
62
return -1;
63
}
64
}
65
Log.i(TAG, "gpioctl openDev_native");
66
return openDev_native(path);
67
}
68
69
public int closeDev(){
70
Log.d(TAG, "closeDev");
71
return closeDev_native();
72
}
73
74
public int releaseGpio(int pin){
75
Log.d(TAG, "closeDev");
76
return releaseGpio_native(pin);
77
}
78
79
public int requestGpio(int pin){
80
Log.d(TAG, "requestGpio");
81
return requestGpio_native(pin);
82
}
83
84
private static native int setVal_native(int pin, int val);
85
private static native int getVal_native(int pin);
86
private static native int openDev_native(String path);
87
private static native int closeDev_native();
88
private static native int releaseGpio_native(int pin);
89
private static native int requestGpio_native(int pin);
90
91
}
92
Copied!
第四步:在MainActivity中调用控制蜂鸣器发声的方法
1
RootShellCmdrootShellCmd = new RootShellCmd();
2
GpioUtil g = new GpioUtil();
3
int openDev = g.openDev(DEV_NAME);
4
if (openDev==-1){
5
Log.e("fmq","Gpio初始化错误");
6
}
7
8
rootShellCmd.openBuzzer();//控制蜂鸣器发声
9
if (null!=g){
10
g.setVal(168, 1);
11
}
12
Thread.sleep(50);
13
rootShellCmd.closeBuzzer();//停止发声
14
if (null!=g){
15
g.setVal(168, 0);
16
}
Copied!

2.8 显示和隐藏导航栏

显示/隐藏导航栏示例下载链接:https://pan.baidu.com/s/1EBIw9w7tzpAK4QaaMNHZGQ
提取码:od6p
在Android程序中创建类RootShellCmd.Android程序向安卓屏发送adb指令,实现隐藏和显示导航栏。
第一步:创建RootShellCmd类
1
public class RootShellCmd {
2
private OutputStreamos;
3
/**
4
* 执行shell指令
5
* @paramcmd指令
6
*/
7
public final void exec(String cmd) {
8
try {
9
if (os == null) {
10
os = Runtime.getRuntime().exec("su").getOutputStream();
11
}
12
os.write(cmd.getBytes());
13
os.flush();
14
} catch (Exception e) {
15
e.printStackTrace();
16
}
17
}
18
/**
19
* 后台模拟全局按键
20
* @paramkeyCode键值
21
*/
22
public final void simulateKey(intkeyCode) {
23
exec("input keyevent " + keyCode + "\n");
24
}
25
/**
26
* 隐藏导航栏
27
*/
28
public static void closeBar() {
29
try {
30
// REQUIRES ROOT
31
Build.VERSION_CODESvc = new Build.VERSION_CODES();
32
Build.VERSIONvr = new Build.VERSION();
33
String ProcID = "79"; // HONEYCOMB AND OLDER
34
// v.RELEASE //4.0.3
35
if (vr.SDK_INT>= vc.ICE_CREAM_SANDWICH) {
36
ProcID = "42"; // ICS AND NEWER
37
}
38
Process proc = Runtime.getRuntime().exec(
39
new String[] {"su","-c","service call activity " + ProcID+ " s16 com.android.systemui" }); // WAS 79
40
proc.waitFor();
41
42
} catch (Exception ex) {
43
ex.printStackTrace();
44
}
45
}
46
/**
47
* 显示导航栏
48
*/
49
public static void showBar(){
50
try {
51
// REQUIRES ROOT
52
Build.VERSION_CODESvc = new Build.VERSION_CODES();
53
Build.VERSIONvr = new Build.VERSION();
54
String ProcID = "79"; // HONEYCOMB AND OLDER
55
// v.RELEASE //4.0.3
56
if (vr.SDK_INT>= vc.ICE_CREAM_SANDWICH) {
57
ProcID = "42"; // ICS AND NEWER
58
}
59
//执行指令
60
Process proc = Runtime.getRuntime().exec(
61
new String[] {"su","-c","amstartservice -n com.android.systemui/.SystemUIService " }); // WAS 79
62
proc.waitFor();
63
} catch (Exception ex) {
64
ex.printStackTrace();
65
}
66
}
67
}
Copied!
第二步:在MainActivity中调用隐藏/显示导航栏方法
1
RootShellCmd.closeBar();//隐藏导航栏
2
//RootShellCmd.showBar();//显示导航栏
Copied!

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. 1.
    part0:该文件夹下放置开机显示的动画图片,图片分辨率必须和机器分辨率一致,否则显示会有问题。
  2. 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 其他

提取码:zteg
第一步:导入RootShellCmd.java工具类
第二步:在主类中调用

2.11.1 调节屏幕亮度

1
RootShellCmd.getInstance().setBrightness(0);
Copied!

2.11.2 Home键

1
RootShellCmd.getInstance().home();
Copied!

2.11.3 Back键

1
RootShellCmd.getInstance().back();
Copied!

2.11.4 动态关机

1
RootShellCmd.getInstance().shutDown();
Copied!

2.11.5 动态重启

1
RootShellCmd.getInstance().reboot();
Copied!