Android 开发学习笔记

Android 开发学习笔记

基本概念

Android 应用程序由一些零散的有联系的组件组成,通过一个工程 manifest 绑定在一起。在 manifest 中,描述了每一个组件以及组件的作用,其中有 6 个组件,它们是 Android 应用程序的基石。Android 有四大组件(也有说六大组件的,外加 Intent 和 Notification),分别是 Activity,Service,Content Provider 和 BroadcastReceiver。这四大组件一起组成了完整的 Android 程序。我们将分别简要介绍。

界面显示与逻辑处理:

利用 XML 标记描绘应用界面,使用 Java 代码书写程序逻辑。

Activity

  1. Activity 指一个完整的占了一个屏幕的页面(上下滑动的内容也算这个界面内的内容,所以它的概念可以理解成类似网站的一个网页一样)。
  2. Activity 允许显示一些控件、视图,并可以监听处理用户的事件,做出响应等。Activity 之间通过 Intent 通信(调用、跳转等动作)。
  3. 一个 Activity 实际上是一个 XML 文件,它可以被 Android 系统以可视化的界面展现。每一个 Activity 都与一个 Java 后台程序相联系,这个 Java 程序可以控制这个页面的启动、展示以及数据等信息。页面上展示的内容可以通过 Activity 本身的 xml 文件配置,也可以由相联系的 Java 文件来控制。

Service

  1. Service 是服务的意思。是 Android 程序中 “不可见” 的部分,但是它负责更新数据源、触发通知等。是一种没有界面的长生命周期的适合监控或者后台运行的程序。
  2. 最佳的例子是多媒体播放器,多媒体播放器程序可能含有一个或多个 Activity,用户通过这些 Activity 选择并播放音乐。然而,音乐回放并不需要一个 Activity 来处理,因为用户可能会希望音乐一直播放下去,即使退出了播放器去执行其它程序。为了让音乐一直播放,多媒体播放器 Activity 可能会启动一个 Service 在后台播放音乐。Android 系统会使音乐回放 Service 一直运行,即使在启动这个 Service 的 Activity 退出之后。
  3. Android 服务有两种:一是本地服务,另一种是远程服务。前者只能由托管服务的应用程序访问,后者是指由设备上其他应用程序进行远程访问的服务。

Content Provider

  1. Content Provider 是指内容提供器。App 运行的时候需要很多外部数据作为支撑,这些数据一般由内容提供器存储、共享。
  2. 比如,我们可以配置自己的 Content Provider 来存取其他应用程序,或者是通过其他应用程序给出的 Content Provider 来获取他们的数据。
  3. 系统本身也提供了一些 Content Provider,如联系人信息等。这些数据可以存储在文件系统、SQLite 数据库或者其他一些媒介里。

BroadcastReceiver

  1. 你的应用可以使用它对外部事件进行过滤,只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接受并作出响应。
  2. 广播接收器没有用户界面,然而,它们可以启动一个 activity 或 serice 来响应它们收到的信息,或者用 NotificationManager 来通知用户。
  3. 通知可以用很多种方式来吸引用户的注意力──闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。

Intent

  1. Intent(意图)是各种组件之间通信的桥梁,可以用来连接 Android 的应用组件,它提供了一种在不同应用之间进行任务运行绑定的工具,它最重要的应用是启动活动 Activity。
  2. Intent 是异步消息,可以帮助一个应用组件从另一个组件中请求功能。
  3. Intent 是一个对象,即 android.content.Intent

Notification

  1. Notification 是通知组件,主要是和推送用户信息有关。

