Android R 新特性分析及适配指南

简介: Android R(Android 11 API 30)于2020年9月9日正式发布,随国内各终端厂商在售Android设备的版本更新升级,应用软件对Android R 版本的兼容适配已迫在眉睫。

Android R(Android 11 API 30)于2020年9月9日正式发布,随国内各终端厂商在售Android设备的版本更新升级,应用软件对Android R 版本的兼容适配已迫在眉睫。

对于Android R的新特性,这里按照以下几个方面进行了归纳:分区存储、权限、隐私、性能、安全

官方文档描述:https://developer.android.google.cn/about/versions/11

## 一、分区存储

从Android 10(API 29)开始,Android默认开启分区存储功能,不过Android 10 可通过增加android:requestLegacyExternalStorage="true"配置停用分区存储
从Android 11(API 30)开始,强制执行分区存储,对于Android 11及以上设备,android:requestLegacyExternalStorage="true"配置将不再有效。

Android 11 分区存储官方描述:
https://developer.android.google.cn/training/data-storage#scoped-storage
Android 10 默认开启分区存储:
https://xiaxl.blog.csdn.net/article/details/103125117

1.1、访问目录

开启分区存储后,应用默认情况下只能访问应用专属目录(内部存储、外部存储应用专属目录),以及本应用所创建的特定类型的媒体文件

  • 应用专属目录

包括内部存储外部存储专属目录(若应用包名com.xiaxl.demo):
/data/data/com.xiaxl.demo/files,
/sdcard/Android/data/com.xiaxl.demo/files
分别采用以下API进行访问:
File appFile = new File(context.getFilesDir(), filename);
File appExternalFile = new File(context.getExternalFilesDir(), filename);

  • 共享存储目录

包括媒体、文档和其他文件。例如DCIM、Pictures、Movies、Download等目录;
注:
Android 10(Android Q)中共享存储目录使用MediaStore API访问;
Android 11(Android R)中共享存储目录支持MediaStore API与File API访问。
为保证应用在Android 10、Android 11设备中,使用File API对共享存储目录具有相同的文件访问权限。建议在应用 AndroidManifest配置文件中,增加requestLegacyExternalStorage="true"标识,以关闭Android 10设备上的分区存储功能,使分区存储只对Android 11以上设备生效

1.2、访问所需权限

  • 应用专属目录

应用专属目录(内部存储外部存储专属目录)的读写,Android 4.4以上设备不需要任何权限;

  • 共享存储目录

共享存储路径的读写,需要READ_EXTERNAL_STORAGEWRITE_EXTERNAL_STORAGE 权限;

文件路径的访问权限

Android 11以上设备中,如果您的应用再次请求READ_EXTERNAL_STORAGE权限时,动态权限申请弹窗将变化为“您的应用正在请求访问照片和媒体”

您的应用正在请求访问照片和媒体

文件媒体访问 官方描述:
https://developer.android.google.cn/training/data-storage#scoped-storage

1.3、共享文件

如果需要与其他应用共享单个文件或应用数据,可以使用API:

  • FileProvider(分享自己的一个或多个文件)

如果应用需要将自己的一个或多个文件提供给其他应用,安全的做法是向接收方应用发送文件的内容 URI,并授予对该 URI 的临时访问权限。
Android FileProvider 组件提供了 getUriForFile() 方法,用于生成文件的内容 URI

  • ContentProvider(获取替他应用提供的数据)
    如果您需要向其他应用提供数据,可以使用ContentProvider
    ContentProvider是一种标准接口,可将一个进程中的数据与另一个进程中运行的代码进行连。

    ContentProvider管理存储空间

Android 11 共享文件官方描述:
https://developer.android.google.cn/training/data-storage#scoped-storage

1.4、所有文件的访问权限

有一些应用需要获取所有文件的访问权限,例如:文件管理器软件。
获取所有文件的访问权限,可申请MANAGE_EXTERNAL_STORAGE权限。

// 权限配置
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

// 是否拥有MANAGE_EXTERNAL_STORAGE权限判断
Environment.isExternalStorageManager();

