0%

Android开发

  • Android开发


系统架构

AOSP 软件堆栈架构

通常来讲会被分成五层

  1. 系统应用层
  2. 应用框架层
  3. native库以及Android运行时
    • Native C/C++ Libraries:C/C++程序库
    • Dalvik虚拟机
  4. 硬件抽象层
    • HAL
  5. linux内核层
    • IPC(Binder)

Android文件目录结构

  1. assets
    • 静态文件,apk所使用的各种资源,比如图片、视频等等,通过API获取
  2. res
    • 编译后的资源文件
  3. lib
    • so动态链接库,用于底层的一些调用
  4. dex
    • 虚拟机可执行文件,主要逆向目标
  5. META-INF
    • 数字签名,用于校验apk是否被篡改
  6. AndroidManifest.xml
    • 一个应用程序的配置清单,声明权限、组件等的基本信息
  7. resources.arsc
    • 编译后资源的位置与id之间的映射关系

Android启动流程

  • [Android启动过程-万字长文(Android14) - 柳云居士 - 博客园](https://www.cnblogs.com/anywherego/p/18221943#:~:text=1 1.引导加载程序(Bootloader)启动: 当设备上电或者重启时,首先会由引导加载程序负责启动。 … 2 2.内核加载: 引导加载程序会根据预定义的配置从设备存储中加载操作系统内核。 …,6 6.启动系统服务: 在Zygote进程启动后,还会启动一系列系统服务,例如SurfaceFlinger、ActivityManager、PackageManager等。 … 7 7.启动桌面程序: 一旦系统服务启动完成,Android系统就处于可用状态。 )

APP安装过程

system/app 存放系统自带的应用程序

data/app 用户程序安装的目录

data/data 存放应用程序的数据

  1. 复制APK安装包到data/app目录下
  2. 解压并扫描安装包,把dex文件(Dalvik字节码)保存到dalvik-cache目录
  3. 在data/data目录下创建对应的应用数据目录

App运行流程

  1. BootClassLoader加载系统核心库
  2. PathClassLoader加载App自身dex
  3. 进入App自身组件开始执行
  4. 调用Application的attachBaseContext
  5. 调用Application的onCreate

Activity

简述

img

onCreate():

  • 必须实现,当然也是对activity进行逆向的核心破局点
  • Activity第一次创建时调用

onStart():Activity置于栈顶可见但无焦点状态时调用

onRestart():Activity再次回到栈顶可见时调用

onResume()【活动状态】:在Activity可见并且有焦点时调用

onPause():发生中断时(准备启动或者恢复另一个activity时)调用,进入暂停状态。可以编写一些保存现场或者释放资源的操作。【该Activity仍然用户可见】

onStop():Activity不再对用户可见时调用

onDestory():Activity销毁时调用

场景

  • 打开新Activity。原Activity: onPause()–>onStop()
  • 回到原Activity。onRestart()–>onStart()–>onResume()

切屏

不配置参数

  • 重走生命周期
  • onPause()–>onSaveInstanceState()–>onStop()–>onDestroy()–>onCreate()–>onStart()–>onRestoreInstanceState–>onResume()
  • onSaveInstanceState()和onRestoreInstanceState用于Activity异常退出时候保存和恢复Activity状态

配置参数

1
2
# 选择在什么情况下不重新启动生命周期,而是执行onConfigurationChanged方法
android:configChanges = "orientation| screenSize" # 方向旋转 | 大小变化

四种启动模式

Standard

  • 先进后出,Activity放入栈顶

SingleTop【栈顶复用模式/单顶模式】

  • 如果当前返回栈的顶部不存在该Activity,则新建该Activity并放入栈顶
  • 如果当前返回栈的顶部存在Activity的实例,则调用该实例的onNewIntent()方法向其传送Intent,不创建该Activity的新实例

SingleTask【栈内复用模式/单任务模式】

1
2
3
4
5
// 一个栈中只有一个指定的Activity
<activity android:name="Activity"
android:launchMode="singleTask"
android:taskAffinity="com.android.task"
</activity>

SingleInstance【单例模式】

  • Singletask的升级版,系统直接创建一个新的返回栈并创建Activity的新实例置于新Task返回栈中

Service

service

Service两种启动方式

StartService方式

  • 无限期运行,需要stopService或者stopSelf方法终止
  • onCreate() –> onStartCommand() –> onDestroy()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class MyService extends Service {
public MyService() {
}
//startService()【第一次】后执行onCreate回调
@Override
public void onCreate() {
super.onCreate();
}
//startService()方法执行时调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
//无论是否使用都必须重写的方法
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
//销毁
@Override
public void onDestroy() {
super.onDestroy();
}
}

BindService方式

  • C/S模式。service作为server,组件作为client。单个service,多个client
  • client通过Ibinder接口获取service实例
  • onCreate() -> onBind() -> onUnbind() -> onDestroy()

Server端

  • service的onBind方法中返回IBinder类型实例(也就是service实例)

Client端

  1. 创建ServiceConnection类型实例,重写onServiceConnected()方法和onServiceDisconnected()方法
  2. 执行onServiceConnected回调时,通过IBinder得到Service实例对象
  3. 执行onServiceDisconnected回调时,clientService断开连接

销毁问题

  • client销毁时自动与service解除绑定
  • client调用Context的unBindService()方法解除绑定
  • 没有任何client与service绑定时service自动销毁

Service保活

  • 自定义系统服务
  • 设置成前台服务
  • 注册为系统服务
  • 第三方库来保活,如HelloDaemon
  • 双service轮询检测互相拉起
  • START_STICKY(onStartCommand方法中),kill 后会被重启(等待5秒左右),重传Intent,保持与重启前一样

进程优先级

  1. 前台进程
  2. 可见进程。对用户可见但不能交互的Activity和绑定在上面的Service
  3. 服务进程。startService运行的服务
  4. 后台进程。执行了onStop方法停止的程序。内存不够时被杀死
  5. 空进程

Broadcast Receiver

  • 分类:普通、系统、有序、本地、粘滞广播

  • 广播发送者 广播接收者 AMS(Binder机制)

    1. 接收者向AMS注册
    2. 发送者发送广播到AMS
    3. AMS寻找合适的广播接收者
    4. 广播发送到接收者的消息循环队列中
    5. 广播接收者拿到广播回调onReceive()

定义广播接收类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.example.myapplication.receiver;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
//继承广播接收者类
public class Receiver extends BroadcastReceiver {
public static final String action = "abc"; //设置广播内容,供发送和接收使用
@Override
public void onReceive(Context context, Intent intent) {
if(intent != null && intent.getAction().equals(action)){ //接收到广播打印信息
Log.d("receiver","receiver successful");
}
}
}

动态注册发送广播

  • 非常驻、特定监听
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;

import com.example.myapplication.receiver.Receiver;

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(this); //添加按钮监听事件

}