Android App 项目中的文件简介

  1. AndroidManifest.xml 文件:在 mainfests 文件夹下面,叫做清单文件,它描述了整个项目的信息,包括项目名称、SDK 版本等等。我们在应用程序中的每个活动必须在 AndroidManifest.xml 文件中声明,系统需要根据里面的内容运行 APP 代码,显示界面,注意这个文件名一个字都不能错。

    <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>
    
  2. Java 文件夹:存放 java 源程序的地方。注意我们这里有一些以 Activity 结尾的程序文件,每一个文件其实对应了一个 Activity 的页面,也就是和下面资源文件夹(res)中的 layout 里面的内容绑定的。

  3. Res 文件夹:存放 java 资源的地方,包括图片、布局文件、菜单等等。

    1. drawable 资源:存放位图的文件夹

    2. layout 资源:存放的是布局资源,也就是指 Android 里面的活动和视图。在 Android 中,占用一个屏幕的 UI 界面称之为 Activity(活动),页面中的按钮、标签、文本字段等称之为 View(视图)。一个活动通常包含一个或者多个视图(也就是一个页面里面有按钮,文本之类的东西)。这里的布局文件都是 XML 文件,因为 Android 中视图都是从 XML 文件加载的,里面描述了位置、大小等视图信息。布局资源下每个文件都将根据其文件名(不包含扩展名)生成一个唯一的常量 ID,可以通过某些手段与 java 源文件绑定,或者被其他页面调用。

    3. values 资源:是 Android 中存放数组、颜色、尺寸、字符串和样式的文件夹。其实就是统一存放所有变量的地方,比如主题颜色、app 名称、Logo 的样式等,在 values 资源下统一定义可以使得我们在各个地方都调用同样的资源,在修改的时候也只要更改一处即可。

    /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>
    
    1. minmap:存放程序启动图标的文件夹。一般只存放启动图标(就是桌面图标)。
  4. Gradle Scripts:工程的编译配置文件

    1. build.gradle:分为项目级与模块级,用于描述 App 工程的编译规则
    2. proguard-rules.pro:用于描述 java 代码的混淆规则,对编译好的 class 文件进行混淆处理,目的是防止 java 代码被反编译
    3. gradle.properties:用于配置编译工程的命令行参数,一般无须改动
    4. settings.gradle:配置了需要编译哪些模块。初始内容为 include ':app' ,表示只编译 app 模块
    5. local.properties:项目的本地配置文件,在工程编译时自动生成,用于描述开发者电脑的环境配置,包括 SDK 的本地路径,NDK 的本地路径等
    6. 注意每个版本的 Android Studio 都有对应的 Gradle 版本,只有二者的版本正确对应,App 工程才能成功编译,比如 Android studio 4.1 对应的 Graddle 版本为 6.5。

Android 中的资源访问(R 类 / R.java)

在 Android 开发中,所有的外部资源都通过其资源的 ID 来访问,而所有的资源 ID 都在项目中 R 类中定义,而 R.java 这个类是由 aapt 工具自动生成的,用户本身不用修改添加。只要在资源中申明了 ID,那么 R 类会自动将该资源添加到其中。

编译应用时,aapt 会生成 R 类,其中包含您的 res/ 目录中所有资源的资源 ID。 每个资源类型都有对应的 R 子类(例如,R.drawable 对应于所有可绘制对象资源),而该类型的每个资源都有对应的静态整型数(例如,R.drawable.icon)。这个整型数就是可用来检索资源的资源 ID。

定义资源 ID 主要包括两个部分,一个是资源类型如 string、drawable 和 layout 等。另一个是资源名称,不包括其扩展名(当然也可以是 xml 中 android:name 属性中的值)。

访问这些资源有两种方式,一种是在 Java 程序中,一种是在 XML 文件中。

// Java 程序中访问资源,如下面的程序设置内容显示为某个 activity
// 可以使用 R.layout.activity 名称的方式。其中 layout 是资源类型,后面的是资源名称
setContentView(R.layout.activity_display_message);
// 这种方式是在 XML 文件中访问资源,使用 @ 开头表示什么类型,然后斜杠后面写上资源名称
@string/hello

NDK 相关

  1. NDK 是 Native Development Kit 的缩写,是 Android 的工具开发包。
  2. 作用是快速开发 C/C++ 的动态库,并自动将动态库与应用一起打包到 apk 。

参考:https://blog.csdn.net/afei__/article/details/80897404

开发步骤

创建新的 App 页面

完整的页面创建过程包括三个步骤:

  1. 在 layout 目录下创建 XML 文件
  2. 创建与 XML 文件对应的 Java 代码
  3. 在 AndroidManifest.xml 中注册页面配置

