Android Framework AMS(10)广播组件分析-1

该系列文章总纲链接:专题总纲目录 Android Framework 总纲


本章关键点总结 & 说明:

说明:本章节主要解读应用层广播组件的发送广播和接收处理广播 2个过程,以及从APP层到AMS调用之间的打通。关注思维导图中左上部分即可。

有了前面startActivity流程和service组件启动的流程的过程,我们基于此继续分析广播组件的发送和接收处理流程过程。

我们先对广播组件的基本概念、分类、以及基本的使用方式有所了解。再基于应用的API入手,对关键API进行分析。

1 Android系统中广播的解读

1.1 广播的分类

在Android中,广播(Broadcast)是一种在不同组件之间传递信息的机制。广播可以分为以下几种类型:

  • 普通广播(Normal Broadcasts):异步发送,系统会同时将广播发送给所有注册接收该类型广播的接收器。接收器之间没有执行顺序,几乎同时接收到广播消息。不能被截断。
  • 有序广播(Ordered Broadcasts) :同步发送,系统会按照接收器的优先级顺序逐个发送广播。每个接收器都有机会处理广播消息,并且可以决定是否继续传递广播。如果接收器决定不再传递广播(通过调用abortBroadcast()),那么后续的接收器将不会收到该广播。
  • 粘性广播(Sticky Broadcasts):属于标准广播,在发送后就一直存在于系统的消息容器里面,等待对应的处理器去处理。如果暂时没有处理器处理这个消息则一直在消息容器里面处于等待状态。粘性广播的Receiver如果被销毁,那么下次重建时会自动接收到消息数据。
  • 系统广播(System Broadcasts):Android内置了很多系统级别的广播,我们可以在应用中通过监听这些广播来得到各种系统的状态信息。例如手机开机后会发送一条广播,电池的电量发生变化会发出一条广播,时间或时区发生改变也会发出一条广播等。
  • App应用内广播(Local Broadcast) :用于应用内部组件之间的通信,使用LocalBroadcastManager来管理。相比全局广播,应用内广播更高效,不需要跨进程通信,也不需要考虑安全性问题。

当然,随着Android版本发展的迭代,一些广播和一些广播发送的方法不再使用,参考信息如下:

  1. Android 5.0(API级别21)粘性广播不再支持sendStickyBroadcast 被打上了 Deprecated 标签,逐渐被弃用
  2. Android 7.0 (API 级别 24) 及更高版本不再发送以下系统广播ACTION_NEW_PICTURE和``ACTION_NEW_VIDEO
  3. Android 8.0 (API 级别 26) 开始对清单声明的接收器施加额外限制:如果应用以 Android 8.0 或更高版本为目标平台,则不能使用清单来声明接收器,对于大多数隐式广播(并非针对你的应用)。
  4. Android 9 (API 级别 28) 开始NETWORK_STATE_CHANGED_ACTION 广播不再接收关于用户所在位置或个人身份信息可识别身份的数据。
  5. Android 12 开始ACTION_CLOSE_SYSTEM_DIALOGS 被弃用,为了改善用户控制体验,在应用程序和系统进行交互时。

这些变更意味着开发者需要根据目标Android版本调整广播的使用,以确保应用的兼容性和功能性。

1.2 广播接收器分类

在Android中,广播接收器(BroadcastReceiver)用于接收和处理广播消息。根据广播的类型和用途,广播接收器可以分为以下几种:

1.2.1 静态注册广播接收器

在AndroidManifest.xml文件中声明,系统会在应用启动时自动为这些接收器注册广播。适用于接收系统广播,如电池电量变化、屏幕关闭等。当然,静态声明接收器也可以接收感兴趣的广播类型,参考配置如下:

html 复制代码
<receiver
    android:name=".YourBroadcastReceiver"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="your.action.YOUR_ACTION" />
    </intent-filter>
</receiver>

这里的your.action.YOUR_ACTION是你自定义的广播动作,用于IntentIntentFilter匹配对应动作。

1.2.2 动态注册广播接收器

一般在代码中通过registerReceiver()方法注册,需要在onDestroy()方法中调用unregisterReceiver()来注销。适用于接收应用内部广播或者需要根据应用状态动态注册和注销的广播。在Java代码中动态注册广播接收器代码实现参考如下:

java 复制代码
IntentFilter filter = new IntentFilter();
filter.addAction("your.action.YOUR_ACTION");
registerReceiver(yourBroadcastReceiver, filter);