Receiver receiver; //创建接受器对象
@Override
public void onClick(View view) {
Intent intent = new Intent(Receiver.action); //创建意图对象,传入参数为要发送的广播信息
sendBroadcast(intent); //发送广播信息
}
//实现广播接收的开关
@Override
protected void onStart() {
super.onStart();
receiver = new Receiver();

IntentFilter filter = new IntentFilter(Receiver.action); //创建过滤器,只接收Receiver.action的广播
registerReceiver(receiver, filter); //注册接收器
}
@Override
protected void onStop() {
super.onStop();
unregisterReceiver(receiver); //注销接收器
}
}

静态注册发送广播

  • 常驻,时刻监听,与app是否被用户打开无关
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<receiver
android:enabled=["true" | "false"]
//此broadcastReceiver能否接收其他App的发出的广播
//默认值是由receiver中有无intent-filter决定的如果有intent-filter默认值为true否则为false
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
//继承BroadcastReceiver子类的类名
android:name=".mBroadcastReceiver"
//具有相应权限的广播发送者发送的广播才能被此BroadcastReceiver所接收
android:permission="string"
//BroadcastReceiver运行所处的进程
//默认为app的进程可以指定独立的进程
//Android四大基本组件都可以通过此属性指定自己的独立进程
android:process="string" >

