您的位置:首页 > 职场人生

Android面试题

2018-01-06 19:08 190 查看
1.ListView的优化策略和原理
参考ListView优化
2.Activity和Fragment的生命周期
Activity和Fragment的生命周期

3.View和ViewGroup的关系
View和ViewGroup的组合模式

Android的UI界面都是View和ViewGroup及其子类组合而成的。View是所有UI组件的父类,其子类称为组件(Widget);ViewGroup是布局管理器,本身也是继承自View类,其子类称为布局(Layout)

4.常用布局类型,使用时如何取舍
RelativeLayout和LinearLayout

5.机型如何适配

6.Activity之间如何传递复杂的对象
Intent intent = new Intent(this,SecondActivity.class);
//创建Bundle对象
Bundle bundle = new Bundle();
//传递对象,对象需要实现Serializable接口
bundle.putSerializable("1", new MovieInfo());
//传递字符串,CharSequence是String的父接口
bundle.putCharSequence("2","aaa");
//传递CharSequence数组
String[] str = {"a","b"};
bundle.putCharSequenceArray("3",str);
//传递CharSequence集合
ArrayList arrayList = new ArrayList();
bundle.putCharSequenceArrayList("4",arrayList);
//bundle保存到intent中
intent.putExtras(bundle);
startActivity(intent);
7.Android有哪些Spannable
Spannable是一个接口,有两个实现类,SpannableString和SpannableStringBuilder,
SpannableStringBuilder通常配合下面的Span

BackgroundColorSpan
ForegroundColorSpan
AbsoluteSizeSpan
ImageSpan
StyleSpan

8.Android有哪些Drawable
常用的Drawable有BitmapDrawable,ColorDrawable,ShapeDrawable,LayerDrawable,StateListDrawable
9.FragmentTranscation的commit和commitAllowingStateLoss的区别
Fragment事务提交的方式

10.如何处理多个旧版本的数据库升级
11.反射机制使用的场景
加载数据库驱动,动态调用函数,获取泛型
12.DeadObjectException
我们先看看它的定义
/**
* The object you are calling has died, because its hosting process
* no longer exists.
*/
public class DeadObjectException extends RemoteException {
public DeadObjectException() {
super();
}

public DeadObjectException(String message) {
super(message);
}
}意思是说你调用的对象死了,因为它所在的进程不存在了,转载一篇博客,给出链接
linkToDeath机制了解

13.Java的值传递和引用传递
 基本类型:按值传递,方法的实参是原值的一个副本,

 引用类型:传递的是地址值,在方法内对这个对象的修改是会反映到原来的对象,但是String例外,看下面的例子

public static void main(String[] args) {
 String s = new String("A");
 change(s);
 System.out.println(s);
}

public static void change(String s) {
s = "B";
}


因为String是final修饰的,它的值不能被修改,所以当我们修改引用所指向的内容时,会重新开辟一个控件,所以最后打印的结果是B
14.Looper.prepare主要做了哪些工作
 (1)创建Looper对象并放入到ThreadLocal中(2)创建消息队列Looper.loop主要做了哪些工作(1)从ThreadLocal中取出Looper(2)获取Looper对应的消息队列(3)在for循环中调用MessageQueue的next方法取出一个消息一个线程只能由一个Looper
Handler的构造函数中做了什么工作(1)调用Looper.myLooper获取looper对象(2)获取Looper对应的MessageQueueHandler的sendMessage方法有个返回值,true代表消息成功放到了消息对垒,否则是false,通常是因为Looper退出了,注意,返回值true不代表消息一定会被处理,如果在时间处理消息的时间到达之前Looper退出了,那消息就会被丢弃。sendMessage方法最终会调用到下面的方法public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}//将消息插入到消息队列中等待处理
return enqueueMessage(queue, msg, uptimeMillis);
}
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//msg的target属性是一个Handler,此时Handler就和消息对应起来了
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
boolean enqueueMessage(Message msg, long when) }//把消息插入到消息队列中
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}