// 跳转到设置页,请求用户授权
Intent intent = new Intent();
intent.setAction(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
startActivity(intent);

MANAGE_EXTERNAL_STORAGE相关官方描述:
https://developer.android.google.cn/training/data-storage/manage-all-files

二、权限

Android 11 中对权限进行了如下更改:

  • 新增 READ_PHONE_NUMBERS权限,获取手机号码;
  • 后台访问位置权限调整;
  • 用户多次针对某项特定的权限请求拒绝,表示用户希望不再询问
  • 应用长时间未使用,系统会自动重置用户已授予敏感权限
  • 针对位置、麦克风、摄像头授权弹窗新增仅限这一次授权按钮;
  • SYSTEM_ALERT_WINDOW 权限授权方式改变为系统自动授权;

参考 Android 11 权限更新官方文档:
https://developer.android.google.cn/about/versions/11/privacy/permissions#one-time

2.1、新增 READ_PHONE_NUMBERS 权限

当应用的 targetSdkVersion>=30 时,使用以下API获取手机号码时,需要申请READ_PHONE_NUMBERS权限,而不再是READ_PHONE_STATE 权限。

  • TelephonyManager 类和 TelecomManager 类中的 getLine1Number() 方法。
  • TelephonyManager 类中不受支持的 getMsisdn() 方法。

在Android 10及之前的设备,可以继续使用READ_PHONE_STATE获取手机号;
对Android11及以上设备,需获取READ_PHONE_NUMBERS权限,才能获取手机号;

<manifest>
    <!-- 仅在Android 10及以下设备获取READ_PHONE_STATE权限,以获取终端手机号码-->
    <uses-permission android:name="READ_PHONE_STATE"
                     android:maxSdkVersion="29" />
    <!-- Android 11及以上设备获取READ_PHONE_NUMBERS权限,以获取终端手机号码-->
    <uses-permission android:name="READ_PHONE_NUMBERS" />
</manifest>

对于READ_PHONE_STATE权限

  • Android 10 开始普通应用已经不能再读取设备的硬件ID信息;

相关信息参考 https://xiaxl.blog.csdn.net/article/details/103125117

  • Android 11 开始获取手机号相关API更换为READ_PHONE_NUMBERS权限;

READ_PHONE_NUMBERS权限官方API描述:
https://developer.android.google.cn/reference/android/Manifest.permission#READ_PHONE_NUMBERS

2.2、后台访问位置权限调整

  • 在Android10设备上,同时申请前台、后台位置权限时,并在用户选择始终允许后,才能获得后台位置权限。
  • 在Android11设备上,对于targetSdkVersion<=29(Android 10)的应用,同时申请前台、后台位置权限时,对话框不再提示始终允许字样,而是提供了位置权限的设置入口,需要用户在设置页面选择始终允许才能获得后台位置权限。
  • 在Android11设备上,对于targetSdkVersion=30(Android 11)的应用,同时申请前台、后台位置权限时,系统会忽略该请求,无任何响应(需首先获取前台位置权限,再次申请后台位置权限)。
  • 在Android11设备上,对于targetSdkVersion=30(Android 11)的应用,先申请前台位置权限,后申请后台位置权限

后台访问位置权限 官方描述:
https://developer.android.google.cn/training/location/background

a、Android10设备

在Android10设备上,同时申请前台、后台位置权限时,并在用户选择始终允许后,才能获得后台位置权限。

// 在Android10设备上,同时 申请前台、后台位置权限
ActivityCompat.requestPermissions(this,
    new String[]{
        Manifest.permission.ACCESS_COARSE_LOCATION,
        Manifest.permission.ACCESS_BACKGROUND_LOCATION}, 101);

在Android10设备上,同时 申请前台、后台位置权限

b、Android11设备 targetSdkVersion<=29

在Android11设备上,对于targetSdkVersion<=29(Android 10)的应用,同时申请前台、后台位置权限时,对话框不再提示始终允许字样,而是提供了位置权限的设置入口,需要用户在设置页面选择始终允许才能获得后台位置权限。

// 在Android11设备上,targetSdkVersion<=29的应用,同时 申请前台、后台位置权限
ActivityCompat.requestPermissions(this,
    new String[]{
        Manifest.permission.ACCESS_COARSE_LOCATION,
        Manifest.permission.ACCESS_BACKGROUND_LOCATION}, 101);

Android11设备上targetSdkVersion&lt;=29的应用,申请前台、后台位置权限

c、Android11设备 targetSdkVersion=30 同时申请前台、后台位置权限

  • 在Android11设备上,对于targetSdkVersion=30(Android 11)的应用,同时申请前台、后台位置权限时,系统会忽略该请求,无任何响应(需首先获取前台位置权限,再次申请后台位置权限)。
// 在Android11设备上,targetSdkVersion=30的应用,同时 申请前台、后台位置权限
// 请求无反应,此为错误写法
ActivityCompat.requestPermissions(this,
    new String[]{
        Manifest.permission.ACCESS_COARSE_LOCATION,
        Manifest.permission.ACCESS_BACKGROUND_LOCATION}, 101);

d、Android11设备 targetSdkVersion=30 依次申请前台、后台位置权限

在Android11设备上,对于targetSdkVersion=30(Android 11)的应用,先申请前台位置权限,后申请后台位置权限

// 在Android11设备上,targetSdkVersion=30的应用,申请前台位置权限
ActivityCompat.requestPermissions(this,
    new String[]{
        Manifest.permission.ACCESS_COARSE_LOCATION}, 101);

Android11设备上,targetSdkVersion=30的应用,申请前台位置权限

Android11设备上,targetSdkVersion=30的应用,申请后台位置权限,直接跳转到设置页面。

// 在Android11设备上,targetSdkVersion=30的应用,申请后台位置权限
ActivityCompat.requestPermissions(this,
    new String[]{
        Manifest.permission.ACCESS_BACKGROUND_LOCATION}, 101);

Android11设备上,targetSdkVersion=30的应用,申请后台位置权限

2.3、用户多次针对某项特定的权限请求拒绝

在 Android 11 中,用户多次针对某项特定的权限请求点击了拒绝,那么应用再次请求该项权限时,用户将不会看到系统权限弹窗,该操作表示用户希望不再询问

2.4、长时间未使用,自动重置已授予敏感权限

在 Android 11 中,当targetSdkVersion>=30时,应用在一段时间内未使用,系统会通过自动重置用户已授予应用的运行时敏感权限来保护用户数据;

2.5、新增“仅限这一次”授权按钮

摄像头、位置、麦克风 新增临时访问权限

从 Android 11(API 级别 30)开始,当应用请求与位置、麦克风、摄像头相关权限时,面向用户的授权对话框会包含仅限这一次选项;如果用户在对话框中选择仅限这一次,系统会向应用授予临时的单次授权。

单次授权

权限申请API使用方式不变:

private void showCameraPreview() {
    // 判断是否拥有Camera权限
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
            == PackageManager.PERMISSION_GRANTED) {
        // 进入Camera页面
        // startCamera();
    } else {
        // 请求Camera权限
        requestCameraPermission();
    }
}