右键之后可以一键创建 Activity,系统同时帮我们做好上述三件事情。

实现界面的跳转

监听器:意思是专门监听控件的动作行为,只有控件发生了指定的动作,监听器才会触发开关去执行对应的代码逻辑。

常用的两种监听器如下:

  1. 点击监听器:通过 setOnClickListener 方法设置
  2. 长按监听器:通过 setOnLongClickListener 方法设置
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);
    }
}

还有一种设置监听的方式:button.setOnClickListener(this)

// 如果监听很多,不要创建太多类,可以公用这个类
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);
        }
}

Button 点击之后显示时间

直接在 layout 中的 XML 文件中设置:需要知道调用的方法名称(高耦合)

<Button
    android:layout_width="0dp"
    android:layout_height="40dp"
    android:layout_marginBottom="20dp"
    android:layout_weight="1"
    // 在此处进行设置,点击之后执行 doClick 函数
    android:onClick="doClick" />
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());
}

在 activity 文件对应的 xml 文件中增加一个 button 和一个点击之后显示内容的文本框:

<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" />

Activity

启停活动页面

Activity 的启动和结束

从当前页面跳到新页面:

// Intent 的构造函数需要传入两个参数:上下文和目标组件的 Class 对象。
startActivity(new Intent(源页面.this, 目标页面.class))

从当前页面回到上一个页面,相当于关闭当前页面

finish() // 结束当前的活动页面

Activity 的生命周期

img

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 进行过滤筛选
  1. onCreate():创建活动,把页面布局加载进内存,进入初始状态
  2. onStart():开始活动,把活动页面显示在屏幕上,进入就绪状态
  3. onResume():修复活动,活动页面进入活跃状态,能够与用户正常交互,例如允许响应用户的点击动作、允许输入文字等
  4. onPause():暂停活动,无法与用户正常交互,Activity 处于部分可见状态。这发生在 Activity 失去焦点但仍部分可见的时候,例如有一个对话框或透明的 Activity 遮盖在其上。如果有动画,在这里暂停
  5. onStop():停止活动,Activity 处于不可见状态。Activity 将停止显示在屏幕上,并且它失去了用户焦点,但仍然保留在 Activity 堆栈中
  6. onDestroy():销毁活动,Activity 将被销毁。这发生在用户按下 "Back" 键或调用 finish() 方法关闭 Activity 时。一旦 Activity 被销毁,它就会从 Activity 堆栈中移除
  7. onNewIntent:重用已有的活动实例

Activity 的启动模式

我们可以在配置文件中指定启动模式:

<activity android:name=".LKdemoActivity" android:launchMode="standard" />

在 Android 中,Activity 的启动模式(Launch Mode)定义了 Activity 如何在任务栈中启动和管理。不同的启动模式可以影响 Activity 的实例化和任务栈的行为。以下是常见的 Activity 启动模式:

  1. Standard(标准模式):这是默认的启动模式。每次启动一个 Activity,都会创建一个新的实例,并将其放入任务栈的顶部。无论是否已经存在相同的实例,都会创建新的实例。
  2. SingleTop(单顶模式):在 SingleTop 模式下,如果要启动的 Activity 已经位于任务栈的顶部(即栈顶有一个相同的实例),则不会创建新的实例,而是调用现有实例的 onNewIntent() 方法来传递新的 Intent 数据。如果 Activity 不在栈顶,仍然会创建新的实例。
  3. SingleTask(单任务模式):在 SingleTask 模式下,系统会确保一个特定的 Activity 只存在于一个任务栈中的一个实例。如果要启动的 Activity 已经在其他任务栈中存在,系统会将该任务栈移到前台,并调用现有实例的 onNewIntent() 方法传递新的 Intent 数据。如果要启动的 Activity 在当前任务栈中已经存在,则不会创建新的实例,而是将任务栈中其他 Activity 移除,使该 Activity 变为栈顶。

