2024年Android最全最容易理解的Android6,android常用面试题

写在最后

对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。

![
[]


文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

            init()

            return

        }



        // 权限被拒绝

        val shouldShowRequestPermissionRationale = ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.CAMERA)

        if (shouldShowRequestPermissionRationale) { // 应该解释一下为什么需要此权限

            // 用户拒绝了摄像头权限

            AlertDialog.Builder(this)

                    .setTitle("解释需要此权限的理由")

                    .setMessage("录像时需要用到摄像头权限,请允许此权限,否则无法录制摄像头")

                    .setPositiveButton("确定") { dialog, which -> requestCameraPermission() }

                    .setNegativeButton("取消", null)

                    .setCancelable(false)

                    .create()

                    .show()

        } else {

            // 用户拒绝了摄像头权限,并且勾选了不再提示

            AlertDialog.Builder(this)

                    .setTitle("打开权限步骤提示")

                    .setMessage("您好狠啊,竟然选择拒绝了权限,而且选择不再提示。好吧,如果你后悔了,可以在设置中找到应用来开启此权限")

                    .setPositiveButton("确定", null)

                    .setCancelable(false)

                    .create()

                    .show()

        }

    }

}

}




[]( )4、RxPermissions的使用

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



了解了动态权限的申请之后,我们就会发现,每次申请权限,要写一些模板代码在Activity中,能不能对申请权限的代码进行封装呢?答案是可以的,RxPermissions库就实现了这样的功能,它的原理是给当前Activity添加一个无界面的Fragment,在这个Fragment中申请权限,并在这个Fragment中接收申请权限的结果,所以可以在这个Fragment中封装申请权限的逻辑。并且该库使用RxJava进行封装,使我们更容易的拿到申请权限的结果。



RxPermissions官网:[https://github.com/tbruyelle/RxPermissions]( )



[]( )添加RxPermissions依赖

--------------------------------------------------------------------------------



在项目的根目录下的build.gradle添加如下配置:



allprojects {

repositories {

    maven { url 'https://jitpack.io' }

}

}




在module目录下的build.gradle添加如下配置:



dependencies {

implementation 'com.github.tbruyelle:rxpermissions:0.12'    

}




我们知道RxPermissions是用到了RxJava的,通过查看依赖传递,我们发现rxpermissions中有依赖RxJava,但是并没有依赖RxAndroid,我们知道在Android中使用RxJava必须要依赖RxAndroid的,所以还需要添加RxAndroid依赖,如下:



dependencies {

implementation 'com.github.tbruyelle:rxpermissions:0.12'    

implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'

}




[]( )使用RxPermissions完成前面的例子

-------------------------------------------------------------------------------------



接下来我们使用RxPermissions来完成前面的例子,看看是不是更简洁呢,代码如下:



class MainActivity : AppCompatActivity() {

private val mRxPermissions = RxPermissions(this)



override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    requestCameraPermission()

}



private fun init() {

    // TODO 得到权限后要做的事情

}



private fun requestCameraPermission() {

    mRxPermissions

            .requestEach(Manifest.permission.CAMERA)

            .subscribe { permission ->

                when {

                    permission.granted -> {

                        // 已经授权了,开始使用功能吧

                        init()

                    }

                    permission.shouldShowRequestPermissionRationale -> {

                        // 用户拒绝了摄像头权限,应该解释一下为什么需要此权限

                        AlertDialog.Builder(this)

                                .setTitle("解释需要此权限的理由")

                                .setMessage("录像时需要用到摄像头权限,请允许此权限,否则无法录制摄像头")

                                .setPositiveButton("确定") { dialog, which -> requestCameraPermission() }

                                .setNegativeButton("取消", null)

                                .setCancelable(false)

                                .create()

                                .show()

                    }

                    else -> {

                        // 用户拒绝了摄像头权限,并且勾选了不再提示

                        AlertDialog.Builder(this)

                                .setTitle("打开权限步骤提示")

                                .setMessage("您好狠啊,竟然选择拒绝了权限,而且选择不再提示。好吧,如果你后悔了,可以在设置中找到应用来开启此权限")

                                .setPositiveButton("确定", null)

                                .setCancelable(false)

                                .create()

                                .show()

                    }

                }

            }

}

}




可以看到,使用RxPermission后代码更紧凑了,当然了,用法上还有更多的好处,可以查看RxPermission官网的说明教程。



[]( )封装RxPermissions