//用于指定此广播接收器将接收的广播类型
//本示例中给出的是用于接收网络状态改变时发出的广播
<intent-filter>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
</intent-filter>
</receiver>
1
2
3
4
5
6
7
8
@Override
public void onClick(View v) {
Intent intent = new Intent("广播内容");
// 通过setComponent方法指定接收器的完整路径
ComponentName componentName = new ComponentName(this, "receiverUrl");
intent.setComponent(componentName); // 设置意图的组件信息
sendBroadcast(intent);
}

有序广播

广播设置优先级,有序接收

具体规则

  1. 优先级越大的接收器,越早收到有序广播
  2. 优先级相同,越早注册的接收器越早收到有序广播
1
2
3
4
5
6
7
8
9
10
//实现广播接收的开关
@Override
protected void onStart() {
super.onStart();
receiver = new Receiver();

IntentFilter filter = new IntentFilter(Receiver.action); //创建过滤器,只接收Receiver.action的广播
filter.setPriority(8); //设置优先级
registerReceiver(receiver, filter); //注册接收器
}
1
2
3
4
5
6
<receiver 
android:name=".myReceiver" >
<intent-filter android:priority="100">
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>

Content Provider

URI

content://com.carson.provider/User/1

  • Schema:Android规定的URI前缀
  • Authority:content provider唯一标识符
  • Path:指向数据库某个表名
  • ID:表中特定记录

原理

基于binder实现跨进程数据共享交换

数据共享:允许应用之间共享数据,避免直接访问文件或数据库。

统一接口:提供 CRUD(创建、读取、更新、删除)操作的标准接口。

权限控制:通过 URI 和权限管理,控制数据的访问权限。

抽象数据源:数据可以存储在 SQLite 数据库、文件、网络或其他地方,但对外提供统一访问方式。

ContentProvider:内容提供者,主要作用就是管理数据,比如最常见的增删改查操作,同时为这些数据的访问提供了统一的接口,实现进程间的数据传递和共享。

ContentResolver:内容解析者,ContentResolver可以为不同URI操作不同的ContentProvider中的数据,优点像外部进程和ContentProvider之间的中间商。

ContentObserver:内容观察者,观察ContentProvider中的数据变化,有变化时执行特定操作。

优点

  1. 相比于binder和intent,支持大批量数据在不同进程间传输

  2. 有利于业务层和访问层解耦合

Context & Intent

context

继承关系

Android 开发者,你真的懂Context吗? - 知乎

1
2
3
4
5
6
图要是挂了就挂了
大概就这么几部分
1. context[抽象类]是老大,分出了contextImpl[实现类]和contextWrapper[封装类]
2. contextWrapper的实现需要通过attachBaseContext方法设置其内部的mBaseBianliang来实现,本质还是contextImpl
3. service和application直接继承于contextWrapper
4. (activity extends contextThemeWrapper) extends contextWrapper

broadcast receiver和content provider不是继承于context,它们所拥有的context都来源于其它地方

获取&使用

获取

  • Activity.this
  • getApplication():获取应用实例。仅在Activity和Service可调用
  • getApplicationContext():整个App的context
  • view.getContext():view的context一般是view所在的Activity实例

使用

  • 优先使用application context
  • start activity 和 dialog必须使用activity context

num

context = Activity + Service + 1(本身)

Intent

  • intent的数据传输限制大概在1M左右,超过限制会静默奔溃

显式启动

1
2
Intent intent = new Intent(this, SecondActivity.class);  
startActivity(intent);

隐式启动

  • manifest里设置intent-fliter
  • 在代码调用里通过条件匹配得到特定intent
    1
    2
    3
    4
    5
    6
    7
    8
    <activity android:name=".MainActivity">
    //过滤器
    <intent-filter>
    <action android:name="com.example.demo"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="com.example.demo.category"/>
    </intent-filter>
    </activity>
    1
    2
    3
    4
    Intent intent = new Intent();
    intent.setAction(com.example.demo);
    intent.addCategory(com.example.demo.category);
    startActivity(intent);