synchronized (this) {
if (mQuitting) {//消息队列退出了
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w("MessageQueue", e.getMessage(), e);
msg.recycle();
return false;
}

msg.markInUse();
msg.when = when;
Message p = mMessages;//mMessages代表消息队列的第一个元素,第一次mMessages是空的
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// 消息队列空闲,此时还没有消息
msg.next = p;
mMessages = msg;//把我们要处理的消息赋值给mMessages
needWake = mBlocked;
} else {
//把消息插入到消息队列中
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
for (;;) {
prev = p;
p = p.next;//next代表下一个消息
if (p == null || when < p.when) {//p==null代表消息队列中已经没有消息了,break跳出当前循环
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;
}

// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}

Message next() {//从消息队列读取消息
....
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}

nativePollOnce(ptr, nextPollTimeoutMillis);

synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;//mMessages是当前要处理的消息
if (msg != null && msg.target == null) {
// Stalled by a barrier. Find the next asynchronous message in the queue.
do {
prevMsg = msg;
msg = msg.next;
} while (msg != null && !msg.isAsynchronous());
}
if (msg != null) {
if (now < msg.when) {
// 消息还没准备好,设置一个时间等它准备好
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 获取到一个消息
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}

// Process the quit message now that all pending messages have been handled.
if (mQuitting) {
dispose();
return null;
}
获取到消息以后,然后我们看消息是如何被处理的public static void loop() {
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;//获取消息队列

// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();

for (;;) {
Message msg = queue.next(); // 从消息队列中获取消息
if (msg == null) {
// 没有消息说明消息队列退出了
return;
}
 ...//调用Handler的dispatchMessage方法处理消息
msg.target.dispatchMessage(msg);

public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//空方法,需要我们实现去处理消息
}
}
MessageQueue的存储结构是单链表最后我们看看Looper的quit方法public void quit() {//终止loop方法的执行,不在处理消息队列中剩余的消息
mQueue.quit(false);
}
public void quitSafely() {//处理完消息队列的消息之后终止loop方法的执行
mQueue.quit(true);
}

15.HandlerThread handlerThread = new HandlerThread("ht");//创建HandlerThread对象
handlerThread.start();//启动HandlerThread,在run方法中创建了Looper对象
mHandler = new Handler(handlerThread.getLooper()) {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//这里执行在子线程,线程名称使我们在创建HandlerThread是传入的参数
}
};
}

public void send(View view){
mHandler.sendEmptyMessage(0);
}HandlerThread自带Looper,使它可以通过消息队列,来重复使用当前线程,节省系统资源开销,这是它的优点也是缺点,每一个任务将以队列的方式逐个被执行到,一旦任务中有某个任务执行时间太长,那么就会导致后续的任务都会被延迟处理。16.Service生命周期通过startService: onCreate--- onStartCommand(onStart过时)---onDestory通过bindService:  onCreate--onBind--onUnBind--onDestoryonStartCommand我们可以指定一个返回值,START_STICKY,意思如下
如果Service在启动之后(onStartCommand返回值执行了),被系统杀死了,然后会保留在开启状态,但是不保留intent,然后系统会重建Service,但是注意此时的intent是null.17.通过bindService启动intentService会有什么结果intentService源码中bindService返回的是null,而且通过上面的生命周期我们可以看出,通过bindService启动的Service不会走onStartCommand方法,而onStartCommand放啊中利用Handler发送了消息到handleMessage方法中执行,所以bindService启动intentService不会触发onHandleIntent方法。18.为什么多次启动IntentService会顺序执行事件,停止服务后,后续的时间得不到执行。因为IntentService是使用Handler,Looper,MessageQueue机制把消息发送到线程中去执行的,所以多次启动IntentService不会创建新的服务和新的线程,只是把消息放入到消息队列中等待执行,如果服务停止@Override
public void onDestroy() {
mServiceLooper.quit();//调用了Looper的quit方法,通过之前的分析,我们知道,此时消息队列中的任务是得不到执行的
}
19.Context的结构


Context可以理解为上下文,环境,通过Context我们可以访问资源,和其他组件进行交互,接下来我们看看Context是何时创建的Application:在ApplicationThread的scheduleLaunchActivity方法中,有下面的代码public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,
IVoiceInteractor voiceInteractor, int procState, Bundle state,
PersistableBundle persistentState, List<ResultInfo> pendingResults,
List<Intent> pendingNewIntents, boolean notResumed, boolean isForward,
ProfilerInfo profilerInfo) {
   .....
sendMessage(H.LAUNCH_ACTIVITY, r); }
然后到了ActivityThread中的handleMessage方法 public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case LAUNCH_ACTIVITY: {
...
r.packageInfo = getPackageInfoNoCheck(
r.activityInfo.applicationInfo, r.compatInfo);
handleLaunchActivity(r, null);
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// If we are getting ready to gc after going to the background, well
// we are back active so skip it.
unscheduleGcIdler();
mSomeActivitiesChanged = true;

if (r.profilerInfo != null) {
mProfiler.setProfiler(r.profilerInfo);
mProfiler.startProfiling();
}

// Make sure we are running with the most recent config.
handleConfigurationChanged(null, null);

if (localLOGV) Slog.v(
TAG, "Handling launch of " + r);
  //创建一个Activity
Activity a = performLaunchActivity(r, customIntent);


private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
ActivityInfo aInfo = r.activityInfo;
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}

ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}