------------------------------------------------------------------------------



这里虽然使用RxPermission简单了许多,但是代码还是入侵了Activity,一个申请权限的代码没什么技术含量,但是代码也不算少,影响阅读Activity的逻辑,所以这个权限申请可以封装到工具类中,或者写到Presenter中都是可以的,例如我们封装到工具类中,示例如下,会看到Activity就清爽多了:



class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    PermissionsHelper.requestCameraPermission(this, ::init)

}



private fun init() {

    // TODO 得到权限后要做的事情

}

}


object PermissionsHelper {

fun requestCameraPermission(activity: FragmentActivity, callback: () -> Unit) {

    RxPermissions(activity)

        .requestEach(Manifest.permission.CAMERA)

        .subscribe { permission ->

            when {

                permission.granted -> {

                    // 已经授权了,开始使用功能吧

                    callback()

                }

                permission.shouldShowRequestPermissionRationale -> {

                    // 用户拒绝了摄像头权限,应该解释一下为什么需要此权限

                    AlertDialog.Builder(activity)

                        .setTitle("解释需要此权限的理由")

                        .setMessage("录像时需要用到摄像头权限,请允许此权限,否则无法录制摄像头")

                        .setPositiveButton("确定") { _, _ -> requestCameraPermission(activity, callback) }

                        .setNegativeButton("取消", null)

                        .setCancelable(false)

                        .create()

                        .show()

                }

                else -> {

                    // 用户拒绝了摄像头权限,并且勾选了不再提示

                    AlertDialog.Builder(activity)

                        .setTitle("打开权限步骤提示")

                        .setMessage("您好狠啊,竟然选择拒绝了权限,而且选择不再提示。好吧,如果你后悔了,可以在设置中找到应用来开启此权限")

                        .setPositiveButton("确定", null)

                        .setCancelable(false)

                        .create()

                        .show()

                }

            }

        }

}

}




[]( )5、最新版本权限申请方式(2021-08-16)

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



[]( )新方式描述

--------------------------------------------------------------------



更多关于权限的文章见我的另一篇文章:[https://blog.csdn.net/android\_cai\_niao/article/details/119672133]( )



不知道什么时候有的这种方式,感觉有了这个新的方式,不再需要使用RxPermissions这些第三方的权限申请库了,直接使用Android官方的方式即可,官方使用教程:[https://developer.android.google.cn/training/permissions/requesting#request-permission]( )



需要注意的是,这种新的方式需要使用Jetpack组件中的Activity或Fragment,如下:  

androidx.activity,1.2.0 或更高版本。  

androidx.fragment,1.3.0 或更高版本。  

比如:  

implementation “androidx.activity:activity-ktx:1.3.0”  

implementation “androidx.fragment:fragment-ktx:1.3.6”



注:这两个依赖必须同时声明,即使你只是在Activity中申请权限,但是Activity是用到了FragmentActivity的,此类必须是fragment-ktx里面的,如果这个版本低的话也是不行的,比如我们不添加这个依赖,运行时报如下异常:



IllegalArgumentException: Can only use lower 16 bits for requestCode




对于这个异常,真是让人摸不到头脑,因为我代码中根本就没有使用到请求码(requestCode),为什么会报这个错呢,只要把fragment的依赖设置好问题就没了。



虽然androidx.appcompat组件的依赖传递包含有androidx.activity和androidx.fragment,但是版本都比较低,达不到要求的版本,所以需要单独依赖androidx.activity和androidx.fragment的最新版本。



[]( )startActivityForResult的实现(过时,不推荐)

------------------------------------------------------------------------------------------------



新的权限申请方式使用的是`ActivityResultLauncher`,使用这种方式就不再需要覆盖Activity的`onActivityResult`函数,而且它也能代替`startActivityForResult`的使用,也是为了不再需要使用`onActivityResult`函数来接收结果。我们先写一个`startActivityForResult`的原始使用Demo,如下:



有三个Activity:MainActivity,AActivity、BActivity。



MainActivity界面如下:  

![在这里插入图片描述](https://img-blog.csdnimg.cn/32f8291cd77a40e88da84e925fe6fdf4.png)  

AActivity和BActivity界面分别如下:



![在这里插入图片描述](https://img-blog.csdnimg.cn/e02f9d09357242f288ed8cbd6c0fc8fe.png)  

![在这里插入图片描述](https://img-blog.csdnimg.cn/c1da5b80acb84b2fb4e6dceae977f61f.png)  

MainActivity代码如下:



class MainActivity : AppCompatActivity() {

private val A_ACTIVITY_REQUEST_CODE = 0

private val B_ACTIVITY_REQUEST_CODE = 1



override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)



    findViewById<Button>(R.id.button1).setOnClickListener {

        startActivityForResult(Intent(this, AActivity::class.java), A_ACTIVITY_REQUEST_CODE)

    }



    findViewById<Button>(R.id.button2).setOnClickListener {

        startActivityForResult(Intent(this, BActivity::class.java), B_ACTIVITY_REQUEST_CODE)

    }

}



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

    super.onActivityResult(requestCode, resultCode, data)

    if (resultCode == Activity.RESULT_OK) {

        when (requestCode) {

            A_ACTIVITY_REQUEST_CODE -> Log.i("ABCD","收到A_Activtiy的结果:${data?.getStringExtra("data")}")

            B_ACTIVITY_REQUEST_CODE -> Log.i("ABCD","收到B_Activtiy的结果:${data?.getStringExtra("data")}")

        }

    }

}

}




AActivity代码中如下:



class AActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_a)

}



