11.疑难杂症

1.ListView 中图片错位的问题是如何产生的?

图片错位原理:

如果我们只是简单显示list中数据,而没用convertview的复用机制和异步操作,就不会产生图片错位;重用convertview但没用异步,也不会有错位现象。但我们的项目中list一般都会用,不然会很卡。
在上图中,我们能看到listview中整屏刚好显示7个item,当向下滑动时,显示出item8,而item8是重用的item1,如果此时异步网络请求item8的图片,比item1的图片慢,那么item8就会显示item1的image。当item8下载完成,此时用户向上滑显示item1时,又复用了item8的image,这样就导致了图片错位现象(item1和item8是用的同一块内存哦)。

解决方法:

对imageview设置tag,并预设一张图片。
向下滑动后,item8显示,item1隐藏。但由于item1是第一次进来就显示,所以一般情况下,item1都会比item8先下载完,但由于此时可见的it

// 给 ImageView 设置一个 tag
holder.img.setTag(imgUrl);
// 预设一个图片
holder.img.setImageResource(R.drawable.ic_launcher);

// 通过 tag 来防止图片错位
if (imageView.getTag() != null && imageView.getTag().equals(imageUrl)) {
    imageView.setImageBitmap(result);
}

2.混合开发有了解吗?

现在的app都开始流行混合开发了,这是一个app开发的新技术,作为android程序猿的我们也应该要了解并且掌握他。那么在使用之前,我们一定要搞清楚,我们的哪些场景使用混合开发好一些呢?这个问题一定要搞清楚,因为现在的混合开发还不成熟,Web页面的渲染效率目前还无法和Native的体验相比,而大家如果只是为了采用新技术就盲目的使用混合开发,最后遇到一些体验问题的话,肯定会得不偿失。

那么什么情况适合Html 5开发呢?像一些活动页面,比如秒杀、团购等适合做Html 5,因为这些页面可能涉及的非常炫而且复杂,Html 5开发或许会简单点,关键是这些页面时效性短,更新更快,因为一个活动说不定就一周时间,下周换活动,如果这样的话,你还做Native是肯定不行的,这些场景就需要使用混合开发了。

混合开发的实质就是在JS和Native之间相互调用,其中的第一篇博客中也提到了,实现混合开发的方式主要的有两种:1、js调用Native中的代码;2、WebView拦截页面跳转。第二种方式因为在Android 4.2(API 17)一下存在高危的漏洞,漏洞的原理就是Android系统通过 WebView.addJavascriptInterface(Object o, String interface) 方法注册可供js调用的Java对象,但是系统并没有对注册的Java对象方法调用做限制。导致攻击者可以利用反射调用未注册的其他任何Java对象,攻击者可以根据客户端的能力做任何事情.

总结

  1. 一定要支持javascript,可以通过ws.setJavaScriptEnabled(true)来设置

  2. 要设置一个H5回调Native的ChromeClient对象,可以通过wv.setWebChromeClient来完成

  3. 实现好你的Native回调类,也就是我要在这个类中干些什么事情,比如我要打开Activity,显示对话框,或者获取手机信息返回给H5等等

  4. 要将你的回调类的所有方法通过javascript角本注入到ChromeClient当中,注入是在ChromeClient的onProgressChanged方法中完成的,注入的数据是以string拼接出来的

参考:混合开发全解析


3.知道哪些混合开发的方式?说出它们的优缺点和各自使用场景?(解答:比如:RN,weex,H5,小程序,WPA等。做Android的了解一些前端js等还是很有好处的);

参考混合开发-最全常用的混合开发App方式总结


4.屏幕适配的处理技巧都有哪些?

参考Android 屏幕适配全攻略


5.服务器只提供数据接收接口,在多线程或多进程条件下,如何保证数据的有序到达?

1,有序 多线程要使用同步,多进程要使用进程通信保障数据传输的顺序

2,到达 使用tcp可靠传输,服务器返回传输成功后才能传输下一个

要在传输的包中加入,顺序标识


6.点击事件被拦截,但是想传到下面的View,如何操作?

重写子类的requestDisallowInterceptTouchEvent()方法返回true就不会执行父类的onInterceptTouchEvent(),即可将点击事件传到下面的View


7.动态布局的理解

参考动态加载布局文件的3种方法


8.怎么去除重复代码?

参考https://blog.csdn.net/weixin_33915554/article/details/93208555


9.画出 Android 的大体架构图


10.Recycleview 和 ListView的区别

参考RecyclerView和ListView的区别


11.动态权限适配方案,权限组的概念

备注:

(1)在AndroidManifest.xml 申请你需要所有权限,包括普通权限和需要申请的特殊权限。
(2)checkSelfPermission:检查是否拥有这个权限
(3)requestPermissions:请求权限,一般会弹出一个系统对话框,询问用户是否开启这个权限。
(4)shouldShowRequestPermissionRationale:Android原生系统中,如果第二次弹出权限申请的对话框,会出现“以后不再弹出”的提示框,如果用户勾选了,你再申请权限,则shouldShowRequestPermissionRationale返回true.
(5) 每个应用都有自己的权限管理界面,里面有本应用申请的权限以及各种状态,即使用户已经同意了你申请的权限,他也随时可以关闭

参考动态权限适配方案


12.Android系统为什么会设计ContentProvider?

(1)封装
​ 对数据进行封装,提供统一的接口,使用者完全不必关心这些数据是在DB,XML、Preferences或者网络请求来的。当项目需求要改变数据来源时,使用我们的地方完全不需要修改。

(2)提供一种跨进程数据共享的方式
​ 由系统来管理ContentProvider的创建、生命周期及访问的线程分配,简化我们在应用间共享数据(进程间通信)的方式。我们只管通过ContentResolver访问ContentProvider所提示的数据接口,而不需要担心它所在进程是启动还是未启动 。

(3)更好的数据访问权限管理
​ ContentProvider可以对开发的数据进行权限设置,不同的URI可以对应不同的权限,只有符合权限要求的组件才能访问到ContentProvider的具体操作。

ContentProvider是一个APP间共享数据的接口。一个程序可以通过实现一个Content provider的抽象接口将自己的数据完全暴露出去,例如在A APP中实现建ContentProvider,并在Manifest中生命它的Uri和权限,在B APP中注册权限,并通过ContentResolver和Uri进行增删改查;

ContentProvider也是通过Binder机制实现跨进程的通信,通过匿名共享内存的方式进行数据的传输,一个应用进程有16个Binder线程去和远程线程进行交互,每个线程可占用的缓存空间为128KB,超出会报异常。

ContentProvider的线程安全是跨进程的,不管Provider使用方是同一个进程的不同线程,还是不同的进程,Provider方实际上是同一个Provider对象实例,并发访问时,Provider方query()方法运行在不同的线程,实际上是运行在Provider方的进程的Binder线程池中。在AMS中,获取Provider相关的方法都有同步锁,所以这个Provider远程对象实际上是同一个。


13.下拉状态栏是不是影响activity的生命周期

Android下拉通知栏不会影响Activity的生命周期方法

1、为什么不走生命周期呢?

2、既然不走生命周期方法,开发中怎么监听到用户下拉通知栏呢?

针对第一个问题,我们来具体的来想一想,能引起Activity的生命周期的变化,需要什么条件?创建一个Activity,销毁一个Activity,隐藏一个Activity,跳转一个Activity等都可以,是不是发现,必须得有一个Activity参与,才能引起生命周期的变化,系统的通知栏是一个Activity吗?显然不是,它是一个用于通知的窗口,所以,既然不是Activity,当然也就不走任何生命周期了。

第二个问题,我们可以通过重写onWindowFocusChanged这个方法来监听通知栏的下拉,false为下拉。


14.如果在onStop的时候做了网络请求,onResume的时候怎么恢复?

恢复的是网络请求暂停后恢复?还是页面更新?

stop的时候请求被暂停,onstart的时候检测重新恢复请求即可

如果是恢复页面请求后的页面数据,分两种,1 activity被销毁,那么使用saveInstanceState存储数据,onRestoreInstanceState()恢复数据,2,没有被销毁,那就不需要恢复


15.Bitmap 使用时候注意什么?

为了避免oom 那么我们应该怎么做呢

根据我总结的,

1,要选择合适的图片规格(bitmap类型),即:

    ALPHA_8  每个像素占用1byte内存

    ARGB_4444 每个像素占用2byte内存

    ARGB_8888 每个像素占用4byte内存  不设置的话默认这个。

    RGB_565 每个像素占用2byte内存

2,降低采样率。BitmapFactory.Options 参数inSampleSize的使用,先把options.inJustDecodeBounds设为true,只是去读取图片的大小,在拿到图片的大小之后和要显示的大小做比较通过calculateInSampleSize()函数计算inSampleSize的具体值,得到值之后。options.inJustDecodeBounds设为false读图片资源。

3,复用内存。即,通过软引用(内存不够的时候才会回收掉),复用内存块,不需要在重新给这个bitmap申请一块新的内存,避免了一次内存的分配和回收,从而改善了运行效率。

当一个Bitmap从内存缓存中移除掉的时候,把这个Bitmap加入到复用的Set集合里面去。判断是否有Bitmap可以复用的时候先去这个集合里面拿到Bitmap,然后按照复用图片的规则(Android4.4以下的平台,需要保证inBitmap和即将要得到decode的Bitmap的尺寸规格一致,Android4.4及其以上的平台,只需要满足inBitmap的尺寸大于要decode得到的Bitmap的尺寸规格即可)判断是否可以复用。

4,及时回收。即,recycle。