if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}

Activity activity = null;
try {
java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
activity = mInstrumentation.newActivity(//创建Activity
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
 ...
try {//创建Application
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (activity != null) {//创建Activity的Context
Context appContext = createBaseContextForActivity(r, activity);
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.voiceInteractor);

if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}

activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
r.stopped = true;
if (!r.activity.mFinished) {
activity.performStart();
r.stopped = false;
}
if (!r.activity.mFinished) {
if (r.isPersistable()) {
if (r.state != null || r.persistentState != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
r.persistentState);
}
} else if (r.state != null) {
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
}
}
if (!r.activity.mFinished) {
activity.mCalled = false;
if (r.isPersistable()) {
mInstrumentation.callActivityOnPostCreate(activity, r.state,
r.persistentState);
} else {
mInstrumentation.callActivityOnPostCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onPostCreate()");
}
}
}
r.paused = true;

mActivities.put(r.token, r);
  ...
return activity;

我们看看makeApplication方法,这里的packageInfo是一个loadedApk对象,

public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
if (mApplication != null) {
return mApplication;
}

Application app = null;

String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}

try {
java.lang.ClassLoader cl = getClassLoader();
if (!mPackageName.equals("android")) {
initializeJavaContextClassLoader();
}
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
app = mActivityThread.mInstrumentation.newApplication(//创建Application
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
if (!mActivityThread.mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to instantiate application " + appClass
+ ": " + e.toString(), e);
}
}
mActivityThread.mAllApplications.add(app);
mApplication = app;

if (instrumentation != null) {
try {
instrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!instrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
}

// Rewrite the R 'constants' for all library apks.
SparseArray<String> packageIdentifiers = getAssets(mActivityThread)
.getAssignedPackageIdentifiers();
final int N = packageIdentifiers.size();
for (int i = 0; i < N; i++) {
final int id = packageIdentifiers.keyAt(i);
if (id == 0x01 || id == 0x7f) {
continue;
}

rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
}

return app;
}


20.ListView优化
(1)复用ConvertView,避免每次填充布局,保证了只填充屏幕内看到的条目
(2)使用ViewHolder,保存控件的引用,避免每次查找控件
(3)在getView中不要做复杂逻辑
(4)滑动的时候不加载图片
(5)异步加载图片
(6)ItemView的布局层次减少
(7)数据分批和分页加载
(8)局部刷新
(9)使用内存和本地缓存
(10)设置下面的属性
  //是否允许条目的绘制缓存
listView.setScrollingCacheEnabled(false);
//5.0以后废弃了,通过setLayerType可以控制子View的缓存行为
listView.setAnimationCacheEnabled(false);21.Android语言切换
Androi7.0语言设置

Android7.0语言设置2

Android语言设置

