根据《第一行代码》的顺序结构总结关键知识点,防止忘记,方便查阅。
Activity
1. 生命周期
(1)Activty的状态
① 运行状态:当Activity位于返回栈的栈顶时,Activity处于运行状态。
② 暂停状态:当Activity不处于栈顶,但仍可见,Activity处于暂停状态。
③ 停止状态:当Activity不处于栈顶,并且完全不可见,Activity处于停止状态。此时系统会为Activity保存相应的状态和成员变量。
④ 销毁状态:当Activity从返回栈移除后变为销毁状态。
(2)Activty的生命周期
① onCreate():Activity 第一次被创建的时候调用。该方法中完成Activity 的初始化操作。
② onStart():在Activity 由不可见变为可见的时候调用。
③ onResume():该方法在Activity 准备好和用户进行交互的时候调用,此时的Activity 位于返回栈的栈顶,并且处于运行状态。
④ onPause():该方法在系统准备去启动或者恢复另一个Activity 的时候调用,通常会在该方法中将一些消耗CPU的资源释放掉,以及保存一些关键数据。
⑤ onStop():该方法在Activity 完全不可见的时候调用。
⑥ onDestroy():该方法在Activity 被销毁之前调用。
⑦ onRestart():该方法在Activity 由停止状态变为运行状态之前调用,即重新启动Activity时调用。

2. 启动模式
(1)standard:每次启动Activity都会创建一个新的实例。
(2)singleTop:当Activity已经位于栈顶时不会创建新的实例,而是调用onNewInstance方法,但当Activity不在栈顶是还是会创建新的实例。
(3)singleTask:当Activity已经在任务栈中存在时,不会创建新的实例,会把该Activity后面入栈的Activity全部出栈,使该Activity位于栈顶,并调用onNewInstance方法。
(4)singleInstance:Activity以单例模式运行,整个系统中只存在一个实例。该模式会启动一个新的任务栈,并且该Activity是栈中唯一的Activity,适用于共享Activity的场景。
3. Intent
Intent一般用于启动Activity、启动Service、发送广播、传递数据等。
(1)显示Intent
明确指定目标组件的完整类名的Intent为显示Intent。
(2)隐式Intent
不明确指定目标组件,而是通过Action、Category、Data等属性来描述要执行的操作。
每个Intent中只能指定一个action,但额能指定多个category,只有某个Activity中添加的<actiong>和<category>与Intent中指定的action和category同时匹配,这个Activity才能响应Intent。如果没有Activity能响应Intent会发生崩溃。
隐式Intent还可以启动其他程序的Activity,比如在Intent中添加一个data为百度网址,百度浏览器的Activity会响应该Intent。
4. 返回结果
Activity可以通过startActivityForResult()方法启动一个新Activity,新Activity finish()之前,可以通过setResult()方法将数据返回给上一个Activity,而上一个Activity可以通过onActivityResult()方法接收新Activity返回的结果。
启动目标Activity
Intent intent = new Intent(CurrentActivity.this, TargetActivity.class);
startActivityForResult(intent, REQUEST_CODE);
在目标Activity中返回结果
Intent resultIntent = new Intent();
resultIntent.putExtra("result_key", "result_value");
setResult(RESULT_OK, resultIntent);
finish();
在源Activity中处理结果
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
String result = data.getStringExtra("result_key");
}
}
5. 任务栈
Android使用任务栈task来管理Activity,栈是后进先出(LIFO)的数据结构,当启动一个新的Activity就会入栈处于栈顶,系统总是显示处于栈顶的Activity给用户。
6. 保护和恢复实例状态
(1)onSaveInstanceState保存状态
Activity 中提供了一个onSaveInstanceState()回调方法,该方法保证Activity 被回收之前一定会被调用,用来保存临时数据(保存到系统内存中),在Activity被重新创建之后,通过onCreate()的参数savedInstanceState来恢复销毁之前的数据。
onSaveInstanceState()主要是应对屏幕旋转或系统资源不足的情况。如何用户主动退出Activity或终止应用程序(点击返回或调用finish()),则onSaveInstanceState()不会被调用。
(2)手动处理配置变更
除了使用onSaveInstanceState()保存Activity销毁前的临时数据,也可以在AndroidManifest.xml中为Activity配置android:configChanges属性,避免Activity被销毁重建。
<activity android:name=".MyActivity"
android:configChanges="orientation|screenSize|keyboardHidden" />
Fregment
1. 生命周期
(1)onAttach():当 Fragment 与 Activity 关联时调用。
(2)onCreate():初始化 Fragment 时调用。
(3)onCreateView():创建 Fragment 的视图(加载布局)时调用,返回一个 View 对象。
(4)onViewCreated():在 onCreateView() 之后调用,表示视图已经创建完成。
(5)onActivityCreated():当 Activity 的 onCreate() 方法完成时调用。
(6)onStart():Fragment 可见时调用。
(7)onResume():Fragment 可与用户交互时调用。
(8)onPause():Fragment 失去焦点时调用。
(9)onStop():Fragment 不可见时调用。
(10)onDestroyView():当 与Fragment 关联的视图被移除时调用。
(11)onDestroy():Fragment 被销毁时调用。
(12)onDetach():当 Fragment 与 Activity 解除关联时调用。