单任务模型的应用场景:

  • 程序主界面:我们肯定不希望主界面被创建多次,而且在主界面退出的时候退出整个 app 是最好的效果
  • 耗费系统资源的 activity:设置为单任务模式可以减少资源耗费
  1. SingleInstance(单实例模式):SingleInstance 是最特殊的启动模式。在 SingleInstance 模式下,系统会为该 Activity 创建一个新的任务栈,并且该任务栈中只会存在一个实例。其他应用程序的 Activity 无法与其共享任务栈。这种模式适用于需要独立存在且与其他应用程序隔离的 Activity。

  2. 在 JAVA 代码中动态设置启动模式

    1. 在两个页面之间跳来跳去:设置 setFlags 以避免重复跳转
    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);
        }
    
    1. 登录成功后不再返回登录界面:设置启动标志 FLAG_ACTIVITY_CLEAR_TASK,该标志会清空当前活动栈里的所有实例,不过全部清空之后,意味着当前栈没法用了,必须另外找个活动栈才行,也就是同时设置启动标志 FLAG_ACTIVITY_NEW_TASK,该标志用于开辟新任务的活动栈。这种操作可以实现从登录界面登录成功之后,无法再次返回到登录界面。
    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);  // 跳转到意图指定的活动页面
    

在活动之间传递消息

元素名称 设置方法 说明与用途
componentName setComponent 指定意图的来源与目标
action(动作) setAction 指定意图的动作行为
category(类别) addCategory 指定意图的操作类别
data(数据) setData 指定动作要操纵的数据路径
type(数据类型) setType 指定消息的数据类型
extras(扩展信息) putExtras 指定装载的包裹信息
Flags(标志位) setFlags 指定活动的启动标志

显式 Intent 和隐式 Intent

Intent 是各组件之间信息沟通的桥梁,用于 Android 各组件之间的通信,主要完成下列工作:

  1. 标明本次通信请求从哪里来、到哪里去、要怎么走;
  2. 发起方携带本次通信需要的数据内容,接受方从收到的意图中解析数据;
  3. 发起方若想判断接受方的处理结果,意图就要负责让接受方传回应答的数据内容。

显式 Intent:直接指定来源活动与目标活动,属于精确匹配,有三种构建方式:

  1. 在 Intent 的构造函数中指定
// 创建一个目标明确的意图
Intent intent = new Intent(CurrentActivity.this, NextActivity.class)
  1. 调用意图对象的 setClass 方法指定
// 创建一个新意图
Intent intent = new Intent();
// 设置跳转的 Activity
intent.setClass(CurrentActivity.this, NextActivity.class);
  1. 调用意图对象的 setComponent 方法指定
// 创建一个新意图
Intent intent = new Intent();
// 创建包含目标活动在内的组件名称对象
ComponentName component = new ComponentName(CurrentActivity.this, NextActivity.class);
// 设置意图携带的组件信息
intent.setComponent(component);

隐式 Intent:没有明确指定要跳转的目标活动,只给出一个动作字符串让系统自动匹配,属于模糊匹配

  1. App 不希望向外部暴露活动名称,只给出一个事先定义好的标记串,约定俗成就好
Intent intent = new Intent();
// 设置意图动作为准备拨号
intent.setAction(Intent.ACTION_DIAL);

向下一个 Activity 发送数据

  1. Intent 使用 Bundle 对象存放待传递的数据信息
  2. 在代码中发送消息包裹,调用意图对象的 putExtras 方法,即可存入消息包裹
  3. 在代码中接收消息包裹,调用意图对象的 getExtras 方法,即可取出消息包裹
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");

向上一个 Activity 发送数据

在 Android 中,Activity 之间的数据传递是单向的,即从一个 Activity 向另一个 Activity 发送数据。如果你想要从目标 Activity 返回数据给上一个 Activity,可以通过以下步骤实现:

  1. 在目标 Activity 中,创建一个新的 Intent 并使用 putExtra() 方法来添加要返回的数据到 Intent 中。
Intent intent = new Intent();
intent.putExtra("result", "这是返回的数据");
  1. 在目标 Activity 中调用 setResult() 方法来设置结果码和返回的 Intent。