22.Sqlite数据库面试题
产生的journal文件的作用:用于事务的回滚,如果没有操作异常或者不需要回滚,那么这个文件大小为0.如果程序crash了,该文件会被保留,下次打开数据库文件时,系统会检查有没有journal文件存在,有则用它来恢复数据。
如何插入大量的数据,使用事务+SqliteStatement,代码如下 MyDatabase myDatabase = new MyDatabase(this);
SQLiteDatabase db = myDatabase.getReadableDatabase();
try{
db.beginTransaction();//开启事务
//预编译Sql语句
SQLiteStatement sqLiteStatement = db.compileStatement("");
for (int i = 0; i < 10000; i++) {
//绑定数据
sqLiteStatement.bindLong(1,22);
sqLiteStatement.bindString(2,"test");
sqLiteStatement.executeInsert();//执行插入
}
db.setTransactionSuccessful();
}catch(Exception e){
e.printStackTrace();
}finally {
db.endTransaction();
db.close();
}其他优化
1.为表创建索引,语句如下
create index index_name on table_name
注意:索引不应该使用在比较小的表上,索引会增加查询的性能,降低插入和更新的性能,所以如果查询多,可以用索引。
2.语句拼接使用Stringbuilder代替String
3.查询时返回更少的结果
4.少用cursor.getColumnIndex,可以在建表的时候用Static变量记住列的index
Sqlite多线程访问
SQLiteDatabase 多线程访问需要注意的问题

23.线程池 public static void main(String[] args) {
//线程同时执行
ExecutorService service = Executors.newCachedThreadPool();
//线程依次执行
ExecutorService service2 = Executors.newSingleThreadExecutor();
//每次最多同时执行固定数量的线程
ExecutorService service3 = Executors.newFixedThreadPool(2);
//
ScheduledExecutorService service4 = Executors.newScheduledThreadPool(2);
//执行下面的会发生RejectedExecutionException
// service.shutdown();
// service.submit(new Task());
//以固定间隔执行任务
// service4.scheduleAtFixedRate(new Task(),0,2, TimeUnit.SECONDS);
// //固定延迟执行任务
// service4.scheduleWithFixedDelay(new Task(),0,2,TimeUnit.SECONDS);
// service3.execute(new Task());
// service.submit(new Task());
ThreadPoolExecutor executor = new ThreadPoolExecutor(2,3,30,
TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(1));
for (int i = 0; i < 4; i++) {
executor.execute(new Task());
}
}

static class Task implements Runnable{

@Override
public void run() {
try {
Thread.sleep(2000);
System.out.println("===========run");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

接下来看看每种线程池内部的实现
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
核心线程数为0,最大线程数为整数最大值,任务队列为SynchronousQueue,这个队列在接收到任务的时候,会直接提交给线程池处理。
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}这个线程池很有意思,没有直接返回ThreadPoolExecutor,而是包装了一个FinalizableDelegatedExecutorService,它的源码如下private static class FinalizableDelegatedExecutorService
extends DelegatedExecutorService {
FinalizableDelegatedExecutorService(ExecutorService executor) {
super(executor);
}
protected void finalize() {
super.shutdown();
}
}我们知道,在当前对象要被垃圾回收器回收之前,会调用它的finalize方法
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}核心线程数等于最大线程数ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}核心线程树由我们指定,最大线程数是整数最大值,
24.Java中程序何时终止
当程序中的所有非守护线程终止的时候,程序就终止了。 public static void main(String[] args) {
Thread t1 = new Thread(){
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("run");
}
};
t1.start();
System.out.println("main");
}运行上面的程序,会立即打印出main,然后隔2秒打印出run,程序结束。加入我们把t1设置为守护线程呢。 public static void main(String[] args) {
Thread t1 = new Thread(){
@Override
public void run() {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("run");
}
};
//设置为守护线程,当程序中只剩下守护线程时,JVM就退出了
t1.setDaemon(true);
t1.start();
System.out.println("main");
}再次运行上面的程序,结果是打印出main之后程序就退出了,因为main线程执行完以后,就剩下一个守护线程了,JVM就退出了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: