有Java基础的人怎么学Android原生开发------一个20年后端程序员的26个类
文章目录
- 有Java基础的人怎么学Android原生开发------一个20年后端程序员的26个类
-
- 一、前提:你有基础,只是没碰过Android
- 二、一张图:先理解Android跟你熟悉的Java有什么不同
- 三、我是怎么学的:一个项目覆盖所有核心概念
-
- [Activity 生命周期 --- 最核心的概念](#Activity 生命周期 — 最核心的概念)
- [Intent --- Android的URL路由](#Intent — Android的URL路由)
- [BroadcastReceiver --- Android的事件总线](#BroadcastReceiver — Android的事件总线)
- [Service --- 后台任务守护](#Service — 后台任务守护)
- [SQLite --- 没有网络开销的数据库](#SQLite — 没有网络开销的数据库)
- [ContentResolver --- 读写联系人不需要权限?](#ContentResolver — 读写联系人不需要权限?)
- [运行时权限 --- 为什么打电话要弹窗](#运行时权限 — 为什么打电话要弹窗)
- [自定义控件 --- 封装可重用的View](#自定义控件 — 封装可重用的View)
- 剩下的概念,接触了就知道
- 四、这个项目能证明什么
- 五、结语
一、前提:你有基础,只是没碰过Android
我说的是这种情况:Java写了十年以上,知道反射、泛型、设计模式,写过前后端、数据库、工作流。但从来没打开过 Android Studio。有一天你决定------学一下Android。
你和零基础的人最大的区别不是"学得快",而是你能判断什么东西是重要的,什么东西以后能查到。
零基础的人看到 onCreate 要背下来。你看到 onCreate 会想------这就是一个构造回调,生命周期的一部分,我的初始化逻辑放这里就行。零基础的人在记API,你在建认知框架。
我的认知框架是什么?就是这张图:
二、一张图:先理解Android跟你熟悉的Java有什么不同
| 你熟悉的(后端Java) | Android的对应 | 关键差异 |
|---|---|---|
| JVM上跑,程序不挂就一直运行 | Dalvik/ART上跑,Activity随时可能被杀 | 生命周期是核心,不是边缘情况 |
| 请求-响应,短暂持有状态 | 用户在界面间跳转,状态要持久化 | SharedPreferences、SQLite、onSaveInstanceState |
| 数据库直连,想开几个连接就开 | SQLite是嵌入式,文件就在手机里 | 没有网络开销,但有并发限制 |
| RPC调用、消息队列 | Intent显式/隐式跳转 | Intent就是Android的IPC和路由 |
| 多线程、ExecutorService | AsyncTask(已废弃)/Handler/Service | 不许在非UI线程操作UI |
| 配置文件 + 数据库配置 | AndroidManifest.xml | 四大组件必须在这声明 |
| 部署用Tomcat启动 | 打包APK,系统发广播,四大组件响应 | 你的代码是被"调用"的,不是一直跑的 |
这张图清楚了,学Android就是填每一行的细节,而不是重新建一个认知体系。
三、我是怎么学的:一个项目覆盖所有核心概念
我建了一个项目叫 ActivityTest,26个Java类,12个Activity,每个Activity练一个概念。不是照着教程抄,是自己设计场景,把概念用出来。
Activity 生命周期 --- 最核心的概念
Android的Activity不像Servlet------Servlet随请求创建和销毁,Activity是在"可见"和"不可见"之间反复切换。用户切到桌面、接了个电话、旋转了屏幕------你的Activity可能被回收。
我写了一个 BaseAcitvity(注意拼写,这是我学习路上真实的痕迹),让所有Activity继承它:
java
public class BaseAcitvity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityContain.addActivity(this); // 自己维护一个Activity栈
Log.d("BaseActivity", getClass().getSimpleName() + " onCreate");
}
@Override
protected void onDestroy() {
super.onDestroy();
ActivityContain.removeActivity(this);
}
}
这个维护Activity栈的做法,是典型的"后端思维"------我想知道当前有哪些Activity活着,要能一键关掉全部。后来我知道 Android Jetpack 有 ActivityLifecycleCallbacks 可以做同样的事,但自己写一遍,理解了它为什么存在。
Intent --- Android的URL路由
后端开发习惯了 @RequestMapping("/user/save") 做URL路由。Android的路由机制叫 Intent。
显式Intent就是直接指定目标类(相当于内部方法调用)。更关键的是隐式Intent:
java
// 打开百度网页
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("http://www.baidu.com"));
startActivity(intent);
// 拨打电话
Intent intent = new Intent(Intent.ACTION_CALL);
intent.setData(Uri.parse("tel:10086"));
startActivity(intent);
你不需要知道"谁来处理这个请求"------Android系统会找能打开的Activity。这就是"隐式路由"。
我在 SecondActivity 上还自定义了一个隐式Intent:
xml
<intent-filter>
<action android:name="com.browise.activitytest.ACTION_START" />
<category android:name="com.browise.activitytest.MY_CATEGORY" />
</intent-filter>
别的应用知道了这个action,就能调起我。这让我想到------Android的IPC本质上就是一个注册+查找+调用的机制,只不过注册通过Manifest,查找通过Intent匹配,调用通过startActivity。
BroadcastReceiver --- Android的事件总线
后端里我们有消息队列、事件总线、配置中心。Android里同样的概念叫 BroadcastReceiver。
系统发出的广播(开机完成、网络变化、电量低)和自定义广播(会话过期、强制下线)都用同样的机制接收。静态注册通过Manifest,动态注册写代码:
java
// 动态注册网络变化监听
IntentFilter intentFilter = new IntentFilter("android.net.conn.CONNECTIVITY_CHANGE");
networkChangeReceiver = new NetworkChangeReceiver();
registerReceiver(networkChangeReceiver, intentFilter);
我写了一个"强制下线"的场景------发一条自定义广播,接收后关闭所有Activity,跳回登录页:
java
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
ActivityContain.finishAll(); // 关掉当前所有Activity
Intent i = new Intent(context, LoginActivity.class);
context.startActivity(i);
}
}
理解了BroadcastReceiver,就看懂了Android的BootReceiver(开机自启动服务)、AlarmReceiver(定时任务)、PushReceiver(推送通知)------它们的本质都是"接收广播→做动作"。
Service --- 后台任务守护
后端的后台任务靠挂一个JVM进程,Android里靠Service。Service有两种:启动型(startService)和绑定型(bindService)。前者适合"下载文件"这种相对独立的操作,后者适合"音乐播放器"这种需要交互的操作。
我的 DownLoadService 是一个绑定型服务,支持断点续传。核心做法是在HTTP请求里加 Range 头:
java
connection.setRequestProperty("Range", "bytes=" + downloadedLength + "-");
然后 RandomAccessFile.seek(downloadedLength) 把写指针移到已有文件末尾。服务关掉后重连,从上次断掉的位置继续。这个思路和后端的断点续传是一样的------只是后端用消息队列做进度通知,Android用 Notification + Handler。
SQLite --- 没有网络开销的数据库
后端数据库是远程的、多连接的、有连接池的。Android的SQLite是文件级的、本地的、单连接的。操作方式封在 SQLiteOpenHelper 里:
java
public class MySqLiteOpenHelper extends SQLiteOpenHelper {
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("create table Book(id integer primary key autoincrement, name text, ...)");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
onCreate(db);
}
}
onUpgrade 这行 drop table 让我有一种"果然如此"的感觉------这是一个学习项目,数据库升级直接删表重建。但它的机制(数据库版本号触发onUpgrade)让我联想到后端Liquibase/Flyway------理念是一样的,只是粒度不同。
ContentResolver --- 读写联系人不需要权限?
Android规定应用之间不能直接互相访问数据。要读通讯录,不能用JDBC、不能用文件IO,只能用 ContentResolver:
java
Cursor cursor = getContentResolver().query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null
);
while (cursor.moveToNext()) {
String name = cursor.getString(cursor.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
String number = cursor.getString(cursor.getColumnIndex(
ContactsContract.CommonDataKinds.Phone.NUMBER));
}
这完全不是后端的思维方式------后端你想查数据库就 select * from contacts。Android在中间拦了一道,用ContentProvider把数据包了一层。理解了这个,对Android的权限模型就有了底。
运行时权限 --- 为什么打电话要弹窗
Android 6.0以后,危险权限(通讯录、电话、定位、存储)需要在运行时弹窗请求,用户点"允许"才能用。后端开发从来没有这个概念------后端权限是角色认证、接口鉴权。
java
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.CALL_PHONE}, 1);
} else {
call(); // 已经有权限,直接打
}
这个模式用了几次就记住了------先检查是否已有权限,没有就申请,申请结果在 onRequestPermissionsResult 里处理。
自定义控件 --- 封装可重用的View
后端封装组件靠类和方法。Android封装UI靠自定义View。我的 TitleLayout 就是一个自定义标题栏------继承 LinearLayout,在构造函数里 inflate 布局,在代码里处理按钮事件。任何一个Activity想用这个标题栏,一行 <include layout="@layout/title"/> 就行。
这不就是前端组件化的思想吗?只是Android比Vue/React早了很多年。
剩下的概念,接触了就知道
- ListView + ViewHolder :列表复用的标准写法,
convertView == null时才inflate,tag存引用 - AlertDialog / ProgressDialog:系统弹窗
- Thread + Handler:非UI线程更新UI的标准套路
- AsyncTask替代 :自己封装
ExecutorService+Handler搞了一套后台任务框架 - 通知(Notification) :
NotificationManager.notify(),带PendingIntent做点击跳转
这些不是要背下来,是用到了知道去哪找。
四、这个项目能证明什么
26个Java类,12个Activity,做完之后我知道:
- 给我一个需求,我能画出Activity跳转关系图------哪些是显式跳转、哪些用隐式Intent
- 需要后台跑了,我知道什么时候用Service什么时候用线程+Handler
- 读写数据了我能判断是存SharedPreferences、SQLite还是文件
- 系统发广播了我知道怎么注册、怎么拦截、怎么响应
- 权限不够我知道要申请、知道申请结果在哪处理
不熟练的地方是有的------比如动画、自定义View测量、多点触摸、NDK。但这些不是常态需求。Android原生的核心概念,这个项目已经全部走了一遍。
五、结语
有Java基础的人学Android原生开发,不是从零开始,是从80分开始。你要补的不是编程基础,是Android这个平台特有的概念和机制------Activity生命周期、Intent路由、Service守护、Broadcast通信、ContentProvider数据共享、运行时权限、SQLite嵌入式数据库。这些概念用一个完备的练习项目全部走一遍,比看十本书都管用。
剩下的不熟练只是练得少。多做几个界面、多填几个生命周期回调日志、多看几次 onStop 和 onDestroy 的打印------慢慢就内化了。
如果你也是Java后端想学Android:不要看文档,写一个项目。一个项目踏完所有基础概念,从此以后查什么都有方向。