1.3 发送/接收广播代码实现解读

1.3.1 普通广播

发送普通广播方法如下:

java 复制代码
// 创建一个Intent,包含要发送的动作
Intent intent = new Intent("your.action.YOUR_ACTION");
// 可以添加一些额外的数据
intent.putExtra("key", "value");

// 发送普通广播
sendBroadcast(intent);

接收普通广播:

java 复制代码
public class NormalReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 检查Intent的动作是否是我们期望的
        if ("your.action.YOUR_ACTION".equals(intent.getAction())) {
            // 处理接收到的数据
            String value = intent.getStringExtra("key");
            // ...
        }
    }
}

在AndroidManifest.xml中静态注册普通广播接收器

html 复制代码
<receiver android:name=".NormalReceiver">
    <intent-filter>
        <action android:name="your.action.YOUR_ACTION" />
    </intent-filter>
</receiver>

或者动态注册:

java 复制代码
IntentFilter filter = new IntentFilter("your.action.YOUR_ACTION");
registerReceiver(new NormalReceiver(), filter);

1.3.2 有序广播

发送有序广播方法如下:

java 复制代码
// 创建一个Intent,包含要发送的动作
Intent intent = new Intent("your.action.YOUR_ACTION");
// 可以添加一些额外的数据
intent.putExtra("key", "value");

// 发送有序广播,第二个参数是权限字符串,null表示没有权限限制
sendOrderedBroadcast(intent, null);

在发送有序广播时,可以通过abortBroadcast()方法来截断广播,阻止其继续传递给其他接收器。接收有序广播:

java 复制代码
public class OrderedReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 检查Intent的动作是否是我们期望的
        if ("your.action.YOUR_ACTION".equals(intent.getAction())) {
            // 处理接收到的数据
            String value = intent.getStringExtra("key");
            // ...

            // 如果需要继续传递广播,不调用abortBroadcast(),否则调用abortBroadcast()来截断广播
            // abortBroadcast();
        }
    }
}

注册普通广播接收器参考1.3.1即可。

请注意,有序广播可能会在未来的Android版本中被进一步限制或弃用,因为它们可能被滥用来执行恶意行为。因此,使用时需要谨慎,并考虑应用的目标API级别。

1.3.3 粘性广播(Sticky Broadcasts)

发送粘性广播:

java 复制代码
// 创建一个Intent,包含要发送的数据
Intent intent = new Intent("com.example.myapp.MY_STICKY_BROADCAST");
intent.putExtra("key", "value");

// 发送粘性广播
sendStickyBroadcast(intent);

接收粘性广播:

java 复制代码
public class StickyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 处理接收到的数据
        String value = intent.getStringExtra("key");
        // ...
    }
}

// 在代码中动态注册粘性广播接收器
IntentFilter filter = new IntentFilter("com.example.myapp.MY_STICKY_BROADCAST");
registerReceiver(new StickyReceiver(), filter);

注意: 如之前描述,从Android 5.0(API 级别 21)开始,sendStickyBroadcastsendStickyOrderedBroadcast 方法被标记为不推荐使用,因为它们可能会引起安全和隐私问题。建议使用其他方式来传递数据。

1.3.4 系统广播(System Broadcasts)

关于系统广播的发送,我们不需要过渡关心,更在于如何接收。

接收系统广播(例如电池电量变化):

java 复制代码
public class SystemReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) {
            int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
            int scale = intent.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
            float batteryPct = level / (float) scale;
            // 处理电池电量变化
        }
    }
}

// 在AndroidManifest.xml中静态注册系统广播接收器
<receiver android:name=".SystemReceiver">
    <intent-filter>
        <action android:name="android.intent.action.BATTERY_CHANGED" />
    </intent-filter>
</receiver>

注册广播接收器参考1.3.1即可。

1.3.5 App应用内广播(Local Broadcast)

发送应用内广播:

java 复制代码
// 创建LocalBroadcastManager实例
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);

// 创建一个Intent,包含要发送的数据
Intent intent = new Intent("com.example.myapp.MY_LOCAL_BROADCAST");
intent.putExtra("key", "value");

// 发送应用内广播
localBroadcastManager.sendBroadcast(intent);

接收应用内广播:

java 复制代码
public class LocalReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 处理接收到的数据
        String value = intent.getStringExtra("key");
        // ...
    }
}