5,压缩图片。compress。

6,尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过java层的createBitmap来完成的,需要消耗更多内存,可以通过BitmapFactory.decodeStream方法,创建出一个bitmap,再将其设为ImageView的 source。


16.Bitmap 的 recycler()

如果只是使用少量的几张图片,回收与否关系不大。可是若有大量bitmap需要垃圾回收处理,那必然垃圾回收需要做的次数就更多也发生地更频繁,会对系统资源造成负荷。所以,这个时候还是自己试用recycle来释放的比较好。


17.Android 中开启摄像头的主要步骤

  1. 获得摄像头管理器CameraManager mCameraManager,mCameraManager.openCamera()来打开摄像头
  2. 指定要打开的摄像头,并创建openCamera()所需要的CameraDevice.StateCallback stateCallback
  3. 在CameraDevice.StateCallback stateCallback中调用takePreview(),这个方法中,使用CaptureRequest.Builder创建预览需要的CameraRequest,并初始化了CameraCaptureSession,最后调用了setRepeatingRequest(previewRequest, null, childHandler)进行了预览
  4. 点击屏幕,调用takePicture(),这个方法内,最终调用了capture(mCaptureRequest, null, childHandler)
  5. 在new ImageReader.OnImageAvailableListener(){}回调方法中,将拍照拿到的图片进行展示

18.ViewPager 使用细节,如何设置成每次只初始化当前的Fragment,其他的不初始化?

自定义一个 LazyLoadFragment 基类,利用 setUserVisibleHint 和 生命周期方法,通过对 Fragment 状态判断,进行数据加载,并将数据加载的接口提供开放出去,供子类使用。然后在子类 Fragment 中实现 requestData 方法即可。这里添加了一个 isDataLoaded 变量,目的是避免重复加载数据。考虑到有时候需要刷新数据的问题,便提供了一个用于强制刷新的参数判断。


19.点击事件被拦截,但是想传到下面的View,如何操作?

重写子类的requestDisallowInterceptTouchEvent()方法返回true就不会执行父类的onInterceptTouchEvent(),即可将点击事件传到下面的View


20.微信主页面的实现方式

实现的原理:

ViewPager+FragmentPagerAdapter

主界面可分为三部分:

  • top标题栏就是一个TextView

  • 中间的ViewPager作为显示的容器,填充Fragment

  • bottom是一个RadioGroup

这里为了布局的优化,将top和bottom抽取出来 ,然后用include将其导入主布局


21.有没有遇到64k问题,为什么会出现这个问题,如何解决?

  • 在DEX文件中,method、field、class等的个数使用short类型来做索引,即两个字节(65535),method、field、class等均有此限制。
  • APK在安装过程中会调用dexopt将DEX文件优化成ODEX文件,dexopt使用LinearAlloc来存储应用信息,关于LinearAlloc缓冲区大小,不同的版本经历了4M/8M/16M的限制,超出 缓冲区时就会抛出INSTALL_FAILED_DEXOPT错误。

解决方案是Google的MultiDex方案,具体参见:配置方法数超过 64K 的应用

22.Activity、Window、DecorView之间关系

首先,来看一下Activity中setContentView的源代码。

 public void setContentView(@LayoutRes int layoutResID) {
        //将xml布局传递到Window当中
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

可以看到, Activity的 setContentView实质是将 View传递到 Window的 setContentView()方法中, Window的 setContenView会在内部调用 installDecor()方法创建 DecorView,代码如下。

 public void setContentView(int layoutResID) { 
        if (mContentParent == null) {
            //初始化DecorView以及其内部的content
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }
        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
        ...............
        } else {
            //将contentView加载到DecorVoew当中
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
        ...............
    }
  private void installDecor() {
        ...............
        if (mDecor == null) {
            //实例化DecorView
            mDecor = generateDecor(-1);
            ...............
            }
        } else {
            mDecor.setWindow(this);
        }
       if (mContentParent == null) {
            //获取Content
            mContentParent = generateLayout(mDecor);
       }  
        ...............
 }
 protected DecorView generateDecor(int featureId) {
        ...............
        return new DecorView(context, featureId, this, getAttributes());
 }

通过 generateDecor()的new一个 DecorView,然后调用 generateLayout()获取 DecorView中 content,最终通过 inflate将 Activity视图添加到 DecorView中的 content中,但此时 DecorView还未被添加到 Window中。添加操作需要借助 ViewRootImpl。

ViewRootImpl的作用是用来衔接 WindowManager和 DecorView,在 Activity被创建后会通过 WindowManager将 DecorView添加到 PhoneWindow中并且创建 ViewRootImpl实例,随后将 DecorView与 ViewRootImpl进行关联,最终通过执行 ViewRootImpl的 performTraversals()开启整个View树的绘制。

posted @ 2022-02-12 15:42  契阔  阅读(90)  评论(0编辑  收藏  举报