组成

  • componentName:目的组件
  • action:用来表现意图的行动
  • category:用来表现动作的类别
  • data:表示与动作要操纵的数据
  • type:对于data范例的描写
  • extras:扩展信息
  • Flags:期望这个意图的运行模式

intent 和 intent 过滤器 | Android Developers

PendingIntent

PendingIntent | Android Developers

异步机制

Handler

异步消息机制,处理父子进程间消息通讯

组成

  • Handler:发送Message和处理Message

  • Message Queue:先进先出,由Looper管理

  • Looper:从消息队列读Message,分配给目标Handler进行处理

一个线程对应一个Looper

一个消息队列对应多个Handler

Looper.loop()不会消耗大量CPU资源或者造成应用卡死

使用Looper判断当前线程是否为主线程

1
2
3
public boolean isMainThread() {
return Looper.getMainLooper() == Looper.myLooper();
}

HandlerThread

介绍

子线程之间通信,一个继承自Thread的Handler类

因此,如果需要在子线程中使用Handler,则考虑

  1. Looper.prepare() –> Looper.loop() –> 实现Handler
  2. 直接实现HandlerThread类

案例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
private static final int MSG_READ_INFO = 100;
Handler mMainHandler = new Handler();
private Handler mThreadHandler;
private HandlerThread mHandlerThread;

@Override
protected void onCreate(Bundle savedInstanceState) {

initHandlerThread();
startHandlerThread();
}

private void initHandlerThread() {
//创建HandlerThread
mHandlerThread = new HandlerThread("thread");
//启动HandlerThread线程
mHandlerThread.start();
//将HandlerThread和Handler绑定
mThreadHandler = new Handler(mHandlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {

}
};
}

private void startHandlerThread() {
mThreadHandler.sendEmptyMessage(MSG_READ_INFO);
}

@Override
protected void onPause() {
super.onPause();
//从 Handler 的消息队列中移除特定类型的消息
mThreadHandler.removeMessages(MSG_READ_INFO);
}

@Override
protected void onDestroy() {
super.onDestroy();
//退出HandlerThread
mHandlerThread.quit();
//清除与 Handler相关的所有消息和回调任务
mMainHandler.removeCallbacksAndMessages(null);
}

AsyncTask

IntentService

基于HandlerThread实现的 继承于Service 的类,适合顺序多任务、高优先级场景。如离线下载、后台下载

特性

  1. 可以startService启动,但是不能onBind()
  2. 任务完成后主动调用stopSelft()结束
  3. 自动创建工作线程来处理多线程任务

Fragement

生命周期

官图

Fragment 生命周期状态,以及它们与 Fragment 的生命周期回调和 Fragment 的视图生命周期之间的关系

对比Activity

Activity::onCreate()

  • **Fragment::onAttach()**:Fragment绑定到Activity时调用
  • Fragment::onCreate()
  • **Fragment::onCreateView()**:创建Fragment视图时调用
  • **Fragment::onActivityCreated()**:Activity的onCreated()方法返回时调用

Activity::onStart()

  • Fragment::onStart()

Activity::onResume()

  • Fragment::onResume()

Activity::onPause()

  • Fragment::onPause()

Activity::onStop()

  • Fragment::onStop()

Activity::onDestroy()

  • **Fragment::onDestroyView()**:销毁Fragment视图时调用
  • Fragment::onDestroy()
  • **Fragment::onDetach()**:Fragment和Activity取消绑定时调用

特点

Activity的每一个生命周期都会触发Fragment同样的回调

使用

静态

布局文件xml中声明Fragment

1
2
3
4
5
<fragment
android:id="@+id/xr_fragment"
android:name="com.example.XrFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
1
2
3
4
5
6
7
8
9
10
11
12
public class MyFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
/*
* 参数1:布局xml文件的ID
* 参数2:容器,被inflate的布局的父ViewGroup
* 参数3:是否将这个生成的View添加到这个容器中去
* 作用是将布局文件封装在一个View对象中,并填充到此Fragment中
* */
View v = inflater.inflate(R.layout.xr_fragment, container, false);
return v;
}
}

动态

……

