Android适配全面总结(二)----版本适配

简介: 版权声明:本文为博主原创文章(部分引用他人博文,已加上引用说明),未经博主允许不得转载。https://www.jianshu.com/p/49fa8ebc0105转载请标明出处:https://www.

版权声明:本文为博主原创文章(部分引用他人博文,已加上引用说明),未经博主允许不得转载。https://www.jianshu.com/p/49fa8ebc0105

转载请标明出处:
https://www.jianshu.com/p/49fa8ebc0105
本文出自 AWeiLoveAndroid的博客


上一篇文章讲了 屏幕适配 http://www.jianshu.com/p/7aa34434ad4d
这一篇文章讲一下 版本适配 https://www.jianshu.com/p/49fa8ebc0105
下一篇文章讲一下 ROM适配 https://www.jianshu.com/p/f9c67a4b908e

在我们的开发中,会对不同安卓版本做适配,比如我之前做过的项目中最低兼容到4.4,最高兼容是最新的系统7.1,由于不同版本的系统中部分API版本也不同,我就要对这些API做特殊处理。新的平台有一些API不能使用旧的API,旧的平台也使用不了新的API。所以这就要考验我们开发人员的能力了。我这里简单给出几点我开发中使用过的一些方式,仅供参考:

一、同一个api在不同版本都存在,只是api的一些接口方法有变更。

这种情况是最好处理的,只要对版本号做判断,对应的系统版本用相应的api方法就好了。为了好维护,建议做一个简单的封装。

举例说明如下:

比如Notification在不同版本的兼容,举例如下:

首先打开谷歌官方文档,看看文档里面的一些说明:

Notification官方文档

1.Notification这个类是added in API level 1,一直都有,只是具体某些方法有变更。继续往下看。

2.这个类有个说明,意思是Notification.Builder是新增的一个内部类,用它创建通知更方便。接着往下看。

A class that represents how a persistent notification is to
be presented to the user using the NotificationManager.

The Notification.Builder has been added to make it easier
to construct Notifications.

3.Public constructors公共的构造方法,其中有3个参数的这个在api 11过时,它被Notification.Builder替代了。

Notification(int icon, CharSequence tickerText, long when)

This constructor was deprecated in API level 11. 
Use Notification.Builder instead.

4.常量

  • EXTRA_LARGE_ICON This constant was deprecated in API level 26. Use getLargeIcon(), which supports a wider variety of icon sources.(在API级别26中已弃用。使用getLargeIcon(),它支持更多种图标源。)

  • EXTRA_SMALL_ICON This constant was deprecated in API level 26. Use getSmallIcon(), which supports a wider variety of icon sources.(在API级别26中已弃用。使用getSmallIcon(),它支持更多种图标源。)

  • FLAG_HIGH_PRIORITY This constant was deprecated in API level 16. Use priority with a positive value.(在api16被弃用,请使用正数priority值替代)

  • FLAG_SHOW_LIGHTS This constant was deprecated in API level 26. use shouldShowLights().(在API级别26中已弃用。请使用 shouldShowLights() 替代)

  • PRIORITY_DEFAULT This constant was deprecated in API level 26. use IMPORTANCE_DEFAULT instead.(在API级别26中已弃用。请使用 IMPORTANCE_DEFAULT 替代)

  • PRIORITY_HIGH This constant was deprecated in API level 26. use IMPORTANCE_HIGH instead.(在API级别26中已弃用。请使用 IMPORTANCE_HIGH 替代)

  • PRIORITY_LOW This constant was deprecated in API level 26. use IMPORTANCE_LOW instead.(在API级别26中已弃用。请使用 IMPORTANCE_LOW 替代)

  • PRIORITY_MAX This constant was deprecated in API level 26. use IMPORTANCE_HIGH instead.(在API级别26中已弃用。请使用 IMPORTANCE_HIGH 替代)

  • PRIORITY_MIN This constant was deprecated in API level 26. use IMPORTANCE_MIN instead.(在API级别26中已弃用。请使用 IMPORTANCE_MIN 替代)

  • STREAM_DEFAULT This constant was deprecated in API level 21. Use getAudioAttributes() instead.(在API级别21中已弃用。请使用 getAudioAttributes() 替代)

