首页 文章 精选 留言 我的

精选列表

搜索[面试],共5083篇文章
优秀的个人博客,低调大师

2017年Android面试题总结

原文链接:http://mp.weixin.qq.com/s/EeoSKLcXlAlFCoRJFCBx7Q 一、Android整体架构 谈谈你对android系统(体系)架构的理解 Linux操作系统为核心,从下往上,依赖关系。 应用程序层:包括系统应用以及第三方应用。 应用程序框架:提供应用开发所必须的一些API框架,是软件复用的重要手段 库:android运行时(核心包(相当于JDK提供的包),虚拟机(优化过的JVM));C/C++的一些库 Linux核心:提供了电源管理、进程调度、内存管理、网络协议栈、驱动模型等核心系统服务 android中的四大组件以及应用场景 1、Activity:在Android应用中负责与用户交互的组件。 2、Service:常用于为其他组件提供后台服务或者监控其他组件的运行状态。经常用来执行一些耗时操作。 3、BroadcastReceiver:用于监听应用程序中的其他组件。 4、ContentProvider:Android应用程序之间实现实时数据交换。 二、Activity的生命周期 Activity生命周期 生命周期:对象什么时候生,什么时候死,怎么写代码,代码往那里写。 注意: 1、当打开新的Activity,采用透明主题的时候,当前Activity不会回调onStop 2、onCreate和onDestroy配对,onStart和onStop配对(是否可见),onResume和onPause配对(是否在前台,可以与用户交互) 3、打开新的Activity的时候,相关的Log为: Main1Activity: onPause Main2Activity: onCreate Main2Activity: onStart Main2Activity: onResume MainA1ctivity: onStop 异常状态下的生命周期: 资源相关的系统配置发生改变或者资源不足:例如屏幕旋转,当前Activity会销毁,并且在onStop之前回调onSaveInstanceState保存数据,在重新创建Activity的时候在onStart之后回调onRestoreInstanceState。其中Bundle数据会传到onCreate(不一定有数据)和onRestoreInstanceState(一定有数据)。 防止屏幕旋转的时候重建,在清单文件中添加配置: android:configChanges="orientation" 2、Fragment的生命周期 Fragment生命周期 正常启动 Activity: onCreate Fragment: onAttach Fragment: onCreate Fragment: onCreateView Fragment: onActivityCreated Activity: onStart Activity: onResume 正常退出 Activity: onPause Activity: onStop Fragment: onDestroyView Fragment: onDestroy Fragment: onDetach Activity: onDestroy 三、Activity的启动模式 1、standard:每次激活Activity时(startActivity),都创建Activity实例,并放入任务栈; 2、singleTop:如果某个Activity自己激活自己,即任务栈栈顶就是该Activity,则不需要创建,其余情况都要创建Activity实例; 3、singleTask:如果要激活的那个Activity在任务栈中存在该实例,则不需要创建,只需要把此Activity放入栈顶,即把该Activity以上的Activity实例都pop,并调用其onNewIntent; 4、singleInstance:应用1的任务栈中创建了MainActivity实例,如果应用2也要激活MainActivity,则不需要创建,两应用共享该Activity实例。 四、Activity与Fragment之间的传值 1、通过findFragmentByTag或者getActivity获得对方的引用(强转)之后,再相互调用对方的public方法,但是这样做一是引入了“强转”的丑陋代码,另外两个类之间各自持有对方的强引用,耦合较大,容易造成内存泄漏。 2、通过Bundle的方法进行传值,例如以下代码: //Activity中对fragment设置一些参数 fragment.setArguments(bundle); //fragment中通过getArguments获得Activity中的方法 Bundle arguments = getArguments() 3、利用eventbus进行通信,这种方法实时性高,而且Activity与Fragment之间可以完全解耦 //Activity中的代码 EventBus.getDefault().post("消息"); //Fragment中的代码 EventBus.getDefault().register(this); @Subscribe public void test(String text) { tv_test.setText(text); } 五、Service Service分为两种: 1、本地服务,属于同一个应用程序,通过startService来启动或者通过bindService来绑定并且获取代理对象。如果只是想开个服务在后台运行的话,直接startService即可,如果需要相互之间进行传值或者操作的话,就应该通过bindService。 2、远程服务(不同应用程序之间),通过bindService来绑定并且获取代理对象。 对应的生命周期如下: context.startService() ->onCreate()- >onStartCommand()->Service running--调用context.stopService() ->onDestroy() context.bindService()->onCreate()->onBind()->Service running--调用>onUnbind() -> onDestroy() Servic生命周期 注意 Service默认是运行在main线程的,因此Service中如果需要执行耗时操作(大文件的操作,数据库的拷贝,网络请求,文件下载等)的话应该在子线程中完成。 特殊情况是:Service在清单文件中指定了在其他进程中运行。 六、Android中的消息传递机制 为什么要使用Handler? 因为屏幕的刷新频率是60Hz,大概16毫秒会刷新一次,所以为了保证UI的流畅性,耗时操作需要在子线程中处理,子线程不能直接对UI进行更新操作。因此需要Handler在子线程发消息给主线程来更新UI。 这里再深入一点,Android中的UI控件不是线程安全的,因此在多线程并发访问UI的时候会导致UI控件处于不可预期的状态。Google不通过锁的机制来处理这个问题是因为: 1、引入锁会导致UI的操作变得复杂 2、引入锁会导致UI的运行效率降低 因此,Google的工程师最后是通过单线程的模型来操作UI,开发者只需要通过Handler在不同线程之间切花就可以了。 概述一下Android中的消息机制? Android中的消息机制主要是指Handler的运行机制。Handler是进行线程切换的关键,在主线程和子线程之间切换只是一种比较特殊的使用情景而已。其中消息传递机制需要了解的东西有Message、Handler、Looper、Looper里面的MessageQueue对象。 Handler工作原理 如上图所示,我们可以把整个消息机制看作是一条流水线。其中: 1、MessageQueue是传送带,负责Message队列的传送与管理 2、Looper是流水线的发动机,不断地把消息从消息队列里面取出来,交给Handler来处理 3、Message是每一件产品 4、Handler就是工人。但是这么比喻不太恰当,因为发送以及最终处理Message的都是Handler 为什么在子线程中创建Handler会抛异常? Handler的工作是依赖于Looper的,而Looper(与消息队列)又是属于某一个线程(ThreadLocal是线程内部的数据存储类,通过它可以在指定线程中存储数据,其他线程则无法获取到),其他线程不能访问。因此Handler就是间接跟线程是绑定在一起了。因此要使用Handler必须要保证Handler所创建的线程中有Looper对象并且启动循环。因为子线程中默认是没有Looper的,所以会报错。 正确的使用方法是: handler = null; new Thread(new Runnable() { private Looper mLooper; @Override public void run() { //必须调用Looper的prepare方法为当前线程创建一个Looper对象,然后启动循环 //prepare方法中实质是给ThreadLocal对象创建了一个Looper对象 //如果当前线程已经创建过Looper对象了,那么会报错 Looper.prepare(); handler = new Handler(); //获取Looper对象 mLooper = Looper.myLooper(); //启动消息循环 Looper.loop(); //在适当的时候退出Looper的消息循环,防止内存泄漏 mLooper.quit(); } }).start(); 主线程中默认是创建了Looper并且启动了消息的循环的,因此不会报错: 应用程序的入口是ActivityThread的main方法,在这个方法里面会创建Looper,并且执行Looper的loop方法来启动消息的循环,使得应用程序一直运行。 子线程中可以通过Handler发送消息给主线程吗? 可以。有时候出于业务需要,主线程可以向子线程发送消息。子线程的Handler必须按照上述方法创建,并且关联Looper。 七、事件传递机制以及自定义View相关 Android的视图树 Android中View的机制主要是Activity的显示,每个Activity都有一个Window(具体在手机中的实现类是PhoneWindow),Window以下有DecorView,DecorView下面有TitleVie以及ContentView,而ContentView就是我们在Activity中通过setContentView指定的。 Android的View树 事件传分发机制 Android事件分发机制 ViewGroup有以下三个与事件分发的方法,而View只有dispatchTouchEvent和onTouchEvent。 @Override public boolean dispatchTouchEvent(MotionEvent ev) { return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); } 事件总是从上往下进行分发,即先到达Activity,再到达ViewGroup,再到达子View,如果没有任何视图消耗事件的话,事件会顺着路径往回传递。其中: 1、dispatchTouchEvent是事件的分发方法,如果事件能够到达该视图的话,就首先一定会调用,一般我们不会去修改这个方法。 2、onInterceptTouchEvent是事件分发的核心方法,表示ViewGroup是否拦截事件,如果返回true表示拦截,在这之后ViewGroup的onTouchEvent会被调用,事件就不会往下传递。 3、onTouchEvent是最低级的,在事件分发中最后被调用。 4、子View可以通过requestDisallowInterceptTouchEvent方法去请求父元素不要拦截。 注意 1、事件从Activity.dispatchTouchEvent()开始传递,只要没有被停止或拦截,从最上层的View(ViewGroup)开始一直往下(子View)传递。子View 可以通过onTouchEvent()对事件进行处理。 2、事件由父View(ViewGroup)传递给子View,ViewGroup 可以通过onInterceptTouchEvent()对事件做拦截,停止其往下传递。 3、如果事件从上往下传递过程中一直没有被停止,且最底层子View 没有消费事件,事件会反向往上传递,这时父View(ViewGroup)可以进行消费,如果还是没有被消费的话,最后会到Activity 的onTouchEvent()函数。 4、如果View 没有对ACTION_DOWN 进行消费,之后的其他事件不会传递过来。 5、OnTouchListener 优先于onTouchEvent()对事件进行消费。 自定义View的分类 1、对现有的View的子类进行扩展,例如复写onDraw方法、扩展新功能等。 2、自定义组合控件,把常用一些控件组合起来以方便使用。 3、直接继承View实现View的完全定制,需要完成View的测量以及绘制。 4、自定义ViewGroup,需要复写onLayout完成子View位置的确定等工作。 View的测量-onMeasure View的测量最终是在onMeasure方法中通过setMeasuredDimension把代表宽高两个MeasureSpec设置给View,因此需要掌握MeasureSpec。MeasureSpec包括大小信息以及模式信息。 MeasureSpec的三种模式: 1、EXACTLY模式:精确模式,对应于用户指定为match_parent或者具体大小的时候(实际上指定为match_parent实质上是指定大小为父容器的大小) 2、AT_MOST模式:对应于用户指定为wrap_content,此时控件尺寸只要不超过父控件允许的最大尺寸即可。 3、UNSPECIFIED模式:不指定大小的测量模式,这种模式比较少用 下面给出模板代码: public class MeasureUtils { /** * 用于View的测量 * * @param measureSpec * @param defaultSize * @return */ public static int measureView(int measureSpec, int defaultSize) { int measureSize; //获取用户指定的大小以及模式 int mode = View.MeasureSpec.getMode(measureSpec); int size = View.MeasureSpec.getSize(measureSpec); //根据模式去返回大小 if (mode == View.MeasureSpec.EXACTLY) { //精确模式(指定大小以及match_parent)直接返回指定的大小 measureSize = size; } else { //UNSPECIFIED模式、AT_MOST模式(wrap_content)的话需要提供默认的大小 measureSize = defaultSize; if (mode == View.MeasureSpec.AT_MOST) { //AT_MOST(wrap_content)模式下,需要取测量值与默认值的最小值 measureSize = Math.min(measureSize, defaultSize); } } return measureSize; } } 最后,复写onMeasure方法,把super方法去掉: @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(MeasureUtils.measureView(widthMeasureSpec, 200), MeasureUtils.measureView(heightMeasureSpec, 200) ); } View的绘制-onDraw View绘制,需要掌握Android中View的坐标体系: View绘制 View的坐标体系是以左上角为坐标原点,向右为X轴正方向,向下为Y轴正方向。 View绘制,主要是通过Android的2D绘图机制来完成,时机是onDraw方法中,其中包括画布Canvas,画笔Paint。下面给出示例代码。相关API不是介绍的重点,重点是Canvas的save和restore方法,通过save以后可以对画布进行一些放大缩小旋转倾斜等操作,这两个方法一般配套使用,其中save的调用次数可以多于restore。 @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); Bitmap bitmap = ImageUtils.drawable2Bitmap(mDrawable); canvas.drawBitmap(bitmap, getLeft(), getTop(), mPaint); canvas.save(); //注意,这里的旋转是指画布的旋转 canvas.rotate(90); mPaint.setColor(Color.parseColor("#FF4081")); mPaint.setTextSize(30); canvas.drawText("测试", 100, -100, mPaint); canvas.restore(); } View的位置-onLayout 与布局位置相关的是onLayout方法的复写,一般我们自定义View的时候,只需要完成测量,绘制即可。如果是自定义ViewGroup的话,需要做的就是在onLayout中测量自身以及控制子控件的布局位置,onLayout是自定义ViewGroup必须实现的方法。 八、性能优化 布局优化 1、使用include标签,通过layout属性复用相同的布局。 <include android:id="@+id/v_test" layout="@layout/include_view" /> 2、使用merge标签,去除同类的视图 3、使用ViewStub来进行布局的延迟加载一些不是马上就用到的布局。例如列表页中,列表在没有拿到数据之前不加载,这样做可以使UI变得流畅。 <ViewStub android:id="@+id/v_stub" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout="@layout/view_stub" /> //需要手动调用inflate方法,布局才会显示出来。 stub.inflate(); //其中setVisibility在底层也是会调用inflate方法 //stub.setVisibility(View.VISIBLE); //之后,如果要使用ViewStub标签里面的View,只需要按照平常来即可。 TextView tv_1 = (TextView) findViewById(R.id.tv_1); 4、尽量多使用RelativeLayout,因为这样可以大大减少视图的层级。 内存优化 APP设计以及代码编写阶段都应该考虑内存优化: 1、珍惜Service,尽量使得Service在使用的时候才处于运行状态。尽量使用IntentService IntentService在内部其实是通过线程以及Handler实现的,当有新的Intent到来的时候,会创建线程并且处理这个Intent,处理完毕以后就自动销毁自身。因此使用IntentService能够节省系统资源。 2、内存紧张的时候释放资源(例如UI隐藏的时候释放资源等)。复写Activity的回调方法。 @Override public void onLowMemory() { super.onLowMemory(); } @Override public void onTrimMemory(int level) { super.onTrimMemory(level); switch (level) { case TRIM_MEMORY_COMPLETE: //... break; case 其他: } } 3、通过Manifest中对Application配置更大的内存,但是一般不推荐 android:largeHeap="true" 4、避免Bitmap的浪费,应该尽量去适配屏幕设备。尽量使用成熟的图片加载框架,Picasso,Fresco,Glide等。 5、使用优化的容器,SparseArray等 6、其他建议:尽量少用枚举变量,尽量少用抽象,尽量少增加类,避免使用依赖注入框架,谨慎使用library,使用代码混淆,时当场合考虑使用多进程等。 7、避免内存泄漏(本来应该被回收的对象没有被回收)。一旦APP的内存短时间内快速增长或者GC非常频繁的时候,就应该考虑是否是内存泄漏导致的。 分析方法 使用Android Studio提供的Android Monitors中Memory工具查看内存的使用以及没使用的情况。 使用DDMS提供的Heap工具查看内存使用情况,也可以手动触发GC。 使用性能分析的依赖库,例如Square的LeakCanary,这个库会在内存泄漏的前后通过Notification通知你。 什么情况会导致内存泄漏 1、资源释放问题:程序代码的问题,长期保持某些资源,如Context、Cursor、IO 流的引用,资源得不到释放造成内存泄露。 2、对象内存过大问题:保存了多个耗用内存过大的对象(如Bitmap、XML 文件),造成内存超出限制。 3、static 关键字的使用问题:static 是Java 中的一个关键字,当用它来修饰成员变量时,那么该变量就属于该类,而不是该类的实例。所以用static 修饰的变量,它的生命周期是很长的,如果用它来引用一些资源耗费过多的实例(Context 的情况最多),这时就要谨慎对待了。 解决方案 应该尽量避免static 成员变量引用资源耗费过多的实例,比如Context。 Context 尽量使用ApplicationContext,因为Application 的Context 的生命周期比较长,引用它不会出现内存泄露的问题。 使用WeakReference 代替强引用。比如可以使用WeakReference<Context> mContextRef 4、线程导致内存溢出:线程产生内存泄露的主要原因在于线程生命周期的不可控。例如Activity中的Thread在run了,但是Activity由于某种原因重新创建了,但是Thread仍然会运行,因为run方法不结束的话Thread是不会销毁的。 解决方案 将线程的内部类,改为静态内部类(因为非静态内部类拥有外部类对象的强引用,而静态类则不拥有)。 在线程内部采用弱引用保存Context 引用。 查看内存泄漏的方法、工具 1、android官方提供的工具:Memory Monitor(当APP占用的内存在短时间内快速增长或者GC变得频繁的时候)、DDMS提供的Heap工具(手动触发GC) 2、Square提供的内存泄漏检测工具,LeakCanary(能够自动完成内存追踪、检测、输出结果),进行演示,并且适当的解说。 性能优化 1、防止过度绘制,通过打开手机的“显示过度绘制区域”即可查看过度绘制的情况。 2、最小化渲染时间,使用视图树查看节点,对节点进行性能分析。 3、通过TraceView进行数据的采集以及分析。在有大概定位的时候,使用Android官方提供的Debug类进行采集。最后通过DDMS即可打开这个.trace文件,分析函数的调用情况(包括在指定情况下执行时间,调用次数) //开启数据采集 Debug.startMethodTracing("test.trace"); //关闭 Debug.stopMethodTracing(); OOM 避免OOM的一些常见方法: 1、App资源中尽量少用大图。使用Bitmap的时候要注意等比例缩小图片,并且注意Bitmap的回收。 BitmapFactory.Options options = new BitmapFactory.Option(); options.inSampleSize = 2; //Options 只保存图片尺寸大小,不保存图片到内存 BitmapFactory.Options opts = new BitmapFactory.Options(); opts.inSampleSize = 2; Bitmap bmp = null; bmp = BitmapFactory.decodeResource(getResources(), mImageIds[position],opts); //回收 bmp.recycle(); 2、结合组件的生命周期,释放资源 3、IO流,数据库查询的游标等应该在使用完之后及时关闭。 4、ListView中应该使用ViewHolder模式缓存ConverView 5、页面切换的时候尽量去传递(复用)一些对象 ANR 不同的组件发生ANR 的时间不一样,主线程(Activity、Service)是5 秒,BroadCastReceiver 是10 秒。 ANR一般有三种类型: 1、KeyDispatchTimeout(5 seconds) 主要类型按键或触摸事件在特定时间内无响应 2、BroadcastTimeout(10 seconds) BroadcastReceiver在特定时间内无法处理完成 3、ServiceTimeout(20 seconds) 小概率类型Service在特定的时间内无法处理完成 解决方案: UI线程只进行UI相关的操作。所有耗时操作,比如访问网络,Socket 通信,查询大量SQL 语句,复杂逻辑计算等都放在子线程中去,然后通过handler.sendMessage、runonUITread、AsyncTask 等方式更新UI。 无论如何都要确保用户界面操作的流畅度。如果耗时操作需要让用户等待,那么可以在界面上显示进度条。 BroadCastReceiver要进行复杂操作的的时候,可以在onReceive()方法中启动一个Service来处理。 九、九切图(.9图)、SVG图片 九切图 点九图,是Android开发中用到的一种特殊格式的图片,文件名以”.9.png“结尾。这种图片能告诉程序,图像哪一部分可以被拉升,哪一部分不能被拉升需要保持原有比列。运用点九图可以保证图片在不模糊变形的前提下做到自适应。点九图常用于对话框背景图片中。 九切图实例 1、1、2部分规定了图像的可拉伸部分,当实际程序中设定了对话框的宽高时,1、2部分就会被拉伸成所需要的高和宽,呈现出于设计稿一样的视觉效果。 2、而3、4部分规定了图像的内容区域。内容区域规定了可编辑区域,例如文字需要被包裹在其内。 微信图片_20180110100007.jpg android5.0的SCG矢量动画机制 1、图像在方法缩小的时候图片质量不会有损失 2、使用XML来定义图形 3、适配不同分辨率 十、Android中数据常见存储方式 1、文件(包括XML、SharePreference等) 2、数据库 3、Content Provider 4、保存在网络 十一、进程间通信 操作系统进程间通信的方法,android中有哪些? 操作系统: 1、Windows:剪贴板、管道、邮槽等 2、Linux:命名管道、共享内存、信号量 Android中的进程通信方式并不是完全继承于Linux: 1、Bundle 2、文件共享 3、AIDL 4、Messenger 5、Content Provider 6、Socket 常见的网络框架 1、HttpURLConnection:在Android 2.2版本之前,HttpClient拥有较少的bug,因此使用它是最好的选择。而在Android 2.3版本及以后,HttpURLConnection则是最佳的选择。它的API简单,体积较小,因而非常适用于Android项目。压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。对于新的应用程序应该更加偏向于使用HttpURLConnection,因为在以后的工作当中我们也会将更多的时间放在优化HttpURLConnection上面。特点:比较轻便,灵活,易于扩展,在3.0后以及4.0中都进行了改善,如对HTTPS的支持,在4.0中,还增加了对缓存的支持。 2、HttpClient:高效稳定,但是维护成本高昂,故android 开发团队不愿意在维护该库而是转投更为轻便的 3、okHttp:okhttp 是一个 Java 的 HTTP+SPDY 客户端开发包,同时也支持 Android。需要Android 2.3以上。特点:OKHttp是Android版Http客户端。非常高效,支持SPDY、连接池、GZIP和 HTTP 缓存。默认情况下,OKHttp会自动处理常见的网络问题,像二次连接、SSL的握手问题。如果你的应用程序中集成了OKHttp,Retrofit默认会使用OKHttp处理其他网络层请求。从Android4.4开始HttpURLConnection的底层实现采用的是okHttp。 4、volley:早期使用HttpClient,后来使用HttpURLConnection,是谷歌2013年推出的网络请求框架,非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。 5、xutils:缓存网络请求数据 6、Retrofit:和Volley框架的请求方式很相似,底层网络请求采用okhttp(效率高,android4.4底层采用okhttp),采用注解方式来指定请求方式和url地址,减少了代码量。 7、AsyncTask 十三、常用的图片加载框架以及特点、源码 1、Picasso:PicassoSquare的网络库一起能发挥最大作用,因为Picasso可以选择将网络请求的缓存部分交给了okhttp实现。 2、Glide:模仿了Picasso的API,而且在他的基础上加了很多的扩展(比如gif等支持),支持图片流,因此在做爱拍之类的视频应用用得比较多一些。 3、Fresco:Fresco中设计有一个叫做image pipeline的模块。它负责从网络,从本地文件系统,本地资源加载图片。 为了最大限度节省空间和CPU时间,它含有3级缓存设计(2级内存,1级文件)。Fresco中设计有一个叫做Drawees模块, 方便地显示loading图,当图片不再显示在屏幕上时,及时地释放内存和空间占用。 Fresco是把图片缓存放在了Ashmem(系统匿名内存共享区) 1、Heap-堆内存:Android中每个App的 Java堆内存大小都是被严格的限制的。每个对象都是使用Java的new在堆内存实例化,这是内存中相对安全的一块区域。内存有垃圾回收机制,所以当 App不在使用内存的时候,系统就会自动把这块内存回收。不幸的是,内存进行垃圾回收的过程正是问题所在。当内存进行垃圾回收时,内存不仅仅进行了垃圾回收,还把 Android 应用完全终止了。这也是用户在使用 App 时最常见的卡顿或短暂假死的原因之一。 2、Ashmem:Android 在操作 Ashmem 堆时,会把该堆中存有数据的内存区域从 Ashmem 堆中抽取出来,而不是把它释放掉,这是一种弱内存释放模式;被抽取出来的这部分内存只有当系统真正需要更多的内存时(系统内存不够用)才会被释放。当 Android 把被抽取出来的这部分内存放回 Ashmem 堆,只要被抽取的内存空间没有被释放,之前的数据就会恢复到相应的位置。 不管发生什么,垃圾回收器都不会自动回收这些 Bitmap。当 Android 绘制系统在渲染这些图片,Android 的系统库就会把这些 Bitmap 从 Ashmem 堆中抽取出来,而当渲染结束后,这些 Bitmap 又会被放回到原来的位置。如果一个被抽取的图片需要再绘制一次,系统仅仅需要把它再解码一次,这个操作非常迅速。 十四、在Android开发里用什么做线程间的通讯工具? 传统点的方法就是往同步代码块里些数据,然后使用回调让另外一条线程去读。在Android里我一般会创建Looper线程,然后Hanlder传递消息。 十五、15、Android新特性相关 1、5.0:Material Design、多种设备的支持、支持64位ART虚拟机、Project Volta电池续航改进计划等 2、6.0:动态权限管理、过度动画、支付、指纹等 3、7.0:分屏、通知消息快捷回复、夜间模式、流量保护模式等 十六、网络请求优化 网络请求优化 1、能够缓存起来的尽量去缓存起来,减轻服务器的压力。例如APP中首页的一些数据,又例如首页的图标、文案都是缓存起来的,而且这些数据通过网络来指定可以使app具有更大的灵活性。 2、不用域名,用 IP 直连,省去了DNS域名解析。 3、连接复用、请求合并、请求数据Body可以利用压缩算法Gzip来进行压缩,使用JSON 代替 XML 网络请求的安全性 这块了解的不多。我给你说说我的思路吧,利用哈希算法,比如MD5,服务器给我们的数据可以通过时间戳和其他参数做个加密,得到一个key,在客户端取出数据后根据数据和时间戳再去生成key与服务端给的做个对比。 十七、新技术相关 RXJava:一个异步请求库,核心就是异步。利用的是一种扩展的观察模式,被观察者发生某种变化的时候,可以通过事件(onNext、onError、onComplete)等方式通过观察者。RXJava同时支持线程的调度和切换,用户可以指定订阅发生的线程以及观察者触发的线程。 Retrofit:通过注解的方式来指定URL、请求方法,实质上底层是通过OKHttp来实现的。

优秀的个人博客,低调大师

Android面试,IntentService的原理及使用

在Android开发中,我们或许会碰到这么一种业务需求,一项任务分成几个子任务,子任务按顺序先后执行,子任务全部执行完后,这项任务才算成功。那么,利用几个子线程顺序执行是可以达到这个目的的,但是每个线程必须去手动控制,而且得在一个子线程执行完后,再开启另一个子线程。或者,全部放到一个线程中让其顺序执行。这样都可以做到,但是,如果这是一个后台任务,就得放到Service里面,由于Service和Activity是同级的,所以,要执行耗时任务,就得在Service里面开子线程来执行。那么,有没有一种简单的方法来处理这个过程呢,答案就是IntentService。 什么是IntentService,IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工作线程来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完后,IntentService会自动停止,而不需要我们去手动控制。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。 所有请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。 那么,用IntentService有什么好处呢?首先,我们省去了在Service中手动开线程的麻烦,第二,当操作完成时,我们不用手动停止Service。 Code 接下来让我们来看看如何使用,我写了一个Demo来模拟两个耗时操作,Operation1与Operation2,先执行1,2必须等1执行完才能执行: 新建工程,新建一个继承IntentService的类,我这里是IntentServiceDemo.java public class IntentServiceDemo extends IntentService { public IntentServiceDemo() { //必须实现父类的构造方法 super("IntentServiceDemo"); } @Override public IBinder onBind(Intent intent) { System.out.println("onBind"); return super.onBind(intent); } @Override public void onCreate() { System.out.println("onCreate"); super.onCreate(); } @Override public void onStart(Intent intent, int startId) { System.out.println("onStart"); super.onStart(intent, startId); } @Override public int onStartCommand(Intent intent, int flags, int startId) { System.out.println("onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public void setIntentRedelivery(boolean enabled) { super.setIntentRedelivery(enabled); System.out.println("setIntentRedelivery"); } @Override protected void onHandleIntent(Intent intent) { //Intent是从Activity发过来的,携带识别参数,根据参数不同执行不同的任务 String action = intent.getExtras().getString("param"); if (action.equals("oper1")) { System.out.println("Operation1"); }else if (action.equals("oper2")) { System.out.println("Operation2"); } try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } } @Override public void onDestroy() { System.out.println("onDestroy"); super.onDestroy(); } } 我把生命周期方法全打印出来了,待会我们来看看它执行的过程是怎样的。接下来是Activity,在Activity中来启动IntentService: public class TestActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); //可以启动多次,每启动一次,就会新建一个work thread,但IntentService的实例始终只有一个 //Operation 1 Intent startServiceIntent = new Intent("com.test.intentservice"); Bundle bundle = new Bundle(); bundle.putString("param", "oper1"); startServiceIntent.putExtras(bundle); startService(startServiceIntent); //Operation 2 Intent startServiceIntent2 = new Intent("com.test.intentservice"); Bundle bundle2 = new Bundle(); bundle2.putString("param", "oper2"); startServiceIntent2.putExtras(bundle2); startService(startServiceIntent2); } } 最后,别忘了配置Service,因为它继承于Service,所以,它还是一个Service,一定要配置,否则是不起作用的,开始我就是忘了,结果半天没反应。 <service android:name=".IntentServiceDemo"> <intent-filter > <action android:name="com.test.intentservice"/> </intent-filter> </service> 最后来看看执行结果: 从结果可以看到,onCreate方法只执行了一次,而onStartCommand和onStart方法执行了两次,开启了两个Work Thread,这就证实了之前所说的,启动多次,但IntentService的实例只有一个,这跟传统的Service是一样的。Operation1也是先于Operation2打印,并且我让两个操作间停顿了2s,最后是onDestroy销毁了IntentService。 我是天王盖地虎的分割线 本文转自我爱物联网博客园博客,原文链接:http://www.cnblogs.com/yydcdut/p/3960886.html,如需转载请自行联系原作者

优秀的个人博客,低调大师

Android -- 面试 -- 数据库升级策略

升级:重写onUpgrade方法 确定 相邻版本 的差别,从版本1开始依次迭代更新,先执行v1到v2,再v2到v3…… 为 每个版本 确定与现在数据库的差别,为每个case撰写专门的升级代码。 降级 onDowngrade()数据库降级:比如从数据库4降级到数据库3必须重写该方法。 @Override public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { super.onDowngrade(db, oldVersion, newVersion); } 迁移数据: 将现有表重命名为临时表; 创建新表; 将临时表的数据导入新表(注意处理修改的列); 删除临时表。 protected void upgradeTables(SQLiteDatabase db, String tableName, String columns) { try { db.beginTransaction(); // 1, 将现有表重命名为临时表 String tempTableName = tableName + "_temp"; String sql = "ALTER TABLE " + tableName +" RENAME TO " + tempTableName; execSQL(db, sql, null); // 2, 创建新表 //onCreateTable(db); createNewTableX(db); // 3, 将临时表数据导入新表中 sql = "INSERT INTO " + tableName + " (" + columns + ") " + " SELECT " + columns + " FROM " + tempTableName; execSQL(db, sql, null); // 4, 删除临时表 execSQL(db, "DROP TABLE IF EXISTS " + tempTableName, null); db.setTransactionSuccessful(); } catch (SQLException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } finally { db.endTransaction(); } }

优秀的个人博客,低调大师

10道面试官喜欢问的微服务面试题Spring Cloud+Spring Boot

前言为什么要使用微服务?随着互联网的快速发展,各行各业都在用互联网。互联网已经离不开人们的形形色色。随着越来越多的用户,业务场景也愈来愈复杂。传统的单体架构已经很难满足互联网技术发展的要求,代码可维护性扩展性和可读性降低,维护成本的提高都是驱动微服务的发展趋势。 微服务哪些框架 Dubbo,是阿里巴巴服务化治理的核心框架,并被广泛应用于阿里巴巴集团的各成员站点。阿里巴巴近几年对开源社区的贡献不论在国内还是国外都是引人注目的,比如:JStorm 捐赠给 Apache 并加入 Apache 基金会等,为中国互联网人争足了面子,使得阿里巴巴在国人眼里已经从电商升级为一家科技公司了。Spring Cloud,从命名我们就可以知道,它是 Spring Source 的产物,Spring 社区的强大背书可以说是 Java 企业界最有影响力的组织了,除了 Spring Source 之外,还有Pivotal 和 Netfix 是其强大的后盾与技术输出。其中 Netflix 开源的整套微服务架构套件是Spring Cloud 的核心。 说说 RPC 的实现原理 首先需要有处理网络连接通讯的模块,负责连接建立、管理和消息的传输。其次需要有编解码的模块,因为网络通讯都是传输的字节码,需要将我们使用的对象序列化和反序列化。剩下的就是客户端和服务器端的部分,服务器端暴露要开放的服务接口,客户调用服务接口的一个代理实现,这个代理实现负责收集数据、编码并传输给服务器然后等待结果返回。 说说 Dubbo 的实现原理 dubbo 作为 rpc 框架,实现的效果就是调用远程的方法就像在本地调用一样。如何做到呢?就是本地有对远程方法的描述,包括方法名、参数、返回值,在 dubbo 中是远程和本地使用同样的接口;然后呢,要有对网络通信的封装,要对调用方来说通信细节是完全不可见的,网络通信要做的就是将调用方法的属性通过一定的协议(简单来说就是消息格式)传递到服务端;服务端按照协议解析出调用的信息;执行相应的方法;在将方法的返回值通过协议传递给客户端;客户端再解析;在调用方式上又可以分为同步调用和异步调用;简单来说基本就这个过程 Spring Boot 有哪些优点? Spring Boot 的优点有:减少开发,测试时间和努力。使用 JavaConfig 有助于避免使用 XML。避免大量的 Maven 导入和各种版本冲突。提供意见发展方法。通过提供默认值快速开始开发。没有单独的 Web 服务器需要。这意味着你不再需要启动 Tomcat,Glassfish 或其他任何东西。需要更少的配置 因为没有 web.xml 文件。只需添加用@ Configuration 注释的类,然后添加用@Bean 注释的方法,Spring 将自动加载对象并像以前一样对其进行管理。您甚至可以将@Autowired 添加到 bean 方法中,以使 Spring 自动装入需要的依赖关系中。基于环境的配置 使用这些属性,您可以将您正在使用的环境传递到应用程序:-Dspring.profiles.active = {enviornment}。在加载主应用程序属性文件后,Spring 将在(application{environment} .properties)中加载后续的应用程序属性文件。如何实现 Spring Boot 应用程序的安全性?为了实现 Spring Boot 的安全性,我们使用 spring-boot-starter-security 依赖项,并且必须添加安全配置。它只需要很少的代码。配置类将必须扩展 WebSecurityConfigurerAdapter 并覆盖其方法。 如何集成 Spring Boot 和 ActiveMQ? 对于集成 Spring Boot 和 ActiveMQ,我们使用spring-boot-starter-activemq依赖关系。 它只需要很少的配置,并且不需要样板代码。 什么是 Swagger?你用 Spring Boot 实现了它吗? Swagger 广泛用于可视化 API,使用 Swagger UI 为前端开发人员提供在线沙箱。Swagger 是用于生成 RESTful Web 服务的可视化表示的工具,规范和完整框架实现。它使文档能够以与服务器相同的速度更新。当通过 Swagger 正确定义时,消费者可以使用最少量的实现逻辑来理解远程服务并与其进行交互。因此,Swagger 消除了调用服务时的猜测。 使用 Spring Cloud 有什么优势? 使用 Spring Boot 开发分布式微服务时,我们面临以下问题与分布式系统相关的复杂性-这种开销包括网络问题,延迟开销,带宽问题,安全问题。服务发现-服务发现工具管理群集中的流程和服务如何查找和互相交谈。它涉及一个服务目录,在该目录中注册服务,然后能够查找并连接到该目录中的服务。冗余-分布式系统中的冗余问题。负载平衡 --负载平衡改善跨多个计算资源的工作负荷,诸如计算机,计算机集群,网络链路,中央处理单元,或磁盘驱动器的分布。性能-问题 由于各种运营开销导致的性能问题。部署复杂性-Devops 技能的要求。 负载平衡的意义什么? 在计算中,负载平衡可以改善跨计算机,计算机集群,网络链接,中央处理单元或磁盘驱动器等多种计算资源的工作负载分布。负载平衡旨在优化资源使用,最大化吞吐量,最小化响应时间并避免任何单一资源的过载。使用多个组件进行负载平衡而不是单个组件可能会通过冗余来提高可靠性和可用性。负载平衡通常涉及专用软件或硬件,例如多层交换机或域名系统服务器进程。 什么是 Netflix Feign?它的优点是什么? Feign 是受到 Retrofit,JAXRS-2.0 和 WebSocket 启发的 java 客户端联编程序。Feign 的第一个目标是将约束分母的复杂性统一到 http apis,而不考虑其稳定性。在 employee-consumer 的例子中,我们使用了 employee-producer 使用 REST 模板公开的 REST 服务。但是我们必须编写大量代码才能执行以下步骤使用功能区进行负载平衡。获取服务实例,然后获取基本 URL。利用 REST 模板来使用服务。 前面的代码如下 @Controller public class ConsumerControllerClient { @Autowired private LoadBalancerClient loadBalancer; public void getEmployee() throws RestClientException, IOException { ServiceInstance serviceInstance=loadBalancer.choose("employee-producer"); System.out.println(serviceInstance.getUri()); String baseUrl=serviceInstance.getUri().toString(); baseUrl=baseUrl+"/employee"; RestTemplate restTemplate = new RestTemplate(); ResponseEntity<String> response=null; try{ response=restTemplate.exchange(baseUrl, HttpMethod.GET, getHeaders(),String.class); }catch (Exception ex) { System.out.println(ex); } System.out.println(response.getBody()); } 之前的代码,有像 NullPointer 这样的例外的机会,并不是最优的。我们将看到如何使用 Netflix Feign使呼叫变得更加轻松和清洁。如果 Netflix Ribbon 依赖关系也在类路径中,那么 Feign 默认也会负责负载平衡。 喜欢文章记得点个赞,感谢支持!

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册