Fragment和Activity通信

  1. Activity有Fragment的引用,可直接通过引用访问Fragment里的public方法

  2. 无引用,通过getFragmentManager.findFragmentByTag()或者findFragmentById()得到Fragment实例

  3. Fragment中通过getActivity()获取绑定的Activity实例

  4. 广播通信、接口交互

  5. Bundle通信

    Activity中建Bundle –> setArguments(bundle)传入Fragment –> Fragment中getArguments()接收

  6. registerForActivityResult() + startActivityForResult + setResult

Binder & AIDL

SharedPreferences

  1. 基于key-value 键值对生成的xml文件,在/data/data/packageName/shared_prefs目录下
  2. 永久性存储,少量存储
1
2
3
4
5
6
7
SharedPreferences sharedPreferences = context.getSharedPreferences(String name, @PreferencesMode int mode); 
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("key", "value");
editor.apply();

//使用
String value = sharedPreferences.getString("key", "defaultValue");

mode

  • MODE_PRIVATE:只能被本应用读写,或者有相同用户ID的应用读写;
  • MODE_WORLD_READABLE:全局读
  • MODE_WORLD_WRITEABLE:全局写
  • MODE_MULTI_PROCESS:可以实现多进程访问SharedPreferences数据
  • MODE_APPED:如果文件存在且对应的key也存在,可以在对应的value中追加新的内容

Native函数动静态注册

Java

1
2
3
4
5
6
7
8
9
10
11
12
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("myapplication"); // 加载 native 库
}
public native int addNumbers(int a, int b);

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

静态注册

1
2
3
4
5
6
extern "C"
JNIEXPORT jint JNICALL
Java_com_demo_myapplication_MainActivity_addNumbers(JNIEnv *env, jobject thiz, jint a, jint b) {
// TODO: implement addNumbers()
return a + b;
}

动态注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <jni.h>
#include <string>

// 实现函数
jint addNumbers(JNIEnv* env, jobject obj, jint a, jint b) {
return a + b;
}

jstring greet(JNIEnv* env, jobject obj, jstring name) {
const char* nameStr = env->GetStringUTFChars(name, nullptr);
std::string greeting = "Hello, " + std::string(nameStr);
env->ReleaseStringUTFChars(name, nameStr);
return env->NewStringUTF(greeting.c_str());
}

// 动态注册的映射表
static JNINativeMethod methods[] = {
{"addNumbers", "(II)I", (void*)addNumbers}
};

// 动态注册逻辑
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) {
return JNI_ERR;
}

jclass clazz = env->FindClass("com/demo/myapplication/MainActivity");
if (clazz == nullptr) {
return JNI_ERR;
}

if (env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])) != JNI_OK) {
return JNI_ERR;
}

return JNI_VERSION_1_6;
}

序列化

bundle只支持基本数据类型,因此传递对象时需要先序列化

Parcelable

把对象分解为Intent支持的类型后通过Intent传输

内存读写

Serializable

基于Java反射

磁盘读写

1
2
3
4
5
//序列化
Bundle bundle = new Bundle();
bundle.putSerializable("");
//反序列化
mContact = getArguments().getSerializable("");

URL格式完整

://:@:/:?#

    1. scheme:方案,访问服务器以获取资源时要使用哪种协议,如:http
    2. host:主机,资源宿主服务器的主机名,ip地址
    3. path:路径,服务端上的资源本地名,由斜杠分割开来,如:index.html
    4. user:password,访问资源时需要的用户名和密码,中间冒号不能丢
    5. port:端口,默认端口为80
    6. params:参数,参数为名/值对(如:name=’xiaodeng’),url可以包含多个参数字段,他们之间以及与路径的其余部分之间用‘&’分隔。
    7. query:查询,用字符‘?’将其与url的其他部分分割开来
  • 片段(#)
    1. 为了引用部分资源或资源的一个片段,url支持使用片段组件来表示一个资源内部的片段
    2. 如:url可以指向html文档中的特定的图片或小节。
    3. 案例:http://www.joes-hardware.com/tools.html#drills

参考文章:

Android高频面试题全解析 - 牛客网