private void requestCameraPermission() {
    // 判断Camera权限,之前是否已被用户"拒绝"
    if (ActivityCompat.shouldShowRequestPermissionRationale(this,
            Manifest.permission.CAMERA)) {
        // 弹窗告诉用户,为什么需要Camera权限
        Snackbar.make(mLayout, R.string.camera_access_required,
                Snackbar.LENGTH_INDEFINITE).setAction(R.string.ok, new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                // 请求Camera权限
                ActivityCompat.requestPermissions(MainActivity.this,
                        new String[]{Manifest.permission.CAMERA},
                        PERMISSION_REQUEST_CAMERA);
            }
        }).show();

    } else {
        // 请求Camera权限
        ActivityCompat.requestPermissions(this,
                new String[]{Manifest.permission.CAMERA}, PERMISSION_REQUEST_CAMERA);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    if (requestCode == PERMISSION_REQUEST_CAMERA) {
        // 用户授权Camera(用户选择"使用使用时允许"、"仅这一次允许")
        if (grantResults.length == 1
                && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            // Permission has been granted. Start camera preview Activity.
            Snackbar.make(mLayout, R.string.camera_permission_granted,
                    Snackbar.LENGTH_SHORT)
                    .show();
            startCamera();
        }
        // 用户选择"拒绝"
        else {
            // Permission request was denied.
            Snackbar.make(mLayout, R.string.camera_permission_denied,
                    Snackbar.LENGTH_SHORT)
                    .show();
        }
    }
}

