最近遇到1些比较有代表性,有点挑战性的面试题,
大概集中这几个方面:
关于这些问题,觉得下面几篇不管是文章的逻辑,文章的深度都是写得比较好的,希望对1些应聘者有所帮助.
思路:
在 Java 中声明1个 native 方法,然后生本钱地接口的函数原型声明,再用 C/C++ 实现这些函数,并生成对应平台的动态同享库放到 Java 程序的类路径下,最后在 Java 程序中调用声明的 native 方法就间接的调用到了 C/C++ 编写的函数了,在 C/C++ 中写的程序可以避开 JVM 的内存开消过大的限制、处理高性能的计算、调用系统服务等功能.
遇到的问题:
其实在JNI中,与java最常接触的不过就是查找 class 和 ID (属性和方法 ID),但是这个查找的进程是10分消耗时间的.
解决方法:
因此在 native 里保存 class 和 member id 是很有必要的,但是class 和 member id 在1定范围内是稳定的,但在动态加载的 class loader 下,保存全局的 class 要末可能失效,要末可能造成没法卸载classloader,在诸如 OSGI(j2e的东西,自己百度) 框架下的 JNI 利用还要特别注意这方面的问题.
总结:
所以在 JNI 开发中,公道的使用缓存技术能给程序提高极大的性能。缓存有两种,分别为使用时缓存和类静态初始化时缓存,区分主要在于缓存产生的时刻。
使用时缓存:
字段 ID、方法 ID 和 Class 援用在函数当中使用的同时就缓存起来.
判断字段 ID 是不是已缓存,如果没有先取出来存到fid_str中,下次再调用的时候该变量已有值了,不用再去JVM中获得,起到了缓存的作用。
遇到的坑:
但是请注意:cls_string是1个局部援用,与方法和字段 ID 不1样,局部援用在函数结束后会被 JVM 自动释放掉,这时候cls_string成了1个野针对(指向的内存空间已被释放,但变量的值依然是被释放后的内存地址,不为 NULL),当下次再调用 Java_com_xxxx_newString 这个函数的时候,会试图访问1个无效的局部援用,从而致使非法的内存访问造成程序崩溃。所以在函数内用 static 缓存局部援用这类方式是毛病的。
类静态初始化时缓存:
在调用1个类的方法或属性之前,Java 虚拟机会先检查该类是不是已加载到内存当中,如果没有则会先加载,然后紧接着会调用该类的静态初始化代码块,所以在静态初始化该类的进程当中计算并缓存该类当中的字段 ID 和方法 ID 也是个不错的选择。
如果在写 JNI 接口时,不能控制方法和字段所在类的源码的话,用使用时缓存比较公道。但比起类静态初始化时缓存来讲,用使用时缓存有1些缺点:
使用前,每次都需要检查是不是已缓存该 ID 或 Class 援用
如果在用使用时缓存的 ID,要注意只要本地代码依赖于这个 ID 的值,那末这个类就不会被 unload。另外1方面,如果缓存产生在静态初始化时,当类被 unload 或 reload 时,ID 会被重新计算。由于,尽可能在类静态初始化时就缓存字段 ID、方法 ID 和类的 Class 援用。
点击详情
自定义控件的流程大致分为以下几步
OnDraw
对自定义View,我们通常会重写这3个方法,重写那些,取决于我们的自定义View从哪里继承,然后要实现甚么样的功能。大致归纳有以下几点。
继承View
实现1些不规则的图形,需要重写onDraw方法进行绘制
通过反编译的结果, 普通的非static内部类比static内部类多了1个field: final com.qihoo.browser.OuterClass this$0
;在默许的构造方法中,用外部类的对象对这个filed赋值.
用intellijidea打开OuterClass$NormallInnerClass.class
, 可以看到内部类调用外部类的方法就是通过这个filed实现的. 这也就是static 内部类没法调用外部类普通方法的缘由,由于static内部类中没有这个field: final com.qihoo.browser.OuterClass this$0
;
解决方法
publicclass MainActivity extends Activity {
privateMyThread mThread;
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
exampleThree();
}
privatevoid exampleThree() {
mThread = new MyThread();
mThread.start();
}
//static内部类可以免,持有外部类的援用.
private static class MyThread extends Thread {
private boolean mRunning = false;
@Override
public void run() {
//对1些死循环的耗时操作,需要设置退出线程的标识 flag
mRunning = true;
while(mRunning) {
SystemClock.sleep(1000);
}
}
//定义1个flag 来停止线程的运行.
public void close() {
mRunning = false;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//activity被烧毁的时候关闭线程
mThread.close();
}
}
点击详情1
点击详情2
非静态方法 按重写规则调用相应的类实现方法,而静态方法只与类相干。
所谓静态,就是在运行时,虚拟机已认定此方法属于哪一个类。
专业术语有严格的含义,用语要准确.”重写”只能适用于实例方法.不能用于静态方法.对静态方法,只能隐藏(可以重写那只是情势上的 ,其实不满足多态的特点,所以严格说不是重写)。
静态方法的调用不需要实例化吧.. 不实例化也就不能用多态了,也就没有所谓的父类援用指向子类实例.由于不能实例化 也就没有机会去指向子类的实例。所以也就不存在多态了。
静态方法不依赖类就能够访问。这就是它的用处啊,没有new对象可调用的方法。但是,重写是依赖对象的。重写父类的某1方法,而static不依赖类。
善用自定义 View,自定义 View 可以有效的减小 Layout 的层级。
包括利用好 ConvertView、利用好 ViewType、Layout 层次结构、ViewHolder、使用自定义布局、保证 Adapter 的 hasStableIds() 返回 true、Item 不能太高、getView() 中要做尽可能少的事情、ListView 中元素避免半透明、尽可能开启硬件加速、 AnimationCache、 ScrollingCache 和 SmoothScrollbar。
个人收获:
比viewHolder更有效的方式是使用自定义布局,
自定义布局有个好处就是可以省略 ViewHolder。说出来可能你不会信, ViewHolder 首先会占用 setTag() ,其次每次取出后都需要转换1下类的类型。如果是自定义布局的话,findViewById() 这个进程可以在构造函数中进行.
点击详情
Android游戏开发中主要的类除控制类就是显示类,比较重要也很复杂的就是显示和游戏逻辑的处理。在J2ME中可以通过Display和Canvas来实现显示,而Android中处理显示的是View类。下面为大家简单介绍android.view.View
和android.view.SurfaceView
。
SurfaceView是从View基类中派生出来的显示类,直接子类有GLSurfaceView和VideoView,可以看出GL和视频播放和Camera摄像头1般均使用SurfaceView,到底有哪些优势呢? SurfaceView可以控制表面的格式,比如大小,显示在屏幕中的位置,最关键是的提供了SurfaceHolder类,使用getHolder方法获得,相干的有Canvas lockCanvas()、 Canvas lockCanvas(Rect dirty) 、void removeCallback(SurfaceHolder.Callback callback)、void unlockCanvasAndPost(Canvas canvas)
控制图形和绘制,而在SurfaceHolder.Callback
接口回调中可以通过下面3个抽象类可以自己定义具体的实现(比如第1个更改格式和显示画面):
java
abstract void surfaceChanged(SurfaceHolder holder, int format, int width, int height) ;
abstract void surfaceCreated(SurfaceHolder holder) ;
abstract void surfaceDestroyed(SurfaceHolder holder) ;
对Surface相干的,Android底层还提供了GPU加速功能,所以1般实时性很强的利用中主要使用SurfaceView而不是直接从View构建,同时后面会讲到的OpenGL中的GLSurfaceView也是从该类实现。
点击详情
OkHttp + volley
OkHttp还处理了代理服务器问题和SSL握手失败问题。
*目前,该封装库志支持:
1般的get要求
1般的post要求
基于Http的文件上传
文件下载
上传下载的进度回调
加载图片
支持要求回调,直接返回对象、对象集合
支持session的保持
支持自签名网站https的访问,提供方法设置下证书就行
支持取消某个要求
点击详情
1个Handler的创建它就会被绑定到这个线程的消息队列中,如果是在主线程创建的,那就不需要写代码来创建消息队列了,默许的消息队列会在主线程被创建。但是如果是在子线程的话,就必须在创建Handler之前先初始化线程的消息队列。
点击详情
transaction.replace()
使用另外一个Fragment替换当前的,实际上就是remove()然后add()的合体
点击详情