精华内容
下载资源
问答
  • AndroidX适配教程

    千次阅读 2019-08-14 14:58:21
    与支持库一样,AndroidX与Android操作系统分开提供,并提供跨Android版本的向后兼容性。与支持库不同的是通过AndroidX可以看到实时实现的特性和bug修复,升级个别依赖,不需要对使用的所有其他库...

    AndroidX概述

    AndroidX是Android团队用于在Jetpack中开发,测试,打包,版本和发布库的开源项目 。AndroidX是对原始Android 支持库的重大改进 。与支持库一样,AndroidX与Android操作系统分开提供,并提供跨Android版本的向后兼容性。与支持库不同的是通过AndroidX可以看到实时实现的特性和bug修复,升级个别依赖,不需要对使用的所有其他库进行更新。AndroidX未来将完全取代支持库。此外,AndroidX还包括以下功能:

    AndroidX中的所有软件包都以字符串开头,位于一致的命名空间中androidx。支持库包已映射到相应的androidx.*包中。
    与支持库不同,AndroidX软件包是单独维护和更新的。这些androidx包使用 从版本1.0.0开始的严格语义版本控制。您可以单独更新项目中的AndroidX库。
    所有新的支持库开发都将在AndroidX库中进行。这包括维护原始支持库工件和引入新的Jetpack组件。

    AndroidX带来的依赖库变化

    以下为常用的依赖库,

    Old build artifactAndroidX build artifact

    com.android.support:support-compat

    androidx.core:core:1.0.0+

    com.android.support:appcompat-v7androidx.appcompat:appcompat:1.0.0+
    com.android.support:recyclerview-v7

    androidx.recyclerview:recyclerview:1.0.0+

    com.android.support.constraint:constraint-layout

    androidx.constraintlayout:constraintlayout:1.1.3+

    com.android.support:design

    com.google.android.material:material:1.0.0+

    com.android.support:viewpager

    androidx.viewpager:viewpager:1.0.0+

    com.android.support:support-fragment

    androidx.fragment:fragment:1.0.0+

    com.android.support:multidex

    androidx.multidex:multidex:2.0.1+

    com.android.support:support-v4

    androidx.legacy:legacy-support-v4:1.0.0+

    迁移到AndroidX步骤

    1.首先你的Android Studio版本至少为3.2.0以上,gradle版本4.10以上,以及compileSdkVersion为28以上

    2.修改project目录下 gradle.properties,在里面添加两行代码:

        # 表示使用 androidx
        android.useAndroidX=true
        # 表示将第三方库迁移到 androidx
        android.enableJetifier=true

    3.使用Android Studio的一键迁移的功能Refactor -> Migrate to AndroidX ,然后点击sync

    4.进行完转换后发现代码是一片报红,无法编译运行,此时则只能进行手动修改导包了

    需要检查修改报红的导包和xml,修改后如下:

    5.项目中引用的第三方库进行升级兼容AndroidX,现在主流的三方库都已进行了AndroidX适配,若有些库必须使用并没有适配AndroidX,则只能降低版本使用

    6.从support库迁移至androidx后,若使用了混淆,则必须在混淆文件中添加以下配置:

    -keep class com.google.android.material.** {*;}
    -keep class androidx.** {*;}
    -keep public class * extends androidx.**
    -keep interface androidx.** {*;}
    -dontwarn com.google.android.material.**
    -dontnote com.google.android.material.**
    -dontwarn androidx.**

     

        技术无止境,适配无尽头,最后祝大家适配AndroidX顺利!

          androidx官方文档

    展开全文
  • Android适配全攻略(学习笔记总结)一、为什么要进行屏幕适配某厂商统计如下数据2012年,支持Android的设备共有3997种2013年,支持Android的设备共有11868种2014年,支持Android的设备共有18796种2015年,支持Android...

    Android适配全攻略(学习笔记总结)

    一、为什么要进行屏幕适配

    某厂商统计如下数据

    2012年,支持Android的设备共有3997种

    2013年,支持Android的设备共有11868种

    2014年,支持Android的设备共有18796种

    2015年,支持Android的设备的共有24093种

    二、屏幕适配对象

    我们到底应该对哪些屏幕进行适配

    首先来看下最新的Android设备分辨率(2016年)

    b0c21a5bac70

    进行适配的还是以上的主流的几个分辨率

    三、重要概念

    (1)、什么是屏幕尺寸、屏幕分辨率、屏幕像素密度

    屏幕尺寸

    屏幕尺寸指屏幕的对角线的长度

    单位是英寸,1英寸=2.54厘米

    屏幕分辨率

    屏幕分辨率是指横纵向撒花姑娘的像素点数

    单位是px ,1px=1个像素点

    一般以纵像素横向像素,如19201080

    屏幕像素密度

    屏幕像素密度是指每英寸上的像素点数

    单位是dpi,即“dot per inch”的缩写

    屏幕像素密度与屏幕尺寸和屏幕分辨率有关

    如Nexus 5

    屏幕4.95

    1920*1080

    445dpi 19202+10802 进行开方 然后除以对角线长度4.95 等于445

    (2)、什么是dp,dip,dpi,sp、px ?之间的关系是什么?

    px

    构成图像的最小单位

    dp 、dip

    Density Independent Pixels的缩写,即密度无关像素

    以160dpi为基准,1dpi = 1px

    sp

    即Scale-Independent Pixels

    可以根据文字大小首选项进行缩放

    绝大部分用于文字的大小推荐12sp、14sp、18sp、22sp

    (3)、什么是mdpi、hdpi、xdpi、xxdpi、xxxdpi?如何计算和区分?

    在新创建项目的时候,会自动创建不同的drawable或者mipmap文件夹(在不同像素密度上提供不同的图片)

    或者不同的value下面(在不同像素密度提供不同的值)dimens.xml(这个放在不同的values下面)

    名称

    像素密度范围

    mdpi

    120dpi-160dpi

    hdpi

    160dpi-240dpi

    xhdpi

    240dpi-320dpi

    xxhdpi

    320dpi-480dpi

    xxxhdpi

    480dpi-640dpi

    四、解决方案

    支持各种屏幕尺寸

    使用wrap_content、match_parent、weight

    android:text="button1"

    android:layout_width="0dp"

    android:layout_weight="1"

    android:layout_height="wrap_content" />

    android:text="button2"

    android:layout_width="0dp"

    android:layout_weight="2"

    android:layout_height="wrap_content" />

    b0c21a5bac70

    如果是下面的match_parent的情况

    android:text="button1"

    android:layout_width="match_parent"

    android:layout_weight="1"

    android:layout_height="wrap_content" />

    android:text="button2"

    android:layout_width="match_parent"

    android:layout_weight="2"

    android:layout_height="wrap_content" />

    b0c21a5bac70

    这里就介绍一下:

    weight

    计算出的宽度 = 原来的宽度 + 剩余空间所占百分比宽度

    假设屏幕的宽度是L

    BUTTON1为例 (第二种的0dp宽度)

    1/3L --> 0 + (L) * 1/3 = 1/3L

    BUTTON1为例 (第二种的match_parent)

    2/3L --> L + (L-2L) * 1/3 = 2/3L

    通过以上的算法,就知道了为啥上面的两个图的显示大小了,同时高度也是同样的适用。

    使用相对布局,禁用绝对布局

    一般使用线性布局LinearLayout、相对布局RelativeLayout、帧布局FrameLayout,不使用绝对布局AbsoluteLayout

    使用限定符

    使用尺寸限定符(android3.2之前)

    res/layout/main.xml 单面板

    android:orientation="vertical"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

    android:layout_height="fill_parent"

    android:name="com.example.android.newsreader.HeadlinesFragment"

    android:layout_width="match_parent" />

    res/layout-large/main.xml 双面板 (当运行到平板的时候,就会选用下面的布局(大于7英寸))

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:orientation="horizontal">

    android:layout_height="fill_parent"

    android:name="com.example.android.newsreader.HeadlinesFragment"

    android:layout_width="400dp"

    android:layout_marginRight="10dp"/>

    android:layout_height="fill_parent"

    android:name="com.example.android.newsreader.ArticleFragment"

    android:layout_width="fill_parent" />

    * 使用最小宽度限定符(android3.2之后)

    res/layout/main.xml,单面板(默认)布局:

    android:orientation="vertical"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

    android:layout_height="fill_parent"

    android:name="com.example.android.newsreader.HeadlinesFragment"

    android:layout_width="match_parent" />

    res/layout-sw600dp/main.xml,双面板布局: Small Width 最小宽度(宽或者高最小的一边大于600dp就是用以下布局)

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:orientation="horizontal">

    android:layout_height="fill_parent"

    android:name="com.example.android.newsreader.HeadlinesFragment"

    android:layout_width="400dp"

    android:layout_marginRight="10dp"/>

    android:layout_height="fill_parent"

    android:name="com.example.android.newsreader.ArticleFragment"

    android:layout_width="fill_parent" />

    如果适配android3.2之前,要有以上的两个维护布

    * 使用布局别名

    使用布局别名

    res/layout/main.xml: 单面板布局

    res/layout-large/main.xml: 多面板布局

    res/layout-sw600dp/main.xml: 多面板布局

    多面板布局共同部分抽取出来,生成main_twopanes文件

    res/layout/main.xml 单面板布局

    res/layout/main_twopanes.xml 双面板布局

    setContentView(R.layout.main);

    默认布局

    res/values/layout.xml:

    @layout/main

    Android3.2之前的平板布局

    res/values-large/layout.xml:

    @layout/main_twopanes

    Android3.2之后的平板布局

    res/values-sw600dp/layout.xml:

    @layout/main_twopanes

    * 使用屏幕方向限定符

    使用屏幕方向限定符

    res/values-sw600dp-land/layouts.xml:

    @layout/main_twopanes

    res/values-sw600dp-port/layouts.xml:

    @layout/main

    小屏幕,纵向:

    1.单面板

    小屏幕,横向:

    单面板

    7 英寸平板电脑,纵向:

    2.单面板,带操作栏

    7 英寸平板电脑,横向:

    3.双面板,宽,带操作栏

    10 英寸平板电脑,纵向:

    4.双面板,窄,带操作栏

    10 英寸平板电脑,横向:

    双面板,宽,带操作栏

    电视,横向:

    双面板,宽,带操作栏

    1.res/layout/onepane.xml:(单面板)

    android:orientation="vertical"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

    android:layout_height="fill_parent"

    android:name="com.example.android.newsreader.HeadlinesFragment"

    android:layout_width="match_parent" />

    2.res/layout/onepane_with_bar.xml:(单面板带操作栏)

    android:orientation="vertical"

    android:layout_width="match_parent"

    android:layout_height="match_parent">

    android:id="@+id/linearLayout1"

    android:gravity="center"

    android:layout_height="50dp">

    android:layout_height="wrap_content"

    android:layout_width="wrap_content"

    android:src="@drawable/logo"

    android:paddingRight="30dp"

    android:layout_gravity="left"

    android:layout_weight="0" />

    android:id="@+id/view1"

    android:layout_width="wrap_content"

    android:layout_weight="1" />

    android:background="@drawable/button_bg"

    android:layout_height="match_parent"

    android:layout_weight="0"

    android:layout_width="120dp"

    style="@style/CategoryButtonStyle"/>

    android:layout_height="fill_parent"

    android:name="com.example.android.newsreader.HeadlinesFragment"

    android:layout_width="match_parent" />

    3.res/layout/twopanes.xml:(双面板,宽布局)

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:orientation="horizontal">

    android:layout_height="fill_parent"

    android:name="com.example.android.newsreader.HeadlinesFragment"

    android:layout_width="400dp"

    android:layout_marginRight="10dp"/>

    android:layout_height="fill_parent"

    android:name="com.example.android.newsreader.ArticleFragment"

    android:layout_width="fill_parent" />

    4.res/layout/twopanes_narrow.xml:(双面板,窄布局)

    android:layout_width="fill_parent"

    android:layout_height="fill_parent"

    android:orientation="horizontal">

    android:layout_height="fill_parent"

    android:name="com.example.android.newsreader.HeadlinesFragment"

    android:layout_width="200dp"

    android:layout_marginRight="10dp"/>

    android:layout_height="fill_parent"

    android:name="com.example.android.newsreader.ArticleFragment"

    android:layout_width="fill_parent" />

    1.res/values/layouts.xml:

    @layout/onepane_with_bar

    false

    2.res/values-sw600dp-land/layouts.xml:

    @layout/twopanes

    true

    3.res/values-sw600dp-port/layouts.xml:

    @layout/onepane

    false

    4.res/values-large-land/layouts.xml:

    @layout/twopanes

    true

    5.res/values-large-port/layouts.xml:

    @layout/twopanes_narrow

    true

    支持各种屏幕密度

    1、使用非密度制约像素

    使用sp来控制文字的大小

    使用dp来控制布局控件的位置(这里有坑,看看下面)

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:background="@color/colorAccent"

    android:orientation="vertical">

    android:layout_width="150dp"

    android:background="@android:color/holo_blue_dark"

    android:layout_height="match_parent" />

    android:layout_width="200dp"

    android:layout_alignParentRight="true"

    android:background="@android:color/darker_gray"

    android:layout_height="match_parent" />

    由此可知,虽然我们已经使用了dp(密度无关)但是显示还是不能达到统一的效果

    b0c21a5bac70

    问题的关键所在--->各种设备的宽度不是一致的,即使使用dp,也是会有误差的。

    解决思路--->不使用dp

    多个values下面提供不同的dp值,(必须为每一种设备提供对应的资源文件,如果没有,就去默认的资源文件里面查找),但这个最终还是以px为单位的,这个就要自己斟酌了。

    2、提供备用位图

    不同的屏幕密度提供相匹配的图片

    但是为了打包的小,一般还是一套图,一些比较重要的匹配的,可以多切几套图

    使用和设备相符合的,使用的内存占用是最小的

    实施自适应用户界面流程

    1、确定当前布局

    2、根据当前布局做出响应

    3、重复使用其他活动中的片段

    4、处理屏幕配置变化

    最佳实践

    关于高清设计图尺寸

    动态设置

    在使用popupwindow的时候动态设置布局

    展开全文
  • Android 6 权限适配 Android 7 文件适配 Android 10/11 存储适配 ok,接下来以一个更换头像的小例子来讲解一下。...===========================...《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目..
    • Android 6 权限适配

    • Android 7 文件适配

    • Android 10/11 存储适配

    ok,接下来以一个更换头像的小例子来讲解一下。

    示例

    =============================================================

    在这里插入图片描述

    点击头像,然后弹窗,给出不同的选项,执行不同的操作。

    mBinding.llImg.setOnClickListener {

    TakeImage

    《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

    开源分享完整内容戳这里

    Dialog {

    when (it) {

    TakeImageDialog.ALBUM -> {

    openAlbum()

    }

    TakeImageDialog.CAMERA -> {

    checkPermission()

    }

    }

    }.show(supportFragmentManager, “TakeImageDialog”)

    }

    定义后面会用到的一些参数变量

    //相机拍照保存的位置

    private lateinit var photoUri: Uri

    companion object {

    private const val REQUEST_CODE_PERMISSIONS = 1000 //权限

    private const val REQUEST_CODE_ALBUM = 1001 //相册

    private const val REQUEST_CODE_CAMERA = 1002 //相机

    }

    打开相册

    ===============================================================

    选择图片

    private fun openAlbum() {

    val intent = Intent()

    intent.type = “image/*”

    intent.action = “android.intent.action.GET_CONTENT”

    intent.addCategory(“android.intent.category.OPENABLE”)

    startActivityForResult(intent, REQUEST_CODE_ALBUM)

    }

    固定写法,大差不差。

    既然是startActivityForResult启动方式,来看看onActivityResult回调

    回调

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

    super.onActivityResult(requestCode, resultCode, data)

    if (resultCode == RESULT_OK) {

    when (requestCode) {

    REQUEST_CODE_ALBUM -> {

    doCrop(data?.data!!)

    }

    }

    }

    }

    requestCodeREQUEST_CODE_ALBUM的情况下:

    doCrop(data?.data!!)

    data?.data!!即是选择图片返回的Uri,可以直接使用,这里进行了下一步操作,剪裁

    剪裁

    private fun doCrop(sourceUri: Uri) {

    Intrinsics.checkParameterIsNotNull(sourceUri, “资源为空”)

    UCrop.of(sourceUri, getDestinationUri())//当前资源,保存目标位置

    .withAspectRatio(1f, 1f)//宽高比

    .withMaxResultSize(500, 500)//宽高

    .start(this)

    }

    为了方便,这里使用了一个三方库UCrop,使用简单方便。

    getDestinationUri()是当前资源裁剪后保存的目标位置

    private fun getDestinationUri(): Uri {

    val fileName = String.format(“fr_crop_%s.jpg”, System.currentTimeMillis())

    val cropFile = File(getExternalFilesDir(Environment.DIRECTORY_PICTURES), fileName)

    return Uri.fromFile(cropFile)

    }

    UCrop的回调同样也在onActivityResult

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {

    super.onActivityResult(requestCode, resultCode, data)

    if (resultCode == RESULT_OK) {

    when (requestCode) {

    REQUEST_CODE_ALBUM -> {

    doCrop(data?.data!!)

    }

    UCrop.REQUEST_CROP -> {

    val resultUri: Uri = UCrop.getOutput(data!!)!!

    val bitmap = BitmapFactory.decodeStream(contentResolver.openInputStream(resultUri))

    // todo

    }

    UCrop.RESULT_ERROR -> {

    val error: Throwable = UCrop.getError(data!!)!!

    ToastUtil.show(“图片剪裁失败” + error.message)

    }

    }

    }

    }

    UCrop.getOutput(data!!)!!,即是返回的Uri,可以直接操作,也可以转成bitmap

    ok,到这里打开相册就介绍完了。

    接下来看重点,打开相机。

    author:yechaoa

    打开相机

    ===============================================================

    打开相机的流程就要稍微复杂一点了。

    权限

    第一步不是打开,而是先检查是否有相机权限,这个在某些手机上是必须的,比如华为。

    • 配置文件添加:
    • 代码:

    private fun checkPermission() {

    if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {

    openCamera()

    } else {

    ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), REQUEST_CODE_PERMISSIONS)

    }

    }

    • 回调:

    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {

    super.onRequestPermissionsResult(requestCode, permissions, grantResults)

    if (requestCode == REQUEST_CODE_PERMISSIONS) {

    if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

    openCamera()

    } else {

    ToastUtil.show(“拒绝会导致无法使用相机”)

    }

    }

    }

    openCamera方法就是打开相机了。

    打开前适配

    private fun openCamera() {

    val intent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)

    photoUri = getDestinationUri()

    photoUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {

    //适配Android 7.0文件权限,通过FileProvider创建一个content类型的Uri

    FileProvider.getUriForFile(this, “$packageName.fileProvider”, File(photoUri.path!!))

    } else {

    getDestinationUri()

    }

    //android11以后强制分区存储,外部资源无法访问,所以添加一个输出保存位置,然后取值操作

    intent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri)

    startActivityForResult(intent, REQUEST_CODE_CAMERA)

    }

    • 适配一:

    FileProvider.getUriForFile(this, “$packageName.fileProvider”, File(photoUri.path!!))

    7.0以上,使用fileProvider的方式共享文件。

    展开全文
  • Android7.0适配教程,心得

    千次阅读 2018-05-31 09:28:12
    Android7.0发布已经有一个多月了,Android7.0在给用户带来一些新的特性的同时,也给开发者带来了新的挑战,这几天我将应用适配Android7.0,其中也遇到了不少问题也踩了一些坑,在这里就把我在Android7.0适配上的...

    Android7.0发布已经有一个多月了,Android7.0在给用户带来一些新的特性的同时,也给开发者带来了新的挑战,这几天我将应用适配到Android7.0,其中也遇到了不少问题也踩了一些坑,在这里就把我在Android7.0适配上的一些心得分享给大家,让大家的应用能早一天跑在Android7.0上。

    权限更改

    随着Android版本越来越高,Android对隐私的保护力度也越来越大。从Android6.0引入的动态权限控制(Runtime Permissions)到Android7.0的“私有目录被限制访问”,“StrictMode API 政策”。这些更改在为用户带来更加安全的操作系统的同时也为开发者带来了一些新的任务。如何让你的APP能够适应这些改变而不是cash,是摆在每一位Android开发者身上的责任。

    目录被限制访问

    一直以来,在目录及文件的访问保护方面iOS做的是很到位的,如:iOS的沙箱机制。但,Android在这方面的保护就有些偏弱了,在Android中应用可以读写手机存储中任何一个目录及文件,这也带来了很多的安全问题。现在Android也在着力解决这一问题。

    在Android7.0中为了提高私有文件的安全性,面向 Android N 或更高版本的应用私有目录将被限制访问。对于这个权限的更改开发者需要留意一下改变:

    应对策略:这项权限的变更将意味着你无法通过File API访问手机存储上的数据了,基于File API的一些文件浏览器等也将受到很大的影响,看到这大家是不是惊呆了呢,不过迄今为止,这种限制尚不能完全执行。 应用仍可能使用原生 API 或 File API 来修改它们的私有目录权限。 但是,Android官方强烈反对放宽私有目录的权限。可以看出收起对私有文件的访问权限是Android将来发展的趋势。

    • 给其他应用传递 file:// URI 类型的Uri,可能会导致接受者无法访问该路径。 因此,在Android7.0中尝试传递 file:// URI 会触发 FileUriExposedException。

    应对策略:大家可以通过使用FileProvider来解决这一问题。

    应对策略:大家可以通过[ContentResolver.openFileDescriptor()](https://developer.android.com/reference/android/content/ContentResolver.html#openFileDescriptor(android.net.Uri, java.lang.String))来访问由 DownloadManager 公开的文件。

    应用间共享文件

    在Android7.0系统上,Android 框架强制执行了 StrictMode API 政策禁止向你的应用外公开 file:// URI。 如果一项包含文件 file:// URI类型 的 Intent 离开你的应用,应用失败,并出现 FileUriExposedException 异常,如调用系统相机拍照,或裁切照片

    应对策略:若要在应用间共享文件,可以发送 content:// URI类型的Uri,并授予 URI 临时访问权限。 进行此授权的最简单方式是使用 FileProvider类。 如需有关权限和共享文件的更多信息,请参阅共享文件。

    在Android7.0上调用系统相机拍照,裁切照片

    调用系统相机拍照

    在Android7.0之前,如果你想调用系统相机拍照可以通过以下代码来进行:

    File file=new File(Environment.getExternalStorageDirectory(), "/temp/"+System.currentTimeMillis() + ".jpg");
    if (!file.getParentFile().exists())file.getParentFile().mkdirs();
    Uri imageUri = Uri.fromFile(file);
    Intent intent = new Intent();
    intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//设置Action为拍照
    intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//将拍取的照片保存到指定URI
    startActivityForResult(intent,1006);
    
    Android7.0拍照.png
    Android7.0拍照.png

    在Android7.0上使用上述方式调用系统相拍照会抛出如下异常:

    android.os.FileUriExposedException: file:storage/emulated/0/temp/1474956193735.jpg exposed beyond app through Intent.getData()
    at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799)
    at android.net.Uri.checkFileUriExposed(Uri.java:2346)
    at android.content.Intent.prepareToLeaveProcess(Intent.java:8933)
    at android.content.Intent.prepareToLeaveProcess(Intent.java:8894)
    at android.app.Instrumentation.execStartActivity(Instrumentation.java:1517)
    at android.app.Activity.startActivityForResult(Activity.java:4223)
    ...
    at android.app.Activity.startActivityForResult(Activity.java:4182)
    
    Android7.0拍照闪退.png
    Android7.0拍照闪退.png

    这是由于Android7.0执行了“StrictMode API 政策禁”的原因,不过小伙伴们不用担心,上文讲到了可以用FileProvider来解决这一问题,
    现在我们就来一步一步的解决这个问题。

    使用FileProvider

    使用FileProvider的大致步骤如下:
    第一步:在manifest清单文件中注册provider

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

    心得:exported:要求必须为false,为true则会报安全异常。grantUriPermissions:true,表示授予 URI 临时访问权限。

    第二步:指定共享的目录

    为了指定共享的目录我们需要在资源(res)目录下创建一个xml目录,然后创建一个名为“file_paths”(名字可以随便起,只要和在manifest注册的provider所引用的resource保持一致即可)的资源文件,内容如下:

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <paths>
            <external-path path="" name="camera_photos" />
        </paths>
    </resources>
    
    • <files-path/>代表的根目录: Context.getFilesDir()
    • <external-path/>代表的根目录: Environment.getExternalStorageDirectory()
    • <cache-path/>代表的根目录: getCacheDir()

    心得:上述代码中path="",是有特殊意义的,它代码根目录,也就是说你可以向其它的应用共享根目录及其子目录下任何一个文件了,如果你将path设为path="pictures"
    那么它代表着根目录下的pictures目录(eg:/storage/emulated/0/pictures),如果你向其它应用分享pictures目录范围之外的文件是不行的。

    第三步:使用FileProvider

    上述准备工作做完之后,现在我们就可以使用FileProvider了。
    还是以调用系统相机拍照为例,我们需要将上述拍照代码修改为如下:

    File file=new File(Environment.getExternalStorageDirectory(), "/temp/"+System.currentTimeMillis() + ".jpg");
    if (!file.getParentFile().exists())file.getParentFile().mkdirs();
    Uri imageUri = FileProvider.getUriForFile(context, "com.jph.takephoto.fileprovider", file);//通过FileProvider创建一个content类型的Uri
    Intent intent = new Intent();
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时授权该Uri所代表的文件
    intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//设置Action为拍照
    intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//将拍取的照片保存到指定URI
    startActivityForResult(intent,1006);
    

    上述代码中主要有两处改变:

    1. 将之前Uri的scheme类型为file的Uri改成了有FileProvider创建一个content类型的Uri。
    2. 添加了intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);来对目标应用临时授权该Uri所代表的文件。

    心得:上述代码通过FileProviderUri getUriForFile (Context context, String authority, File file)
    静态方法来获取Uri,该方法中authority参数就是清单文件中注册provider的android:authorities="com.jph.takephoto.fileprovider"
    对Web服务器如tomcat,IIS比较熟悉的小伙伴,都只知道为了网站内容的安全和高效,Web服务器都支持为网站内容设置一个虚拟目录,其实FileProvider也有异曲同工之处。

    getUriForFile方法获取的Uri打印出来如下:

    content://com.jph.takephoto.fileprovider/camera_photos/temp/1474960080319.jpg`。  
    

    其中camera_photos就是file_paths.xml中paths的name。

    因为上述指定的path为path="",所以content://com.jph.takephoto.fileprovider/camera_photos/代表的真实路径就是根目录,即:/storage/emulated/0/
    content://com.jph.takephoto.fileprovider/camera_photos/temp/1474960080319.jpg代表的真实路径是:/storage/emulated/0/temp/1474960080319.jpg

    另外,推荐大家使用开源工具库TakePhoto
    TakePhoto是一款在Android设备上获取照片(拍照或从相册、文件中选择)、裁剪图片、压缩图片的开源工具库。

    裁切照片

    在Android7.0之前,你可以通过如下方法来裁切照片:

    File file=new File(Environment.getExternalStorageDirectory(), "/temp/"+System.currentTimeMillis() + ".jpg");
    if (!file.getParentFile().exists())file.getParentFile().mkdirs();
    Uri outputUri = Uri.fromFile(file);
    Uri imageUri=Uri.fromFile(new File("/storage/emulated/0/temp/1474960080319.jpg"));
    Intent intent = new Intent("com.android.camera.action.CROP");
    intent.setDataAndType(imageUri, "image/*");
    intent.putExtra("crop", "true");
    intent.putExtra("aspectX", 1);
    intent.putExtra("aspectY", 1);
    intent.putExtra("scale", true);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
    intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
    intent.putExtra("noFaceDetection", true); // no face detection
    startActivityForResult(intent,1008);
    

    和拍照一样,上述代码在Android7.0上同样会引起android.os.FileUriExposedException异常,解决办法就是上文说说的使用FileProvider

    然后,将上述代码改为如下即可:

    File file=new File(Environment.getExternalStorageDirectory(), "/temp/"+System.currentTimeMillis() + ".jpg");
    if (!file.getParentFile().exists())file.getParentFile().mkdirs();
    Uri outputUri = FileProvider.getUriForFile(context, "com.jph.takephoto.fileprovider",file);
    Uri imageUri=FileProvider.getUriForFile(context, "com.jph.takephoto.fileprovider", new File("/storage/emulated/0/temp/1474960080319.jpg");//通过FileProvider创建一个content类型的Uri
    Intent intent = new Intent("com.android.camera.action.CROP");
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.setDataAndType(imageUri, "image/*");
    intent.putExtra("crop", "true");
    intent.putExtra("aspectX", 1);
    intent.putExtra("aspectY", 1);
    intent.putExtra("scale", true);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
    intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
    intent.putExtra("noFaceDetection", true); // no face detection
    startActivityForResult(intent,1008);
    

    另外,裁切照片推荐大家使用开源工具库TakePhoto
    TakePhoto是一款在Android设备上获取照片(拍照或从相册、文件中选择)、裁剪图片、压缩图片的开源工具库。

    电池和内存

    Android 6.0(API 级别 23)引入了低电耗模式,Android7.0在电池和内存上又做了进一步优化,
    来减少Android应用对电量的消耗以及对内存的占用。这些优化所带来的一些规则的变更可能会影响你的应用访问系统资源,以及你的系统通过特定隐式 Intent 与其他应用互动的方式。
    所以开发人员需要特别注意这些改变。

    低电耗模式

    在低电耗模式下,当用户设备未插接电源、处于静止状态且屏幕关闭时,该模式会推迟 CPU 和网络活动,从而延长电池寿命。
    Android7.0通过在设备未插接电源且屏幕关闭状态下、但不一定要处于静止状态(例如用户外出时把手持式设备装在口袋里)时应用部分 CPU 和网络限制,进一步增强了低电耗模式。

    也就是说,Android7.0会在手机屏幕关闭的状态下,限时应用对CPU以及网络的使用。

    具体规则如下:

    1. 当设备处于充电状态且屏幕已关闭一定时间后,设备会进入低电耗模式并应用第一部分限制: 关闭应用网络访问、推迟作业和同步。
    2. 如果进入低电耗模式后设备处于静止状态达到一定时间,系统则会对 PowerManager.WakeLockAlarmManager
      闹铃、GPS 和 Wi-Fi 扫描应用余下的低电耗模式限制。 无论是应用部分还是全部低电耗模式限制,系统都会唤醒设备以提供简短的维护时间窗口,在此窗口期间,应用程序可以访问网络并执行任何被推迟的作业/同步。

    后台优化

    小伙伴们都知道在Android中有一些隐式广播,使用这些隐式广播可以做一些特定的功能,如,当手机网络变成WiFi时自动下载更新包等。
    但,这些隐式广播会在后台频繁启动已注册侦听这些广播的应用,从而带来很大的电量消耗,为缓解这一问题来提升设备性能和用户体验,在Android 7.0中删除了三项隐式广播,以帮助优化内存使用和电量消耗。

    Android 7.0 应用了以下优化措施:

    • 在 Android 7.0上 应用不会收到 CONNECTIVITY_ACTION 广播,即使你在manifest清单文件中设置了请求接受这些事件的通知。 但,在前台运行的应用如果使用BroadcastReceiver 请求接收通知,则仍可以在主线程中侦听 CONNECTIVITY_CHANGE。
    • 在 Android 7.0上应用无法发送或接收 ACTION_NEW_PICTUREACTION_NEW_VIDEO 类型的广播。

    应对策略:Android 框架提供多个解决方案来缓解对这些隐式广播的需求。 例如,JobScheduler API
    提供了一个稳健可靠的机制来安排满足指定条件(例如连入无限流量网络)时所执行的网络操作。 您甚至可以使用 JobScheduler API 来适应内容提供程序变化。

    另外,大家如果想了解更多关于后台的优化可查阅后台优化

    移动设备会经历频繁的连接变更,例如在 Wi-Fi 和移动数据之间切换时。 目前,可以通过在应用清单中注册一个接收器来侦听隐式 CONNECTIVITY_ACTION 广播,
    让应用能够监控这些变更。 由于很多应用会注册接收此广播,因此单次网络切换即会导致所有应用被唤醒并同时处理此广播。

    以上是,我在Android7.0上适配上的一些心得,小伙伴们如果有遇到问题可以在下方留言。

    最后

    既然来了,留下个喜欢再走吧,鼓励我继续创作(_)∠※

    如果喜欢我的文章,那就关注我的博客吧,让我们一起做朋友~~

    戳这里,加关注哦:

    微博:第一时间获取推送
    个人博客:干货文章都在这里哦
    GitHub:我的开源项目



    作者:CrazyCodeBoy
    链接:https://www.jianshu.com/p/56b9fb319310
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
    展开全文
  • 安卓适配详解

    千次阅读 2018-01-01 01:41:48
    概述一直想把安卓开发过程中所有的关于不同手机类型,手机系统,及API版本的适配做一个整理,这些内容虽然简单但细节太多,所以想整理一下,以便自己的总结,由于各种原因一直被放下,现在下定决心会持续更新博客上...
  • Android7.0适配教程与心得

    万次阅读 2016-10-12 09:43:37
    这篇文章是在APP架构师的微信公众号上看到的,整理的相当全面,分享给大家一起看 权限更改 随着Android版本越来越高,Android对隐私的保护力度也越来越大。...这些更改在为用户带来更加安全的操作系统的同
  • 酷比魔方iWork10pro安卓root+单系统+双系统恢复教程(理论适配所有x86安卓root)-附件资源
  • 华为消费者 BG 软件部总裁王成录近期表示,...那么华为鸿蒙系统适配流程有哪些呢?下面就让小编给大家介绍一下。 麒麟9000为第一批;麒麟 990 5G为第二批;麒麟990 4G(部分)/985/820(部分)第三批;麒麟820(部分),9...
  • Android7.0(Android N)适配教程,心得

    万次阅读 多人点赞 2016-09-28 10:19:58
    Android7.0发布已经有一个多月了,Android7.0在给用户带来一些新的特性的同时,也给开发者带来了新的挑战,这几天我将应用适配Android7.0,其中也遇到了不少问题也踩了一些坑,在这里就把我在Android7.0适配上的...
  • 前言手头有淘汰的Android手机,想来将这个手机做服务器使用,收集资料发现了一个Linux on Android的项目,可以直接将常见的Linux发行版安装到手机上Ubuntu、Fedora 等等。查阅不少资料,都有很多缺漏,折腾了很久。...
  • 一、前言本文主要是从官方文档中筛选出一些常见的适配项,若有任何纰漏或需要补充的,欢迎大家在评论区指出。二、版本适配1. 限制 HTTP 网络请求Android 9.0 中限制了 HTTP(明文传输)网络请求,若仍继续使用HTTP请求...
  • 博主14年毕业后从事Android移动应用开发,至今已是第六年了。在深圳摸爬滚打了好几年,也都是在小厂的圈子里跳来跳去。在我拿到这份新offer钱,15K是我拿过的最高工资。已经有快一年没有涨过工资了。疫情在家隔离了...
  • Flyme6系统适配教程(Patchrom)

    万次阅读 2017-10-18 12:36:57
    先来说下几个基本的概念patchrom这个简单理解下:通过一种技术将第三方定制的系统功能代码反编译成smali注入到将要适配的机器官方rom中(cm aosp miui nubiaUI 360os等) ,这里改变的主要是调整框架层framework的...
  • 最新开源版ThinkPHP V免签支付系统源码 带安卓监控端+视频教程 程序含支付宝/微信免签约收款回调系统安卓监控系统 V免签 —— 个人开发者收款解决方案 V免签(PHP) 是基于ThinkPHP5.1 + Mysql 实现的一套免签支付...
  • Android7.0(Android N)适配教程

    千次阅读 2016-10-12 13:46:43
    Android7.0发布已经有一个多月了,Android7.0在给用户带来一些新的特性的同时,也给开发者带来了新的挑战,这几天我将应用适配Android7.0,其中也遇到了不少问题也踩了一些坑,在这里就把我在Android7.0适配上的...
  • Android ROM适配基础

    千次阅读 2016-08-05 11:24:11
    Android ROM适配大体上可以分成两部分:硬件跨平台适配和UI适配。 (1)硬件跨平台适配 如果ROM从A设备适配到B设备,A、B硬件平台不同,譬如A属于高通平台,B属于MTK或者英特尔平台。每个平台的底层驱动不同,...
  • 随着 Android 12 正式版的发布,越来越多的用户将升级至最新版本。Android 12 带来大量新 API 和功能更新的同时也带来了平台兼容性的变更,我们建议开发者优先对当前应用进...
  • import android.content.Context import android.content.res.Configuration import androidx.appcompat.app.AppCompatDelegate /** * 深色主题工具类 * @author Moriafly * @since 2020//8/20 */ object ...
  • Bluetooth结构 1、JAVA层 frameworks/base/core/java/android/bluetooth/ 包含了bluetooth的JAVA类。...调用硬件适配层的接口system/bluetooth/bluedroid/bluetooth.c 3、bluez库 external/bluez/ 这是b
  • Android11系统应用兼容适配

    千次阅读 2021-05-27 14:36:59
    客户使用了安卓11系统手机,发现应用安装闪退,于是分析,发现需要做升级适配。 二、遇到问题 应用中之前有提供一个手机唯一标识的功能,是通过调用安卓系统接口TelephoneManager 来获取IMEI的,然后再新的系统...
  • 安卓虚拟机Vmware安装运行安卓系统4.0教程:第一步,下载必备软件:安卓4.0 iso文件官网下载地址:点击进入第二步,新建虚拟机选择自定义点击安装盘镜像,选择下载好的安卓4.0ISO文件,虚拟机会识别为FreeBSD...
  • Windows 11 安卓系统安装教程

    千次阅读 2022-01-04 15:10:43
    Windows 11 上周开始引入了对安卓系统的支持,...Windows 11 的安卓系统(Windows Subsystem for Android,简称 WSA)包括子系统框架和应用商店(Amazon Appstore)两部分。这两者目前已经打包上架到美区微软应用商
  • Android 9.0 适配指南

    2019-11-28 13:58:22
    Android适配系列: Android 6.0 的动态权限管理 Android 7.0脱坑指南 Android 8.0适配指北 2.准备工作 进入正题,首先将我们项目中的targetSdkVersion改为 28。接下来运行你的项目,看有没中枪。...
  • 我魅族16thplus还在用安卓8好久没有刷过机了,之前不管什么手机都要看看有没有适配这个UI,但是不管哪个第三方都是bug满满,之前第三方简直bue华为mate 9能刷吗依然不如miui,国内最好就是小米,华为最垃圾,最不人...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 7,608
精华内容 3,043
关键字:

安卓系统适配教程