源码参考:
https://github.com/android/permissions-samples/tree/main/RuntimePermissionsBasic

2.6、SYSTEM_ALERT_WINDOW 权限授权方式

在 Android 11 中,SYSTEM_ALERT_WINDOW 权限授权方式更改为:根据请求自动向某些应用授予 SYSTEM_ALERT_WINDOW 权限

  • 系统会自动向具有 ROLE_CALL_SCREENING 且请求 SYSTEM_ALERT_WINDOW 的所有应用授予该权限。如果应用失去 ROLE_CALL_SCREENING,就会失去该权限。
    ROLE_CALL_SCREENINGRoleManager中的常量类,多用于通知用户将我们的应用替换掉手机自带的预搭载应用(短信、电话拨号);
  • 系统会自动向通过 MediaProjection 截取屏幕且请求 SYSTEM_ALERT_WINDOW 的所有应用授予该权限,除非用户已明确拒绝向应用授予该权限。当应用停止截取屏幕时,就会失去该权限。此用例主要用于游戏直播应用。

SYSTEM_ALERT_WINDOW权限 官方描述:
https://developer.android.google.cn/about/versions/11/privacy/permissions#system-alert

三、隐私保护

主要更改涉及以下几个方面:

  • 软件包可见性:获取其他应用信息需在AndroidManifest中增加<queries>标签;
  • 前台服务:访问位置信息、摄像头、麦克风限制;
  • 永久 SIM 卡标识符 ICCID 获取受限;
  • 新增AppOpsManager.OnOpNotedCallback监听危险权限的调用,从而保护用户的私密数据;

这样对于第三方依赖库的权限使用申请可以做一个监控

3.1、软件包可见性

  • 在 Android 11 及更高版本设备中,当应用的 targetSdkVersion>=30 时,如果应用希望获取其他应用的信息(比如:包名、软件名称),原有方式将无法获取到。
  • 如需获取其他应用信息,需要在AndroidManifest中增加<queries>元素标签,告知系统希望获取哪些应用的信息或者哪一类应用的信息。
  • 如果需要获取所有应用的信息(比如:Launcher应用、设备管理器应用):这种情况只需要在AndroidManifest中添加QUERY_ALL_PACKAGES权限即可。
    QUERY_ALL_PACKAGES权限为普通权限,不需要进行动态申请。但提交应用市场后,应用市场可能会进行审核

    软件包可见性 官方描述:

https://developer.android.google.cn/about/versions/11/privacy/package-visibility

 <manifest package="com.xiaxl.myapp">

    // 1、若知道具体应用的包名
    <queries>
        <package android:name="com.xiaxl.otherapp01" />
        <package android:name="com.xiaxl.otherapp01" />
    </queries>
    // 2、不知道包名,但想知道某一类App的应用信息
    <queries>
        <intent>
            <action android:name="android.intent.action.SEND" />
            <data android:mimeType="image/jpeg" />
        </intent>
    </queries>    
</manifest>

3.2、前台服务:访问位置信息、摄像头、麦克风限制

当应用的 targetSdkVersion>=30 时,前台服务访问位置信息、摄像头、麦克风时,需添加foregroundServiceType

<manifest>
    // 前台服务访问:位置信息、摄像头、麦克风
    <service
        android:foregroundServiceType="location|camera|microphone" />
</manifest>

