Android 开发学习笔记
<h1 id="android-开发学习笔记">Android 开发学习笔记</h1><h2 id="基本概念">基本概念</h2>
<p>Android 应用程序由一些零散的有联系的组件组成,通过一个工程 manifest 绑定在一起。在 manifest 中,描述了每一个组件以及组件的作用,其中有 6 个组件,它们是 Android 应用程序的基石。Android 有四大组件(也有说六大组件的,外加 Intent 和 Notification),分别是 Activity,Service,Content Provider 和 BroadcastReceiver。这四大组件一起组成了完整的 Android 程序。我们将分别简要介绍。</p>
<p>界面显示与逻辑处理:</p>
<p>利用 XML 标记描绘应用界面,使用 Java 代码书写程序逻辑。</p>
<h3 id="activity">Activity</h3>
<ol>
<li>Activity 指一个完整的占了一个屏幕的页面(上下滑动的内容也算这个界面内的内容,所以它的概念可以理解成类似网站的一个网页一样)。</li>
<li>Activity 允许显示一些控件、视图,并可以监听处理用户的事件,做出响应等。Activity 之间通过 Intent 通信(调用、跳转等动作)。</li>
<li>一个 Activity 实际上是一个 XML 文件,它可以被 Android 系统以可视化的界面展现。每一个 Activity 都与一个 Java 后台程序相联系,这个 Java 程序可以控制这个页面的启动、展示以及数据等信息。页面上展示的内容可以通过 Activity 本身的 xml 文件配置,也可以由相联系的 Java 文件来控制。</li>
</ol>
<h3 id="service">Service</h3>
<ol>
<li>Service 是服务的意思。是 Android 程序中 “不可见” 的部分,但是它负责更新数据源、触发通知等。是一种没有界面的长生命周期的适合监控或者后台运行的程序。</li>
<li>最佳的例子是多媒体播放器,多媒体播放器程序可能含有一个或多个 Activity,用户通过这些 Activity 选择并播放音乐。然而,音乐回放并不需要一个 Activity 来处理,因为用户可能会希望音乐一直播放下去,即使退出了播放器去执行其它程序。为了让音乐一直播放,多媒体播放器 Activity 可能会启动一个 Service 在后台播放音乐。Android 系统会使音乐回放 Service 一直运行,即使在启动这个 Service 的 Activity 退出之后。</li>
<li>Android 服务有两种:一是本地服务,另一种是远程服务。前者只能由托管服务的应用程序访问,后者是指由设备上其他应用程序进行远程访问的服务。</li>
</ol>
<h3 id="content-provider">Content Provider</h3>
<ol>
<li>Content Provider 是指内容提供器。App 运行的时候需要很多外部数据作为支撑,这些数据一般由内容提供器存储、共享。</li>
<li>比如,我们可以配置自己的 Content Provider 来存取其他应用程序,或者是通过其他应用程序给出的 Content Provider 来获取他们的数据。</li>
<li>系统本身也提供了一些 Content Provider,如联系人信息等。这些数据可以存储在文件系统、SQLite 数据库或者其他一些媒介里。</li>
</ol>
<h3 id="broadcastreceiver">BroadcastReceiver</h3>
<ol>
<li>你的应用可以使用它对外部事件进行过滤,只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接受并作出响应。</li>
<li>广播接收器没有用户界面,然而,它们可以启动一个 activity 或 serice 来响应它们收到的信息,或者用 NotificationManager 来通知用户。</li>
<li>通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。</li>
</ol>
<h3 id="intent">Intent</h3>
<ol>
<li>Intent(意图)是各种组件之间通信的桥梁,可以用来连接 Android 的应用组件,它提供了一种在不同应用之间进行任务运行绑定的工具,它最重要的应用是启动活动 Activity。</li>
<li>Intent 是异步消息,可以帮助一个应用组件从另一个组件中请求功能。</li>
<li>Intent 是一个对象,即 <code>android.content.Intent</code>。</li>
</ol>
<h3 id="notification">Notification</h3>
<ol>
<li>Notification 是通知组件,主要是和推送用户信息有关。</li>
</ol>
<h2 id="android-app-项目中的文件简介">Android App 项目中的文件简介</h2>
<ol>
<li>
<p><strong>AndroidManifest.xml 文件</strong>:在 mainfests 文件夹下面,叫做<strong>清单文件</strong>,它描述了整个项目的信息,包括项目名称、SDK 版本等等。我们在应用程序中的每个活动必须在 <code>AndroidManifest.xml</code> 文件中声明,系统需要根据里面的内容运行 APP 代码,显示界面,注意这个文件名一个字都不能错。</p>
<pre><code class="language-xml"><application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".SmsNewActivity">
<!-- <intent-filter>-->
<!-- <action android:name="android.intent.action.MAIN" />-->
<!-- <category android:name="android.intent.category.LAUNCHER" />-->
<!-- </intent-filter>-->
</activity>
<activity android:name=".NerEvalActivity">
<!-- 下面两行代码设定程序入口 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</code></pre>
</li>
<li>
<p><strong>Java 文件夹</strong>:存放 java 源程序的地方。注意我们这里有一些以 Activity 结尾的程序文件,每一个文件其实对应了一个 Activity 的页面,也就是和下面资源文件夹(res)中的 layout 里面的内容绑定的。</p>
</li>
<li>
<p><strong>Res</strong> <strong>文件夹</strong>:存放 java 资源的地方,包括图片、布局文件、菜单等等。</p>
<ol>
<li>
<p><strong>drawable 资源</strong>:存放位图的文件夹</p>
</li>
<li>
<p><strong>layout</strong> <strong>资源</strong>:存放的是布局资源,也就是指 Android 里面的活动和视图。在 Android 中,占用一个屏幕的 UI 界面称之为 Activity(活动),页面中的按钮、标签、文本字段等称之为 View(视图)。一个活动通常包含一个或者多个视图(也就是一个页面里面有按钮,文本之类的东西)。这里的布局文件都是 XML 文件,因为 Android 中视图都是从 XML 文件加载的,里面描述了位置、大小等视图信息。布局资源下每个文件都将根据其文件名(不包含扩展名)生成一个唯一的常量 ID,可以通过某些手段与 java 源文件绑定,或者被其他页面调用。</p>
</li>
<li>
<p><strong>values 资源</strong>:是 Android 中存放数组、颜色、尺寸、字符串和样式的文件夹。其实就是<strong>统一存放所有变量</strong>的地方,比如主题颜色、app 名称、Logo 的样式等,在 values 资源下统一定义可以使得我们在各个地方都调用同样的资源,在修改的时候也只要更改一处即可。</p>
</li>
</ol>
<pre><code class="language-xml">/res/values/strings.xml
/res/values/colors.xml
/res/values/dimens.xml
/res/values/attrs.xml
/res/values/styles.xml
# 定义资源:尖括号定义资源类型,name 表示资源名称,里面表示内容
<string name="app_name">乐购</string>
<string name="edit_message">请输入您想查询的地点</string>
</code></pre>
<ol start="4">
<li><strong>minmap</strong>:存放程序启动图标的文件夹。一般只存放启动图标(就是桌面图标)。</li>
</ol>
</li>
<li>
<p><strong>Gradle Scripts</strong>:工程的编译配置文件</p>
<ol>
<li><code>build.gradle</code>:分为项目级与模块级,用于描述 App 工程的编译规则</li>
<li><code>proguard-rules.pro</code>:用于描述 java 代码的混淆规则,对编译好的 class 文件进行混淆处理,目的是防止 java 代码被反编译</li>
<li><code>gradle.properties</code>:用于配置编译工程的命令行参数,一般无须改动</li>
<li><code>settings.gradle</code>:配置了需要编译哪些模块。初始内容为 include ':app' ,表示只编译 app 模块</li>
<li><code>local.properties</code>:项目的本地配置文件,在工程编译时自动生成,用于描述开发者电脑的环境配置,包括 SDK 的本地路径,NDK 的本地路径等</li>
<li>注意每个版本的 Android Studio 都有对应的 Gradle 版本,只有二者的版本正确对应,App 工程才能成功编译,比如 Android studio 4.1 对应的 Graddle 版本为 6.5。</li>
</ol>
</li>
</ol>
<h2 id="android-中的资源访问r-类--rjava">Android 中的资源访问(R 类 / R.java)</h2>
<p>在 Android 开发中,所有的外部资源都通过其资源的 ID 来访问,而所有的资源 ID 都在项目中 R 类中定义,而 R.java 这个类是由 aapt 工具自动生成的,用户本身不用修改添加。只要在资源中申明了 ID,那么 R 类会自动将该资源添加到其中。</p>
<p>编译应用时,aapt 会生成 R 类,其中包含您的 <code>res/</code> 目录中所有资源的资源 ID。 每个资源类型都有对应的 R 子类(例如,R.drawable 对应于所有可绘制对象资源),而该类型的每个资源都有对应的静态整型数(例如,R.drawable.icon)。这个整型数就是可用来检索资源的资源 ID。</p>
<p>定义资源 ID 主要包括两个部分,一个是资源类型如 string、drawable 和 layout 等。另一个是资源名称,不包括其扩展名(当然也可以是 xml 中 android:name 属性中的值)。</p>
<p>访问这些资源有两种方式,一种是在 Java 程序中,一种是在 XML 文件中。</p>
<pre><code class="language-Java">// Java 程序中访问资源,如下面的程序设置内容显示为某个 activity
// 可以使用 R.layout.activity 名称的方式。其中 layout 是资源类型,后面的是资源名称
setContentView(R.layout.activity_display_message);
// 这种方式是在 XML 文件中访问资源,使用 @ 开头表示什么类型,然后斜杠后面写上资源名称
@string/hello
</code></pre>
<h2 id="ndk-相关">NDK 相关</h2>
<ol>
<li>NDK 是 Native Development Kit 的缩写,是 Android 的工具开发包。</li>
<li>作用是快速开发 C/C++ 的动态库,并自动将动态库与应用一起打包到 apk 。</li>
</ol>
<blockquote>
<p>参考:https://blog.csdn.net/afei__/article/details/80897404</p>
</blockquote>
<h2 id="开发步骤">开发步骤</h2>
<h3 id="创建新的-app-页面">创建新的 App 页面</h3>
<p>完整的页面创建过程包括三个步骤:</p>
<ol>
<li>在 layout 目录下创建 XML 文件</li>
<li>创建与 XML 文件对应的 Java 代码</li>
<li>在 AndroidManifest.xml 中注册页面配置</li>
</ol>
<p>右键之后可以一键创建 Activity,系统同时帮我们做好上述三件事情。</p>
<h3 id="实现界面的跳转">实现界面的跳转</h3>
<p><strong>监听器</strong>:意思是专门监听控件的动作行为,只有控件发生了指定的动作,监听器才会触发开关去执行对应的代码逻辑。</p>
<p>常用的两种监听器如下:</p>
<ol>
<li>点击监听器:通过 <code>setOnClickListener</code> 方法设置</li>
<li>长按监听器:通过 <code>setOnLongClickListener</code> 方法设置</li>
</ol>
<pre><code class="language-Java">public class LKdemoActivity extends AppCompatActivity {
// 注意 onCreate 方法有两个,我们需要选择参数是 @Nullable Bundle savedInstanceState 的这一个
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lk);
// 跳转到 MobileRecActivity
Button button = findViewById(R.id.button2);
// 点击监听器
button.setOnClickListener((new View.OnClickListener() {
@Override
public void onClick(View view) {
Intent intent = new Intent();
// 设置跳转的 Activity
intent.setClass(LKdemoActivity.this, MobileRecActivity.class);
startActivity(intent);
}
}));
TextView tv_hello = findViewById(R.id.tv_hello);
tv_hello.setText(R.string.welcome);
tv_hello.setTextSize(30);
}
}
</code></pre>
<p>还有一种设置监听的方式:<code>button.setOnClickListener(this)</code></p>
<pre><code class="language-Java">// 如果监听很多,不要创建太多类,可以公用这个类
public class LKdemoActivity extends AppCompatActivity implements View.OnClickListener {
// 注意 onCreate 方法有两个,我们需要选择参数是 @Nullable Bundle savedInstanceState 的这一个
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lk);
// 跳转到 MobileRecActivity
Button button = findViewById(R.id.button2);
// 点击监听器
button.setOnClickListener(this);
}
@Override
public void onClick(View view) {
// 有很多监听事件,我们需要判断是哪个按钮被点击了
if (view.getId() == R.id.button2) {
Intent intent = new Intent();
// 设置跳转的 Activity
intent.setClass(LKdemoActivity.this, MobileRecActivity.class);
startActivity(intent);
}
}
</code></pre>
<h3 id="button-点击之后显示时间">Button 点击之后显示时间</h3>
<p>直接在 layout 中的 XML 文件中设置:需要知道调用的方法名称(高耦合)</p>
<pre><code class="language-XML"><Button
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginBottom="20dp"
android:layout_weight="1"
// 在此处进行设置,点击之后执行 doClick 函数
android:onClick="doClick" />
</code></pre>
<pre><code class="language-Java">public class LKdemoActivity extends AppCompatActivity {
// bt_time 在两个函数中都要用,所以要设置为类变量
private TextView bt_time;
// 注意 onCreate 方法有两个,我们需要选择参数是 @Nullable Bundle savedInstanceState 的这一个
@SuppressLint("MissingInflatedId")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_lk);
bt_time = findViewById(R.id.tv_result6);
}
public void doClick(View view) {
String desc = String.format("%s 您好,欢迎使用按钮: %s", Common.getNowTime(), ((Button) view).getText());
bt_time.setText(desc);
}
}
// 在 utils 文件夹 Common 类增加获取当前时间的函数
public static String getNowTime() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return sdf.format(new Date());
}
</code></pre>
<p>在 activity 文件对应的 xml 文件中增加一个 button 和一个点击之后显示内容的文本框:</p>
<pre><code class="language-XML"><Button
android:id="@+id/button6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="显示时间"
android:onClick="doClick"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.489"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.408" />
<TextView
android:id="@+id/tv_result6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这里查看按钮的点击结果"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.335" />
</code></pre>
<h2 id="activity-1">Activity</h2>
<h3 id="启停活动页面">启停活动页面</h3>
<h4 id="activity-的启动和结束">Activity 的启动和结束</h4>
<p>从当前页面跳到新页面:</p>
<pre><code class="language-Java">// Intent 的构造函数需要传入两个参数:上下文和目标组件的 Class 对象。
startActivity(new Intent(源页面.this, 目标页面.class))
</code></pre>
<p>从当前页面回到上一个页面,相当于关闭当前页面</p>
<pre><code class="language-Java">finish() // 结束当前的活动页面
</code></pre>
<h4 id="activity-的生命周期">Activity 的生命周期</h4>
<p><img src="https://img2023.cnblogs.com/blog/1641623/202407/1641623-20240717002233703-1993411360.png" alt="img" loading="lazy"></p>
<pre><code class="language-Java">private static final String TAG ="ning";
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate: LKdemoActivity");
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "onStart: LKdemoActivity");
}
// 在 Logcat 控制台输入 tag:ning 进行过滤筛选
</code></pre>
<ol>
<li><code>onCreate()</code>:创建活动,把页面布局加载进内存,进入初始状态</li>
<li><code>onStart()</code>:开始活动,把活动页面显示在屏幕上,进入就绪状态</li>
<li><code>onResume()</code>:修复活动,活动页面进入活跃状态,能够与用户正常交互,例如允许响应用户的点击动作、允许输入文字等</li>
<li><code>onPause()</code>:暂停活动,无法与用户正常交互,Activity 处于部分可见状态。这发生在 Activity 失去焦点但仍部分可见的时候,例如有一个对话框或透明的 Activity 遮盖在其上。如果有动画,在这里暂停</li>
<li><code>onStop()</code>:停止活动,Activity 处于不可见状态。Activity 将停止显示在屏幕上,并且它失去了用户焦点,但仍然保留在 Activity 堆栈中</li>
<li><code>onDestroy()</code>:销毁活动,Activity 将被销毁。这发生在用户按下 "Back" 键或调用 <code>finish()</code> 方法关闭 Activity 时。一旦 Activity 被销毁,它就会从 Activity 堆栈中移除</li>
<li><code>onNewIntent</code>:重用已有的活动实例</li>
</ol>
<h4 id="activity-的启动模式">Activity 的启动模式</h4>
<p>我们可以在配置文件中指定启动模式:</p>
<pre><code class="language-XML"><activity android:name=".LKdemoActivity" android:launchMode="standard" />
</code></pre>
<p>在 Android 中,Activity 的启动模式(Launch Mode)定义了 Activity 如何在任务栈中启动和管理。不同的启动模式可以影响 Activity 的实例化和任务栈的行为。以下是常见的 Activity 启动模式:</p>
<ol>
<li><strong>Standard(标准模式)</strong>:这是默认的启动模式。每次启动一个 Activity,都会创建一个新的实例,并将其放入任务栈的顶部。无论是否已经存在相同的实例,都会创建新的实例。</li>
<li><strong>SingleTop(单顶模式)</strong>:在 SingleTop 模式下,如果要启动的 Activity 已经位于任务栈的顶部(即栈顶有一个相同的实例),则不会创建新的实例,而是调用现有实例的 <code>onNewIntent()</code> 方法来传递新的 Intent 数据。如果 Activity 不在栈顶,仍然会创建新的实例。</li>
<li><strong>SingleTask(单任务模式)</strong>:在 SingleTask 模式下,系统会确保一个特定的 Activity 只存在于一个任务栈中的一个实例。如果要启动的 Activity 已经在其他任务栈中存在,系统会将该任务栈移到前台,并调用现有实例的 <code>onNewIntent()</code> 方法传递新的 Intent 数据。如果要启动的 Activity 在当前任务栈中已经存在,则不会创建新的实例,而是将任务栈中其他 Activity 移除,使该 Activity 变为栈顶。</li>
</ol>
<blockquote>
<p>单任务模型的应用场景:</p>
<ul>
<li>程序主界面:我们肯定不希望主界面被创建多次,而且在主界面退出的时候退出整个 app 是最好的效果</li>
<li>耗费系统资源的 activity:设置为单任务模式可以减少资源耗费</li>
</ul>
</blockquote>
<ol start="4">
<li>
<p><strong>SingleInstance(单实例模式)</strong>:SingleInstance 是最特殊的启动模式。在 SingleInstance 模式下,系统会为该 Activity 创建一个新的任务栈,并且该任务栈中只会存在一个实例。其他应用程序的 Activity 无法与其共享任务栈。这种模式适用于需要独立存在且与其他应用程序隔离的 Activity。</p>
</li>
<li>
<p><strong>在 JAVA 代码中动态设置启动模式</strong>:</p>
<ol>
<li>在两个页面之间跳来跳去:设置 <code>setFlags </code>以避免重复跳转</li>
</ol>
<pre><code class="language-java">public void onClick(View view) {
// 有很多监听事件,我们需要判断是哪个按钮被点击了
if (view.getId() == R.id.button2) {
Intent intent = new Intent();
// 设置跳转的 Activity
intent.setClass(LKdemoActivity.this, MobileRecActivity.class);
// 栈中存在待跳转的活动实例时,则重新创建该活动的实例,并清除原实例上方的所有实例
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
}
</code></pre>
<ol start="2">
<li>登录成功后不再返回登录界面:设置启动标志 FLAG_ACTIVITY_CLEAR_TASK,该标志会清空当前活动栈里的所有实例,不过全部清空之后,意味着当前栈没法用了,必须另外找个活动栈才行,也就是同时设置启动标志 FLAG_ACTIVITY_NEW_TASK,该标志用于开辟新任务的活动栈。这种操作可以实现从登录界面登录成功之后,无法再次返回到登录界面。</li>
</ol>
<pre><code class="language-java">Intent intent = new Intent();
// 设置跳转的 Activity
intent.setClass(LKdemoActivity.this, MobileRecActivity.class);
// 设置启动标志:跳转到新页面时,栈中的原有实例都被清空,同时开辟新任务的活动栈
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);// 跳转到意图指定的活动页面
</code></pre>
</li>
</ol>
<h3 id="在活动之间传递消息">在活动之间传递消息</h3>
<table>
<thead>
<tr>
<th style="text-align: left">元素名称</th>
<th style="text-align: left">设置方法</th>
<th style="text-align: left">说明与用途</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: left">componentName</td>
<td style="text-align: left">setComponent</td>
<td style="text-align: left">指定意图的来源与目标</td>
</tr>
<tr>
<td style="text-align: left">action(动作)</td>
<td style="text-align: left">setAction</td>
<td style="text-align: left">指定意图的动作行为</td>
</tr>
<tr>
<td style="text-align: left">category(类别)</td>
<td style="text-align: left">addCategory</td>
<td style="text-align: left">指定意图的操作类别</td>
</tr>
<tr>
<td style="text-align: left">data(数据)</td>
<td style="text-align: left">setData</td>
<td style="text-align: left">指定动作要操纵的数据路径</td>
</tr>
<tr>
<td style="text-align: left">type(数据类型)</td>
<td style="text-align: left">setType</td>
<td style="text-align: left">指定消息的数据类型</td>
</tr>
<tr>
<td style="text-align: left">extras(扩展信息)</td>
<td style="text-align: left">putExtras</td>
<td style="text-align: left">指定装载的包裹信息</td>
</tr>
<tr>
<td style="text-align: left">Flags(标志位)</td>
<td style="text-align: left">setFlags</td>
<td style="text-align: left">指定活动的启动标志</td>
</tr>
</tbody>
</table>
<h4 id="显式-intent-和隐式-intent">显式 Intent 和隐式 Intent</h4>
<p><strong>Intent 是各组件之间信息沟通的桥梁,用于 Android 各组件之间的通信,主要完成下列工作:</strong></p>
<ol>
<li>标明本次通信请求从哪里来、到哪里去、要怎么走;</li>
<li>发起方携带本次通信需要的数据内容,接受方从收到的意图中解析数据;</li>
<li>发起方若想判断接受方的处理结果,意图就要负责让接受方传回应答的数据内容。</li>
</ol>
<p><strong>显式 Intent:直接指定来源活动与目标活动,属于精确匹配,有三种构建方式:</strong></p>
<ol>
<li>在 Intent 的构造函数中指定</li>
</ol>
<pre><code class="language-java">// 创建一个目标明确的意图
Intent intent = new Intent(CurrentActivity.this, NextActivity.class)
</code></pre>
<ol start="2">
<li>调用意图对象的 <code>setClass</code> 方法指定</li>
</ol>
<pre><code class="language-java">// 创建一个新意图
Intent intent = new Intent();
// 设置跳转的 Activity
intent.setClass(CurrentActivity.this, NextActivity.class);
</code></pre>
<ol start="3">
<li>调用意图对象的 setComponent 方法指定</li>
</ol>
<pre><code class="language-java">// 创建一个新意图
Intent intent = new Intent();
// 创建包含目标活动在内的组件名称对象
ComponentName component = new ComponentName(CurrentActivity.this, NextActivity.class);
// 设置意图携带的组件信息
intent.setComponent(component);
</code></pre>
<p><strong>隐式 Intent:没有明确指定要跳转的目标活动,只给出一个动作字符串让系统自动匹配,属于模糊匹配</strong></p>
<ol>
<li>App 不希望向外部暴露活动名称,只给出一个事先定义好的标记串,约定俗成就好</li>
</ol>
<pre><code class="language-java">Intent intent = new Intent();
// 设置意图动作为准备拨号
intent.setAction(Intent.ACTION_DIAL);
</code></pre>
<h4 id="向下一个-activity-发送数据">向下一个 Activity 发送数据</h4>
<ol>
<li>Intent 使用 Bundle 对象存放待传递的数据信息</li>
<li>在代码中发送消息包裹,调用意图对象的 <code>putExtras</code> 方法,即可存入消息包裹</li>
<li>在代码中接收消息包裹,调用意图对象的 <code>getExtras</code> 方法,即可取出消息包裹</li>
</ol>
<pre><code class="language-Java">Intent intent = new Intent(CurrentActivity.this, NextActivity.class);
// 1. 可以直接传入一个字符串
intent.putExtra("key", value);
// 2. 也可以创建一个包裹
Bundle bundle = new Bundle();
bundle.putString("request_time", DateUtil.getNowTime());
bundle.putString("request_content", tv_send.getText().toString());
intent.putExtras(bundle);
startActivity(intent);
// 1. 在目标 Activity 中接收数据:
Intent intent = getIntent();
String name = intent.getStringExtra("key");
// 2. 在目标 Activity 中接收包裹
Bundle bundle = getIntent().getExtras();
String request_time = bundle.getString("request_time");
String request_content = bundle.getString("request_content");
</code></pre>
<h4 id="向上一个-activity-发送数据">向上一个 Activity 发送数据</h4>
<p>在 Android 中,Activity 之间的数据传递是单向的,即从一个 Activity 向另一个 Activity 发送数据。如果你想要从目标 Activity 返回数据给上一个 Activity,可以通过以下步骤实现:</p>
<ol>
<li>在目标 Activity 中,创建一个新的 Intent 并使用 <code>putExtra()</code> 方法来添加要返回的数据到 Intent 中。</li>
</ol>
<pre><code class="language-Java">Intent intent = new Intent();
intent.putExtra("result", "这是返回的数据");
</code></pre>
<ol start="2">
<li>在目标 Activity 中调用 <code>setResult()</code> 方法来设置结果码和返回的 Intent。</li>
</ol>
<pre><code class="language-Java">setResult(Activity.RESULT_OK, intent);
</code></pre>
<ol start="3">
<li>在目标 Activity 中调用 <code>finish()</code> 方法来关闭当前 Activity,并返回到上一个 Activity。</li>
</ol>
<pre><code class="language-Java">finish();
</code></pre>
<ol start="4">
<li>在上一个 Activity 中,可以在 <code>onActivityResult()</code> 方法中接收返回的数据。</li>
</ol>
<pre><code class="language-Java">@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
if (data != null && data.hasExtra("result")) {
String result = data.getStringExtra("result");
// 在这里处理返回的数据
}
}
}
</code></pre>
<p>上述步骤中的 <code>REQUEST_CODE</code> 是一个自定义的请求码,用于标识请求的来源。在调用 <code>startActivityForResult()</code> 方法启动目标 Activity 时,可以传递这个请求码。</p>
<p><strong>示例代码如下:</strong></p>
<ol>
<li>在当前 Activity 中启动目标 Activity:</li>
</ol>
<pre><code class="language-Java">private static final int REQUEST_CODE = 1;
// 启动目标
ActivityIntent intent = new Intent(CurrentActivity.this, TargetActivity.class);
startActivityForResult(intent, REQUEST_CODE);
</code></pre>
<ol start="2">
<li>在目标 Activity 中返回数据:</li>
</ol>
<pre><code class="language-Java">// 在目标 Activity 中设置返回数据
Intent intent = new Intent();
intent.putExtra("result", "这是返回的数据");
setResult(Activity.RESULT_OK, intent);
finish();
</code></pre>
<ol start="3">
<li>在上一个 Activity 中接收返回的数据:</li>
</ol>
<pre><code class="language-Java">@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == Activity.RESULT_OK) {
if (data != null && data.hasExtra("result")) {
String result = data.getStringExtra("result");
// 在这里处理返回的数据
}
}
}
</code></pre>
<p>通过这种方式,你可以在目标 Activity 中返回数据给上一个 Activity,并在上一个 Activity 中获取并处理这些返回的数据。</p>
<h2 id="android-asynctask-异步任务">Android AsyncTask 异步任务</h2>
<p><code>AsyncTask</code> 是 Android 中用于在后台线程执行异步任务并在主线程更新 UI 的类。它提供了一种简单的方法来执行后台计算、网络请求或其他耗时操作,然后将结果返回到主线程,以便更新用户界面。<code>AsyncTask</code> 可以帮助开发者避免在主线程执行耗时操作而导致的界面卡顿和 ANR(Application Not Responding)问题。</p>
<p><code>AsyncTask</code> 包含了四个关键的回调方法,它们分别是:</p>
<ol>
<li><code>onPreExecute()</code>: 在后台任务开始执行之前,在主线程中调用。通常用于初始化 UI 或显示进度对话框。</li>
<li><code>doInBackground(Params...)</code>: 在后台线程中执行耗时操作的方法。在该方法内部进行计算、网络请求或其他耗时任务,但不要更新 UI。<code>doInBackground</code> 结束后会返回一个 String 类型的数据到 <code>onPostExecute</code> 中,接下来 <code>onPostExecute</code> 就可以使用这个返回的数据来做 UI 更新的逻辑了。</li>
<li><code>onProgressUpdate(Progress...)</code>: 当调用 <code>publishProgress(Progress...)</code> 方法时,在主线程中调用。通常用于更新 UI 进度。</li>
<li><code>onPostExecute(Result)</code>: 在后台任务执行完成后,在主线程中调用。通常用于处理后台任务的结果,并更新 UI。</li>
</ol>
<p><strong>使用 <code>AsyncTask</code> 的步骤如下:</strong></p>
<ol>
<li>创建一个继承自 <code>AsyncTask</code> 的子类,并指定泛型参数:<code>Params</code>(传递给 <code>doInBackground</code> 的参数类型)、<code>Progress</code>(传递给 <code>onProgressUpdate</code> 的参数类型)、<code>Result</code>(传递给 <code>onPostExecute</code> 的参数类型)。</li>
<li>实现 <code>doInBackground</code> 方法,执行后台任务,并在需要时调用 <code>publishProgress</code> 方法来更新进度。</li>
<li>实现 <code>onPostExecute</code> 方法,处理后台任务的结果,并更新 UI。</li>
<li>在主线程中实例化并执行 AsyncTask,调用 <code>execute()</code> 方法。</li>
</ol>
<p>下面是一个简单的示例,演示了使用 <code>AsyncTask</code> 进行后台计算,并在完成后更新 UI。</p>
<pre><code class="language-Java">import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private ProgressBar progressBar;
private TextView resultTextView;
private Button calculateButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = findViewById(R.id.progressBar);
resultTextView = findViewById(R.id.resultTextView);
calculateButton = findViewById(R.id.calculateButton);
calculateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 执行 AsyncTask
MyAsyncTask myAsyncTask = new MyAsyncTask();
myAsyncTask.execute(10); // 将计算的参数传递给 AsyncTask
}
});
}
private class MyAsyncTask extends AsyncTask<Integer, Integer, Integer> {
@Override
protected void onPreExecute() {
super.onPreExecute();
// 初始化 UI 或显示进度对话框
progressBar.setVisibility(View.VISIBLE);
calculateButton.setEnabled(false);
resultTextView.setText("");
}
@Override
protected Integer doInBackground(Integer... params) {
// 后台计算任务
int num = params;
int result = 0;
for (int i = 1; i <= num; i++) {
// 假设这是一个耗时的计算任务
result += i;
// 更新进度
// publishProgress 方法被调用后,会触发 onProgressUpdate 方法的执行
// 这里的参数会自动传递给 onProgressUpdate
// 注意:不能写成 onProgressUpdate(i * 10),因为这样会导致在错误的线程中执行
publishProgress(i * 10);
}
return result;
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
// 更新 UI 进度
progressBar.setProgress(values);
}
@Override
protected void onPostExecute(Integer result) {
super.onPostExecute(result);
// 处理计算结果,并更新 UI
progressBar.setVisibility(View.GONE);
calculateButton.setEnabled(true);
resultTextView.setText("计算结果:" + result);
}
}
}
</code></pre>
<p>在这个示例中,当用户点击按钮时,会触发 AsyncTask 的执行。<code>doInBackground</code> 方法会在后台线程中执行计算任务,并使用 <code>publishProgress</code> 方法来更新进度。在 <code>onProgressUpdate</code> 中更新 UI 进度,最终在 <code>onPostExecute</code> 中更新计算结果。这样,整个计算过程在后台进行,不会阻塞主线程,并且在计算完成后更新了 UI。</p>
<h2 id="参考资料">参考资料</h2>
<ol>
<li>Android 开发入门基础</li>
</ol><br><br>
来源:https://www.cnblogs.com/lockegogo/p/18306388
頁:
[1]