// 在代码中动态注册应用内广播接收器
LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(context);
IntentFilter filter = new IntentFilter("com.example.myapp.MY_LOCAL_BROADCAST");
localBroadcastManager.registerReceiver(new LocalReceiver(), filter);

1.4 广播发送和接收案例

以下是一个简单的 Android 应用示例,它演示了如何发送和接收普通广播。这个示例包含两个部分:一个用于发送广播的 Activity 和一个广播接收器 BroadcastReceiver

首先,你需要在AndroidManifest.xml文件中声明Activity

html 复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.dynamicbroadcastdemo">

    <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=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

这是一个简单的Activity,它将发送一个自定义的广播,并动态注册一个广播接收器:

java 复制代码
package com.example.dynamicbroadcastdemo;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private Button sendBroadcastButton;
    private MyBroadcastReceiver myBroadcastReceiver;

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

        sendBroadcastButton = findViewById(R.id.sendBroadcastButton);
        sendBroadcastButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                sendBroadcast();
            }
        });

        // 动态注册广播接收器
        myBroadcastReceiver = new MyBroadcastReceiver();
        IntentFilter filter = new IntentFilter();
        filter.addAction("com.example.dynamicbroadcastdemo.MY_CUSTOM_ACTION");
        registerReceiver(myBroadcastReceiver, filter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 注销广播接收器
        unregisterReceiver(myBroadcastReceiver);
    }

    private void sendBroadcast() {
        Intent intent = new Intent("com.example.dynamicbroadcastdemo.MY_CUSTOM_ACTION");
        // 可以添加额外的数据
        intent.putExtra("key", "value");
        sendBroadcast(intent);
    }

    public class MyBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            // 检查Intent的动作是否是我们期望的
            if ("com.example.dynamicbroadcastdemo.MY_CUSTOM_ACTION".equals(intent.getAction())) {
                // 处理接收到的数据
                String value = intent.getStringExtra("key");
                Toast.makeText(context, "Received broadcast: " + value, Toast.LENGTH_SHORT).show();
            }
        }
    }
}

编写一个简单的布局文件,包含一个按钮用于发送广播:

html 复制代码
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/sendBroadcastButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Send Broadcast"
        android:layout_centerInParent="true" />
</RelativeLayout>

这个示例展示了如何发送和接收一个简单的广播。当用户点击按钮时,MainActivity 会发送一个广播,MyBroadcastReceiver 接收到广播后会显示一个 Toast 消息。这个示例涵盖了广播的发送和接收的基本流程。

基于此,我们对于广播组件的研究起点就从2个关键方法入手:

  • 动态注册/注销:registerReceiver unregisterReceiver
  • 发送普通/有序广播:sendBroadcast sendOrderedBroadcast

2 从activity场景到AMS调用的流程

2.1 动态注册/注销流程

2.1.1 从activity.registerReceiver到AMS.registerReceiver

activity.registerReceiver方法是从Context中的registerReceiver开始调用的,接口代码实现如下:

java 复制代码
//Context 
@Nullable
public abstract Intent registerReceiver(@Nullable BroadcastReceiver receiver,IntentFilter filter);

@Nullable
public abstract Intent registerReceiver(BroadcastReceiver receiver,IntentFilter filter, @Nullable String broadcastPermission,@Nullable Handler scheduler);

注册时,用户的第一个 参数receiver 是继承于 BroadcastReceiver 的。其真正的实现是在ContextImpl中,代码实现如下:

java 复制代码
//ContextImpl
    //关键流程:step1
    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        return registerReceiver(receiver, filter, null, null);
    }
    //关键流程:step2
    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler) {
        return registerReceiverInternal(receiver, getUserId(),
                filter, broadcastPermission, scheduler, getOuterContext());
    }
    //关键流程:step3
    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
                IntentFilter filter, String broadcastPermission,
                Handler scheduler, Context context) {
        // 创建一个IIntentReceiver对象,用于与ActivityManagerService通信
        IIntentReceiver rd = null;
        if (receiver != null) {
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    // 如果scheduler(调度器)为null,则使用主线程的Handler
                    scheduler = mMainThread.getHandler();
                }
                // 创建一个ReceiverDispatcher对象,用于调度广播
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
                if (scheduler == null) {
                    // 如果scheduler为null,则使用主线程的Handler
                    scheduler = mMainThread.getHandler();
                }
                // 创建一个LoadedApk.ReceiverDispatcher对象,用于调度广播
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
            // 调用AMS的registerReceiver方法注册广播接收器
            return ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission, userId);
        } catch (RemoteException e) {
            return null;
        }
    }