override fun onBackPressed() {

    setResult(Activity.RESULT_OK, Intent().apply { putExtra("data", "AAA") })

    super.onBackPressed()

}

}




BActivity代码如下:



class BActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_b)

}



override fun onBackPressed() {

    setResult(Activity.RESULT_OK, Intent().apply { putExtra("data", "BBB") })

    super.onBackPressed()

}

}




代码很简单,运行后,当打开AActivity并返回到MainActivity时就能收到“AAA”,当打开BActivity后再返回时就能收到“BBB”。



[]( )startActivityForResult的替代方式(推荐)

----------------------------------------------------------------------------------------------



### []( )直接在Activity中实现:



在AndroidStudio开发工具中,可以看到`startActivityForResult`方法和`onActivityResult`方法都是过时的,也就是说不推荐使用了,那我们就使用它推荐的方式,示例如下:



class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    

    val aActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult: ActivityResult ->

        Log.i("ABCD","收到A_Activtiy的结果:${activityResult.data?.getStringExtra("data")}")

    }

    

    val bActivityResultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult: ActivityResult ->

        Log.i("ABCD","收到B_Activtiy的结果:${activityResult.data?.getStringExtra("data")}")

    }



    findViewById<Button>(R.id.button1).setOnClickListener {

        aActivityResultLauncher.launch(Intent(this, AActivity::class.java))

    }



    findViewById<Button>(R.id.button2).setOnClickListener {

        bActivityResultLauncher.launch(Intent(this, BActivity::class.java))

    }

}

}




可以看到,这种方式更紧凑了,不需要覆盖`onActivityResult`方法来接收结果了,这样的好处是代码可以更加解耦,因为`registerForActivityResult`这种代码我们是可以封装到工具类中去的,方便封装。



需要注意的是,使用新方式后,发现没有地方可设置`requestCode`参数了,所以,对于每一个开启Activity的请求,都需要分别注册一个监听器(调用`registerForActivityResult`来注册),以获取对应的回调结果。



另外还有一个注意事项,registerForActivityResult的调用必须在`STARTED`状态之前调用,否则会抛出如下异常:



> IllegalStateException: LifecycleOwner cn.dazhou.permissionrequestdemo.MainActivity@f263cb9 is attempting to register while current state is RESUMED. LifecycleOwners must call register before they are STARTED.



所以,必须提前注册好,不能等到你要开启Activity了才去注册,那时就晚了!



### []( )封装到工具类中



注册的语句一般注册一次就够了,所以一般放在`onCreate`方法中进行注册,所以,如果我们要把注册封装到工具类中的话,需要在onCreate中调用注册的代码,示例如下:



object Utils {

private lateinit var aActivityResultLauncher: ActivityResultLauncher<Intent?>

private lateinit var bActivityResultLauncher: ActivityResultLauncher<Intent?>



fun registerForAActivityResult(activity: FragmentActivity) {

    aActivityResultLauncher = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult: ActivityResult ->

        Log.i("ABCD", "收到A_Activtiy的结果:${activityResult.data?.getStringExtra("data")}")

    }

}



fun registerForBActivityResult(activity: FragmentActivity) {

    bActivityResultLauncher = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult: ActivityResult ->

        Log.i("ABCD", "收到B_Activtiy的结果:${activityResult.data?.getStringExtra("data")}")

    }

}



