Android开发合集
目录
项目中偶尔需要一些Android开发,在此记录一下。API版本普遍为编译32,最低支持23。
概述
Android是基于Linux开发的一款优秀的操作系统,尤其适用于在手机端使用,目前也是用户数量最多的操作系统。本文从实用角度出发,给出一些使用的项目代码记录。
Android Studio安装
- 最简单粗暴的办法,在HTTP Proxy设置中,直接使用Auto-detect proxy settings,然后用本机的任何上网代理即可。这也是最稳定的办法。
- 但一定要在第一次安装运行前就准备好代理,否则可能出现包安装不完整导致的一系列非常奇怪的问题。
- android studio的代理(SDK)和gradle代理(各类第三方库)并不是同一回事,在settings.gradle中还可以选择所使用的代码仓库的URL
- 或者你也可以选择配置镜像:由于Android SDK存在版权问题,这里要搜索下可以使用的镜像源(清华TUNA在撰写本文时并不能使用)。可以使用中科院开源协会,填写如下图。
- 由于Android Studio更新较快,新版本可能出现问题,建议在出现难以解决的问题的时候,可以使用低版本重试
编译篇
SDK
SDK Build Tool
Gradle配置
- 概述
- 全局配置是gradle.properties,位于用户目录的.gradle/下
- 项目配置是gradle-wrapper.properties,位于项目下
- 代理:注意http和https都必须写上
#代理服务器IP/域名 systemProp.http.proxyHost=127.0.0.1 #代理服务器端口 systemProp.http.proxyPort=10809 #代理服务器IP/域名 systemProp.https.proxyHost=127.0.0.1 #代理服务器端口 systemProp.https.proxyPort=10809
- build.gradle示例
plugins {
id 'com.android.application'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.afclab.pr_tpuservice"
minSdk 23
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
// 自定义字段,将会最终写入到BuildConfig.java中
buildConfigField("String","BUILD_TIME","\""+getBuildDate()+"\"")
}
// apk签名配置
signingConfigs {
release {
storeFile file('../yourKey.jks')
storePassword 'yourPassword'
keyAlias 'yourKeyAlias'
keyPassword 'yourKeyPassword'
}
}
// 不同构建类型的配置
buildTypes {
debug {
signingConfig signingConfigs.release
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
// 在线下载的依赖
implementation 'com.github.licheedev:Android-SerialPort-API:2.0.0'
// 本地下载好的依赖
implementation fileTree(dir: '..\\3rdParty', include: ['*.aar', '*.jar'], exclude: [])
implementation 'com.google.code.gson:gson:2.9.1'
implementation 'com.squareup.okhttp:okhttp:2.7.5'
// 对本地其他项目的依赖
implementation project(path: ':pr-common')
}
// 自定义函数要写在做外侧
String getBuildDate() {
TimeZone.setDefault(TimeZone.getDefault().getTimeZone("GMT+8"));
Date date =new Date();
return date.toString();
}
调用外部C++库
- 关键词:
- JNI:Java Native Interface,作为Java代码和Native代码的桥梁,提供数据转换,接口映射等功能。注意虽然Native效率很高,但是JNI效率很低,因此要尽量避免频繁调用JNI层
- NDK(Native Develop Kit):提供本地代码开发的各种工具(主要就是一个C/C++编译器和标准库)
- 主要方式
- 低难度:提供Java接口、so库。这意味着JNI代码已经写好了,使用者只需将so放到指定位置,调用Java接口即可
- 高难度:C/C++接口、so库。需要用户编写编译规则和JNI代码,难点主要在于接口名称映射。当然这一点如果使用Android Studio,选择新建NativeLibrary进行开发,自动化程度也已经非常高了
- 坑:
- 外部库的名字可以变动,调用加载时指定正确的名称即可,但是路径必须是固定的,而且要和so对应的CPU架构一致,如armeabi、armeabi-v7a、x86。
- 虽然一些基础的数据类型如jint、jbyte是可以直接使用的,但是数组、对象等类型,都需要使用jenv进行一定的转换,否则会发生无法预期的错误。比如未pin住对象内存,导致被GC回收,从而段错误退出。
- 参考
原理
- 四大组件:活动(Activity)、服务(Service)、广播接收者(BroadCast Receiver)、内容提供者(Content Provider)
代码应用
数据存储
- 五种主要方式
- SharePreferences:本质是xml文件
- SQLite
- Content Provider:唯一可以用于共享的办法,用一个uri标记资源,如content://路径/id
- File:一般用于大文件
- 网络存储
ListView
- ListView容易出现卡顿问题,一定要做其中的View优化
- 最好准备完整的数据之后再进行setAdapter,减少触发数据变动
- 而且在回调中的数据变动,不一定会立刻影响到界面绘制
- 参考:
权限获取
开机自启动
- 流程:配置监听权限、设置监听类、监听系统广播并启动
- 参考:设置程序开机自启动
Activity Manager Service
- 参考:
跨线程通信
- 关键词:
- Handler:核心方法如sendMessage、handleMessage,用于发送、处理一个消息
- Looper:执行Handler的handleMessage所在的事件循环线程
- Message、Bundle:发送消息的数据结构
- 基本流程
- [可选]自定义Handler,尤其是handleMessage函数
- 创建Handler、创建Looper,启动Looper
- 将Handler发给不同的线程
- 各线程内自己组Message并发送
Camera2和TextureView
- 关键词
- 补充一张Camera2的类图
- 基本流程
- 为TextureView添加SurfaceViewListener
- 由于TextureView的SurfaceView在Listener的onAvailable阶段才可用,所以初始化需要放到该位置
- 根据需要调用capture拍照、或者setRepeatingRequest预览
- 难点
- 记得控制画面长宽
- 相机权限获取
- 保证使用到的各种所有变量都是类成员变量,尤其是Surface,不能作为函数局部变量,可能导致被GC回收从而引发异常
- 参考
无界面Service及通信
- 基本流程
- 根据需要继承Service、IntentService,实现必要的函数,并提供Binder、AIDL(Android Interface Define Language)
// Service主体 public class TPUService extends Service { private String TAG = "TPUService"; @Override public void onCreate() { super.onCreate(); Log.i(TAG, "onCreate"); } @Nullable @Override public IBinder onBind(Intent intent) { Log.i(TAG, "onBind"); // 在此处提供接口的具体实现 return new TPUServiceInterface.Stub() { @Override public String ping() throws RemoteException { return "pong"; } @Override public boolean reset() throws RemoteException { return false; } @Override public boolean init() throws RemoteException { return false; } @Override public String readCard() throws RemoteException { return null; } }; } } // 接口主题(.aidl文件),该文件会被Android Studio自动转为java文件 interface TPUServiceInterface { String ping(); boolean reset(); boolean init(); String readCard(); }
- 由于无界面,启动上一般需要设置开机启动、或者允许由其他程序启动,则其AndroidManifest.xml一般形如
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.afclab.pr_tpuservice"> <!-- 自启动权限 --> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.PRTPUService"> <!-- 接收启动完成广播信号 --> <receiver android:name="com.afclab.pr_tpuservice.OnBootBroadCast" android:enabled="true" android:exported="true"> <intent-filter android:priority="1000"> <action android:name="android.intent.action.BOOT_COMPLETED"></action> </intent-filter> </receiver> <!-- exported设置为true,允许其他应用启动 --> <service android:name=".TPUService" android:exported="true"/> </application> </manifest>
- 自定义广播信号处理类,接收广播,并运行自定义Service,代码形如
public class OnBootBroadCast extends BroadcastReceiver { @Override public void onReceive(Context arg0, Intent arg1) { Intent mBootIntent = new Intent(arg0, TPUService.class); arg0.startService(mBootIntent); } }
- 受安卓系统限制(Android3.1+),初次安装后如果不启动一次,则并不会真正监听启动信号。如果不由外部应用启动,则需要通过adb,手动启动一次,此后监听将会正式生效。
- 客户端使用
public class MainActivity extends AppCompatActivity { private String TAG = "MainActivity"; private TPUServiceInterface mService = null; // 连接 private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { //绑定Service成功后执行 mService = TPUServiceInterface.Stub.asInterface(iBinder);//获取远程服务的代理对象,一个IBinder对象 try { Log.i(TAG, mService.ping()); //调用远程服务的接口。 } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName componentName) { mService = null; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button connectButton = findViewById(R.id.service_connect); connectButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 必须要正确提供包名、服务名,并传递给Intent ComponentName componentName = new ComponentName("com.afclab.pr_tpuservice", "com.afclab.pr_tpuservice.TPUService"); Intent intent = new Intent(); intent.setComponent(componentName); boolean result = bindService(intent, mConnection, BIND_AUTO_CREATE); Log.i(TAG, "bind result: " + result); } }); } }
- 根据需要继承Service、IntentService,实现必要的函数,并提供Binder、AIDL(Android Interface Define Language)
- 参考
ADB
- 概述:Android Debug Bridge,即ADB,安卓调试桥,可以通过ADB完成各种安装、调试、调用系统shell等能力。由于Android Studio有良好的调试工具,个人建议只把adb作为辅助手段,但是对于批量安装应用等场景,adb还是更有优势
- 一些指令
# 主机侧 adb root # 进入root adb shell # 进入shell adb connect x.x.x.x # 连接置顶ip的安卓设备 adb tcpip 5555 # 尝试打开对端5555端口连接 adb install -r xxx.apk # 覆盖安装 adb shell am start com.你的包路径/com.你的Activity # 启动普通程序 adb shell am startservice com.你的包路径/com.你的Service # 启动服务 adb shell screenrecord /存储/路径.mp4 # 录制屏幕 adb shell input ... # 模拟各类输入事件(键鼠等) adb shell logcat >> xxx.txt # 将日志追加输出到指定文件 adb -s (ip):5555 shell # 指定连接 # 安卓shell侧 setprop service.adb.tcp.port 5555 pm list packages # 查看已安装的包 pm install 你的包路径 # 安装 pm uninstall 包名 # 卸载 am force-stop 包名 # 杀程序 # adb shell命令也都可以用在这里 # .. 可以使用其他部分linux指令
- 设置永久网络调试接口:adb实际上默认就是开启的,网络端口也是,可以直接尝试连接(首次连接也会需要安卓端确认)
实用第三方库
repositories一般会添加:maven(jitpack.io),jcenter,方便查找正确的包
- 串口通信:com.github.licheedev:Android-SerialPort-API
坑
- 新建项目提示类似于:plugin com.android.application not found in any repositories
- 尚不清楚原理,但是大概率和gradle有关,建议删除默认gradle目录(C:/Users/你的名字/.gradle/),完全重新下载。
- 其他的可能性包括:仓库中确实没有该版本、gradle和插件版本不兼容、gradle和gradle所用java版本不兼容(Gradle7.0+要求java11)、网路代理错误等
- 参考
- 尚不清楚原理,但是大概率和gradle有关,建议删除默认gradle目录(C:/Users/你的名字/.gradle/),完全重新下载。
- 设置整个Layout的背景是图片动画,会造成绘制掉帧
- 不应将图片直接放到drawable下面,而应当放到mipmap下
- 当然实际上也不推荐直接用图片做背景
- 安卓设备连接上USB设备时,必须使用弹出式的权限获取窗口来获取权限,不能自动授权。该权限是动态获取的,在AndroidManifest中无法进行静态获取。
- Gson和Fastjson在对于byte[]的序列化方面,并不兼容,尽量使用同一种序列化方式。
参考资料
- 2022最新Android基础视频教程
- Google官方文档
- Android Studio已经将文档融合进Android SDK源代码中,只需要安装项目所使用版本的SDK时同时安装其源代码即可