5.字段Fields

  • audioAttributes 在api 26弃用. 使用 getAudioAttributes() 替代.

  • audioStreamType 在api 21弃用. 使用 audioAttributes 替代.

  • defaults 此字段在API 26弃用。使用getSound()shouldShowLights()shouldVibrate()

  • icon 此字段已在API级别26中弃用。使用setSmallIcon(Icon)替代。

  • largeIcon This field was deprecated in API level 23. Use `setLargeIcon(Icon) instead.

  • ledARGB This field was deprecated in API level 26. use `shouldShowLights().

  • ledOffMS This field was deprecated in API level 26. use `shouldShowLights().

  • ledOnMS This field was deprecated in API level 26. use shouldShowLights().

  • priority This field was deprecated in API level 26. use getImportance() instead.

  • sound This field was deprecated in API level 26. use getSound() instead.

  • vibrate This field was deprecated in API level 26. use getVibrationPattern().


二、Android6.0的动态权限介绍

因为Android6.0(API23)开始需要动态申请权限,需要手动申请的权限有8组(短信、电话、联系人、存储、位置、麦克风、日历、相机),共24个,如下所示:

所属权限组 权限
短信 SEND_SMS
短信 RECEIVE_SMS
短信 READ_SMS
短信 RECEIVE_WAP_PUSH
短信 RECEIVE_MMS
电话 READ_PHONE_STATE
电话 CALL_PHONE
电话 READ_CALL_LOG
电话 WRITE_CALL_LOG
电话 ADD_VOICEMAIL
电话 USE_SIP
电话 PROCESS_OUTGOING_CALLS
联系人 READ_CONTACTS
联系人 WRITE_CONTACTS
联系人 GET_ACCOUNTS
存储 READ_EXTERNAL_STORAGE
存储 WRITE_EXTERNAL_STORAGE
位置 ACCESS_FINE_LOCATION
位置 ACCESS_COARSE_LOCATION
麦克风 RECORD_AUDIO
日历 READ_CALENDAR
日历 WRITE_CALENDAR
相机 CAMERA
传感器 BODY_SENSORS

注意:如果应用程序请求在AndroidManifest中列出的危险权限,并且应用程序已经在同一权限组中具有另一个危险权限,系统会立即授予权限,而不会与用户进行任何交互。
例如,如果一个应用程序先前已经请求并被授予READ_CONTACTS权限,然后它请求WRITE_CONTACTS(同属于联系人一组),系统会立即授予该权限,不会再弹出权限授予询问的对话框。


三、Android6.0如何申请动态权限

开发中经常会遇到拍照的权限申请,这里就讲一下如何动态设置拍照权限:

//别忘记在清单文件也加上CAMERA权限
//<uses-permission android:name="android.permission.CAMERA" />

// 定义识别码
public static final int CAMERA_OK = 1;

//动态申请拍照权限
if (Build.VERSION.SDK_INT>22){
   if (ContextCompat.checkSelfPermission(this,Manifest.permission.CAMERA)
        != PackageManager.PERMISSION_GRANTED){
           //先判断有没有权限 ,没有就在这里进行权限的申请
           requestPermissions(new String[]{Manifest.permission.CAMERA}, CAMERA_OK);       
    }else {
            //说明已经获取到摄像头权限了,可以去选择照片或者拍照了。
            toSelectPhotoOrOpenCamera();
    }
}else {
      //这个说明系统版本在6.0之下,不需要动态获取权限,直接去选择照片或者拍照。
      toSelectPhotoOrOpenCamera();
}

//在Activity中重写权限获取方法:

/**
* 权限操作结果处理
*/
@Override
public void onRequestPermissionsResult(int requestCode,
                       String[] permissions, int[] grantResults) {
    switch (requestCode) {
        case CAMERA_OK: 
            if (grantResults.length > 0 && grantResults[0]
                         == PackageManager.PERMISSION_GRANTED) {
                 //用户已授权
                toSelectPhotoOrOpenCamera();
            } else {
                //用户拒绝权限
                ToastUtils.show(this, 
                    "缺少相机权限,暂时无法提供扫描功能,请尝试在设置中打开相机权限!", 
                    Toast.LENGTH_LONG);
            }
            break;
        }
    }
}

四、Android7.0对文件权限进一步升级,提出了新的类FileProvider来获取文件。所以适配的时候一定要注意这一点api的变化。

FileProviderContentProvider的子类,把原来文件共享的 file://uri 换成了 content://uri 。一个Uri允许你获取临时权限去读写文件,当使用含有Uri的Intent,可以使用Intent.setFlags来添加临时权限。

下面来看看调用系统相机拍摄照片有如何变化,大致步骤如下所示

(一)在manifest中添加Provider

<manifest>
    ...
    <application>
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.lzw.demo.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            ...
        </provider>
        ...
    </application>
</manifest>

(二)配置你要获取的文件所在的文件夹 --> 创建一个xml文件,比如file_demo.xml,文件内容如下:

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <files-path name="my_images" path="images/"/>
    ...
</paths>

路径说明:

<files-path name="name" path="path/" />   
   <!--等同于Context.getFilesDir()下面的path文件夹的所有文件--> 

<cache-path name="name" path="path/" />  
   <!--等同于Context.getCacheDir()下面的path文件夹--> 

<external-path name="name" path="path/" /> 
   <!--等同于Environment.getExternalStorageDirectory()下面的path文件夹--> 

<external-files-path name="name" path="path/" /> 
   <!--等同于 Context#getExternalFilesDir(String)下面子文件path文件夹--> 

<external-cache-path name="name" path="path/" /> 
   <!--相当于 Context.getExternalCacheDir()下边的path文件夹--> 

(三)添加路径信息到provier

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="com.lzw.demo.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_demo" />
</provider>

(四)现在可以去拍照了。(由于Android6.0开始要动态申请权限,所以别忘了,这里就不写了,主要讲FileProvider的使用)

//适配7.0的fileprovider,imgfile是图片文件路径
public void TakePhotoAdaption(File imgFile){
    Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    //适配android7.0 手机拍照取uri的处理
    if(Build.VERSION.SDK_INT<24){
        //7.0如果用会Uri.fromFile(XXX)会闪退,所以这里要特别做一个判断。
        //imgfile是图片文件路径
        uri = Uri.fromFile(imgFile);
        cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
    }else{
        //7.0+使用FileProvider.getUriForFile这个api
        uri=FileProvider.getUriForFile(DemoActivity.this,
                "com.lzw.demo.fileprovider",imgFile);
        cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        //添加这一句表示对目标应用临时授权该Uri所代表的文件
        cameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION );
    }
    startActivityForResult(cameraIntent, FLAG_CHOOSE_CAMERA);
}

想看到拍照、选择照片、裁剪等完整流程的描述,可以参考这篇博客 解决安卓7.0拍照,相册选择崩溃的问题(包括压缩图片在内)


五、关于Android7.0相机闪退以及相册获取不到图片问题


六、Android 8.0适配报错:Only fullscreen opaque activities can request orientation解决方案:

出现的原因:绝大多数都是因为我们为了提高用户体验,手动取消App启动白屏或者黑屏的时候,将Splash界面设为了透明,然后这个时候又设置了方向为垂直,从而导致了这个问题。

解决方案:

  • 1.找到你设置透明的Activity,然后在他的theme中将android:windowIsTranslucent改为false

      <item name="android:windowIsTranslucent">false</item>
    
  • 2.再加入下面这行代码就搞定了。

      <item name="android:windowDisablePreview">true</item>
    

这个坑来自于博客: https://www.jianshu.com/p/d0d907754603


七、Android8.0版本更新相关api适配

  • 创建通知渠道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationManager manager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
            NotificationChannel mChannel = new NotificationChannel("channel_01",
                    "消息推送", NotificationManager.IMPORTANCE_DEFAULT);
            manager.createNotificationChannel(mChannel);
        }
  • 创建Notification
Context context = DJApplication.getInstance();
        Notification.Builder builder = new Notification.Builder(context);
        builder.setTicker("开始下载");
        builder.setSmallIcon(R.mipmap.ic_launcher);
        builder.setLargeIcon(BitmapFactory.decodeResource(DJApplication.getInstance().getResources(), 
            R.mipmap.ic_launcher));
        builder.setAutoCancel(true);
        PendingIntent pIntent = PendingIntent.getActivity(context, 0, new Intent(), PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentTitle("下载中");
        builder.setContentIntent(pIntent);
        builder.setContentText(text);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            builder.setChannelId("channel_01");//设置有效的通知渠道 ID,这个ID要和之前创建时候的Channel_ID相同
        }
        manager.notify(1,  builder.build());
  • 安装apk权限

在 Android 8.0 中,安装未知应用权限提高了安装未知来源应用时的安全性。此权限与其他运行时权限一样,会与应用绑定,在安装时进行提示,确保用户授予使用安装来源的权限后,此权限才会提示用户安装应用。在运行 Android 8.0 或更高版本的设备上使用此权限时,恶意下载程序将无法骗取用户安装未获得预先授权的应用,所以我们需要加入安装apk文件的权限。

<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

这个坑来自微信公众号“代码集中营”。



【好消息】我的微信公众号正式开通了,关注一下吧!
关注一下我的公众号吧
相关文章
|
1月前
|
Shell 开发工具 Android开发
android 修改kernel编译版本信息
android 修改kernel编译版本信息
27 0
|
1月前
|
存储 Android开发
Android 高版本 packageManager.getPackageArchiveInfo 总是返回null
Android 高版本 packageManager.getPackageArchiveInfo 总是返回null
25 1
|
1月前
|
Android开发
Android RIL 动态切换 4G 模块适配
Android RIL 动态切换 4G 模块适配
27 0
|
1月前
|
编解码 人工智能 测试技术
安卓适配性策略:确保应用在不同设备上的兼容性
【4月更文挑战第13天】本文探讨了提升安卓应用兼容性的策略,包括理解平台碎片化、设计响应式UI(使用dp单位,考虑横竖屏)、利用Android SDK的兼容工具(支持库、资源限定符)、编写兼容性代码(运行时权限、设备特性检查)以及优化性能以适应低端设备。适配性是安卓开发的关键,通过这些方法可确保应用在多样化设备上提供一致体验。未来,自动化测试和AI将助力应对设备碎片化挑战。
|
1月前
|
Android开发
Android保存图片到相册(适配android 10以下及以上)
Android保存图片到相册(适配android 10以下及以上)
40 1
|
1月前
|
Android开发
Android Uri转File方法(适配android 10以上版本及android 10以下版本)
Android Uri转File方法(适配android 10以上版本及android 10以下版本)
140 0
|
1月前
|
编解码 开发工具 Android开发
Android获取设备各项信息(设备id、ip地址、设备名称、运行商、品牌、型号、分辨率、处理器、国家码、系统语言、网络类型、oaid、android版本、操作系统版本、mac地址、应用程序签名..)2
Android获取设备各项信息(设备id、ip地址、设备名称、运行商、品牌、型号、分辨率、处理器、国家码、系统语言、网络类型、oaid、android版本、操作系统版本、mac地址、应用程序签名..)2
68 2
|
1月前
|
编解码 Android开发
Android获取设备各项信息(设备id、ip地址、设备名称、运行商、品牌、型号、分辨率、处理器、国家码、系统语言、网络类型、oaid、android版本、操作系统版本、mac地址、应用程序签名..)1
Android获取设备各项信息(设备id、ip地址、设备名称、运行商、品牌、型号、分辨率、处理器、国家码、系统语言、网络类型、oaid、android版本、操作系统版本、mac地址、应用程序签名..)
92 1
|
1月前
|
运维 监控 Java
应用研发平台EMAS产品常见问题之安卓构建版本失败如何解决
应用研发平台EMAS(Enterprise Mobile Application Service)是阿里云提供的一个全栈移动应用开发平台,集成了应用开发、测试、部署、监控和运营服务;本合集旨在总结EMAS产品在应用开发和运维过程中的常见问题及解决方案,助力开发者和企业高效解决技术难题,加速移动应用的上线和稳定运行。
|
1月前
|
监控 安全 Android开发
安卓发展历程和主要版本的简要介绍
安卓发展历程和主要版本的简要介绍
46 1