这里准备一个IIntentReceiver对象,调用ActivityManagerNative.getDefault().registerReceiver,实际上最终就是调用到AMS的registerReceiver方法中。这一部分参考binder系列文章即可,有了或者基础分析起来就较为简单了。系列文章链接为:专题分纲目录 android 系统核心机制 binder,尤其是这2篇偏实操的:

android 系统核心机制binder(11)binder java层 TestServer分析

android 系统核心机制binder(12)binder java层 TestClient 分析

2.1.2 从activity.unregisterReceiver到AMS.unregisterReceiver

activity.unregisterReceiver方法是从Context中的unregisterReceiver开始调用的,接口代码实现如下:

java 复制代码
//Context
public abstract void unregisterReceiver(BroadcastReceiver receiver);

其真正的实现是在ContextImpl中,代码实现如下:

java 复制代码
//ContextImpl
    @Override
    public void unregisterReceiver(BroadcastReceiver receiver) {
        if (mPackageInfo != null) {
            IIntentReceiver rd = mPackageInfo.forgetReceiverDispatcher(
                    getOuterContext(), receiver);
            try {
                ActivityManagerNative.getDefault().unregisterReceiver(rd);
            } catch (RemoteException e) {
            }
        } else {
            throw new RuntimeException("Not supported in system context");
        }
    }

这里直接调用到ActivityManagerNative.getDefault().unregisterReceiver,实际上最终就是调用到AMS的unregisterReceiver方法中。其中过程参考2.1节即可。

2.2 发送普通/有序广播流程

2.2.1 从activity.sendBroadcast到AMS.broadcastIntent

activity.sendBroadcast方法是从Context中的sendBroadcast开始调用的,接口代码实现如下:

java 复制代码
//Context
public abstract void sendBroadcast(Intent intent);

public abstract void sendBroadcast(Intent intent,@Nullable String receiverPermission);

public abstract void sendBroadcast(Intent intent,String receiverPermission, int appOp);

其真正的实现是在ContextImpl中,代码实现如下:

java 复制代码
//ContextImpl
    @Override
    public void sendBroadcast(Intent intent) {
        // 如果这个方法是从系统进程调用的,则发出警告。
        // 说明:这是为了限制系统进程滥用发送广播的能力,因为这可能会影响到整个系统的稳定性。
        warnIfCallingFromSystemProcess();
        // 确定Intent的类型,并使用getContentResolver()进行解析。
        // 说明:这一步是为了确保Intent中的数据类型是正确的,并且可以被系统正确理解。
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            // 准备Intent,使其可以离开当前进程。
            // 说明:这是为了确保Intent在发送给其他应用或组件时,包含所有必要的信息,并且是安全的。
            intent.prepareToLeaveProcess();
            // 调用AMS的broadcastIntent方法发送广播
            ActivityManagerNative.getDefault().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false,
                getUserId());
        } catch (RemoteException e) {
        }
    }

    //解读同上,内容逻辑基本一致,仅传递参数不同
    @Override
    public void sendBroadcast(Intent intent, String receiverPermission) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            intent.prepareToLeaveProcess();
            ActivityManagerNative.getDefault().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE,
                false, false, getUserId());
        } catch (RemoteException e) {
        }
    }

    //解读同上,内容逻辑基本一致,仅传递参数不同
    @Override
    public void sendBroadcast(Intent intent, String receiverPermission, int appOp) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            intent.prepareToLeaveProcess();
            ActivityManagerNative.getDefault().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, receiverPermission, appOp, false, false,
                getUserId());
        } catch (RemoteException e) {
        }
    }

这里直接调用到ActivityManagerNative.getDefault().broadcastIntent,实际上最终就是调用到AMS的broadcastIntent方法中。其中过程参考2.1节即可。

2.2.2 从activity.sendOrderedBroadcast到AMS.broadcastIntent

activity.sendOrderedBroadcast方法是从Context中的sendOrderedBroadcast开始调用的,接口代码实现如下:

