安卓程序开发入门
Step1
本步骤主要复习安卓软件开发的基本流程;实验共有四个要求,即编写一个会重启后自启动的位置显示,并具备在子进程中调用住进程UIChange函数功能的,同时能够使用反射方法调用libs库中依赖的jar文件的安卓app。按照实验指导书的要求依次实现上述要求。
1-1后台服务
实现了接收重启命令启动程序、后台更新gps数据、获取前台显示和位置权限等三项核心功能。
思路
- 首先编写接收重启广播的代码,即
SecretBootReceicer.java
文件;通过Toast方法实现调试。
public class SecretBootReceiver extends BroadcastReceiver {
public SecretBootReceiver(){}
@Override
public void onReceive(Context context, Intent intent) {}
- 接下来进行主活动页面代码的编写,位置和悬浮显示的权限也在该
MainActivity.java
文件中申请。
该环节中的``floatingWindow的权限申请方法因为随着安卓系统版本不同,申请方法,颇费了一番周折。并且在技术选型上,也存在系统通知 (
Notification) 或者浮动窗口 (
FloatingWindow) 来代替
Toast`的问题。最后也是根据就近原则(资料最多原则),选择浮动窗口方案。
另外值得说明的是在实验过程中,申请权限以及注册服务都需要在AndroidManifest.xml
文件中进行事先声明,由于缺少这一先验知识,在调试代码的过程中走了一些弯路。当然根据SDK版本判断不同的GPS申请权限方法也是踩了坑之后才发现的。
private ActivityResultLauncher<Intent> floatingWindowLauncher;
startActivityForResult(intent, REQUEST_CODE_FLOATING_WINDOW_PERMISSION);
ActivityCompat.requestPermissions(this,new String[] {Manifest.permission.ACCESS_FINE_LOCATION},
REQUEST_LOCATION);
startService(new Intent(this, SecretService.class));
onRequestPermissionsResult;
onActivityResult;
- 最后是获取位置服务并显示的功能,也是本软件的核心逻辑,在
SecretService.java
文件中。主要包括服务注册函数,后台执行函数,位置获取函数和显示函数。
private Runnable runnable = new Runnable() {
@Override
public void run() {
showLocation();
handler.postDelayed(this, MIN_TIME_BW_UPDATES);
}
};
private void getLocation();
private void showLocation();
@Override
public void onLocationChanged(Location location) {}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {}
完成上述的代码逻辑编写之后,根据网络上的提示进行Manifest清单文件的配置就可以完成这个app的1-1后台运行显示位置服务的功能了。
结果
观察是否自启动:无论是adb模拟重启还是重启模拟器均实现了reboot后自启动✅
释放内存是否结束:手动清理内存后,进程被杀死且没有被再次唤醒。✅
1-2子线程中更新ui
该问题是创建一个子线程进行数据输入输出的处理,同时需要在子线程收到数据后更新住进程的ui。通过在子线程中调用runOnUiThread
方法来实现子线程中更新住线程ui
思路
在app中画出输入输出框,并在MainActivity
类中进行按钮行为的绑定。在按钮绑定的行为中,点击后新建一个线程来显示对话框。获取输入框中的内容并据此新建一个对话框,然后利用runOnUiThread
在主线程中显示对话框。
mEditText = findViewById(R.id.edit_text);
mButton = findViewById(R.id.button);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String text = mEditText.getText().toString();
// 创建一个新的线程来显示对话框
new Thread(new Runnable() {
@Override
public void run() {
// 在子线程中创建对话框并设置文本内容
final AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
builder.setTitle("Content");
builder.setMessage(text);
// 在主线程中显示对话框
runOnUiThread(new Runnable() {
@Override
public void run() {
builder.show();
}
});
}
}).start();
}
});
结果
1-3反射调用类
通过反射的方法调用calsses.jar
文件中的PoRELab
类中的变量和方法。采用jd-gui工具查看给定jar文件中的包名,类名,变量名,方法名及其参数方便调用。
方法
下载安装jd-gui后打开classes.jar
仔细观察。
jar -tf Step1_Task3_classes.jar| grep 'class'
java -jar /Users/aibot/Downloads/jd-gui-osx-1.6.6/JD-GUI.app/Contents/Resources/Java/jd-gui-1.6.6-min.jar
之后进行反射调用。
try {
Class<?> poRELabClass = Class.forName("com.pore.mylibrary.PoRELab", true, getClassLoader());
// 创建 PoRELab 类的实例
Object poRELabInstance = poRELabClass.newInstance();
// 获取 curStr 字段的值
Field curStrField = poRELabClass.getDeclaredField("curStr");
curStrField.setAccessible(true);
String curStrValue = (String) curStrField.get(poRELabInstance);
// 调用 privateMethod 方法
Method privateMethod = poRELabClass.getDeclaredMethod("privateMethod", String.class, String.class);
privateMethod.setAccessible(true);
privateMethod.invoke(poRELabInstance,"privateMethod","hello");
Log.d("MainActivity", "curStrValue = " + curStrValue);
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException |
NoSuchFieldException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
结果
得到实验指导书中的结果:
1-4签名打包
思路
按照AS集成开发环境中的build,signed-apks,设置key-store和key
key_store和key的密码都是xjtuosv
结果
打包好的apk包含Step中1-1至1-4的全部要求,命名为lab3-1_Step1_tanghangyun.apk
。