2. Fragment用法
(1)静态添加Fragment
在布局文件中直接声明Fragment。
<fragment
android:id="@+id/fragment_container"
android:name="com.example.MyFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
(2)动态添加Fragment
通过 FragmentManager 和 FragmentTransaction 动态管理 Fragment。
// 获取 FragmentManager
FragmentManager fragmentManager = getSupportFragmentManager();
// 开始一个事务
FragmentTransaction transaction = fragmentManager.beginTransaction();
// 添加 Fragment
MyFragment fragment = new MyFragment();
transaction.add(R.id.fragment_container, fragment);
// 异步提交事务
transaction.commit();
(3)Fragment导航
可以通过Android Jetpack 的 Nanvigation 组件提供了更简单的 Fragment 导航方式。
<fragment
android:id="@+id/fragment_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
// 指定了要使用的Fragment类是NavHostFragment
android:name="androidx.navigation.fragment.NavHostFragment"
// 指定了要使用的导航图(Navigation Graph)
app:navGraph="@navigation/navigation_main"
app:defaultNavHost="true"/>
Fragment Nanvigation仍然需要通过 FragmentManager 和 FragmentTransaction 动态管理 Fragment的添加、替换、移除等操作。
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, new AnotherFragment());
transaction.addToBackStack(null); // 将事务添加到返回栈
transaction.commit();
3. Fragment返回栈
(1)addToBackStack()
使用 addToBackStack() 将 Fragment 事务添加到返回栈。
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, new AnotherFragment());
transaction.addToBackStack(null); // 将事务添加到返回栈
transaction.commit();
(2)popBackStack()
可以通过 FragmentManager 的 popBackStack() 方法手动回退到上一个 Fragment.
(3)onBackPressed()
在 Activity 中重写 onBackPressed(),或者在 Fragment 中监听返回键事件。
4. Fragment嵌套
FragmentManager 支持嵌套 Fragment。主Fragment使用 getChildFragmentManager() 来管理子 Fragment。
5. Fragment重叠
(1)重叠原因
当旋转屏幕或语言更改的情况下Activity会被销毁重建,在Activity被销毁时系统会调用Activity的onSaveInstanceState()方法保存Activity的状态,包括Fragment的状态,当Activity重建时在onCreate()中会恢复这些状态,但Activity重新启动再次创建新的Fragment就导致Fragment重叠。
(2)解决方法
① 在Activity的onCreate()中检查Fragment是否已存在,不存在的话再添加新的Fragment。
if (savedInstanceState == null) {
// 只有在没有保存状态时才添加 Fragment
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new MyFragment(), TAG_FRAGMENT)
.commit();
} else {
// 如果已存在,直接获取 Fragment 实例
mFragment = getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT);
}
② 通过 FragmentManager 查找即将被创建的的 Fragment是否已存在。
// 查找已存在的 Fragment
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (fragment == null) {
// 如果 Fragment 不存在,则添加新的 Fragment
fragment = new MyFragment();
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
Broadcast
1. 广播类型
(1)标准广播(Normal Broadcast)
标准广播是一种完全异步执行的广播,广播发出后所有的广播接收器几乎同时接收到该广播,无先后顺序。
(2)有序广播(Ordered Broadcast)
有序广播是一种同步执行的广播,同一时刻只有一个广播接收器能收到,当前接收到广播的接收器逻辑执行完之后才会继续向后传递广播。优先级高的广播器会先接收到广播,还可以截断广播不再传递广播。
2. 发送广播
发送自定义广播,只需要将广播action添加到Intent,然后通过sendBroadcast()或sendOrderedBroadcast()方法发送广播即可。
发送标准广播:
Intent intent = new Intent("com.example.MY_ACTION");
sendBroadcast(intent);
发送有序广播:
Intent intent = new Intent("com.example.MY_ACTION");
sendOrderedBroadcast(intent, null);
3. 广播接收器
(1)静态注册
静态注册广播可以让程序在未启动的情况下也能接收到广播。
在应用中创建一个广播接收器类,然后在AndroidManifest.xml 文件中静态注册刚创建的 BroadcastReceiver类。
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="com.example.MY_ACTION" />
</intent-filter>
</receiver>
(2)动态注册
动态注册广播接收器需要在Activity中创建BroadcastReceiver类来接收广播,然后在IntentFilter 中指定接收哪些广播,再将BroadcastReceiver实例和IntentFilter 传入registerReceiver()方法动态注册广播监听。
BroadcastReceiver myReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
// 处理接收到的广播
}
};
IntentFilter intentFilter = new IntentFilter("com.example.MY_ACTION");
registerReceiver(myReceiver, intentFilter);
4. 广播的生命周期
广播的生命周期从注册广播开始到取消注册结束,在注册广播期间接收到的所有广播都可以通过onReceive()方法中的Intent参数看到,通过Intent的action筛选广播来处理。
当不需要接收广播时要通过unregisterReceiver ()方法取消广播注册,避免当注册BroadcastReceiver的Activity销毁时系统仍持有该广播接收器,而BroadcastReceiver又一直持有Activity的引用,造成内存泄漏。
持久化存储
1. 文件存储
文件存储适合存储简单的文本数据或二进制数据,Context类中提供了openFileOutput()方法用于将数据存储到指定文件中,存储和读取文件的默认位置为 /data/data/<packageName>/files/
存储文件输入的第二个参数为文件的操作模式:MODE_PRIVATE表示写入的内容覆盖原文件中的内容、MODE_APPEND表示往文件里追加内容,文件不存在的话创建新文件。
<写入文件>
FileOutputStream output = openFileOutput("data.txt", MODE_PRIVATE);
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(output));
try{
writer.write("Hello, World!");
} catch (IOException e) {
e.printStackTrace();
} finally {
writer.close();
}
<读取文件>
StringBuilder stringBuilder = new StringBuilder();
FileInputStream input= openFileInput("data");
BufferedReader reader = new BufferedReader(new InputStreamReader(input));
try{
String line;
whild ((line = reader.readLine()) != null){
stringBuilder.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
reader.close();
}
2. SharedPreferences
SharedPreferences支持不同的数据类型存储,使用键值对方式存储。
(1)获取SharedPreferences
① 通过Context类中的getSharedPreferences()方法获取SharedPreferences对象,该方法与写入文件方法类似,需要传入文件名称和操作模式,SharedPreferences文件存放路径为 /data/data/<packageName>/shared_prefs/,操作模式只有MODE_PRIVATE一种。
② 通过Activity类中的getPreferences()方法获取对象,该方法只需要传一个操作模式,文件名为当前Activity类名。
(2)向SharedPreferences文件存储数据
① 调用SharedPreferences对象的edit()方法获取SharedPreferences.Editor对象。
② 通过putString()等方法向SharedPreferences.Editor对象添加数据。
③ 调用apply()方法将添加的数据提交,完成数据存储操作。
<写入数据>
SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = sharedPreferences.edit();
editor.putString("username", "小明");
editor.putInt("age", 25);
editor.apply();
<读取数据>
SharedPreferences sharedPreferences = getSharedPreferences("MyPrefs", Context.MODE_PRIVATE);
String name = sharedPreferences.getString("username", "");
int age = sharedPreferences.getInt("age", 0);
3. 数据库存储
SQLite 数据库适用于存储结构化数据,可以通过 SQLiteOpenHelper 来管理数据库的创建和版本更新。
(1)创建SQLite数据库
public class DBHelper extends SQLiteOpenHelper {
public DBHelper(Context context) {
super(context, "mydatabase.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
// 创建表
String createTable = "CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)";
db.execSQL(createTable);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 该方法用来更新数据库,先删除已存在表再重新创建
db.execSQL("DROP TABLE IF EXISTS users");
onCreate(db);
}
}
(2)写入SQLite数据库
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("name", "John Doe");
values.put("age", 30);
db.insert("users", null, values);
(3)读取SQLite数据库
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor cursor = db.query("users", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
String name = cursor.getString(cursor.getColumnIndex("name"));
int age = cursor.getInt(cursor.getColumnIndex("age"));
} while (cursor.moveToNext());
}
cursor.close();
ContentProvider
ContentProvider 是 Android 中一种用于不同应用程序之间共享数据的机制。提供了统一的接口,使得应用程序可以安全地访问和操作其他应用程序的数据,可以被视为一种数据访问层,通过URI(统一资源标识符)来标识不同的资源。
1. 功能
(1)数据共享
ContentProvider 允许一个应用程序访问另一个应用程序的数据。这些数据可以是数据库、文件或其他持久化存储。
(2)封装数据访问
通过 ContentProvider,可以封装对底层数据存储(如 SQLite 数据库)的操作,提供统一的数据访问接口。应用程序不需要直接接触底层的实现细节,只需要使用 ContentResolver 来与 ContentProvider 进行交互。
(3)数据操作的权限管理
通过 ContentProvider,可以控制哪些应用程序有权访问某些数据。Android 系统会通过声明权限来控制不同应用程序之间的数据访问权限。
2. 组件
(1)URI
每个 ContentProvider 都有一个唯一的 URI,用于标识数据。
访问某包的某表中的数据:content://com.example.appdemo.provider/table
访问某包的某表中id为1的数据:
content://com.example.appdemo.provider/table/1
(2)CRUD 操作
ContentProvider 支持标准的 CRUD(创建、读取、更新、删除)操作:
insert():插入数据。
query():查询数据。
update():更新数据。
delete():删除数据。
(3)ContentResolver
ContentResolver 是用于与 ContentProvider 交互的主要接口。通过 ContentResolver,应用可以向 ContentProvider 发起请求,执行增、删、改、查等操作。
3. ContentProvider使用
(1)创建 ContentProvider 类。
public class MyContentProvider extends ContentProvider {
@Override
public boolean onCreate() {
// 初始化数据库或其他数据源
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
// 实现查询操作
return null;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// 插入数据
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
// 更新数据
return 0;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// 删除数据
return 0;
}
@Override
public String getType(Uri uri) {
// 返回数据类型
return "vnd.android.cursor.dir/vnd.com.example.provider";
}
}
(2)声明ContentProvider
<AndroidManifest.xml>
<provider
android:name=".MyContentProvider"
android:authorities="com.example.provider"
android:exported="true" />
(3)使用 ContentProvider
ContentResolver resolver = getContentResolver();
Uri uri = Uri.parse("content://com.example.provider/data");
Cursor cursor = resolver.query(uri, null, null, null, null);
Service
1. Service类型
(1)Started Service(启动的服务):当一个应用组件(如 Activity)调用 startService() 启动服务时,服务被启动并继续运行,直到它调用 stopSelf() 或 stopService() 被停止。
(2)Bound Service(绑定的服务):当其他组件通过 bindService() 方法绑定到服务时,服务可以在多个组件之间共享。在组件调用 unbindService() 后,服务会被停止。
2. 生命周期
(1)onCreate():当服务第一次创建时调用。用于初始化资源。
(2)onStartCommand():当 startService() 被调用,启动Service时调用。用于执行实际的后台任务。
(3)onBind():当 bindService() 被调用时,返回一个 IBinder 对象,用于通信。如果服务不需要绑定,则返回 null。
(4)onUnbind():当所有客户端解绑服务时调用,用来释放资源。
(5)onDestroy():服务销毁时调用,清理资源,停止线程等。
3. Service使用
(1)注册:Service需要在AndroidMainfest.xml中注册才能生效。
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"/>
(2)启动和停止
Context类中提供了startService()和stopService()方法来启动和停止服务。
<Activity.java>
startService(Intent(context, MyService::class.java))
stopService(Intent(context, MyService::class.java))
Service中也可以自己停止运行。
返回值选项:
START_NOT_STICKY:服务不会在系统杀死后自动重启,适用于不需要持续运行的任务。
START_STICKY:服务会在系统杀死后自动重启,适用于需要持续运行的任务。
START_REDELIVER_INTENT:服务会在系统杀死后自动重启,并重新传递上次的 Intent,适用于需要重新处理任务的场景
<MyService.java>
public int onStartCommand(Intent intent, int flags, int startId) {
...
// 任务完成后停止服务
stopSelf(startId);
return START_NOT_STICKY;
}
(3)与Activity通信
Service与Activity通信需要借助Service的onBind()方法实现。
① Service中创建Binder
public class MyService extends Service {
private final IBinder mBinder = new MyBinder();
public class MyBinder extends Binder {
public void fun1(){
Log.d("MyService", "fun1 executed!")
}
public void fun2(){
Log.d("MyService", "fun2 executed!")
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
② Activity调用Service中的方法
Activity通过bindService()方法绑定Service后可通过ServiceConnection实例调用Service的Binder中提供的方法。
绑定标志:
BIND_AUTO_CREATE:用于自动创建服务并绑定客户端(只会执行Service的onCreate(),不执行onStartCommand())。
BIND_ABOVE_CLIENT:服务的优先级高于客户端,即使客户端被杀死,服务仍会继续运行。
BIND_ALLOW_OOM_MANAGEMENT:允许系统在内存不足时杀死服务。
BIND_WAIVE_PRIORITY:服务的优先级不会因为绑定而提升。
public class MainActivity extends AppCompatActivity {
private MyService myService;
private boolean isBound = false;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
myService = (MyService.MyBinder) service;
isBound = true;
// 调用服务中的方法
myService.fun1();
myService.fun2();
}
@Override
public void onServiceDisconnected(ComponentName name) {
isBound = false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 绑定服务
Intent intent = new Intent(this, MyService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (isBound) {
unbindService(serviceConnection);
isBound = false;
}
}
}
4. 前台Service
当使用Service的应用程序进入后台后,Service可能会被系统收回,可以使用前台Service防止被系统收回。
前台Serevice会又运行图标显示在系统状态栏,防止恶意应用长期在后台偷偷占用手机资源。
(1)前台Service必须添加权限
<!-- 前台Service -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
(2)使用前台Service
<MyService.java>
public void onCreate() {
super.onCreate();
// 前台 Service
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(this, "my_service")
.setContentTitle("Service title")
.setContentText("Service content")
.setSmallIcon(R.drawable.ic_launcher_background)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_background))
.setContentIntent(pi)
.build();
startForeground(1, notification);
}