java 复制代码
//Context
    public abstract void sendOrderedBroadcast(Intent intent,@Nullable String receiverPermission);

    public abstract void sendOrderedBroadcast(@NonNull Intent intent,
            @Nullable String receiverPermission, BroadcastReceiver resultReceiver,
            @Nullable Handler scheduler, int initialCode, @Nullable String initialData,
            @Nullable Bundle initialExtras);

    public abstract void sendOrderedBroadcast(Intent intent,
            String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
            Handler scheduler, int initialCode, String initialData,
            Bundle initialExtras);

其真正的实现是在ContextImpl中,代码实现如下:

java 复制代码
//ContextImpl
    //解读同sendBroadcast,内容逻辑基本一致,仅传递参数不同
    @Override
    public void sendOrderedBroadcast(Intent intent,
            String receiverPermission) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            intent.prepareToLeaveProcess();
            ActivityManagerNative.getDefault().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, receiverPermission, AppOpsManager.OP_NONE, true, false,
                getUserId());
        } catch (RemoteException e) {
        }
    }
    //sendOrderedBroadcast 关键流程:step1
    @Override
    public void sendOrderedBroadcast(Intent intent,
            String receiverPermission, BroadcastReceiver resultReceiver,
            Handler scheduler, int initialCode, String initialData,
            Bundle initialExtras) {
        sendOrderedBroadcast(intent, receiverPermission, AppOpsManager.OP_NONE,
                resultReceiver, scheduler, initialCode, initialData, initialExtras);
    }
    //sendOrderedBroadcast 关键流程:step2
    @Override
    public void sendOrderedBroadcast(Intent intent,
            String receiverPermission, int appOp, BroadcastReceiver resultReceiver,
            Handler scheduler, int initialCode, String initialData,
            Bundle initialExtras) {
        warnIfCallingFromSystemProcess();

        // 初始化IIntentReceiver,用于接收广播结果
        IIntentReceiver rd = null;
        if (resultReceiver != null) {
            // 如果提供了resultReceiver,则需要创建一个IIntentReceiver对象
            if (mPackageInfo != null) {
                // 如果mPackageInfo不为空,使用它来创建ReceiverDispatcher
                if (scheduler == null) {
                    // 如果scheduler为null,则使用主线程的Handler
                    scheduler = mMainThread.getHandler();
                }
                rd = mPackageInfo.getReceiverDispatcher(
                    resultReceiver, getOuterContext(), scheduler,
                    mMainThread.getInstrumentation(), false);
            } else {
                // 如果mPackageInfo为空,使用LoadedApk来创建ReceiverDispatcher
                if (scheduler == null) {
                    // 如果scheduler为null,则使用主线程的Handler
                    scheduler = mMainThread.getHandler();
                }
                rd = new LoadedApk.ReceiverDispatcher(
                        resultReceiver, getOuterContext(), scheduler, null, false).getIIntentReceiver();
            }
        }

        // 解析Intent的类型
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());

        try {
            intent.prepareToLeaveProcess();
            
            // 调用ActivityManagerService的broadcastIntent方法发送有序广播
            ActivityManagerNative.getDefault().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, rd,
                initialCode, initialData, initialExtras, receiverPermission, appOp,
                    true, false, getUserId());
        } catch (RemoteException e) {
        }
    }

这里直接调用到ActivityManagerNative.getDefault().broadcastIntent,实际上最终就是调用到AMS的broadcastIntent方法中。其中过程参考2.1节即可。

从这里也可以看到,对于有序广播的发送,最终和普通广播调用的broadcastIntent方法是一致的。只不过过程中的参数不同。

相关推荐
Henry_He3 小时前
桌面列表小部件不能点击的问题分析
android
工程师老罗3 小时前
Android笔试面试题AI答之Android基础(1)
android
qq_397562314 小时前
android studio更改应用图片,和应用名字。
android·ide·android studio
峥嵘life4 小时前
Android Studio版本升级那些事
android·ide·android studio
新手上路狂踩坑4 小时前
Android Studio的笔记--BusyBox相关
android·linux·笔记·android studio·busybox
TroubleMaker7 小时前
OkHttp源码学习之retryOnConnectionFailure属性
android·java·okhttp
叶羽西9 小时前
Android Studio IDE环境配置
android·ide·android studio
发飙的蜗牛'9 小时前
23种设计模式
android·java·设计模式
花追雨18 小时前
Android -- 双屏异显之方法一
android·双屏异显
小趴菜822718 小时前
安卓 自定义矢量图片控件 - 支持属性修改矢量图路径颜色
android