前台服务 官方描述:
https://developer.android.google.cn/about/versions/11/privacy/foreground-services

3.3、永久 SIM 卡标识符 ICCID 获取受限

在 Android 11 及更高版本中,使用 SubscriptionInfo.getIccId() 方法访问不可重置的 ICCID 受到限制。

SubscriptionInfo.getIccId() 方法会返回一个非null的空字符串

如需唯一标识设备上安装的 SIM 卡,请改用 getSubscriptionId() 方法。SubscriptionId会提供一个索引值,用于唯一识别已安装的 SIM 卡(包括实体 SIM 卡和电子 SIM 卡),除非设备恢复出厂设置,否则此标识符的值对于给定 SIM 卡是保持不变的。

3.4、监听危险权限的调用

Android 11新增AppOpsManager.OnOpNotedCallback为开发者提供对应用危险权限的使用监听,从而保护用户的私密数据
当应用以及应用的依赖包中,申请某项危险权限时,AppOpsManager.OnOpNotedCallback的对应回调方法将会被调用,从而打印申请的权限对应的API调用栈

举例:
使用位置权限获取位置信息时,将会回调AppOpsManager.OnOpNotedCallback中的onNoted方法,并打印使用的权限对应的API调用栈

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    //
    AppOpsManager.OnOpNotedCallback appOpsCallback =
            new AppOpsManager.OnOpNotedCallback() {
                private void logPrivateDataAccess(String opCode, String trace) {
                    Log.i("xiaxl: ", "opCode: " + opCode + "\n trace: " + trace);
                }

                @Override
                public void onNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
                    Log.i("xiaxl: ", "---onNoted---");
                    logPrivateDataAccess(syncNotedAppOp.getOp(),
                            Arrays.toString(new Throwable().getStackTrace()));
                }

                @Override
                public void onSelfNoted(@NonNull SyncNotedAppOp syncNotedAppOp) {
                    Log.i("xiaxl: ", "---onSelfNoted---");
                    logPrivateDataAccess(syncNotedAppOp.getOp(),
                            Arrays.toString(new Throwable().getStackTrace()));
                }

                @Override
                public void onAsyncNoted(@NonNull AsyncNotedAppOp asyncNotedAppOp) {
                    Log.i("xiaxl: ", "---onAsyncNoted---");
                    logPrivateDataAccess(asyncNotedAppOp.getOp(),
                            asyncNotedAppOp.getMessage());
                }
            };

    AppOpsManager appOpsManager = getSystemService(AppOpsManager.class);
    if (appOpsManager != null) {
        appOpsManager.setOnOpNotedCallback(getMainExecutor(), appOpsCallback);
    }
}

public void getLocation() {
    // 创建归因
    Context attributionContext = createAttributionContext("shareLocation");
    // 获取位置信息
    LocationManager locationManager =
            attributionContext.getSystemService(LocationManager.class);
    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
            && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
        return;
    }
    Location lastKnownLocation = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
}

打印日志如下:

---onNoted---
opCode: android:coarse_location
trace: 
[com.xiaxl.android_test.MainActivity$1.onNoted(MainActivity.java:42), 
 android.app.AppOpsManager.readAndLogNotedAppops(AppOpsManager.java:8204), 
 android.os.Parcel.readExceptionCode(Parcel.java:2304), 
 android.os.Parcel.readException(Parcel.java:2279), 
 android.location.ILocationManager$Stub$Proxy.getLastLocation(ILocationManager.java:1225),
 android.location.LocationManager.getLastKnownLocation(LocationManager.java:648),
 com.xiaxl.android_test.MainActivity.getLocation(MainActivity.java:87),
 com.xiaxl.android_test.MainActivity$2.onClick(MainActivity.java:70),
 android.view.View.performClick(View.java:7448),
 com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:967),
 android.view.View.performClickInternal(View.java:7425),
 android.view.View.access$3600(View.java:810),
 android.view.View$PerformClick.run(View.java:28305),
 android.os.Handler.handleCallback(Handler.java:938),
 android.os.Handler.dispatchMessage(Handler.java:99),
 android.os.Looper.loop(Looper.java:223),
 android.app.ActivityThread.main(ActivityThread.java:7656),
 java.lang.reflect.Method.invoke(Native Method),
 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592),
 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)]
 

