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

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

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


上一篇文章讲了 屏幕适配 //www.greatytc.com/p/7aa34434ad4d
这一篇文章讲一下 版本适配 //www.greatytc.com/p/49fa8ebc0105
下一篇文章讲一下 ROM适配 //www.greatytc.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相机闪退以及相册获取不到图片问题

  • 1、没有动态申请权限,按照上述思路去做就好了。
  • 2、华为手机的一些特殊处理方式,详情参见 ROM适配 //www.greatytc.com/p/f9c67a4b908e

六、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>
    

这个坑来自于博客: //www.greatytc.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" />

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345

推荐阅读更多精彩内容