setResult(Activity.RESULT_OK, intent);
  1. 在目标 Activity 中调用 finish() 方法来关闭当前 Activity,并返回到上一个 Activity。
finish();
  1. 在上一个 Activity 中,可以在 onActivityResult() 方法中接收返回的数据。
@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");
            // 在这里处理返回的数据
        }
    }
}

上述步骤中的 REQUEST_CODE 是一个自定义的请求码,用于标识请求的来源。在调用 startActivityForResult() 方法启动目标 Activity 时,可以传递这个请求码。

示例代码如下:

  1. 在当前 Activity 中启动目标 Activity:
private static final int REQUEST_CODE = 1;

// 启动目标
ActivityIntent intent = new Intent(CurrentActivity.this, TargetActivity.class);
startActivityForResult(intent, REQUEST_CODE);
  1. 在目标 Activity 中返回数据:
// 在目标 Activity 中设置返回数据
Intent intent = new Intent();
intent.putExtra("result", "这是返回的数据");
setResult(Activity.RESULT_OK, intent);
finish();
  1. 在上一个 Activity 中接收返回的数据:
@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");
            // 在这里处理返回的数据
        }
    }
}

通过这种方式,你可以在目标 Activity 中返回数据给上一个 Activity,并在上一个 Activity 中获取并处理这些返回的数据。

Android AsyncTask 异步任务

AsyncTask 是 Android 中用于在后台线程执行异步任务并在主线程更新 UI 的类。它提供了一种简单的方法来执行后台计算、网络请求或其他耗时操作,然后将结果返回到主线程,以便更新用户界面。AsyncTask 可以帮助开发者避免在主线程执行耗时操作而导致的界面卡顿和 ANR(Application Not Responding)问题。

AsyncTask 包含了四个关键的回调方法,它们分别是:

  1. onPreExecute(): 在后台任务开始执行之前,在主线程中调用。通常用于初始化 UI 或显示进度对话框。
  2. doInBackground(Params...): 在后台线程中执行耗时操作的方法。在该方法内部进行计算、网络请求或其他耗时任务,但不要更新 UI。doInBackground 结束后会返回一个 String 类型的数据到 onPostExecute 中,接下来 onPostExecute 就可以使用这个返回的数据来做 UI 更新的逻辑了。
  3. onProgressUpdate(Progress...): 当调用 publishProgress(Progress...) 方法时,在主线程中调用。通常用于更新 UI 进度。
  4. onPostExecute(Result): 在后台任务执行完成后,在主线程中调用。通常用于处理后台任务的结果,并更新 UI。

使用 AsyncTask 的步骤如下:

  1. 创建一个继承自 AsyncTask 的子类,并指定泛型参数:Params(传递给 doInBackground 的参数类型)、Progress(传递给 onProgressUpdate 的参数类型)、Result(传递给 onPostExecute 的参数类型)。
  2. 实现 doInBackground 方法,执行后台任务,并在需要时调用 publishProgress 方法来更新进度。
  3. 实现 onPostExecute 方法,处理后台任务的结果,并更新 UI。
  4. 在主线程中实例化并执行 AsyncTask,调用 execute() 方法。

下面是一个简单的示例,演示了使用 AsyncTask 进行后台计算,并在完成后更新 UI。

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[0];
            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[0]);
        }

        @Override
        protected void onPostExecute(Integer result) {
            super.onPostExecute(result);
            // 处理计算结果,并更新 UI
            progressBar.setVisibility(View.GONE);
            calculateButton.setEnabled(true);
            resultTextView.setText("计算结果:" + result);
        }
    }
}

在这个示例中,当用户点击按钮时,会触发 AsyncTask 的执行。doInBackground 方法会在后台线程中执行计算任务,并使用 publishProgress 方法来更新进度。在 onProgressUpdate 中更新 UI 进度,最终在 onPostExecute 中更新计算结果。这样,整个计算过程在后台进行,不会阻塞主线程,并且在计算完成后更新了 UI。

参考资料

  1. Android 开发入门基础
posted @ 2024-07-17 00:22  Lockegogo  阅读(12)  评论(0编辑  收藏  举报