从以上日志可以看出,当应用申请ACCESS_COARSE_LOCATION权限并获取位置信息时,打印了应用申请的权限对应的API调用栈

AppOpsManager 相关官方描述:
https://developer.android.google.cn/guide/topics/data/audit-access#audit-by-attribution-tag

四、性能

  • JobScheduler使用频率进行限制

4.1、JobScheduler使用频率进行限制

Android 11 为对JobScheduler使用频率进行一定限制。
对于 debuggable 清单属性设置为 true 的应用,过多的调用 JobScheduler API 将返回 RESULT_FAILURE

JobScheduler主要用于在未来某个时间下满足一定条件时触发执行某项任务,例如:当设备在空闲状态, 并且使用wifi时, 自动下载Apk
JobScheduler典型的使用举例如下:

 JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);  
ComponentName jobService = new ComponentName(this, MyJobService.class);

//任务Id等于123
JobInfo jobInfo = new JobInfo.Builder(123, jobService) 
     // 任务最少延迟时间  
     .setMinimumLatency(5000)
     // 任务deadline,当到期没达到指定条件也会开始执行  
     .setOverrideDeadline(60000)
     // 网络条件,网络无需付费时执行
     .setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
     // 是否充电  
     .setRequiresCharging(true)
     // 是否在空闲时执行
     .setRequiresDeviceIdle(true)
     // 设备重启后是否继续执行
     .setPersisted(true) 
     // 设置退避/重试策略
     .setBackoffCriteria(3000,JobInfo.BACKOFF_POLICY_LINEAR) 
     .build();  
scheduler.schedule(jobInfo);

官方描述参考:
https://developer.android.google.cn/about/versions/11/behavior-changes-all

官方Demo参考:
https://github.com/googlearchive/android-JobScheduler

## 五、安全

  • 非 SDK 接口限制

5.1、非 SDK 接口限制

官方从 Android 9(API 级别 28)开始,对应用使用的非 SDK 接口实施了限制。
如果你的APP通过引用非 SDK 接口或尝试使用反射或 JNI 来获取句柄,这些限制就会起作用。官方给出的解释是为了提升用户体验、降低应用崩溃风险

a、非SDK接口检测工具

官方给出了一个检测工具,下载地址:veridex

veridex使用方法:

appcompat.sh --dex-file=apk.apk

veridex检测截图

b、blacklist、greylist、greylist-max-o、greylist-max-p含义

以上截图中,blacklist、greylist、greylist-max-o、greylist-max-p含义如下:

  • blacklist 黑名单:禁止使用的非SDK接口,运行时直接Crash(因此必须解决)
  • greylist 灰名单:即当前版本仍能使用的非SDK接口,但在下一版本中可能变成被限制的非SDK接口
  • greylist-max-o: 在targetSDK<=O中能使用,但是在targetSDK>=P中被禁止使用的非SDK接口
  • greylist-max-p: 在targetSDK<=P中能使用,但是在targetSDK>=Q中被禁止使用的非SDK接口

非SDK接口限制 官方描述:
https://developer.android.google.cn/about/versions/11/non-sdk-11

========== THE END ==========

文章首发于公众号”CODING技术小馆“,如果文章对您有帮助,可关注我的公众号。
文章首发于公众号”CODING技术小馆“,如果文章对您有帮助,可关注我的公众号。
文章首发于公众号”CODING技术小馆“,如果文章对您有帮助,可关注我的公众号。

