> 技术文档 > as笔记csdn_sp任务视频

as笔记csdn_sp任务视频


activity与fragment通信

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

接口

在 Android 开发中,ActivityFragment 的通信是一个非常常见的场景。一般来说,有三种常见方式来实现 ActivityFragment 之间的通信:


✅ 一、通过接口回调(Fragment 向 Activity 通信)

这是最推荐也是最规范的方式,Fragment 向宿主 Activity 发送数据或事件

步骤如下:

  1. 定义接口
public interface OnFragmentInteractionListener { void onFragmentEvent(String data);}
  1. Fragment 中声明接口变量并绑定
public class MyFragment extends Fragment { private OnFragmentInteractionListener mListener; @Override public void onAttach(@NonNull Context context) { super.onAttach(context); if (context instanceof OnFragmentInteractionListener) { mListener = (OnFragmentInteractionListener) context; } else { throw new RuntimeException(context.toString()  + \" must implement OnFragmentInteractionListener\"); } } private void someEvent() { if (mListener != null) { mListener.onFragmentEvent(\"来自Fragment的数据\"); } }}
  1. Activity 实现接口
public class MainActivity extends AppCompatActivity implements OnFragmentInteractionListener { @Override public void onFragmentEvent(String data) { // 收到来自 Fragment 的数据 Log.d(\"MainActivity\", \"收到Fragment数据: \" + data); }}

fragment生命周期

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

onAttach和onDetach用于activity和fragment的绑定

📌 一、Created(已创建状态)

这个阶段主要是初始化 Fragment 的关键部分:

  • onAttach():Fragment 与宿主 Activity 绑定时调用。
  • onCreate():初始化 Fragment,比如保留数据、初始化组件等。
  • onCreateView():为 Fragment 创建视图(UI界面)。
  • onActivityCreated():当宿主 Activity 的 onCreate() 方法返回时调用,表明 Activity 和 Fragment 的 view 都已初始化完成。

🚀 二、Started(已启动状态)
  • onStart():Fragment 对用户可见(但还不能交互)时调用。

🟢 三、Resumed(已恢复状态)
  • onResume():Fragment 对用户可见且可以交互时调用(处于活动状态)。

⏸️ 四、Paused(已暂停状态)
  • onPause():Fragment 不再处于前台但仍可见时调用。常用于释放不需要的资源,保存数据等。

⏹️ 五、Stopped(已停止状态)
  • onStop():Fragment 完全不可见时调用。可以进一步释放资源。

❌ 六、Destroyed(已销毁状态)

Fragment 被销毁时,依次调用以下方法:

  • onDestroyView():移除与 Fragment 相关的视图。
  • onDestroy():清理剩余资源。
  • onDetach():Fragment 与宿主 Activity 分离。

✅ 补充说明

  • Fragment 生命周期与 Activity 生命周期紧密相关,但它也拥有自己独立的回调方法。
  • onCreateView() 是 UI 初始化的关键;如果 Fragment 没有界面,这个方法可以返回 null
  • onAttach()onDetach() 构成了 Fragment 的完整生命周期。

生命周期切换

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

当你在 切换 Fragment(例如从 Fragment A 切换到 Fragment B) 时,生命周期的执行顺序会根据是否将 Fragment 加入回退栈(Back Stack) 而有所不同。下面我为你详细说明两种典型情况:


📌 情况一:未加入回退栈(默认行为)

此时切换 Fragment 是“替换”的行为,原 Fragment 会被彻底销毁,生命周期如下:

➤ Fragment A(被移除):

scss复制代码onPause() → onStop() → onDestroyView() → onDestroy() → onDetach()

➤ Fragment B(新加入):

scss复制代码onAttach() → onCreate() → onCreateView() → onActivityCreated() → onStart() → onResume()

也就是说,A 完整退出,B 全新进入。


📌 情况二:加入回退栈(通过 addToBackStack() 添加)

此时是“隐藏当前 Fragment,显示另一个 Fragment”,原 Fragment 并不会被销毁,只是被暂停:

➤ Fragment A(被隐藏):

scss复制代码onPause() → onStop()

(不会执行 onDestroyView(),除非你显式移除)

➤ Fragment B(被显示):

scss复制代码onAttach()(若第一次加载)→ onCreate()(若第一次加载)→ onCreateView()→ onActivityCreated()→ onStart()→ onResume()

然后如果你按返回键回到 Fragment A,它的生命周期会继续:

scss复制代码onStart() → onResume()

✅ 总结:Fragment 切换生命周期对比

操作 Fragment A Fragment B 替换(不加入回退栈) onPause → onStop → onDestroyView → onDestroy → onDetach onAttach → onCreate → onCreateView → onActivityCreated → onStart → onResume 切换(加入回退栈) onPause → onStop 可能 onAttach → onCreate(首次) onCreateView → onActivityCreated → onStart → onResume

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

视图View

具体内容见ppt

视图View为那些控件,button、text view等,match_parent为填充父布局的尺寸,wrap_parewnt为包裹内容尺寸,文本有多大,空间就有多大。其他控件共有属性待补充

创建布局和View有两种方式:

Java代码和xml布局方式

容器视图 包括线性布局linear layout,相对布局 relativeLayout,帧布局frame layout,新的constrain layout

viewpager的使用流程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传


📄 1. 布局文件(res/layout/activity_main.xml)
<?xml version=\"1.0\" encoding=\"utf-8\"?><androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" xmlns:app=\"http://schemas.android.com/apk/res-auto\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\"> <androidx.viewpager2.widget.ViewPager2 android:id=\"@+id/viewPager\" android:layout_width=\"0dp\" android:layout_height=\"0dp\" app:layout_constraintTop_toTopOf=\"parent\" app:layout_constraintBottom_toBottomOf=\"parent\" app:layout_constraintStart_toStartOf=\"parent\" app:layout_constraintEnd_toEndOf=\"parent\" /></androidx.constraintlayout.widget.ConstraintLayout>

🧩 2. 每一页的布局(res/layout/item_page.xml)
<?xml version=\"1.0\" encoding=\"utf-8\"?><LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" android:orientation=\"vertical\" android:gravity=\"center\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\"> <TextView android:id=\"@+id/textViewPage\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" android:textSize=\"24sp\" /></LinearLayout>

🎯 3. 页面适配器(MyPagerAdapter.java)
import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.TextView;import androidx.annotation.NonNull;import androidx.recyclerview.widget.RecyclerView;import java.util.List;// 自定义适配器类,继承自 RecyclerView.Adapterpublic class MyPagerAdapter extends RecyclerView.Adapter<MyPagerAdapter.PageViewHolder> { // 用于保存每一页要显示的文本数据 private final List<String> pageData; // 构造函数:传入页面数据列表 public MyPagerAdapter(List<String> pageData) { this.pageData = pageData; } // 定义内部类 ViewHolder,用于缓存 item 视图的控件 public static class PageViewHolder extends RecyclerView.ViewHolder { TextView textView; // 用于显示页面文本内容 public PageViewHolder(@NonNull View itemView) { super(itemView); // 绑定布局中的 TextView 控件 textView = itemView.findViewById(R.id.textViewPage); } } // 创建新的 ViewHolder,加载每一页的布局 @NonNull @Override public PageViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { // 将 item_page.xml 布局转为 View 对象 View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.item_page, parent, false); return new PageViewHolder(view); // 返回新建的 ViewHolder } // 为每个 ViewHolder 设置数据 @Override public void onBindViewHolder(@NonNull PageViewHolder holder, int position) { // 将数据设置到 TextView 上 holder.textView.setText(pageData.get(position)); } // 返回页面的总数 @Override public int getItemCount() { return pageData.size(); }}

🚀 4. 主页面代码(MainActivity.java)
import android.os.Bundle;import androidx.annotation.Nullable;import androidx.appcompat.app.AppCompatActivity;import androidx.viewpager2.widget.ViewPager2;import java.util.Arrays;import java.util.List;public class MainActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewPager2 viewPager = findViewById(R.id.viewPager); List<String> pages = Arrays.asList(\"第一页\", \"第二页\", \"第三页\"); MyPagerAdapter adapter = new MyPagerAdapter(pages); viewPager.setAdapter(adapter); }}
✅ 小结
步骤 操作内容 1 在布局中添加 ViewPager2 控件 2 创建 Adapter,继承 RecyclerView.Adapter 3 构建 ViewHolder 显示每一页 4 实现必要方法绑定数据 5 设置 adapter 到 ViewPager 上

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Activity组件

Activity切换代码

D:\\Android_studio\\activity_change_04072

Activity生命周期

📘 一、Activity 生命周期各阶段及方法
生命周期方法 说明 onCreate() Activity 第一次创建时调用。用于初始化:设置布局、绑定控件、恢复状态等。 onStart() Activity 对用户可见但还不能交互时调用。 onResume() Activity 完全进入前台、可交互时调用onPause() Activity 部分不可见(如弹窗出现) 时调用。适合保存临时数据、暂停动画等。 onStop() Activity 完全不可见 时调用(如切换到别的 Activity)。适合释放资源。 onRestart() Activity 从停止状态恢复时调用(如按 Home 键后重新进入)。 onDestroy() Activity 被销毁前调用,常用于最终资源释放、注销广播等。
🔁 二、常见生命周期流程
  1. 首次启动 Activity
onCreate()onStart()onResume()
  1. 按 Home 键退到后台
onPause()onStop()
  1. 重新打开 App
onRestart()onStart()onResume()
  1. 切换到另一个 Activity
A: onPause()onStop() B: onCreate()onStart()onResume()
  1. 销毁 Activity(如用户退出或系统回收)
onPause()onStop()onDestroy()

🧠 三、生命周期图示(文字版)
 onCreate() ↓ onStart() ↓ onResume() ↑ onRestart() ← onStop() ↑  ↑ onPause() ← onDestroy()

🛠️ 四、实际开发中如何使用这些方法
方法 常见用途 onCreate() 加载 UI、初始化变量、绑定监听器等 onStart() 开始动画、注册广播(如果不是每次都需要) onResume() 恢复动画、重新获取焦点等 onPause() 暂停动画、保存草稿、关闭相机等 onStop() 释放资源、保存数据到数据库 onDestroy() 最终清理工作,如取消注册、关闭连接等
✅ 小提示
  • 如果你只想监听App是否在前台/后台,监听 onResume() / onStop() 就够了。
  • 不要在 onCreate() 里执行耗时操作,避免界面卡顿。
  • 如果 Activity 需要频繁启动/停止,比如切换界面,要合理释放资源防止内存泄漏。

Activity的生命周期

具体内容见ppt

Activity之间的跳转

,显式与隐式

隐式跳转是跳到其他app的页面,显式跳转是跳app内部页面

Activity的页面任务栈

Activity的启动模式

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

多页面情况下再看activity的生命周期

点击返回键退出与点击home键退出

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在Java代码里的添加的模式,只有一次有效,当按返回键重新跳转到当前页面时,还是标准模式。

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Activity跳转的参数传递

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分散传递 打包传递(bundle)

Service组件

PPT资料:

E:\\Android\\origin_coding\\课程全套pdf-手把手教你学AndroidApp开发入门篇\\手把手教你学Androidapp开发入门篇-课程PDF 35.1-35.3

启动型startService

📦 一、什么是 Service?

Service 是 Android 的四大组件之一(Activity、Service、BroadcastReceiver、ContentProvider),用于在后台执行耗时任务,例如:

  • 播放音乐
  • 网络下载/上传
  • 长连接推送
  • 后台日志收集等

与 Activity 不同,Service 没有界面,不与用户交互。


🔁 二、Service 的生命周期

▶ 启动型 Service(startService()

适合执行不需要与界面交互的后台任务

生命周期方法:

方法 说明 onCreate() Service 第一次创建时调用(只调用一次) onStartCommand() 每次通过 startService() 启动都会调用 onDestroy() Service 被系统或开发者终止时调用

生命周期流程:

onCreate() → onStartCommand() → onDestroy()

onStartCommand()Android 服务(Service) 中非常关键的生命周期方法,主要用于处理通过 startService() 启动服务时的逻辑。

我们一起来深入了解它的作用和用法👇


🧩 一、onStartCommand() 是什么?

它是 Service 类中的一个回调方法,在你通过:

Intent intent = new Intent(context, MyService.class);context.startService(intent);

调用启动服务时,系统会自动回调 onStartCommand() 方法。


🎯 二、它的核心作用

功能 说明 ✅ 接收启动服务的 Intent 参数 可以通过 Intent 传值,执行不同任务 ✅ 启动任务/线程 例如启动一个下载线程、播放音乐等 ✅ 告诉系统服务的重启行为 返回值决定:服务被系统杀死后是否自动重启

📥 三、方法签名详解

@Overridepublic int onStartCommand(Intent intent, int flags, int startId) { // 你的服务逻辑,比如开启线程、执行任务等 return START_STICKY;}

参数含义:

  • Intent intent:启动服务时传入的 Intent 对象(可携带数据)
  • int flags:附加信息(不常用)
  • int startId:本次启动的唯一标识,用于停止服务时识别

返回值说明:

返回值 含义 START_STICKY 默认推荐,服务被杀后系统会尝试重建,但不会保留原来的 Intent START_NOT_STICKY 不会自动重启,适合不重要的任务 START_REDELIVER_INTENT 被杀后重启,同时系统会重新传入原始 Intent START_STICKY_COMPATIBILITY 兼容旧版本,不推荐使用

🔗 绑定型 Service(bindService()

适合需要和 Activity 通信的场景(如获取播放进度、传递参数等)

生命周期方法:

方法 说明 onCreate() 创建时调用 onBind() 绑定时调用,返回 IBinder 对象 onUnbind() 所有客户端都解绑时调用 onDestroy() Service 被销毁时调用

🧠 三、Service 与 Thread 的区别

项目 Service Thread 是否运行在主线程 是(默认) 否,运行在子线程 生命周期管理 被系统托管 需开发者手动控制 是否有 UI 没有 没有 推荐用途 后台任务(长时间) 临时任务(短时间)

⚠️ 如果在 Service 中执行耗时任务,建议使用 IntentService手动开启子线程,避免 ANR(界面卡顿)。


🧪 四、启动 Service 的代码示例

启动型 Service 示例

  1. 创建服务类(MyService.java):
public class MyService extends Service { @Override public void onCreate() { super.onCreate(); Log.d(\"Service\", \"onCreate 被调用\"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.d(\"Service\", \"onStartCommand 被调用\"); // 模拟耗时任务 new Thread(() -> { // 执行后台任务 }).start(); return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); Log.d(\"Service\", \"onDestroy 被调用\"); } @Nullable @Override public IBinder onBind(Intent intent) { return null; // 启动型服务返回 null }}
  1. 注册到 AndroidManifest.xml
<service android:name=\".MyService\" />
  1. 在 Activity 中启动/停止:
Intent intent = new Intent(this, MyService.class);startService(intent); // 启动服务stopService(intent); // 停止服务

🔗 五、常用场景

场景 推荐组件 后台持续播放音乐 Service + MediaPlayer 下载任务(长时间) IntentServiceWorkManager 短时间后台任务 JobIntentService(兼容旧版本) 与前台交互 Bound Service

绑定型bindservice

📘 一、什么是 bindService

bindService() 是 Android 中用来将 Activity 与 Service 建立绑定关系 的方法,使得客户端可以:

  • 与服务交互
  • 获取服务提供的数据
  • 调用服务中的方法

常用于播放进度获取、后台通信、计时器等场景。


🔁 二、绑定服务的生命周期(Bound Service)

方法 说明 onCreate() 服务第一次创建时调用 onBind() 当客户端绑定服务时调用,返回一个 IBinder 对象 onUnbind() 所有客户端解绑后调用(可选) onDestroy() 服务被销毁时调用(如没有客户端再绑定)

注意:绑定的服务不会自动销毁,你需要手动解绑。


🧱 三、实现步骤(Java 示例)

步骤 1️⃣:创建 Service 类

public class MyBindService extends Service { // 用于客户端访问服务的接口 private final IBinder binder = new LocalBinder(); // 提供给外部调用的服务方法 public String getMessage() { return \"Hello from MyBindService\"; } // 返回绑定器对象 @Nullable @Override public IBinder onBind(Intent intent) { return binder; } // 自定义 Binder public class LocalBinder extends Binder { public MyBindService getService() { return MyBindService.this; // 返回当前服务实例 } }}

步骤 2️⃣:在 AndroidManifest.xml 中注册 Service

<service android:name=\".MyBindService\" />

步骤 3️⃣:在 Activity 中绑定服务

public class MainActivity extends AppCompatActivity { private MyBindService myService; private boolean isBound = false; // 创建 ServiceConnection 实例 private final ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { // 通过 Binder 获取服务实例 MyBindService.LocalBinder binder = (MyBindService.LocalBinder) service; myService = binder.getService(); isBound = true; // 调用服务方法 String msg = myService.getMessage(); Toast.makeText(MainActivity.this, msg, Toast.LENGTH_SHORT).show(); } @Override public void onServiceDisconnected(ComponentName name) { isBound = false; } }; @Override protected void onStart() { super.onStart(); // 绑定服务 Intent intent = new Intent(this, MyBindService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE); } @Override protected void onStop() { super.onStop(); // 解绑服务 if (isBound) { unbindService(connection); isBound = false; } }}

✅ 四、关键参数说明

bindService(intent, connection, Context.BIND_AUTO_CREATE);
参数 作用 intent 指向目标 Service connection 绑定的回调接口,用于拿到服务对象 BIND_AUTO_CREATE 没有服务就自动创建

⚠️ 五、注意事项

  • 若 Service 没有客户端绑定,默认系统可能会销毁(除非是前台服务)。
  • 不要在 onServiceConnected 里执行耗时任务。
  • 服务通信对象(IBinder)可以拓展实现更复杂的 AIDL(跨进程通信)。

🧪 常见应用场景

场景 说明 音乐播放器 播放控制 + UI 获取进度 定时任务服务 Activity 显示计时器进度 Socket 通信 后台服务维护连接,Activity 展示状态

前台service

Activity与Service之间的关系类型

  1. Activity 启动 Service

Activity 可以通过以下方式启动 Service:

➤ 启动型 Service:

startService(new Intent(this, MyService.class));
  • Activity 和 Service 没有直接通信通道
  • Service 会一直运行,直到调用 stopService()stopSelf()

➤ 绑定型 Service:

bindService(intent, connection, Context.BIND_AUTO_CREATE);
  • Activity 和 Service 通过 IBinder 进行通信
  • Activity 可调用 Service 中的公开方法

👉 若 Activity 销毁,也应及时解绑 Service,避免内存泄漏。

  1. Activity 与 Service 通信(双向交互)

常见方式包括:

通信方式 说明 IBinder 对象 Activity 绑定 Service 并调用其方法 Handler + Messenger 用于轻量跨线程或跨进程通信 BroadcastReceiver 用于 Service 主动向 Activity 通知消息 EventBus(第三方) 解耦的发布/订阅机制,Activity 和 Service 可互不引用地通信

广播组件(BroadcastReceiver)

在 Android 中,广播组件(BroadcastReceiver) 是四大组件之一,主要用于监听和响应系统或应用内部发送的 广播消息。这种机制允许应用之间或应用内部不同组件之间进行松耦合的消息传递。

下面我将从原理、使用方法、生命周期、应用场景、代码示例等多个方面为你详细讲解。


📘 一、BroadcastReceiver 是什么?

BroadcastReceiver 是一个 监听广播消息的组件,可以响应来自系统或应用内的广播事件,如:

  • 系统广播(如电量变化、网络变化、开机完成)
  • 自定义广播(应用内广播消息)

📦 二、广播的分类

类型 说明 系统广播 Android 系统发出的广播,如网络状态变化、屏幕关闭、收到短信等 自定义广播 开发者在 App 中自定义发送的广播 有序广播(ordered) 多个接收者可依优先级顺序接收,前一个可以拦截广播 无序广播(normal) 所有接收者同时接收,互不干扰,效率高但不能中断

🔁 三、BroadcastReceiver 生命周期

BroadcastReceiver 没有独立的生命周期,它的生命周期非常短:

  • 当广播到来时,系统自动调用其 onReceive() 方法
  • 执行完 onReceive() 后立即销毁(不适合耗时操作

✅ 四、使用步骤

步骤 1️⃣:创建广播接收者类 MyReceiver.java

public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); // com.example.MY_BROADCAST是action 可以作为唯一标识符 if (\"com.example.MY_BROADCAST\".equals(action)) { Toast.makeText(context, \"收到自定义广播\", Toast.LENGTH_SHORT).show(); } }}

步骤 2️⃣:注册广播

(1)静态注册(写在 AndroidManifest.xml 中)

xml复制代码<receiver android:name=\".MyReceiver\"> <intent-filter> <action android:name=\"com.example.MY_BROADCAST\"/> </intent-filter></receiver>

⚠️ Android 8.0(API 26)之后,大部分 静态注册 的广播(特别是隐式广播)被限制,建议使用动态注册。

(2)动态注册(推荐)(在mainActivity中动态注册)

private MyReceiver receiver;@Overrideprotected void onStart() { super.onStart(); // 在onstart中注册广播(订阅) // 动态使用Java代码注册一个广播接收者 receiver = new MyReceiver(); IntentFilter filter = new IntentFilter(\"com.example.MY_BROADCAST\"); registerReceiver(receiver, filter);}// 在动态注册中,注册接收者和发送信息是在同一个activity中@Overrideprotected void onStop() { super.onStop(); unregisterReceiver(receiver);}

步骤 3️⃣:发送广播 (写在mainActivity中)

Intent intent = new Intent(\"com.example.MY_BROADCAST\");sendBroadcast(intent); // 普通广播

🧠 五、常见系统广播

广播名称 说明 Intent.ACTION_BOOT_COMPLETED 设备开机完成 Intent.ACTION_BATTERY_CHANGED 电量变化 Intent.ACTION_AIRPLANE_MODE_CHANGED 飞行模式切换 ConnectivityManager.CONNECTIVITY_ACTION 网络状态变化 SmsReceiver.ACTION_SMS_RECEIVED 收到短信(需权限)

🛠️ 六、注意事项

  • 不能在 onReceive() 里执行耗时操作,否则会触发 ANR(应用无响应)
  • ✅ 可使用 IntentService 或开启子线程做耗时任务
  • 🆕 Android 8.0 后必须使用动态注册监听大部分广播(除了系统明确允许的)

🧪 七、应用场景示例

场景 示例广播 网络变化监听 CONNECTIVITY_ACTION 接收推送消息 自定义广播 接收系统更新 ACTION_PACKAGE_ADDED 等 多组件之间通信 Activity → Service 通过广播发送消息

✅ 八、总结:BroadcastReceiver 特点

特性 内容 生命周期 非常短,仅在 onReceive() 存在 是否有界面 ❌ 没有 适用场景 轻量消息通知、系统事件监听、跨组件通信 耗时操作 ❌ 不推荐,应转交给其他组件

具体内容见ppt

静态广播与动态广播

静态广播注册界面的action可以加多个,action代表了可以接收与发送方的action相匹配的广播信息

Intent.setPackage()是解决Android 8.0以上,静态隐式广播无法发送的问题

动态注册时,接收方可以单独写一个接收.java文件,也可以直接在activity文件中写接收方和发送方

无序广播与有序广播

有序广播进行修改广播时,是对同一个发送的intent进行修改,即在上一个intent信息的基础上,添加新的键值对,下一个广播通过键值获取字段值。

ContentProvider组件

📘 一、ContentProvider 介绍

  1. 什么是 ContentProvider?

ContentProvider 是一个 数据共享机制,它为不同的应用提供统一的接口来访问数据。通过它,应用可以共享数据,而不必直接暴露数据库或文件。其他应用可以使用 ContentResolverContentProvider 进行交互。

  1. 常见用途
  • 访问系统数据(例如联系人、短信)
  • 应用内部数据库共享
  • 与其他应用进行数据交互

🛠️ 二、如何使用 ContentProvider?

步骤 1️⃣:创建 ContentProvider (创建MyContentProvider.java)

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 null; }}

步骤 2️⃣:注册 ContentProvider

AndroidManifest.xml 中注册:

<provider android:name=\".MyContentProvider\" android:authorities=\"com.example.myapp.provider\" android:exported=\"true\" />

步骤 3️⃣:访问 ContentProvider (在mainActivity中使用)

通过 ContentResolver 来访问数据:

ContentResolver resolver = getContentResolver();Uri uri = Uri.parse(\"content://com.example.myapp.provider/data\");Cursor cursor = resolver.query(uri, null, null, null, null);

🧠 三、ContentProvider 生命周期

ContentProvider 的生命周期较长,它通常在应用程序启动时创建,并且在整个应用程序生命周期内有效。onCreate() 方法在 ContentProvider 被首次访问时调用。


✅ 四、ContentProvider 特点

特性 说明 访问范围 允许不同应用之间共享数据 数据存储 可以访问应用内部数据库、文件等 通信机制 通过 ContentResolverUri 进行交互 生命周期 长时间有效,直到应用退出

🧪 五、常见应用场景

场景 说明 系统提供的内容提供者 如访问联系人、短信、媒体库等数据 应用间数据共享 一个应用通过 ContentProvider 向其他应用暴露数据 应用内部数据库共享 通过 ContentProvider 将数据库暴露给应用的其他组件或其他应用

Andriod常用框架——热修复

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Tinker是微信开源的一个热修复解决方案,支持dex、库和资源更新,无需重新安装apk。开源地址:https://github.com/Tencent/tinker

可以直接使用Tinker,也可以使用腾讯Bugly服务集成Tinker热修复,后者提供了补丁管理服务建议大家根据官方案例工程进行使用,按照Bugly文档由于版本问题会导致无法使用
https://github.com/BuglyDevTeam/Bugly-Android-Demo/tree/master/BuglyHotfixDemo

版本不一致,但是流程应该近似

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

SampleApplicationLike中,appId替换成你的在Bugly平台申请的appId

AppId为在bugly平台注册软件得到的号码

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Glide使用

引入到build.gradle文件中

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

命令是在MainActivity中使用

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

过渡动画设置

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

变换

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Glide更简单的使用

其中依赖是在build.gridle中,AppGlideModule为自己定义的java文件,

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

将频繁使用的glide功能自己定义一个集合,使用时命令就简单了

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

网络加载框架OkHttp与Retrofit

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

同步请求

Call call=okHttpclient.newCall(request); 会造成线程的阻塞

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

异步请求

不用创建单独的线程,不会阻塞

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

OkHttp有GET和POST请求

.add( name:“a”,value:“1”).add( name:“b”, value:\"2”)为提交的数据

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

OkHttp配置

当然可以!下面是关于 OkHttp 的配置与使用教程(Java 版),适合在 Android 开发中进行高效的网络请求操作,包括:基本配置、请求方式(GET、POST)、拦截器、超时设置等内容。


🔧 一、引入 OkHttp 依赖

✅ 在 build.gradle(app)中添加依赖:

dependencies { implementation \'com.squareup.okhttp3:okhttp:4.12.0\' // 可替换为最新版本}

📦 二、创建 OkHttpClient 实例(全局可复用)

OkHttpClient client = new OkHttpClient();

⚠️ 建议使用单例(全局复用一个 OkHttpClient 对象),避免资源浪费。


🌐 三、常用请求示例

1️⃣ GET 请求

Request request = new Request.Builder() .url(\"https://api.example.com/data\") .build();client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { e.printStackTrace(); } @Override public void onResponse(Call call, Response response) throws IOException { if (response.isSuccessful()) { String result = response.body().string(); Log.d(\"OkHttp\", \"响应结果:\" + result); } }});

2️⃣ POST 请求(携带 JSON)

MediaType JSON = MediaType.get(\"application/json; charset=utf-8\");String jsonBody = \"{\\\"name\\\":\\\"张三\\\", \\\"age\\\":25}\";RequestBody body = RequestBody.create(jsonBody, JSON);Request request = new Request.Builder() .url(\"https://api.example.com/user\") .post(body) .build();client.newCall(request).enqueue(...); // 同上

3️⃣ POST 表单上传(application/x-www-form-urlencoded

RequestBody formBody = new FormBody.Builder() .add(\"username\", \"zhangsan\") .add(\"password\", \"123456\") .build();Request request = new Request.Builder() .url(\"https://api.example.com/login\") .post(formBody) .build();client.newCall(request).enqueue(...);

⏱️ 四、配置 OkHttpClient(超时、拦截器、重定向)

OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(10, TimeUnit.SECONDS) // 连接超时 .readTimeout(20, TimeUnit.SECONDS) // 读取超时 .writeTimeout(15, TimeUnit.SECONDS) // 写入超时 .retryOnConnectionFailure(true) // 自动重试 .addInterceptor(new LoggingInterceptor()) // 自定义拦截器 .build();

🧩 五、添加日志拦截器(查看请求和响应)

public class LoggingInterceptor implements Interceptor { @Override public Response intercept(Chain chain) throws IOException { Request request = chain.request(); long t1 = System.nanoTime(); Log.d(\"OkHttp\", String.format(\"发送请求 %s%n%s\", request.url(), request.headers())); Response response = chain.proceed(request); long t2 = System.nanoTime(); Log.d(\"OkHttp\", String.format(\"接收响应 %s in %.1fms%n%s\",  response.request().url(), (t2 - t1) / 1e6d, response.headers())); return response; }}

🧠 六、注意事项

  • OkHttp 的 enqueue() 方法是异步请求,不能直接更新 UI,需用 Handler 或 runOnUiThread 切回主线程。
  • response.body().string() 只能调用一次,会关闭流。
  • POST 请求建议设置超时及日志,方便调试。
  • 不建议每次都创建新的 OkHttpClient,影响性能。

✅ 总结:OkHttp 配置要点

功能 方式 GET/POST 请求 Request + RequestBody 异步执行 enqueue() 自定义配置 OkHttpClient.Builder() 添加拦截器 addInterceptor() 设置超时 connectTimeout()readTimeout() 全局单例 推荐使用 static OkHttpClient

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Retrofit简介

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

写在HttpbinService.java中,是一个接口文件
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 Android 开发中,Retrofit 是基于 OkHttp 封装的网络请求库,简化了网络通信流程。而它的强大之处之一,就是通过**注解(Annotations)**来描述网络请求的结构和参数,做到接口即调用。


📘 一、Retrofit 注解的作用:

Retrofit 使用 Java 注解来定义网络请求的 类型、路径、参数、请求体、头部信息等,使你只需定义一个接口,就能自动完成网络调用。

核心理念:用注解代替繁琐的请求构建过程

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Retrofit转换器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

📘 一、什么是 Retrofit 的转换器?

Retrofit 默认只返回 ResponseBody,如果你想直接接收到 Java 对象(比如实体类),就需要一个“转换器(Converter)”来自动解析数据格式。

换句话说:

类型 功能 请求转换器 将 Java 对象转成请求体(如 JSON) 响应转换器 将服务器响应(JSON/XML)转成 Java 对象

🔧 二、常见转换器类型

转换器库 用途 依赖 GsonConverterFactory ✅ 最常用,用于 JSON 解析 com.squareup.retrofit2:converter-gson MoshiConverterFactory 更现代的 JSON 解析器(Google 出品) converter-moshi ScalarsConverterFactory 处理基本类型(如 String、int) converter-scalars SimpleXmlConverterFactory 用于解析 XML converter-simplexml JacksonConverterFactory 更高性能的 JSON 解析库 converter-jackson

🔍 三、最常用:GsonConverter 使用方式

✅ 步骤 1️⃣:添加依赖

gradle复制代码implementation \'com.squareup.retrofit2:converter-gson:2.9.0\'

✅ 步骤 2️⃣:构建 Retrofit 实例并设置转换器

java复制代码Retrofit retrofit = new Retrofit.Builder() .baseUrl(\"https://api.example.com/\") .addConverterFactory(GsonConverterFactory.create()) // 添加 Gson 转换器 .build();

✅ 步骤 3️⃣:定义 API 接口

java复制代码public interface ApiService { @GET(\"user/{id}\") Call getUser(@Path(\"id\") int userId);}

如果返回的是:

json复制代码{ \"id\": 1, \"name\": \"张三\", \"email\": \"zhangsan@example.com\"}

Retrofit 会自动将其转为如下实体类:

java复制代码public class User { public int id; public String name; public String email;}

✨ 四、多个转换器搭配使用

Retrofit 支持链式转换器,例如:

java复制代码Retrofit retrofit = new Retrofit.Builder() .baseUrl(\"https://api.example.com/\") .addConverterFactory(ScalarsConverterFactory.create()) // 放前面,处理基础类型 .addConverterFactory(GsonConverterFactory.create()) // 后面处理 JSON .build();

❗ 顺序很重要,Retrofit 会按注册顺序查找能处理的数据类型。


🧠 五、什么时候用哪些转换器?

场景 推荐转换器 通常使用 JSON 接口 GsonConverterFactory ✅ 接口返回 XML SimpleXmlConverterFactory 接口只返回字符串 ScalarsConverterFactory 更强性能/自定义序列化 MoshiJackson

✅ 六、总结

名称 作用 是否常用 .addConverterFactory(GsonConverterFactory.create()) JSON ↔ Java 自动转换 ✅ 推荐 .addConverterFactory(ScalarsConverterFactory.create()) 纯字符串/int 等原始类型 ✅ .addConverterFactory(SimpleXmlConverterFactory.create()) XML ↔ Java ❌(较少用)

Retrofit嵌套请求与适配器

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

先登录再获取列表,直接嵌套会显得非常冗余

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

转换器适配器的添加

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Retrofit适配器

Retrofit 的适配器(CallAdapter / Adapter),这是 Retrofit 架构中另一个重要扩展点,用来决定你接口定义中方法的返回类型,比如你是否要用:

  • Call
  • LiveData
  • RxJava 的 Observable
  • Kotlin 的 suspend 函数

下面我来详细讲解 Retrofit 的适配器作用、原理、常用类型、配置方式以及实际场景使用👇


📘 一、什么是 Retrofit 的适配器(CallAdapter)?

🔍 定义:

CallAdapter 决定你接口方法的返回类型是 同步/异步/响应式等,而且能与其他框架(如 LiveData、RxJava、协程等)结合使用。

🧠 举个例子:

java复制代码@GET(\"user/{id}\")Call getUser(@Path(\"id\") int id); // 使用默认 CallAdapter// 或者改成 RxJavaObservable getUserRx(@Path(\"id\") int id); // 使用 RxJavaCallAdapter

📌 Retrofit 内部将请求结果通过 CallAdapter 转换为你想要的返回类型!


🧩 二、常见 CallAdapter 类型及用途

适配器名称 对应返回类型 用途场景 是否需要依赖 默认(无添加) Call 同步/异步原始封装 ❌ 无需依赖 RxJava2CallAdapterFactory Observable / Single RxJava 响应式处理 ✅ 需要依赖 CoroutineCallAdapterFactory suspend fun Kotlin 协程 ✅ 需要依赖 LiveDataCallAdapter(自定义) LiveData MVVM 架构中 LiveData ✅ 需手动封装或引入 Jetpack lib

🔧 三、如何配置 CallAdapter?

✅ 1. 默认(不需要配置)

Retrofit 默认支持 Call,你只需这样写:

java复制代码@GET(\"user/{id}\")Call getUser(@Path(\"id\") int id);

✅ 2. RxJava2 / RxJava3 适配器配置

添加依赖:

groovy复制代码implementation \'com.squareup.retrofit2:adapter-rxjava2:2.9.0\' // 或 rxjava3implementation \'io.reactivex.rxjava2:rxjava:2.x.x\'

配置 Retrofit:

java复制代码Retrofit retrofit = new Retrofit.Builder() .baseUrl(\"https://api.example.com/\") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build();

接口使用 RxJava:

java复制代码@GET(\"user/{id}\")Observable getUserRx(@Path(\"id\") int id);

✅ 3. Kotlin 协程适配器(推荐 Kotlin 开发)

添加依赖(Retrofit 2.6+ 内置支持协程 CallAdapter):

suspend fun getUser(@Path(\"id\") Int): User

Retrofit 配置:

kotlin复制代码Retrofit.Builder() .baseUrl(\"https://api.example.com/\") .addConverterFactory(GsonConverterFactory.create()) .build()

✅ 不再需要显式添加 CallAdapter,Retrofit 会自动识别 suspend


✅ 4. 自定义 LiveData 适配器(进阶)

适合 MVVM + Jetpack 架构:

java复制代码@GET(\"user/{id}\")LiveData<ApiResponse> getUserLiveData();

需要手动实现 CallAdapterFactory,或使用 Jetpack Lifecycle Retrofit Adapter


🎯 四、总结:CallAdapter 用法对比

返回类型 对应适配器 用法特点 Call 默认 最基础的同步/异步支持 Observable / Single RxJava2CallAdapterFactory 支持响应式编程 suspend fun 内置协程适配器 简洁、推荐 Kotlin 项目 LiveData 自定义或 Jetpack 结合 ViewModel 和数据观察

🧪 五、实际场景推荐

项目类型 推荐适配器 原因 Java 项目 Call or RxJava 保持简洁或响应式链式操作 Kotlin 项目 suspend 协程 简洁、内置支持、更现代 MVVM 架构 LiveData 可自动感知生命周期

✅ 六、总结一句话

Retrofit 的 CallAdapter 就是你接口返回什么类型,Retrofit 就如何包装请求结果的“适配桥梁”。

Retrofit文件上传与下载

Retrofit 不仅可以处理常规的 GET/POST 请求,也支持 文件上传(如图片、视频、文档)文件下载(如APK、PDF等),并且结合 OkHttp 可以做到高效、可靠且支持进度监听。


📤 一、Retrofit 上传文件(支持多文件、多参数)

✅ 步骤 1:接口定义(使用 @Multipart + @Part

java复制代码@Multipart@POST(\"upload\")Call uploadFile( @Part MultipartBody.Part file, // 单个文件 @Part(\"description\") RequestBody description  // 额外参数);

✅ 步骤 2:Java 调用上传文件

java复制代码// 创建文件对象File file = new File(\"/storage/emulated/0/DCIM/test.jpg\");// 创建 RequestBody(设置媒体类型)RequestBody requestFile = RequestBody.create( file, MediaType.parse(\"image/jpeg\"));// 包装成 MultipartBody.PartMultipartBody.Part body = MultipartBody.Part.createFormData(\"file\", file.getName(), requestFile);// 其他参数(文本字段)RequestBody desc = RequestBody.create(\"上传说明\", MediaType.parse(\"text/plain\"));// 发起请求api.uploadFile(body, desc).enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { Log.d(\"上传成功\", \"code: \" + response.code()); } @Override public void onFailure(Call call, Throwable t) { Log.e(\"上传失败\", t.getMessage()); }});

📁 多文件上传

java复制代码@Multipart@POST(\"upload/multi\")Call uploadMultipleFiles(@Part List files);

你可以将多个文件都构造为 MultipartBody.Part 并一并上传。


📥 二、Retrofit 下载文件(保存到本地)

✅ 步骤 1:接口定义(使用 @Streaming 防止内存溢出)

java复制代码@Streaming@GETCall downloadFile(@Url String fileUrl);

@Streaming 关键字避免大文件加载进内存造成 OOM。


✅ 步骤 2:下载并保存到本地(Java 实现)

java复制代码Call call = api.downloadFile(\"https://example.com/file.apk\");call.enqueue(new Callback() { @Override public void onResponse(Call call, Response response) { boolean result = writeResponseBodyToDisk(response.body()); Log.d(\"下载\", result ? \"成功\" : \"失败\"); } @Override public void onFailure(Call call, Throwable t) { Log.e(\"下载失败\", t.getMessage()); }});// 保存到本地方法private boolean writeResponseBodyToDisk(ResponseBody body) { try { File file = new File(getExternalFilesDir(null) + \"/downloaded_file.apk\"); InputStream inputStream = null; OutputStream outputStream = null; try { byte[] fileReader = new byte[4096]; inputStream = body.byteStream(); outputStream = new FileOutputStream(file); while (true) { int read = inputStream.read(fileReader); if (read == -1) break; outputStream.write(fileReader, 0, read); } outputStream.flush(); return true; } catch (IOException e) { return false; } finally { if (inputStream != null) inputStream.close(); if (outputStream != null) outputStream.close(); } } catch (IOException e) { return false; }}

🧠 三、上传与下载的注意事项

项目 上传 下载 请求方式 @Multipart + POST @Streaming + GET 文件类型 image/*, video/* 等 任意格式(apk、pdf等) 防止 OOM 不建议上传大文件 下载必须加 @Streaming 权限需求 访问外部存储权限 同上(API 29+ 要用 SAF)

✅ 四、总结

类型 方法 注解 特点 上传单文件 @Multipart + @Part POST 支持表单+文件一起提交 上传多文件 @Part List POST 灵活扩展 下载文件 @Streaming + @GET GET 防止大文件占用内存

Json解析框架——Gson

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Java对象序列化与反序列化

gson.toJson()的使用

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Java嵌套对象序列化与反序列化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Array数组的序列化与反序列化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

List的序列化与反序列化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Map与Set的序列化与反序列化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

RXJava

响应式编程思维

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

什么是响应式编程:根据上一层的响应,影响下一层的变化

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

RxJava RXJS RxXXX RX系列框架为什么把所有函数都称为操作符 因为我们的函数要去操作 从起点 流向 终点7

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

🚀 一、RxJava 是什么?

RxJava(ReactiveX for Java)是一种响应式编程框架,核心思想是:

“一切皆数据流(Stream)”,数据的产生、变换、消费通过链式操作完成。


📦 二、RxJava 三大核心角色

概念 类名 说明 数据源 Observable / Flowable 发射数据(emit) 观察者 Observer / Subscriber 接收数据(onNext/onError/onComplete) 连接者 subscribe() 将数据源与观察者连接

📌 三、基础使用示例(RxJava2)

java复制代码Observable observable = Observable.just(\"Hello RxJava\");Observer observer = new Observer() { @Override public void onSubscribe(Disposable d) { Log.d(\"Rx\", \"订阅开始\"); } @Override public void onNext(String s) { Log.d(\"Rx\", \"收到数据:\" + s); } @Override public void onError(Throwable e) { Log.e(\"Rx\", \"出错:\" + e.getMessage()); } @Override public void onComplete() { Log.d(\"Rx\", \"数据接收完毕\"); }};// 建立连接observable.subscribe(observer);

💡 四、线程切换(Schedulers)

RxJava 内置强大的线程调度功能:

方法 说明 Schedulers.io() 用于 I/O 操作(如网络请求) Schedulers.computation() 计算密集任务 Schedulers.newThread() 每次都创建新线程 AndroidSchedulers.mainThread() 切回主线程(UI 操作)

示例:

java复制代码Observable.just(\"网络数据\") .subscribeOn(Schedulers.io())  // 在子线程执行发射 .observeOn(AndroidSchedulers.mainThread()) // 在主线程处理结果 .subscribe(s -> { Log.d(\"Rx\", \"UI线程收到:\" + s); });

⚙️ 五、常用操作符(链式操作的核心)

操作符 用途 示例 map() 变换数据类型 String → Integer flatMap() 拆分合并流 多对多请求 filter() 过滤 只保留长度 > 3 的字符串 debounce() 防抖处理 输入框 500ms 内只响应一次 zip() 多源合并 请求 A 和 B 都完成后合并结果
java复制代码Observable.just(\"100\") .map(s -> Integer.parseInt(s)) // String → int .subscribe(i -> Log.d(\"Rx\", \"数字:\" + i));

📱 六、结合 Retrofit 使用

配置 Retrofit + RxJava:

gradle复制代码implementation \'com.squareup.retrofit2:adapter-rxjava2:2.9.0\'implementation \'io.reactivex.rxjava2:rxjava:2.2.21\'implementation \'io.reactivex.rxjava2:rxandroid:2.1.1\'

接口定义:

java复制代码@GET(\"user/info\")Observable getUserInfo();

发起请求:

java复制代码api.getUserInfo() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(user -> showUser(user), error -> showError(error));

✅ 七、RxJava 优缺点

优点 ✅ 缺点 ⚠️ 强大、链式操作、线程控制灵活 学习曲线陡、容易内存泄漏 更少回调、更少嵌套 不合理使用会导致复杂难维护

🧠 八、RxJava 推荐使用场景

  • 🧲 网络请求链式处理(配合 Retrofit)
  • 🔍 搜索输入防抖
  • ⏱️ 定时器、倒计时、轮询
  • ⚡ 多请求并发(如同时请求多个接口并合并数据)
  • 🎯 按事件驱动 UI(响应式 UI 绑定)

🧰 九、注意事项

  • 记得释放资源Disposable.dispose(),建议使用 CompositeDisposable
  • 避免内存泄漏 → 绑定生命周期(可用 RxLifecycleAutoDispose
  • 尽量链式写法 + Lambda 简洁表达
RxJava 下载图片的完整流程思维

目标:从网络下载图片,并展示到 ImageView 中

🌐 基本流程思维图:

mathematica复制代码下载URL → 发起请求 → 获取InputStream → 转Bitmap → 显示到ImageView

这个过程可以用 RxJava 表达为:

java复制代码Observable.just(imageUrl) .subscribeOn(Schedulers.io()) // 在IO线程下载图片 .map(url -> downloadBitmap(url)) // 转为 Bitmap .observeOn(AndroidSchedulers.mainThread()) // 回到主线程更新UI .subscribe(bitmap -> imageView.setImageBitmap(bitmap));

🧰 二、完整示例代码(Java)

📦 工具方法:下载图片(返回 Bitmap)

java复制代码private Bitmap downloadBitmap(String urlStr) throws IOException { URL url = new URL(urlStr); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoInput(true); connection.connect(); InputStream input = connection.getInputStream(); return BitmapFactory.decodeStream(input);}

🚀 使用 RxJava 下载图片并展示

java复制代码String imageUrl = \"https://example.com/image.jpg\";Observable.just(imageUrl) .subscribeOn(Schedulers.io()) // 网络请求在线程池 .map(this::downloadBitmap) // 将 URL → Bitmap .observeOn(AndroidSchedulers.mainThread()) // 回到主线程设置 UI .subscribe( bitmap -> imageView.setImageBitmap(bitmap), throwable -> Log.e(\"RxDownload\", \"下载失败: \" + throwable.getMessage()) );

🎨 三、补充:使用 Glide 或 Picasso 替代手写下载?

虽然 RxJava 能做到非常优雅,但如果你只是下载图片显示,推荐使用:

  • Glide: Glide.with(context).load(url).into(imageView);
  • Picasso: Picasso.get().load(url).into(imageView);

✅ 它们内部已处理线程、缓存、错误处理,非常适合直接用。


📌 四、RxJava 适合的进阶场景

场景 Rx 优势 批量下载多张图片 使用 flatMap()zip() 并发处理 下载并保存到本地 加上 map() 写入文件 下载失败重试 retry() 自动重试机制 下载进度监听 可结合 Flowable 或自定义 Observable 发射进度值
细节代码

🧩 一、实现 downloadBitmap(String urlStr) 方法

这个方法从网络上下载图片并返回 Bitmap

java复制代码private Bitmap downloadBitmap(String urlStr) throws IOException { URL url = new URL(urlStr); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoInput(true); connection.connect(); InputStream input = connection.getInputStream(); return BitmapFactory.decodeStream(input); // 解析图片流成Bitmap}

注意要 import java.net.*;android.graphics.BitmapFactory;


🧱 二、确保引入的 RxJava + AndroidSchedulers 依赖

在你的 build.gradle 中添加以下依赖:

gradle复制代码implementation \'io.reactivex.rxjava2:rxjava:2.2.21\'implementation \'io.reactivex.rxjava2:rxandroid:2.1.1\'

然后在 Java 文件中引入:

java复制代码import io.reactivex.Observable;import io.reactivex.android.schedulers.AndroidSchedulers;import io.reactivex.schedulers.Schedulers;

🧷 三、确保你的代码处于 Activity 或 Fragment 中,并 imageView 是有效对象

java复制代码ImageView imageView = findViewById(R.id.imageView); // 确保已绑定 ImageViewString imageUrl = \"https://example.com/image.jpg\"; // 替换为你自己的图片链接

📦 四、完整整合示例(可直接运行)

java复制代码public class MainActivity extends AppCompatActivity { private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); imageView = findViewById(R.id.imageView); String imageUrl = \"https://example.com/image.jpg\"; // 替换为真实链接 Observable.just(imageUrl) .subscribeOn(Schedulers.io()) // 下载图片在子线程 .map(this::downloadBitmap) // URL → Bitmap .observeOn(AndroidSchedulers.mainThread()) // 回主线程更新 UI .subscribe(bitmap -> imageView.setImageBitmap(bitmap), throwable -> Log.e(\"RxImage\", \"下载失败\", throwable)); } // 实现下载并转为Bitmap的方法 private Bitmap downloadBitmap(String urlStr) throws IOException { URL url = new URL(urlStr); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setDoInput(true); connection.connect(); InputStream input = connection.getInputStream(); return BitmapFactory.decodeStream(input); }}

🔐 权限(如需访问网络)

确保在 AndroidManifest.xml 中加入权限:

xml复制代码

✅ 小结

你实现的每一个函数/步骤:

函数 / 类 用途 downloadBitmap(url) 用于从网络下载图片并转为 Bitmap Schedulers.io() 运行在子线程 AndroidSchedulers.mainThread() 回到主线程更新 UI Observable.just() 创建数据源

数据存储

数据存储在APP内部,data文件夹

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据存储有哪些

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

SP存储

很小很简单的数据可以保存到首选项SP里面去

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

数据文件夹在Device File Explorer,data/data/com.xxx.myproject/

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

SQLite存储

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

SQLite增删改查

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

通过继承 SQLiteOpenHelper 类来创建数据库和表,管理版本升级等。下面我将一步一步带你实现:


📘 一、基本结构:SQLite 创建数据库与表的方式

你需要做的主要步骤:

  1. 创建一个继承自 SQLiteOpenHelper 的类
  2. onCreate() 方法中执行建表 SQL
  3. 使用 getWritableDatabase()getReadableDatabase() 获取数据库操作对象

🧱 二、示例:创建数据库和一个用户表

👇 1. 创建数据库帮助类 MyDatabaseHelper.java

java复制代码public class MyDatabaseHelper extends SQLiteOpenHelper { private static final String DATABASE_NAME = \"MyApp.db\"; // 数据库名 private static final int DATABASE_VERSION = 1;  // 数据库版本 // 表名和建表语句 public static final String TABLE_USER = \"user\"; private static final String CREATE_TABLE_USER = \"CREATE TABLE \" + TABLE_USER + \" (\" + \"id INTEGER PRIMARY KEY AUTOINCREMENT, \" + \"name TEXT NOT NULL, \" + \"age INTEGER, \" + \"email TEXT UNIQUE)\"; public MyDatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } // 创建数据库时调用,只执行一次 @Override public void onCreate(SQLiteDatabase db) { db.execSQL(CREATE_TABLE_USER); // 执行建表语句 } // 数据库升级时调用 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 可选:表结构变更时执行 db.execSQL(\"DROP TABLE IF EXISTS \" + TABLE_USER); onCreate(db); // 重新创建 }}

👇 2. 在 Activity 中使用

java复制代码public class MainActivity extends AppCompatActivity { private MyDatabaseHelper dbHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 创建数据库和表(如不存在) dbHelper = new MyDatabaseHelper(this); SQLiteDatabase db = dbHelper.getWritableDatabase(); // 会自动调用 onCreate 或 onUpgrade }}

📦 三、建表语句详解

sql复制代码CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, -- 主键自增 name TEXT NOT NULL,  -- 不允许为空 age INTEGER, -- 整型 email TEXT UNIQUE  -- 唯一约束)

你可以根据需要增加更多字段或创建多个表。


🛠️ 四、常用的 SQLite 数据类型

SQLite 类型 Android 映射 示例 INTEGER int, long 主键、自增字段 REAL float, double 浮点数 TEXT String 名字、邮箱等 BLOB byte[] 图片、文件等 NULL 空类型 用于未赋值字段

🧠 五、小技巧

  • SQLiteDatabase 有两种获取方式:getReadableDatabase()getWritableDatabase()
  • 不需要自己手动创建 .db 文件,系统会自动创建在 /data/data/包名/databases/ 目录下。
  • 升级数据库时记得修改版本号并处理 onUpgrade() 方法逻辑。
  • 表创建完成后,可以用 insert()update()delete()query() 等方法操作数据。

SQLite增删改查

📦 表结构回顾

我们之前建表语句是:

sql复制代码CREATE TABLE user ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, age INTEGER, email TEXT UNIQUE)

✅ 一、插入数据(Insert)

java复制代码public void insertUser(SQLiteDatabase db, String name, int age, String email) { ContentValues values = new ContentValues(); values.put(\"name\", name); values.put(\"age\", age); values.put(\"email\", email); long rowId = db.insert(\"user\", null, values); if (rowId != -1) { Log.d(\"SQLite\", \"插入成功,ID:\" + rowId); } else { Log.d(\"SQLite\", \"插入失败\"); }}

🔍 二、查询数据(Query)

java复制代码public void queryAllUsers(SQLiteDatabase db) { Cursor cursor = db.query(\"user\", null, null, null, null, null, null); while (cursor.moveToNext()) { int id = cursor.getInt(cursor.getColumnIndexOrThrow(\"id\")); String name = cursor.getString(cursor.getColumnIndexOrThrow(\"name\")); int age = cursor.getInt(cursor.getColumnIndexOrThrow(\"age\")); String email = cursor.getString(cursor.getColumnIndexOrThrow(\"email\")); Log.d(\"SQLite\", \"用户:\" + id + \", \" + name + \", \" + age + \", \" + email); } cursor.close();}

📝 三、更新数据(Update)

java复制代码public void updateUserAge(SQLiteDatabase db, String email, int newAge) { ContentValues values = new ContentValues(); values.put(\"age\", newAge); int rows = db.update(\"user\", values, \"email = ?\", new String[]{email}); Log.d(\"SQLite\", \"更新了 \" + rows + \" 条记录\");}

❌ 四、删除数据(Delete)

java复制代码public void deleteUserByEmail(SQLiteDatabase db, String email) { int rows = db.delete(\"user\", \"email = ?\", new String[]{email}); Log.d(\"SQLite\", \"删除了 \" + rows + \" 条记录\");}

📄 五、完整调用示例(在 Activity 中使用)

java复制代码MyDatabaseHelper dbHelper = new MyDatabaseHelper(this);SQLiteDatabase db = dbHelper.getWritableDatabase();insertUser(db, \"张三\", 25, \"zhangsan@example.com\");queryAllUsers(db);updateUserAge(db, \"zhangsan@example.com\", 30);deleteUserByEmail(db, \"zhangsan@example.com\");

🧠 小技巧 & 建议

技巧 说明 ContentValues 用于封装列名和数据的映射,适用于插入和更新 Cursor 用于遍历查询结果,一定记得 close() 占位符 ? 避免 SQL 注入问题 db.execSQL() 也可以执行原生 SQL,但不推荐用于复杂参数场景

Room存储

Room是SQLite数据库的抽象

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

使用注解

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Room实现增删改查

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Intent

Intent传递基本类型

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 Android 中,Intent 是用于不同 Activity组件 之间进行通信的主要方式。通过 Intent,你可以在 Activity 之间传递各种类型的数据,特别是 基本数据类型对象

下面我将详细介绍如何通过 Intent 传递常见的基本数据类型(如 int, String, boolean, float 等)。


📘 一、如何传递基本数据类型?

Intent 提供了一个方法 putExtra() 来将数据传递给目标 Activity,接收方则通过 getIntent().get...Extra() 来获取这些数据。

基本数据类型支持的方式:

类型 方法 boolean putExtra(key, value)getBooleanExtra(key, defaultValue) int putExtra(key, value)getIntExtra(key, defaultValue) float putExtra(key, value)getFloatExtra(key, defaultValue) String putExtra(key, value)getStringExtra(key) long putExtra(key, value)getLongExtra(key, defaultValue) double putExtra(key, value)getDoubleExtra(key, defaultValue)

🧩 二、代码示例

👇 1. 传递数据(在 源 Activity 中)

java复制代码Intent intent = new Intent(this, SecondActivity.class);intent.putExtra(\"user_name\", \"John Doe\");intent.putExtra(\"user_age\", 25);intent.putExtra(\"is_active\", true);intent.putExtra(\"user_score\", 95.5f);startActivity(intent);

这里我们通过 putExtra()String, int, boolean, float 四种基本数据类型传递给目标 SecondActivity


👇 2. 接收数据(在 目标 Activity 中)

java复制代码Intent intent = getIntent();// 获取基本数据类型String userName = intent.getStringExtra(\"user_name\");int userAge = intent.getIntExtra(\"user_age\", 0); // 第二个参数是默认值boolean isActive = intent.getBooleanExtra(\"is_active\", false);float userScore = intent.getFloatExtra(\"user_score\", 0.0f);// 显示数据Log.d(\"IntentData\", \"用户名:\" + userName);Log.d(\"IntentData\", \"年龄:\" + userAge);Log.d(\"IntentData\", \"是否激活:\" + isActive);Log.d(\"IntentData\", \"得分:\" + userScore);

你可以通过 getIntent() 获取传递过来的数据,并用相应的 get...Extra() 方法提取数据。

Intent传递Bundle

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

在 Android 中,Intent 可以通过 Bundle 来传递更复杂的数据集合。Bundle 是一个 键值对集合,可以存储多种类型的数据,比如基本数据类型、SerializableParcelable 对象等。

Bundle 通常用于:

  • 传递多个数据项
  • 传递复杂的数据集合
  • Activity 之间传递数据

📘 一、基本使用方式

Intent 通过 putExtras() 方法将 Bundle 对象添加到意图中,接收方通过 getExtras() 方法获取。

👇 1. 在发送方 (源 Activity) 中使用 Bundle 传递数据:

java复制代码// 创建一个 IntentIntent intent = new Intent(this, SecondActivity.class);// 创建一个 BundleBundle bundle = new Bundle();bundle.putString(\"name\", \"John\");bundle.putInt(\"age\", 25);bundle.putBoolean(\"isActive\", true);// 将 Bundle 添加到 Intentintent.putExtras(bundle);// 启动目标 ActivitystartActivity(intent);

👇 2. 在接收方 (目标 Activity) 中获取 Bundle 数据:

java复制代码// 获取 Intent 中的 BundleBundle bundle = getIntent().getExtras();// 从 Bundle 中获取数据if (bundle != null) { String name = bundle.getString(\"name\"); int age = bundle.getInt(\"age\", 0); // 默认值 0 boolean isActive = bundle.getBoolean(\"isActive\", false); // 默认值 false Log.d(\"ReceivedData\", \"Name: \" + name + \", Age: \" + age + \", Is Active: \" + isActive);}

📦 二、传递多种类型的数据

Bundle 支持多种类型的数据:

  • 基本数据类型:int, boolean, String, float, double, long
  • Serializable 对象
  • Parcelable 对象

👇 示例:传递更多类型的数据

java复制代码// 创建 Intent 和 BundleIntent intent = new Intent(this, SecondActivity.class);Bundle bundle = new Bundle();// 放入基本数据类型bundle.putString(\"userName\", \"Alice\");bundle.putInt(\"userAge\", 30);bundle.putFloat(\"userHeight\", 1.75f);// 放入 Parcelable 对象Person person = new Person(\"Bob\", 35);bundle.putParcelable(\"personData\", person);// 放入 Serializable 对象Car car = new Car(\"Toyota\", 2020);bundle.putSerializable(\"carData\", car);// 将 Bundle 传递给 Intentintent.putExtras(bundle);startActivity(intent);

Person 类(实现 Parcelable

java复制代码public class Person implements Parcelable { private String name; private int age; // 构造方法,Getter 和 Setter 略去 // Parcelable 实现 protected Person(Parcel in) { name = in.readString(); age = in.readInt(); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeInt(age); } @Override public int describeContents() { return 0; } public static final Creator CREATOR = new Creator() { @Override public Person createFromParcel(Parcel in) { return new Person(in); } @Override public Person[] newArray(int size) { return new Person[size]; } };}

👇 3. 在目标 Activity 中接收数据(从 Bundle 中提取数据):

java复制代码// 获取 Intent 中的 BundleBundle bundle = getIntent().getExtras();if (bundle != null) { String userName = bundle.getString(\"userName\"); int userAge = bundle.getInt(\"userAge\"); float userHeight = bundle.getFloat(\"userHeight\"); // 获取 Parcelable 对象 Person person = bundle.getParcelable(\"personData\"); // 获取 Serializable 对象 Car car = (Car) bundle.getSerializable(\"carData\"); Log.d(\"ReceivedData\", \"Name: \" + userName + \", Age: \" + userAge + \", Height: \" + userHeight); Log.d(\"ReceivedData\", \"Person: \" + person.getName() + \", Car: \" + car.getModel());}

🧩 三、总结:BundleIntent 中的使用

操作 说明 putExtras()Bundle 数据添加到 IntentgetExtras() 获取传递的 Bundle 数据 基本数据类型 putString(), putInt(), putFloat()Parcelable putParcelable(), getParcelable() Serializable putSerializable(), getSerializable()

传递Serializable接口

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Android 中使用 Intent传递实现了 Serializable 接口的对象。这是在组件之间(如 Activity → Activity)传递自定义类对象的经典方式之一。


📘 一、什么是 Serializable

Serializable 是 Java 提供的一种 对象序列化机制,通过实现它,你的类对象就可以被写入或读取为字节流,从而传输到另一个组件(比如另一个 Activity)。

相比 Parcelable,它实现更简单,但性能略低。适合数据量小、开发快的场景。


✅ 二、完整使用流程(传递对象)

1️⃣ 创建实体类并实现 Serializable

java复制代码import java.io.Serializable;public class User implements Serializable { private String name; private int age; // 构造函数 public User(String name, int age) { this.name = name; this.age = age; } // Getter public String getName() { return name; } public int getAge() { return age; } // Setter public void setName(String name) { this.name = name; } public void setAge(int age) { this.age = age; }}

⚠️ 注意:实体类必须实现 java.io.Serializable 接口,并且不需要实现任何方法!


2️⃣ 在源 Activity 中传递对象

java复制代码User user = new User(\"张三\", 28);Intent intent = new Intent(this, SecondActivity.class);intent.putExtra(\"user_data\", user); // 通过 Intent 传递 Serializable 对象startActivity(intent);

3️⃣ 在目标 Activity 中接收对象

java复制代码Intent intent = getIntent();User user = (User) intent.getSerializableExtra(\"user_data\");if (user != null) { Log.d(\"UserData\", \"姓名:\" + user.getName() + \",年龄:\" + user.getAge());}

📦 三、传递 Serializable 列表(List)

你也可以传递实现了 Serializable 的对象集合:

1️⃣ 源 Activity 中传递:

java复制代码ArrayList userList = new ArrayList();userList.add(new User(\"张三\", 28));userList.add(new User(\"李四\", 32));Intent intent = new Intent(this, SecondActivity.class);intent.putExtra(\"user_list\", userList);startActivity(intent);

2️⃣ 目标 Activity 中接收:

java复制代码ArrayList userList = (ArrayList) getIntent().getSerializableExtra(\"user_list\");for (User user : userList) { Log.d(\"ListData\", user.getName() + \" - \" + user.getAge());}

🔐 四、注意事项

要点 说明 所有字段都必须是可序列化的 包括嵌套的对象也要实现 Serializable 尽量避免频繁传递大型对象 SerializableParcelable 性能差一些 不建议传递 UI 控件 控件如 View, Context 等不能序列化

传递Parcelable接囗

通过 Intent 传递实现了 Parcelable 接口 的对象。这是 Android 推荐的对象传递方式,性能优于 Serializable,特别适用于频繁传递或大量数据。


📘 一、什么是 Parcelable

  • Parcelable 是 Android 特有的对象序列化接口
  • 它的设计目标是:更快、更轻量,适合在组件(如 Activity / Service)之间传递对象
  • 它使用显式读写字段的方式进行序列化(不像 Serializable 使用反射)

✅ 二、完整使用流程(传递自定义对象)


1️⃣ 创建实体类并实现 Parcelable

import android.os.Parcel;import android.os.Parcelable;// User 类实现 Parcelable 接口,用于 Intent 传递对象public class User implements Parcelable { private String name; private int age; // 构造方法 public User(String name, int age) { this.name = name; this.age = age; } // Getter 方法 public String getName() { return name; } public int getAge() { return age; } // =========== Parcelable 必须实现的方法 =========== // 从 Parcel 中读取数据(构造函数) protected User(Parcel in) { name = in.readString(); // 顺序要和写入顺序一致 age = in.readInt(); } // 写入数据到 Parcel(用于传递) @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); // 写入字段 dest.writeInt(age); } // 通常返回 0 @Override public int describeContents() { return 0; } // Parcelable 的 CREATOR,必须定义 public static final Creator CREATOR = new Creator() { @Override public User createFromParcel(Parcel in) { return new User(in); // 从 Parcel 创建 User 对象 } @Override public User[] newArray(int size) { return new User[size]; } };}

2️⃣ 在源 Activity 中传递对象

// 创建 User 对象User user = new User(\"李四\", 30);// 创建 Intent,跳转到 SecondActivityIntent intent = new Intent(this, SecondActivity.class);// 通过 putExtra 传递 Parcelable 对象intent.putExtra(\"user_data\", user);// 启动 SecondActivitystartActivity(intent);

3️⃣ 在目标 Activity 中接收对象

// 接收 Intent 中传递的 Parcelable 对象User user = getIntent().getParcelableExtra(\"user_data\");if (user != null) { // 使用获取到的数据(如打印) Log.d(\"UserData\", \"姓名:\" + user.getName() + \",年龄:\" + user.getAge());}

📦 三、传递 Parcelable 对象列表

1️⃣ 传递方:

ArrayList userList = new ArrayList();userList.add(new User(\"张三\", 25));userList.add(new User(\"王五\", 33));// 放入 IntentIntent intent = new Intent(this, SecondActivity.class);intent.putParcelableArrayListExtra(\"user_list\", userList);startActivity(intent);

2️⃣ 接收方:

ArrayList userList = getIntent().getParcelableArrayListExtra(\"user_list\");if (userList != null) { for (User user : userList) { Log.d(\"UserList\", \"姓名:\" + user.getName() + \",年龄:\" + user.getAge()); }}

🧠 四、Parcelable 与 Serializable 对比

比较项 Parcelable Serializable 性能 ✅ 更快(无反射) 慢(使用反射) 实现复杂度 稍复杂(手动读写字段) 简单(只需实现接口) 兼容性 Android 专用 Java 通用 适用场景 推荐 Android 组件间对象传递 快速调试、小数据场景

🛠️ 五、快捷写法(Android Studio 插件)

你可以使用 Android Studio 的插件 来快速生成 Parcelable 代码:

  1. 安装插件 Parcelable Code Generator
  2. 右键类名 → GenerateParcelable
  3. 自动生成读写代码

✅ 总结

步骤 操作说明 1️⃣ 实现 Parcelable 接口、重写 writeToParcel 和构造方法 2️⃣ 使用 putExtra() 传递对象 3️⃣ 使用 getParcelableExtra() 接收对象 4️⃣ 支持传递对象列表 putParcelableArrayListExtra()

Android多媒体开发

MediaRecorder录制视频

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

MediaPlayer播放视频

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

VideoView播放视频

VideoView封装了MediaPlayer,使用更方便。但UI等界面不可调整,仅适用于简单的使用。

SoundPool播放音效

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

前端做好,是一个网页

DOCKER封装后端的代码,可以独立运行

rabiitMQ前后端通信

QT写个简单的前端,直接打包过去,PYC加密。

SP真正实战编写代码

可以通过这个生成APK

GIT使用

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

版本历史

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Git和其他VCS的主要区别

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Git基本操作

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Git入门图文教程(1.5W字40图)🔥🔥–深入浅出、图文并茂 - 安木夕 - 博客园

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

🧠 详细解析:git pull 做了什么?

执行:

bashgit pull

其实 Git 做了以下两步操作:

  1. git fetch:从远程仓库获取(fetch)更新的分支信息,更新本地的远程分支(如 origin/main);
  2. git merge:把这些远程更新合并(merge)到你当前的本地分支(如 main)。

📌 使用前提

  • 当前分支已经设置了远程关联(upstream):

    bash复制代码git branch -vv
  • 否则你需要明确指定:

    bash复制代码git pull origin main

origin main这两个参数是远程的项目名和分支名

SP实战

manifest中的启动项activity、

<?xml version=\"1.0\" encoding=\"utf-8\"?><manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" xmlns:tools=\"http://schemas.android.com/tools\"><application android:allowBackup=\"true\" android:dataExtractionRules=\"@xml/data_extraction_rules\" android:fullBackupContent=\"@xml/backup_rules\" android:icon=\"@mipmap/ic_launcher\" android:label=\"@string/app_name\" android:roundIcon=\"@mipmap/ic_launcher_round\" android:supportsRtl=\"true\" android:theme=\"@style/Theme.SPcoding_0414\" tools:targetApi=\"31\"> <!--MainActivity 改为普通页面(非启动页) --> <activity android:name=\".MainActivity\" android:exported=\"false\" android:label=\"@string/app_name\" android:theme=\"@style/Theme.SPcoding_0414\" /> <!-- 设置 MainActivity2 为启动入口 --> <activity android:name=\".MainActivity2\" android:exported=\"true\" android:label=\"@string/app_name\"> <intent-filter> <action android:name=\"android.intent.action.MAIN\" /> <category android:name=\"android.intent.category.LAUNCHER\" /> </intent-filter> </activity></application>

启动页配置说明

配置项 说明 android:exported=\"true\" 必须的(Android 12+ 有 intent-filter 的组件都必须加) intent-filter 内的 MAINLAUNCHER 表示这是应用的启动 Activity 只允许一个 Activity 设置启动入口 多个会导致冲突或无法编译

主题报错说明

首先说解决办法:在manifest文件中,下需要加入android:theme=“@style/Theme.AppCompat.Light.NoActionBar”>这一行代码,替换原先主题

在MainActivity中,第一句定义public class MainActivity2 extends AppCompatActivity {

这里继承了某一个风格,但是创建activity时res/values/themes.xml文件中也parent了一个

<?xml version=\"1.0\" encoding=\"utf-8\"?><resources> <style name=\"Theme.SPcoding_0414\" parent=\"android:Theme.Material.Light.NoActionBar\" /></resources>

发生了冲突

在manifest文件中,需要加入android:theme=“@style/Theme.AppCompat.Light.NoActionBar”>这一行代码

<activity android:name=\".MainActivity2\" android:exported=\"true\" android:label=\"@string/app_name\" android:theme=\"@style/Theme.AppCompat.Light.NoActionBar\"> <intent-filter> <action android:name=\"android.intent.action.MAIN\" /> <category android:name=\"android.intent.category.LAUNCHER\" /> </intent-filter>

你问的是:

❓ 为什么我们在 AndroidManifest.xml 中给 MainActivity2 设置的是
android:theme=\"@style/Theme.AppCompat.Light.NoActionBar\"
而不是你自己定义的 android:theme=\"@style/Theme.SPcoding_0414\"


✅ 答案分两种情况来看:


🚩 情况 1:你自己的主题 Theme.SPcoding_0414 是正确的(继承了 AppCompat)

✅ 那么你完全可以写成:

复制代码android:theme=\"@style/Theme.SPcoding_0414\"

只要这个主题是在 res/values/themes.xml 里定义,而且继承的是 Theme.AppCompat,比如:

xml复制代码 

这才是最终推荐做法

为什么我在创建 Empty Activity 时,Android Studio 自动在 AndroidManifest.xml 中设置:

xml复制代码android:theme=\"@style/Theme.SPcoding_0414\"

而不是用系统主题 Theme.AppCompat.Light


✅ 原因:Android Studio 模板默认会帮你生成一个自定义主题

当你新建项目时,Android Studio 会自动:

  1. ✅ 创建一个和项目名相匹配的主题,比如 Theme.SPcoding_0414
  2. ✅ 这个主题会自动写入 res/values/themes.xml
  3. ✅ 并且设置为 Application 和 Activity 的默认主题
  4. ✅ 这个主题默认是基于 MaterialComponents 的!

🔍 示例:自动生成的主题长这样

xml复制代码  @color/purple_500 @color/purple_700 @color/white

你可以看到,默认模板用的是:

xml复制代码parent=\"Theme.MaterialComponents.DayNight.DarkActionBar\"

也就是说:它并不是 Theme.AppCompat


❗所以为什么你后面用 AppCompatActivity 会崩溃?

因为你用了 AppCompatActivity,但主题却不是 Theme.AppCompat 风格,而是 MaterialComponents 风格 → 不兼容 → 抛出异常。


🛠 解决方案(两种选择):

✅ 方案 1:保留 AppCompatActivity → 修改主题继承链

xml复制代码

👉 然后在 Manifest 中继续用:

android:theme=\"@style/Theme.SPcoding_0414\"

✅ 方案 2:保留 MaterialComponents 主题 → 不要用 AppCompatActivity

你可以把 Activity 改成继承:

public class MainActivity2 extends ComponentActivity

或者使用 MaterialTheme 支持的方式去设计布局。

Button R.id.bt_标红问题

一、问题描述
as笔记csdn_sp任务视频

二、解决办法

在修改了manifest之后,需要重新build文件。从上方菜单栏选择build,clean project,然后assemble project, run app。

在gradle.properties中加上下面四行代码:

android.enableJetifier=true
android.defaults.buildfeatures.buildconfig=true
android.nonTransitiveRClass=true
android.nonFinalResIds=false

关键是android.nonFinalResIds=false

✅ 作用:

R.id.xx 等资源保持为 final 常量(可以用于 switch case

❗ 为什么影响 Button 使用?

如果设为 true,所有 R.id.xxx 就不再是 final,你在 Java 中就无法:

java复制代码switch (view.getId()) { case R.id.bt_login: // ❌ 报错“需要常量表达式”}

这正是你之前遇到的核心错误!✅

✅ 结论:

必须设为 false,否则会破坏 Java 中很多经典语法场景(如 switch-case 控件点击事件判断)

android studio build菜单常用构建操作对比说明:

菜单项 功能说明 是否构建 APK ▶️ Assemble ‘app’ Run Configuration 构建并运行 app ✅(但可能不保存 apk 文件) 🔨 Compile ‘SPcoding0414.app’ 编译当前模块的 Java/Kotlin 代码 ❌ 只编译,不打包 🔨 Compile All Sources 编译所有代码文件 ❌ 只编译,不打包资源和生成 APK ✅ Assemble Module ‘SPcoding0414.app’ 只构建当前模块(app 模块) ✅ 推荐打包 debug apk ✅ Assemble Project 构建整个项目(所有模块) ✅ 最推荐做完整构建 📦 Generate Signed Bundle / APK… 用于生成正式发布包(签名) ✅ 生成 release.apk.aab 🧹 Clean Project 清除 build 中所有编译文件 ❌ 清理,不构建 🧪 Analyze APK… 分析已生成的 APK 文件 ❌ 不参与构建流程

app打包一图看懂:两者区别对比

功能菜单 是否需要签名 用途 生成文件 推荐使用时机 🔹 Generate App Bundles or APKs ❌ 不一定签名 本地调试 .apk / .aab(debug) 开发阶段快速打包 🔸 Generate Signed App Bundle or APK… ✅ 需要签名文件(Keystore) 正式发布 .apk / .aab(release) 上架 Google Play / 对外发包

✅ 详细解释


🔹 Generate App Bundles or APKs

这是个通用入口,有时可用于构建 debug 或 release 版本。

点击后可能进一步提供选择:

  • Build APK(s)
  • Build Bundle(s)

⚠️ 这个入口并不强制你使用签名,也不一定生成最终正式包。

适合:

  • 你只是想临时打一个 .apk 来测试或装到手机上
  • 用于本地调试,不需要正式发布

🔸 Generate Signed App Bundle or APK…

这是用于生成“正式发布包”的流程,整个流程会引导你:

  1. 选择 APK 还是 AAB(App Bundle)
  2. 选择 Keystore 签名文件
  3. 输入密码、别名等
  4. 选择 debug 或 release 构建类型
  5. 生成签名后的 .apk.aab 文件

适合:

  • 上架应用市场(如 Google Play)
  • 对外分发正式版本
  • 测试带签名的 APK 安装兼容性

🎯 总结推荐:

你的目标 推荐选项 本地调试、安装测试 ✅ Build APK(s)Generate App Bundles or APKs 发布/发给客户 ✅ Generate Signed App Bundle or APK...(必须签名) 上架 Google Play ✅ 选择 AAB,生成 .aab 文件上传

✅ 补充知识点:APK vs AAB

格式 说明 上传 Play 商店推荐? .apk Android 安装包,旧标准 ❌ Google Play 已不推荐 .aab App Bundle,更小更优化 ✅ 推荐上传

APK打包教程

Android Studio 打包APK(详细版)_as打包apk-CSDN博客

重命名由于版本问题仍存在问题

APK发布到实际手机运行

如何把android studio中的项目发布到手机上(超详细版)_android studio工程部署至手机-CSDN博客

源码使用说明

b站 子林Android 源码由于版本冲突,无法直接使用,但新建一个project并复制过来后是可以直接使用的

匿名内部类(Anonymous Inner Class)

Java 中的一个非常强大的语法特性:匿名内部类(Anonymous Inner Class)


回答概括:

你看到的这个语句:

private Handler mHandler = new Handler(Looper.myLooper()) { @Override public void handleMessage(@NonNull Message msg) { ... }};

是在 声明变量的同时创建了一个匿名内部类对象,并重写了方法


✅ 拆解解释

🔹 正常写法

如果你平时熟悉这样写:

class MyHandler extends Handler { @Override public void handleMessage(@NonNull Message msg) { ... }}private Handler mHandler = new MyHandler();

那你现在这句其实就是它的简写版(匿名写法)


🔹 当前写法 = 匿名类写法

new Handler(Looper.myLooper()) { @Override public void handleMessage(Message msg) { ... }}

这个表示:

“创建一个继承 Handler 的子类,并立即重写 handleMessage() 方法,然后直接用这个子类实例来给变量 mHandler 赋值”。

也就是说,Handler 是个类,你在 new Handler(...) 时,立刻就定义了一个它的子类,并立即重写了它的方法。


✳️ 为什么要这么写?

  1. 代码更简洁
    不用多写一个类名,适合只用一次的情况。
  2. 逻辑更集中
    mHandler 是一个带有特定行为的对象,你可以一眼看到它具体要做什么。
  3. 避免类污染
    不用为一次性用途写额外类文件。

✅ 小结:

语法形式 说明 new 类名() { ... } 匿名子类写法 在变量赋值时直接写 是合法的、常用的 Java 用法 多用于回调、监听器 如 HandlerOnClickListenerRunnable

虽然你在 Android 项目中看不到传统 Java 程序那种:

java复制代码public static void main(String[] args) { ... }

Android 应用有自己的启动机制,它的入口不是 main(),而是由 系统框架控制的。下面是详细解释:


Android 应用的启动流程(简要版)

入口不是 main(),而是 Activity 的生命周期方法

在 Android 中,应用程序的执行入口由 操作系统的 Zygote 进程 负责启动。Zygote 会在应用第一次启动时调用你的 Activity,并最终执行:

java复制代码onCreate(Bundle savedInstanceState)

也就是说:

你写的 MainActivity2 是 Android 系统调用的,不是自己主动运行的。

谁控制了启动?→ AndroidManifest.xml

关键启动配置在你的 AndroidManifest.xml 中:

xml复制代码    

意思是:

  • MAIN:这个 Activity 是主入口
  • LAUNCHER:会显示在桌面应用图标中
  • 所以你一点击图标,系统就会通过这个配置来启动 MainActivity2
Android 系统底层的 main() 存在吗?

是的,整个 App 的真正入口仍然是 main() 方法,但是由 Android 系统自己维护:

java复制代码public static void main(String[] args) { // 由 ActivityThread 类控制,之后调用 onCreate() 等}

你写的 Java 代码并不会直接接触这个 main(),而是通过 Android 框架在幕后帮你处理了。


🔁 总结

问题 回答 为什么没写 main()? Android 系统负责启动,不需要你手动写 程序从哪里开始? 从 Android 系统通过 Manifest 找到 MainActivity2,然后执行 onCreate() 谁控制启动过程? Zygote → ActivityThread → Instrumentation → 你的 Activity

开辟异步线程,在new Thread()、选择new Runnable()后,会直接出现@Override public void run() {},里面的内容就是异步线程的实现。最后.start()代表启动了线程

public void start(View view) { // 做一个耗时任务// String strFromNet = getStringFromNet(); new Thread(new Runnable() { @Override public void run() { strFromNet = getStringFromNet(); Message message = new Message(); message.what = 0; message.obj = strFromNet; mHandler.sendMessage(message);// tvContent.setText(strFromNet);// try {//  Thread.sleep(1000);// } catch (InterruptedException e) {//  e.printStackTrace();// } } }).start(); Toast.makeText(this, \"任务完成!\", Toast.LENGTH_SHORT).show(); }

应用内通信方式(Handler)和进程间通信方式(AIDL)

Handler内容

E:\\Android\\origin_coding\\课程全套pdf-手把手教你学AndroidApp开发入门篇\\手把手教你学Androidapp开发入门篇-课程PDF 30-Android多线程通信-handler机制

AIDL内容

所有进程通信的原理都是这张图所示,请求bindservice,服务器同意链接返回Binder,客户端调用远程的方法

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这张图展示了 AIDL(Android Interface Definition Language)机制的通信流程,它体现的是 基于 Binder 的跨进程通信(IPC)。下面我详细解析这个过程:


🧠 一、AIDL 的核心机制:Binder 跨进程通信

关键词解释:

  • Client:调用端(例如 App)
  • Server:服务端(例如运行在另一个进程的 Service)
  • Binder:Android 的核心 IPC 框架,用于跨进程通信
  • AIDL:定义了客户端和服务端都能识别的接口(IRemoteInterface)

🧩 二、流程解析(配合图)

Client 调用 bindService

  • Client 通过调用 bindService(),请求绑定远程服务。
  • 这个请求会发送给 AMS(ActivityManagerService),由系统完成进程间连接。

Server 返回 Binder

  • 服务端实现了 AIDL 接口,并在 onBind() 中返回一个 Stub 类型的 Binder。
  • 这个 Binder 被系统传回给 Client。
java复制代码@Overridepublic IBinder onBind(Intent intent) { return myStub; // myStub 是 Stub 的实例}

Client 拿到 Binder,进行接口转换

java复制代码IMyAidlInterface service = IMyAidlInterface.Stub.asInterface(serviceBinder);
  • Client 将 Binder 转换成 AIDL 接口对象,从而调用远程方法。

④ 调用远程方法(跨进程通信)

  • Client 实际是调用本地 Proxy 对象的方法。
  • Proxy 会通过 Binder 调用 Server 进程中的 Stub

🔄 过程总结:

步骤 描述 1 Client 调用 bindService() 2 AMS 调度,调用 Server 的 onBind(),返回 Binder 对象 3 Client 拿到 Binder,使用 asInterface() 得到接口 4 Client 调用接口方法,系统通过 Binder 跨进程调用到 Server 5 Server 执行方法逻辑,结果返回给 Client

🔐 三、为什么 AIDL 需要?

  • 普通对象不能跨进程传递,AIDL 利用了 Binder 机制,让我们像调用本地接口一样调用远程服务。
  • 适合多线程/跨进程场景,例如音乐播放器服务、后台下载服务等。

✅ 示例

  1. AIDL 接口定义(IMyService.aidl):
interface IMyService { String getData();}
  1. Server 实现接口:
public class MyService extends Service { private final IMyService.Stub mBinder = new IMyService.Stub() { public String getData() { return \"来自远程服务的数据\"; } }; @Override public IBinder onBind(Intent intent) { return mBinder; }}
  1. Client 获取服务:
IMyService myService = IMyService.Stub.asInterface(service);String result = myService.getData();

demo实例

下面是一个 完整的 AIDL 跨进程通信 Demo 实例,包括客户端和服务端代码、AIDL 接口、AndroidManifest 配置,适合你直接运行实验。


🌟 项目结构概览

text复制代码app/├── aidl/│ └── com/example/myaidldemo/│ └── IMyAidlInterface.aidl├── java/│ └── com/example/myaidldemo/│ ├── MyService.java│ └── MainActivity.java

1️⃣ AIDL 接口文件(IMyAidlInterface.aidl

📁 路径:app/src/main/aidl/com/example/myaidldemo/IMyAidlInterface.aidl

package com.example.myaidldemo;// AIDL接口,定义方法interface IMyAidlInterface { String getData();}

💡 注意:

  • aidl 文件必须和 Java 包名一致。
  • 创建 AIDL 文件时,确保 aidl 文件夹已标记为 AIDL source directory(右键 -> Mark Directory as)。

2️⃣ 服务端:MyService.java

📁 路径:app/src/main/java/com/example/myaidldemo/MyService.java

package com.example.myaidldemo;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;public class MyService extends Service {//////////////定义mBinder并new出来后,AS会自动跳出aidl中的函数,使其在service中进行实现////////////////////////// private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() { @Override public String getData() throws RemoteException { return \"这是从服务端返回的数据\"; } };////创建service自带函数/////////// TODO: Return the communication channel to the service.//// @Override public IBinder onBind(Intent intent) { return mBinder; }}

3️⃣ 客户端调用:MainActivity.java

📁 路径:app/src/main/java/com/example/myaidldemo/MainActivity.java

java复制代码package com.example.myaidldemo;import android.content.ComponentName;import android.content.Intent;import android.content.ServiceConnection;import android.os.Bundle;import android.os.IBinder;import android.os.RemoteException;import android.widget.TextView;import android.widget.Button;import android.view.View;import android.content.Context;import android.widget.Toast;import androidx.appcompat.app.AppCompatActivity;public class MainActivity extends AppCompatActivity {/////////////////类内私有变量,aidl接口和与service通信的textview////////////////////////////////// private IMyAidlInterface myAidlInterface; private TextView textView;/////////////ServiceConnection也是AS自带的类,用于service通信,一旦定义就会自动出现两个函数进行实现//////////////// private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { myAidlInterface = IMyAidlInterface.Stub.asInterface(service); Toast.makeText(MainActivity.this, \"服务已连接\", Toast.LENGTH_SHORT).show(); } @Override public void onServiceDisconnected(ComponentName name) { myAidlInterface = null; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = findViewById(R.id.tv_content); // 绑定服务 Intent intent = new Intent(this, MyService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE); Button button = findViewById(R.id.btn_get_data); button.setOnClickListener(v -> { if (myAidlInterface != null) { try {  String result = myAidlInterface.getData();  textView.setText(result); } catch (RemoteException e) {  e.printStackTrace(); } } }); } @Override protected void onDestroy() { super.onDestroy(); unbindService(connection); }}

4️⃣ activity_main.xml 示例界面

📁 路径:res/layout/activity_main.xml

xml复制代码  

5️⃣ AndroidManifest.xml 注册服务
xml复制代码   

✅ 总结:如何运行?

  1. 创建 IMyAidlInterface.aidl 文件并同步 Gradle。
  2. 创建服务端 MyService.java 并实现接口。
  3. 创建 MainActivity 调用 AIDL 接口。
  4. 运行 App,点击按钮触发 AIDL 调用。
代码详解

🧩 一、AIDL 接口文件

📄 IMyAidlInterface.aidl

aidl复制代码package com.example.myaidldemo;interface IMyAidlInterface { String getData();}

✍️ 解释:

  • package:包名必须和 Java 文件一致。
  • interface:定义一个跨进程使用的接口。
  • String getData():声明一个方法供客户端远程调用。

💡 注意:只能使用基本类型、String、CharSequence、Parcelable、自定义AIDL类型。


🧩 二、服务端代码(MyService.java

public class MyService extends Service {

📌 表示这是一个后台服务。

java复制代码private final IMyAidlInterface.Stub mBinder = new IMyAidlInterface.Stub() { @Override public String getData() throws RemoteException { return \"这是从服务端返回的数据\"; }};

🔍 解释:

  • Stub 是系统自动生成的 Binder 实现类。
  • mBinder 实际是服务端的 远程代理对象,客户端会通过这个代理访问服务端方法。
  • getData() 是对 AIDL 接口中方法的具体实现。
java复制代码@Overridepublic IBinder onBind(Intent intent) { return mBinder;}

📌 onBind() 用来将 mBinder 返回给客户端,以便它获得远程接口。

你提到的 onBind() 方法没有调用 —— 这个问题非常常见,而且与 启动 Service 的方式 有直接关系。

调用了 bindService(...),就会执行 onBind() 方法


✅ 关键点:只有绑定服务时才会调用 onBind()

也就是说:

✅ 调用了 bindService(...),才会执行 onBind() 方法。

java复制代码Intent intent = new Intent(this, MyService.class);bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);

❌ 如果只调用了 startService(...) 呢?

java复制代码Intent intent = new Intent(this, MyService.class);startService(intent);

这种方式只会触发 onCreate()onStartCommand()不会调用 onBind()


🧩 三、客户端代码(MainActivity.java

  1. 定义 ServiceConnection
java复制代码private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { myAidlInterface = IMyAidlInterface.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { myAidlInterface = null; }};

✅ 用于管理 服务连接 的回调。

  • onServiceConnected() 中拿到服务端传过来的 Binder,然后通过 asInterface() 转换成 IMyAidlInterface
  • onServiceDisconnected() 是意外断开时的回调。

myAidlInterface = IMyAidlInterface.Stub.asInterface(service);

AIDL(Android Interface Definition Language)通信机制中最关键的一步之一,它的作用是将 远程 Binder 对象 转换为你在本地可以直接调用的方法接口对象。

IMyAidlInterface.Stub.asInterface(service) 做了什么?

  • IMyAidlInterface 是你定义的 .aidl 接口文件。
  • Android 编译器根据这个 .aidl 文件自动生成了一个 Java 类,结构如下:
java复制代码public interface IMyAidlInterface extends android.os.IInterface { public static abstract class Stub extends android.os.Binder implements IMyAidlInterface { public static IMyAidlInterface asInterface(android.os.IBinder obj) { // 核心逻辑 } }}

asInterface(...) 方法的逻辑:

public static IMyAidlInterface asInterface(IBinder obj) { if (obj == null) { return null; } IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if ((iin != null) && (iin instanceof IMyAidlInterface)) { return (IMyAidlInterface) iin; } return new IMyAidlInterface.Stub.Proxy(obj); // 使用代理类封装远程调用}

👉 总结:把远程 service 这个 Binder 对象转换成你可以直接调用的接口(其实底层会通过 Binder 做 IPC 通信)。


✅ 最终效果

调用后你就可以像调用普通对象一样访问远程服务方法了:

String result = myAidlInterface.getData();

系统会自动通过 Binder 通信把这个调用发送到 Service 端。

补充知识:为什么要用这个转换?

因为 Android 的进程是隔离的,你不能直接使用另一个进程的对象。Binder 提供了一种方式,让你像本地方法一样调用另一个进程的方法。asInterface() 就是这中间的“桥梁”。


  1. 绑定服务
Intent intent = new Intent(this, MyService.class);bindService(intent, connection, Context.BIND_AUTO_CREATE);

📌 使用 Intent 启动服务并绑定,自动创建服务实例(BIND_AUTO_CREATE)。


  1. 按钮点击调用服务
if (myAidlInterface != null) { String result = myAidlInterface.getData(); textView.setText(result);}

🧠 这段逻辑是通过 AIDL 远程调用服务端的 getData() 方法,并把结果显示在界面上。


  1. Activity 生命周期清理
@Overrideprotected void onDestroy() { unbindService(connection);}

🔐 防止内存泄漏,解绑服务。


🧩 四、布局文件(activity_main.xml)

  • TextView 显示服务端返回数据。
  • Button 点击触发远程调用。

🧩 五、AndroidManifest.xml 注册服务

xml复制代码
  • exported=\"true\" 表示该服务可以被外部(或其他 App)绑定。
  • exported=false,只能当前 App 绑定。

🔄 执行流程总结(图示)

text复制代码MainActivity(客户端) —bindService→ MyService(服务端) ↓ ↑ onServiceConnected() 返回 Binder 接口 ↓myAidlInterface.getData() —→ Stub 调用 getData() ←— 返回数据

❗ 常见问题排查

问题 原因 Stub 报错 没有 Sync AIDL 文件或包名错 ServiceConnection 不回调 没有 bindService() 或服务未注册 RemoteException 抛出 跨进程通信中必须处理 AIDL 不支持对象类型 需要实现 ParcelableAIDL interface
onServiceConnected(ComponentName name, IBinder service)` 是怎么被自动调用的

你是想问 onServiceConnected(ComponentName name, IBinder service) 是怎么被自动调用的?你明明没有手动调用它,它怎么就执行了?
——这其实是 Android 系统在服务绑定完成后自动调用的,下面是完整原理 👇


🧠 一句话解释

onServiceConnected()Android 系统自动回调的,用于告诉你:你请求绑定的服务已经连接成功了!


🧩 触发流程详解(基于 AIDL 或普通服务都一样)

① 你调用 bindService(intent, conn, flags)

bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
  • serviceConnection 就是你写的 ServiceConnection 实例
  • 这个接口里有两个重要方法:
    • onServiceConnected(...)
    • onServiceDisconnected(...)

② Android 系统会把 ServiceConnection 存起来,并尝试连接你指定的服务

如果连接成功(包括 AIDL 进程间服务或本地 Service):

➡️ 系统就会自动调用:

java复制代码serviceConnection.onServiceConnected(ComponentName name, IBinder service);
  • name:连接成功的服务组件
  • service:就是你在 Service 里返回的 onBind() 方法中的 IBinder

③ 你拿到的 service 参数,就是你跟服务通信的“桥梁”🔗

比如:

java复制代码myAidlInterface = IMyAidlInterface.Stub.asInterface(service);

你就可以通过 myAidlInterface 去调用 AIDL 方法。


🖼️ 流程图示意

text复制代码你调用 bindService() ——————————————→ 系统启动/连接目标服务 ↓ 服务创建成功 ↓  系统自动调用你的: serviceConnection.onServiceConnected(...)

复现AIDL代码过程中的问题

AIDL文件如何创建,应该放在哪个目录下

放在app/src/main下,aidl文件和java同级目录

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

如何创建 AIDL 文件?
  1. 在 Project 视图中切换到 “Project” 模式(不是 Android 模式)
  2. 找到:app/src/main 目录
  3. 右键 main 目录,选择:
New > Directory > 命名为 aidl
  1. aidl 目录下按照包名结构创建文件夹:
com/example/aidlwithservice_0416/
  1. 右键 aidlwithservice_0416,选择:
New > AIDL > AIDL File

命名为:IMyAidlInterface.aidl

此时若出现

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

build.gradle 启用 AIDL

请打开你模块下的 build.gradle(一般是 app/build.gradle.ktsbuild.gradle)文件,然后根据你使用的是 Kotlin DSL(kts)还是 Groovy 格式,添加如下配置:

选择这个文件:build.gradle.kts (Module: app)


✅ 如果你使用的是 Kotlin DSL(build.gradle.kts):将false改为true

android { // 其他配置... buildFeatures { aidl = true }}

如果你使用的是 Groovy 格式(build.gradle):

android { // 其他配置... buildFeatures { aidl true }}

修改 build.gradle 后,点击 Sync Now

若没有④ 右键 aidl 文件夹 → Mark Directory as → 会出现 AIDL source directory

这时候已经成功了,可以通过以下方法进行检查:

AIDL文件编译后会自动生成一个 Java 文件:

生成文件位置:

build/generated/aidl_source_output_dir/debug/out/com/example/aidldemo/IMyAidlInterface.java

你也可以点击 Android Studio 顶部菜单:

Build > Make Project(或 Rebuild Project)
  • 包名必须与 Java 文件的包名一致
  • 编译时会自动生成 IMyAidlInterface.java 文件(在 build/generated 文件夹里)
  • 若没有自动生成,请点击 Build > Rebuild Project

APK的构成和编译原理

构成

APK的基本结构 - 知乎

[【APK文件结构深度解析】:全面掌握从基础到高级的APK架构 - CSDN文库](https://wenku.csdn.net/column/7c4xpwm69q#1. APK文件结构概述)

✅ 一、APK 的构成(本质是一个 ZIP 文件)

APK 是经过压缩的 .zip 文件,里面包含了运行 Android 应用所需的所有资源和代码,主要目录如下:

目录 / 文件 说明 AndroidManifest.xml 应用的核心配置文件(如权限、组件信息)已被 AAPT 编译为二进制格式 classes.dex 编译后的 Java/Kotlin 源码,转换为 Dalvik 字节码(DEX)文件 resources.arsc 编译后的资源索引文件,包含字符串、颜色、样式等信息 res/ 未压缩的资源文件(布局、图片等) assets/ 原始资源,不会被编译,可以任意访问(比如网页、文本等) lib/ 不同架构的 C/C++ 本地库(如 arm64-v8a, armeabi-v7a) META-INF/ 签名文件和校验信息(用于验证 APK 的完整性和来源) kotlin/ Kotlin 运行所需的标准库(如果项目用了 Kotlin)

二、APK 编译原理(从源码到 APK 的全过程)

编译流程图:
Java/Kotlin + XML资源 ↓ 编译为 .class + R.java ↓ D8 或 R8 转为 .dex ↓资源打包(AAPT2) ↓ 签名 + 对齐(zipalign) ↓ APK

具体编译步骤详解:
  1. 资源编译(AAPT2)
  • .xml(布局、颜色、样式等)文件经过 aapt2 compile 编译成中间二进制格式。
  • AndroidManifest.xml 也会被编译为二进制。
  • 最终通过 aapt2 link 把所有资源链接成一个 resources.apk
  1. Java/Kotlin 源码编译
  • Java → .class 文件(通过 javac
  • Kotlin → .class 文件(通过 kotlinc
  1. 字节码转 DEX
  • .class.dex,使用 D8(旧的是 DX),如果启用了混淆、压缩、删除无用代码,还会走 R8
  1. 合成 APK
  • 所有资源、.dex 文件、AndroidManifest.xml、lib 文件被打包成 .apk
  1. APK 签名
  • 使用 jarsigner 或 Android Gradle Plugin 自动完成签名(开发者必须签名后才能安装)。
  1. APK 对齐(zipalign)
  • Google 要求 APK 进行对齐优化,以提升运行时性能。

三、打包相关的关键工具

工具 用途 aapt2 编译和打包资源文件 javac 编译 Java 源码 kotlinc 编译 Kotlin 源码 D8/R8 转换为 DEX 文件,R8 还做混淆、压缩、删除 zipalign 对 APK 进行字节对齐优化 apksigner 对 APK 进行签名

四、构建产物位置(以 Gradle 为例)

默认打包后的 APK 位于:

app/build/outputs/apk/debug/app-debug.apk

五、补充:构建过程中的 Gradle 脚本作用

文件 作用 build.gradle(Project) 设置全局插件仓库、依赖版本 build.gradle(Module) 设置 SDK 版本、依赖、打包配置 gradle-wrapper.properties 指定 Gradle 版本(如 8.1.0)

🧠 总结

APK 是一个集资源、代码、配置、签名于一体的压缩包,背后经历了资源编译 → 源码编译 → DEX 转换 → 资源链接 → 签名 → 对齐等多个步骤,最终打包生成。整个过程主要由 Gradle 和 Android 构建工具链自动完成。

Java 源码编译为 DEX 的完整过程

💡 简要结论:

Java/Kotlin 源码 先编译为 .class(Java 字节码),然后再使用 D8(或旧版本中的 DX 工具)将这些 .class 转换为 .dex(Dalvik Executable)文件,即 Dalvik 字节码。


🧩 详细流程:

🔹 1. Java 源码(.java

写的源代码文件,例如:

public class HelloWorld { public static void main(String[] args) { System.out.println(\"Hello, world!\"); }}

🔹 2. 编译为 .class 文件(Java 字节码)

使用 javac 工具(由 Gradle 自动调用),将 .java 编译为 JVM 可执行的 .class 文件。

javac HelloWorld.java

生成的就是 .class 文件,里面是标准的 Java 字节码(JVM 指令集)。


🔹 3. .class 转换为 .dex 文件(Dalvik 字节码)

然后使用 D8(Android 8.0 以后推荐)或者 DX(旧版本)工具:

d8 HelloWorld.class

这一步把 Java 字节码转换成 Dalvik 字节码,生成 .dex 文件。这个文件格式是为 Android 虚拟机(Dalvik / ART)设计的。

每个 APK 都必须至少包含一个 .dex 文件(通常叫 classes.dex


⚙️ ⚠️ Kotlin 同理!

Kotlin 会使用 kotlinc 先转为 .class 文件,然后同样走 D8 → .dex 路径。所以:

.java / .kt → .class → .dex(Dalvik 字节码)

✅ DEX 是干什么的?

.dex 文件是 Android 虚拟机(Dalvik/ART)可以直接解释执行的格式,它打包了多个 .class 文件,并进行了优化和压缩,以适应移动端设备内存紧张的情况。


✅ 总结流程图:

 .java / .kt(源代码) ↓ javac / kotlinc .class(Java 字节码) ↓ D8 / R8 .dex(Dalvik 字节码) ↓ 打包进 APK,手机执行

🧱 Java 与 C 的编译流程对比图

步骤 Java 编译流程 C 语言编译流程 1️⃣ 源码文件 .java 文件 .c 文件 2️⃣ 编译器编译 使用 javac 编译为 .class 字节码文件 使用 gccclang 编译为 .s 汇编代码 3️⃣ 汇编器处理 不涉及,.class 就是中间格式 .s 文件 → 汇编器 → .o 目标文件 4️⃣ 链接器 JVM 在运行时动态加载 .class 文件 .o 文件链接为 .exe.out 可执行文件 5️⃣ 最终产物 .class.dex(Android 上)→ JVM/ART 执行 生成原生机器代码,直接运行在硬件上 运行环境 Java 虚拟机(JVM)/ Android ART 操作系统+CPU(无中间层)

🔹 Java 源码

public class Hello { public static void main(String[] args) { System.out.println(\"Hello World\"); }}

🔹 编译后(用 javap -c Hello 反编译 .class 文件)看到的 Java 字节码:

public static void main(java.lang.String[]); Code: 0: getstatic #2  // Field java/lang/System.out:Ljava/io/PrintStream; 3: ldc  #3  // String Hello World 5: invokevirtual #4  // Method java/io/PrintStream.println:(Ljava/lang/String;)V 8: return

这是 Java 字节码,不是 CPU 汇编,但类似“虚拟机的汇编”。

资源(res 文件夹下)最终被编译为二进制

res 文件夹下的资源最终是会被编译为二进制的,不过这个过程是分步骤完成的,我来给你详细讲清楚整个“资源编译为二进制”的过程,以及它和 R.java 是怎么配合的。


🧠 关键理解:资源本身是二进制,但访问资源需要用 Java 代码中的 ID(R.java)来指向它们。


✅ 一、res 资源编译过程详解

📁 你写的资源(原始形式):

res/├── layout/activity_main.xml├── values/strings.xml├── drawable/logo.png

这些都是你人类可读的文本(XML)或图片。


🛠 编译阶段 1:AAPT2 编译资源 → 二进制格式

使用 Android 的资源打包工具 AAPT2(Android Asset Packaging Tool)进行两步处理:

1️⃣ compile 阶段(资源 → 中间二进制)

  • .xml 文件(如布局)被转换成 二进制 XML
  • .png.jpg 可能会被压缩或转换为优化格式

2️⃣ link 阶段(生成 resources.arsc)

  • 所有资源 ID 被分配(对应 R.java 中的 ID)
  • 生成 .flat 文件(中间文件)
  • 最终生成 resources.arsc:包含所有已编译的资源索引、资源值、ID 映射等信息

✅ 二、R.java 的作用:Java → 资源的桥梁

R.layout.activity_main // 实际是一个整数 ID,如 0x7f0a0012R.string.app_name // 也是整数 ID,指向 values/strings.xml 中的字符串

这些 ID 是系统在 resources.arsc 中查表用的!


✅ 三、最终打包进 APK 的结构

当你运行 Build > Build APK(s),最终会在 APK 中看到以下结构:

text复制代码APK 文件内容:├── AndroidManifest.xml(已转二进制)├── classes.dex├── res/  ← 包含所有编译过的图片、布局资源├── resources.arsc ← 所有资源 ID 和内容索引表(二进制)├── META-INF/

🔍 总结对比表

项目 内容 是否二进制 作用 res/ 文件 原始 XML、图片等资源 ❌(人类可读)→ ✅ 编译后 原始资源内容 resources.arsc 所有资源 ID 与内容映射 ✅ 二进制 加载资源时查表用 R.java 定义资源 ID 常量 ✅ 编译成 .class Java 中访问资源的桥梁

🧠 类比一下:

就像你去图书馆借书:

  • R.java → 借书证上的编号
  • resources.arsc → 图书馆的索引系统
  • res/ → 真实的书籍(图片、XML等)

你通过 R.layout.xxx 找编号,系统再去 resources.arsc 找到文件资源的位置,从 APK 中提取出来。

R.java文件

R.java 是系统自动生成的一个非常重要的中间文件,它是你的代码与资源之间的桥梁。


✅ 一句话解释:

R.java 是一个自动生成的 Java 类,包含了你项目中所有资源(res 文件夹下)的 ID 常量引用,让你可以在代码中方便地访问布局、字符串、图片、颜色等资源。


📂 举例说明:你的项目结构

res/├── layout/│ └── activity_main.xml├── values/│ └── strings.xml├── drawable/│ └── icon.png

你在代码中看到的:

R.layout.activity_mainR.string.app_nameR.drawable.icon

这些 R.xxx.yyy 全部来自于自动生成的 R.java 文件!


🧾 自动生成的 R.java 是什么样?

生成路径(编译后)大概是:

app/build/generated/source/r/debug/com/example/myapp/R.java

内容类似这样:

public final class R { public static final class layout { public static final int activity_main = 0x7f0a0000; } public static final class string { public static final int app_name = 0x7f0b0000; } public static final class drawable { public static final int icon = 0x7f0c0000; }}

🧠 原理:

  • 每个资源文件(如 activity_main.xmlicon.pngapp_name)都会被分配一个整数 ID。
  • 你在代码中使用 R.layout.activity_main,其实是使用对应的整型资源 ID。
  • 编译器会将这些 ID 和资源文件绑定,在运行时通过 Resources 类去加载。

⚙️ R.java 的生成时机

在你执行以下任一操作时生成:

  • 点击 “Build > Make Project”
  • 修改任何 res/ 下的资源文件
  • Sync Gradle

如果出错(如报 R cannot be resolved),可能是:

原因 解决方法 XML 文件写错 修复语法错误再 Rebuild 没有设置 buildFeatures { viewBinding true }(如用 binding) 在 build.gradle 中启用 项目未同步 File > Sync Project with Gradle Files

✅ 和 AIDL、.class 有什么关系?

当 Java/Kotlin 源码编译成 .class 文件时,R.java 也会一起被编译成 .class 文件,供应用逻辑调用。

所以:

res/layout/activity_main.xml ↓(aapt2 编译资源)生成 R.java ↓(javac 编译)生成 R.class

✅ 小结

项目 R.java 类型 自动生成的 Java 文件 内容 所有资源的整数 ID 定义 作用 让你在 Java/Kotlin 中通过 ID 访问资源 示例 R.layout.activity_mainR.drawable.icon

AOSP安卓系统架构概览

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

官方文档

https://source.android.com/docs/core/architecture?hl=zh-cn

GPT 自下而上解释

你这张图是 AOSP(Android Open Source Project)软件架构图,非常经典!它展示了 Android 系统从底层到上层的整体分层结构。下面我给你详细逐层解析一下每一部分的作用和相互关系:


🧱 AOSP 架构分层详解(自底向上)

✅ 1. Linux Kernel(Linux 内核)
  • Android 最底层的基础,由 Linux 内核构成。
  • 提供:
    • 进程管理
    • 内存管理
    • 文件系统
    • 网络协议栈
    • 硬件驱动(驱动 WiFi、蓝牙、摄像头、USB、音频等)
  • 厂商可以在内核层添加定制驱动程序。

🔧 类似于 Windows 的 NT 内核,是所有上层功能的基础。


✅ 2. System Services and Daemons(系统服务与守护进程)
  • 包含像 initlogdservicemanagervold 等系统级守护进程。
  • 管理系统启动流程、设备管理、安全策略(如 SELinux)等。
  • 启动时由 init.rc 脚本配置。

🧠 起到调度和资源管理的“幕后管家”作用。


✅ 3. HAL(Hardware Abstraction Layer,硬件抽象层)
  • 用于屏蔽硬件细节,为 Android Runtime 提供统一接口。
  • 每种设备功能(摄像头、音频、传感器等)都对应一个 HAL 模块,如 camera.default.so
  • 厂商定制设备时需要实现 HAL 接口。

📦 相当于 Windows 中的“设备驱动中间层”。


✅ 4. Android Runtime(ART/Dalvik)
  • Android 应用的运行环境,包含:
    • ART 虚拟机:执行 .dex 字节码
    • 栈内存管理、GC、JIT/AOT 编译器
  • 每个 App 都有一个独立的 ART 实例(运行在独立进程中)

🧠 就像 Java 的 JVM,专门为 Android 优化的运行时环境。


✅ 5. System Services(系统服务层)
  • 用 Java/C++ 编写的系统服务,如:
    • ActivityManagerService
    • WindowManagerService
    • PackageManagerService
    • InputManagerService
  • 提供进程/窗口/包管理等功能。
  • 这些服务通常运行在 system_server 进程中。

💡 是 Framework 与底层通信的桥梁,核心逻辑都在这里。


✅ 6. Android Framework(Android 框架层)
  • 所有开发者使用的核心 API 都定义在这里,如:
    • ActivityServiceContentProviderBroadcastReceiver
    • ViewNotificationWindowManagerLocationManager
  • Java/Kotlin 开发的应用通过这些接口操作系统功能。

🧑‍💻 Android 开发者最熟悉的一层,是对系统服务的“门面”。


✅ 7. Application 层(最顶层)

包括三种类型的应用:

类型 描述 Android Apps 普通用户安装的应用(微信、支付宝、浏览器等) Privileged Apps 拥有系统权限的预装应用,如 Contacts、Settings、SystemUI 等 Device Manufacturer Apps 设备厂商定制的应用,如华为相机、小米主题等

这些应用运行在 Framework 提供的 API 之上,通过 Binder 机制与 System Server 中的服务通信。


🔄 通信机制核心:Binder 跨进程通信

在整个 AOSP 架构中,最核心的通信方式是 Binder IPC(跨进程通信),它连接:

  • 应用 ↔ Framework
  • Framework ↔ System Service
  • System Service ↔ HAL

Binder 是 Android 自研的一套高效、安全的通信机制。


🧭 总结:层次与职责关系

[App/Framework 层]👆 高层业务逻辑👇[System Service/Runtime 层]👆 管理逻辑 & 抽象 API👇[HAL/Kernel 层]👆 硬件抽象 & 管理接口👇[Linux 内核] → 驱动硬件

断点调试

单步调试与多步调试

断点类型 普通断点 方法断点 属性断点 条件断点 依赖断点

段页式内存管理

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

页表:用户程序中的某一页 对应物理地址中的某一块

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

分段管理

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

GDB调试

参考博客:

使用GDB、LLDB调试安卓程序 | Xhy’s Blog

参考视频:

【小神仙讲 GDB】 通俗易懂版教程 | 一小时入门GDB | Debug | c/c++程序员必备 | 佩雨小神仙_哔哩哔哩_bilibili

GDB是什么

GD8 调试器 可以运行你在程序运行的时候检查里面到底发生了什么?

GDB可以做以下四件事情

  • Start your program, specifying anything that might affect its behavior.开始并设置参数

  • Make your program stop on specified conditions, 打断点 在特殊情况下停止

  • Examine what has happened, when your program has stopped,当你程序停止,检查发生了什么。

  • Change things in your program, so you can experiment with correcting the effects of one bug and go on to learn about another.

    Those programs might be executing on the same machine as GDB (native), on another machine(remote), or on a simulator, GDB can run on most popular UNlX and Microsoft Windows variants, as well as on Mac OS X.

搭建实验环境

安装gdb

yum install gdb

检查gdb是否成功安装

gdb --version

如果是Windows下的MinGW, 无法使用yum命令,但会自带GDB工具,可以使用检查命令。

Quickstart
#include using namespace std;int main(){ int arr[4] = {1, 2, 3, 4}; for(int i = 0; i < 4; i++){ cout << arr[i] << endl; } return 0;}

使用g++编译为可执行文件

g++ test_2.cpp -o test2

使用gdb运行可执行文件

gdb test2.exe
gdb基本命令
run r 运行程序quit q 退出gdb模式list 查看我们的源代码b break打断点函数的地方 函数名字在第几行打断点info b 查看断点的情况print 打印变量arr[0]&arr[0]step s 跳转到某一个具体的函数调试(将断点打到跳转函数执行那一行,run 执行后,输入step或s进行函数跳转)
gdb小技巧
  • shell 去调用终端的命令(linux下)
(gdb) shell cat test.cpp
  • 日志功能
set logging on

​ 生成一个日志文件,将gdb的过程记录下来(每次运行gdb,都需要执行该命令 才可以记录)

  • watchpoint

​ 观察变量是否变化
​ info 来査看我们的watchpoint

​ 使用tips:需要将先将断点打到定义某一变量i时的命令行,执行watch i, 或者

​ watch *0x5ffe9c(地址),往下继续执行代码,当变量值出现变化时,会显 示新值和旧值

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

以上gdb使用时调试的.exe或者a.out二进制文件,当程序挂掉时,需要调试core文件。或者需要调试一个正在运行的进程。

1 当程序挂掉,需要调试core

编译时要加-g选项

编译后未生成core文件要

一个电脑 多人共享的,shell 做一些限制,core文件 不会默认生成

需要使用ulimit -a 查看core文件的size设置,

ulimit -c unlimited

重新编译会生成

-rw------ 1 root root 245760 Dec 26 11:12 core.19761

之后使用gdb

gdb ./a.out core.19761 //gdb 二进制文件 core文件

会显示程序dump在了哪里

2 调试正在运行的程序

死循环程序,在后端执行程序,使用/a.out &,会返回一个进程号给终端。

[root@iZm5e7bxhsv9ft1z0g5s5aZ gdb_study]# ./a.out &[4]21528

或者

ps -ef grep a.out // 查看进程号

使用gdb调试这段进程

gdb -p 21528

Android中Binder设计原则

参考博客:

Android Binder框架实现之Binder的设计思想-CSDN博客

写给 Android 应用工程师的 Binder 原理剖析 - 知乎

Binder架构也是采用分层架构设计, 每一层都有其不同的功能:

as笔记csdn_sp任务视频

  • Java应用层: 对于上层应用通过调用AMP.startService, 完全可以不用关心底层,经过层层调用,最终必然会调用到AMS.startService.
  • Java IPC层: Binder通信是采用C/S架构, Android系统的基础架构便已设计好Binder在Java framework层的Binder客户类BinderProxy和服务类Binder;
  • Native IPC层: 对于Native层,如果需要直接使用Binder(比如media相关), 则可以直接使用BpBinder和BBinder(当然这里还有JavaBBinder)即可, 对于上一层Java IPC的通信也是基于这个层面.
  • Kernel物理层: 这里是Binder Driver, 前面3层都跑在用户空间,对于用户空间的内存资源是不共享的,每个Android的进程只能运行在自己进程所拥有的虚拟地址空间, 而内核空间却是可共享的. 真正通信的核心环节还是在Binder Driver.

Binder IPC原理

as笔记csdn_sp任务视频

可以看出无论是注册服务和获取服务的过程都需要ServiceManager,需要注意的是此处的Service Manager是指Native层的ServiceManager(C++),并非指framework层的ServiceManager(Java)。ServiceManager是整个Binder通信机制的大管家,是Android进程间通信机制Binder的守护进程,Client端和Server端通信时都需要先获取Service Manager接口,才能开始通信服务, 当然查找懂啊目标信息可以缓存起来则不需要每次都向ServiceManager请求。

图中Client/Server/ServiceManage之间的相互通信都是基于Binder机制。既然基于Binder机制通信,那么同样也是C/S架构,则图中的3大步骤都有相应的Client端与Server端。

1.注册服务:首先AMS注册到ServiceManager。该过程:AMS所在进程(system_server)是客户端,ServiceManager是服务端。

2.获取服务:Client进程使用AMS前,须先向ServiceManager中获取AMS的代理类AMP。该过程:AMP所在进程(app process)是客户端,ServiceManager是服务端。

3.使用服务: app进程根据得到的代理类AMP,便可以直接与AMS所在进程交互。该过程:AMP所在进程(app process)是客户端,AMS所在进程(system_server)是服务端。

图中的Client,Server,Service Manager之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都通过与Binder Driver进行交互的,从而实现IPC通信方式。其中Binder驱动位于内核空间,Client,Server,Service Manager位于用户空间。Binder驱动和Service Manager可以看做是Android平台的基础架构,而Client和Server是Android的应用层.

这3大过程每一次都是一个完整的Binder IPC过程, 接下来从源码角度, 仅介绍第3过程使用服务, 即展开AMP.startService是如何调用到AMS.startService的过程

Framework开发

系统框架AMS(组件管理)、WMS(窗口管理)、PMS(包管理)等核心组件

参考博文:

Framework学习(三)之PMS、AMS、WMS_ams pms-CSDN博客

透视Android系统AMS、PMS和WMS,了解开发中的重要角色 - 知乎
→ 驱动硬件

 ### 断点调试单步调试与多步调试断点类型 普通断点 方法断点 属性断点 条件断点 依赖断点 ### 段页式内存管理 [外链图片转存中...(img-xCH3qQiM-1753187247174)] [外链图片转存中...(img-hc2zBxA9-1753187247174)] 页表:用户程序中的某一页 对应物理地址中的某一块[外链图片转存中...(img-u08meZkP-1753187247174)] [外链图片转存中...(img-3booEpzx-1753187247174)] [外链图片转存中...(img-hnMMPTxk-1753187247174)] [外链图片转存中...(img-Gn2XLcA0-1753187247174)] 分段管理[外链图片转存中...(img-kL3qniif-1753187247174)] ### GDB调试参考博客:[使用GDB、LLDB调试安卓程序 | Xhy\'s Blog](https://blog.xhyeax.com/2022/05/06/debug-android-by-gdb-and-lldb/)参考视频:[【小神仙讲 GDB】 通俗易懂版教程 | 一小时入门GDB | Debug | c/c++程序员必备 | 佩雨小神仙_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1EK411g7Li?spm_id_from=333.788.videopod.episodes&vd_source=780e7ef4b190df7fadfaa9238d24ba0d)#### GDB是什么GD8 调试器 可以运行你在程序运行的时候检查里面到底发生了什么?GDB可以做以下四件事情- Start your program, specifying anything that might affect its behavior.开始并设置参数- Make your program stop on specified conditions, 打断点 在特殊情况下停止- Examine what has happened, when your program has stopped,当你程序停止,检查发生了什么。- Change things in your program, so you can experiment with correcting the effects of one bug and go on to learn about another. Those programs might be executing on the same machine as GDB (native), on another machine(remote), or on a simulator, GDB can run on most popular UNlX and Microsoft Windows variants, as well as on Mac OS X.#### 搭建实验环境安装gdb

yum install gdb

检查gdb是否成功安装

gdb --version

如果是Windows下的MinGW, 无法使用yum命令,但会自带GDB工具,可以使用检查命令。#### Quickstart```c++#include using namespace std;int main(){ int arr[4] = {1, 2, 3, 4}; for(int i = 0; i < 4; i++){ cout << arr[i] << endl; } return 0;}

使用g++编译为可执行文件

g++ test_2.cpp -o test2

使用gdb运行可执行文件

gdb test2.exe
gdb基本命令
run r 运行程序quit q 退出gdb模式list 查看我们的源代码b break打断点函数的地方 函数名字在第几行打断点info b 查看断点的情况print 打印变量arr[0]&arr[0]step s 跳转到某一个具体的函数调试(将断点打到跳转函数执行那一行,run 执行后,输入step或s进行函数跳转)
gdb小技巧
  • shell 去调用终端的命令(linux下)
(gdb) shell cat test.cpp
  • 日志功能
set logging on

​ 生成一个日志文件,将gdb的过程记录下来(每次运行gdb,都需要执行该命令 才可以记录)

  • watchpoint

​ 观察变量是否变化
​ info 来査看我们的watchpoint

​ 使用tips:需要将先将断点打到定义某一变量i时的命令行,执行watch i, 或者

​ watch *0x5ffe9c(地址),往下继续执行代码,当变量值出现变化时,会显 示新值和旧值

[外链图片转存中…(img-QPs3WAWd-1753187247174)]

以上gdb使用时调试的.exe或者a.out二进制文件,当程序挂掉时,需要调试core文件。或者需要调试一个正在运行的进程。

1 当程序挂掉,需要调试core

编译时要加-g选项

编译后未生成core文件要

一个电脑 多人共享的,shell 做一些限制,core文件 不会默认生成

需要使用ulimit -a 查看core文件的size设置,

ulimit -c unlimited

重新编译会生成

-rw------ 1 root root 245760 Dec 26 11:12 core.19761

之后使用gdb

gdb ./a.out core.19761 //gdb 二进制文件 core文件

会显示程序dump在了哪里

2 调试正在运行的程序

死循环程序,在后端执行程序,使用/a.out &,会返回一个进程号给终端。

[root@iZm5e7bxhsv9ft1z0g5s5aZ gdb_study]# ./a.out &[4]21528

或者

ps -ef grep a.out // 查看进程号

使用gdb调试这段进程

gdb -p 21528

Android中Binder设计原则

参考博客:

Android Binder框架实现之Binder的设计思想-CSDN博客

写给 Android 应用工程师的 Binder 原理剖析 - 知乎

Binder架构也是采用分层架构设计, 每一层都有其不同的功能:

as笔记csdn_sp任务视频

  • Java应用层: 对于上层应用通过调用AMP.startService, 完全可以不用关心底层,经过层层调用,最终必然会调用到AMS.startService.
  • Java IPC层: Binder通信是采用C/S架构, Android系统的基础架构便已设计好Binder在Java framework层的Binder客户类BinderProxy和服务类Binder;
  • Native IPC层: 对于Native层,如果需要直接使用Binder(比如media相关), 则可以直接使用BpBinder和BBinder(当然这里还有JavaBBinder)即可, 对于上一层Java IPC的通信也是基于这个层面.
  • Kernel物理层: 这里是Binder Driver, 前面3层都跑在用户空间,对于用户空间的内存资源是不共享的,每个Android的进程只能运行在自己进程所拥有的虚拟地址空间, 而内核空间却是可共享的. 真正通信的核心环节还是在Binder Driver.

Binder IPC原理

as笔记csdn_sp任务视频

可以看出无论是注册服务和获取服务的过程都需要ServiceManager,需要注意的是此处的Service Manager是指Native层的ServiceManager(C++),并非指framework层的ServiceManager(Java)。ServiceManager是整个Binder通信机制的大管家,是Android进程间通信机制Binder的守护进程,Client端和Server端通信时都需要先获取Service Manager接口,才能开始通信服务, 当然查找懂啊目标信息可以缓存起来则不需要每次都向ServiceManager请求。

图中Client/Server/ServiceManage之间的相互通信都是基于Binder机制。既然基于Binder机制通信,那么同样也是C/S架构,则图中的3大步骤都有相应的Client端与Server端。

1.注册服务:首先AMS注册到ServiceManager。该过程:AMS所在进程(system_server)是客户端,ServiceManager是服务端。

2.获取服务:Client进程使用AMS前,须先向ServiceManager中获取AMS的代理类AMP。该过程:AMP所在进程(app process)是客户端,ServiceManager是服务端。

3.使用服务: app进程根据得到的代理类AMP,便可以直接与AMS所在进程交互。该过程:AMP所在进程(app process)是客户端,AMS所在进程(system_server)是服务端。

图中的Client,Server,Service Manager之间交互都是虚线表示,是由于它们彼此之间不是直接交互的,而是都通过与Binder Driver进行交互的,从而实现IPC通信方式。其中Binder驱动位于内核空间,Client,Server,Service Manager位于用户空间。Binder驱动和Service Manager可以看做是Android平台的基础架构,而Client和Server是Android的应用层.

这3大过程每一次都是一个完整的Binder IPC过程, 接下来从源码角度, 仅介绍第3过程使用服务, 即展开AMP.startService是如何调用到AMS.startService的过程

Framework开发

系统框架AMS(组件管理)、WMS(窗口管理)、PMS(包管理)等核心组件

参考博文:

Framework学习(三)之PMS、AMS、WMS_ams pms-CSDN博客

透视Android系统AMS、PMS和WMS,了解开发中的重要角色 - 知乎