fun startAActivity(activity: FragmentActivity) {

    aActivityResultLauncher.launch(Intent(activity, AActivity::class.java))

}



fun startBActivity(activity: FragmentActivity) {

    bActivityResultLauncher.launch(Intent(activity, BActivity::class.java))

}

}


class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    Utils.registerForAActivityResult(this)

    Utils.registerForBActivityResult(this)



    findViewById<Button>(R.id.button1).setOnClickListener {

        Utils.startAActivity(this)

    }



    findViewById<Button>(R.id.button2).setOnClickListener {

        Utils.startBActivity(this)

    }

}

}




可以看到,此时MainActivity中的代码就非常的简洁了,对于开启新Activity和接收Activity结果的代码我们就写到了工具类中去了,这些代码没什么技术含量,封装后的好处就是可以使Activity中的代码更简单,方便我们理解Activity的功能。



一般我们接收到新Activity返回的结果是要在Activity中使用的,这个解决起来就很简单了,给工具类方法添加一个回调就可以了,示例如下:



object Utils {

private lateinit var aActivityResultLauncher: ActivityResultLauncher<Intent?>

private lateinit var bActivityResultLauncher: ActivityResultLauncher<Intent?>

private lateinit var aActivityResultCallback: ((String) -> Unit)

private lateinit var bActivityResultCallback: ((String) -> Unit)



fun registerForAActivityResult(activity: FragmentActivity) {

    aActivityResultLauncher = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult: ActivityResult ->

        val data = activityResult.data?.getStringExtra("data") ?: ""

        aActivityResultCallback(data)

    }

}



fun registerForBActivityResult(activity: FragmentActivity) {

    bActivityResultLauncher = activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { activityResult: ActivityResult ->

        val data = activityResult.data?.getStringExtra("data") ?: ""

        bActivityResultCallback(data)

    }

}



fun startAActivity(activity: FragmentActivity, resultCallback: (String) -> Unit) {

    aActivityResultCallback = resultCallback

    aActivityResultLauncher.launch(Intent(activity, AActivity::class.java))

}



fun startBActivity(activity: FragmentActivity, resultCallback: (String) -> Unit) {

    bActivityResultCallback = resultCallback

    bActivityResultLauncher.launch(Intent(activity, BActivity::class.java))

}

}


class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    Utils.registerForAActivityResult(this)

    Utils.registerForBActivityResult(this)



    findViewById<Button>(R.id.button1).setOnClickListener {

        Utils.startAActivity(this) { result ->

            Log.i("ABCD", "收到A_Activtiy的结果:$result")

        }

    }



    findViewById<Button>(R.id.button2).setOnClickListener {

        Utils.startBActivity(this) { result ->

            Log.i("ABCD", "收到B_Activtiy的结果:$result")

        }

    }

}

}




OK,大功告成!此时的MainActivity的代码还是非常简洁的!



[]( )请求权限的新方式

-----------------------------------------------------------------------



### []( )一次请求一个权限



前面学了使用`registerForActivityResult`的方式来开启新Activity并接收返回结果,而请求权限也是使用这种方式,这样使用方式就很统一了,示例如下:



在清单文件中声明如下权限:






MainActivity界面如下:  

![在这里插入图片描述](https://img-blog.csdnimg.cn/ebd21d1eb9584b6382f02bfbd37ff6b3.png)  

MainActivity代码如下:



class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)



    val requestCameraResultLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->

        Log.i("ABCD", "是否获得了摄像头权限:${if (isGranted) '是' else '否'}")

写在最后

对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。

![
[]


文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

avedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)



    val requestCameraResultLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->

        Log.i("ABCD", "是否获得了摄像头权限:${if (isGranted) '是' else '否'}")

写在最后

对程序员来说,很多技术的学习都是“防御性”的。也就是说,我们是在为未来学习。我们学习新技术的目的,或是为了在新项目中应用,或仅仅是为了将来的面试。但不管怎样,一定不能“止步不前”,不能荒废掉。

[外链图片转存中…(img-dcSo2Vmh-1715634651186)]

[外链图片转存中…(img-qMN618aY-1715634651186)]
[]

[外链图片转存中…(img-1l58BXua-1715634651187)]
[外链图片转存中…(img-k72zw9se-1715634651187)]

文章以下内容会给出阿里与美团的面试题(答案+解析)、面试题库、Java核心知识点梳理等

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 8
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值