目录
相关文章
|
17天前
|
安全 Android开发 数据安全/隐私保护
深入探讨iOS与Android系统安全性对比分析
在移动操作系统领域,iOS和Android无疑是两大巨头。本文从技术角度出发,对这两个系统的架构、安全机制以及用户隐私保护等方面进行了详细的比较分析。通过深入探讨,我们旨在揭示两个系统在安全性方面的差异,并为用户提供一些实用的安全建议。
|
2月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境对比分析
在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统无疑是主角。它们各自拥有独特的特点和优势,为开发者提供了不同的开发环境和工具。本文将深入浅出地探讨安卓和iOS开发环境的主要差异,包括开发工具、编程语言、用户界面设计、性能优化以及市场覆盖等方面,旨在帮助初学者更好地理解两大平台的开发特点,并为他们选择合适的开发路径提供参考。通过比较分析,我们将揭示不同环境下的开发实践,以及如何根据项目需求和目标受众来选择最合适的开发平台。
51 2
|
25天前
|
缓存 Java Shell
Android 系统缓存扫描与清理方法分析
Android 系统缓存从原理探索到实现。
49 15
Android 系统缓存扫描与清理方法分析
|
1月前
|
存储 Linux Android开发
Android底层:通熟易懂分析binder:1.binder准备工作
本文详细介绍了Android Binder机制的准备工作,包括打开Binder驱动、内存映射(mmap)、启动Binder主线程等内容。通过分析系统调用和进程与驱动层的通信,解释了Binder如何实现进程间通信。文章还探讨了Binder主线程的启动流程及其在进程通信中的作用,最后总结了Binder准备工作的调用时机和重要性。
Android底层:通熟易懂分析binder:1.binder准备工作
|
2月前
|
安全 Android开发 数据安全/隐私保护
探索安卓与iOS的安全性差异:技术深度分析与实践建议
本文旨在深入探讨并比较Android和iOS两大移动操作系统在安全性方面的不同之处。通过详细的技术分析,揭示两者在架构设计、权限管理、应用生态及更新机制等方面的安全特性。同时,针对这些差异提出针对性的实践建议,旨在为开发者和用户提供增强移动设备安全性的参考。
139 3
|
1月前
|
开发工具 Android开发 Swift
安卓与iOS开发环境的差异性分析
【10月更文挑战第8天】 本文旨在探讨Android和iOS两大移动操作系统在开发环境上的不同,包括开发语言、工具、平台特性等方面。通过对这些差异性的分析,帮助开发者更好地理解两大平台,以便在项目开发中做出更合适的技术选择。
|
2月前
|
安全 Linux Android开发
探索安卓与iOS的安全性差异:技术深度分析
本文深入探讨了安卓(Android)和iOS两个主流操作系统平台在安全性方面的不同之处。通过比较它们在架构设计、系统更新机制、应用程序生态和隐私保护策略等方面的差异,揭示了每个平台独特的安全优势及潜在风险。此外,文章还讨论了用户在使用这些设备时可以采取的一些最佳实践,以增强个人数据的安全。
|
2月前
|
调度 Android开发 UED
Android经典实战之Android 14前台服务适配
本文介绍了在Android 14中适配前台服务的关键步骤与最佳实践,包括指定服务类型、请求权限、优化用户体验及使用WorkManager等。通过遵循这些指南,确保应用在新系统上顺畅运行并提升用户体验。
199 6
|
3月前
|
Java 开发工具 Android开发
安卓与iOS开发环境对比分析
【8月更文挑战第20天】在移动应用开发的广阔天地中,Android和iOS两大平台各自占据着重要的位置。本文将深入探讨这两种操作系统的开发环境,从编程语言到开发工具,从用户界面设计到性能优化,以及市场趋势对开发者选择的影响。我们旨在为读者提供一个全面的比较视角,帮助理解不同平台的优势与挑战,并为那些站在选择十字路口的开发者提供有价值的参考信息。
|
2月前
|
IDE 开发工具 Android开发
安卓与iOS开发环境对比分析
本文将探讨安卓和iOS这两大移动操作系统在开发环境上的差异,从工具、语言、框架到生态系统等多个角度进行比较。我们将深入了解各自的优势和劣势,并尝试为开发者提供一些实用的建议,以帮助他们根据自己的需求选择最适合的开发平台。
49 1
下一篇
无影云桌面