Android 面试问题

  1. Android线程安全
    Android一般情况下会使用一个主线程控制UI,非主线程无法控制UI,Android4.0以后在主线程中进行网络操作,所有的网络操作在独立的线程中异步运行

  2. android使用返回栈来控制活动
    每个活动生命周期内有四个活动状态,运行状态(栈顶就是运行 可见状态),暂停状态(不位于栈顶,但任然可见),停止状态(不栈顶,完全不可见),销毁状态(从返回栈中移除)

Activity定义了7个回调方法,覆盖活动生命周期的每一个环节
onCreate() 初始化布局,绑定事件
onStart() 活动由不可见变为可见时候调用
onResume() 活动准备交互时候调用
onPause() 活动启动或调用另一个活动时(对话框活动启动)
onStop() 活动完全不可见
onDestory() 活动销毁前调用
onRestart() 活动由停止变为运行
完整生命周期 onCreate()---onDestory()
可见生命周期 onStart()---onStop() 可能无法交互
前台生命周期 onResume()---onPause() 可交互 运行状态

Activity四种启动模式
1.standard 默认进入Activity所属返回栈中 不管是否存在于返回栈中,每次启动都创建一个新的实例
2.singleTop 栈顶复用模式 如果位于栈顶不创建新的实例(Activity不会被重新创建)
3.singleTask 栈内复用模式 解决重复创建栈顶,如果返回栈中有就直接使用,这个活动之上的活动全部出栈
没有就创建一个新的实例
4.singleInstance 单实例模式 在manifiest的里设置android:launchMode="singleInstance"
这样创建单独的返回栈来管理此活动,可以允许其他程序调用
注意:默认情况下,所有activity所需的任务栈的名字为应用的包名,可以通过给activity指定TaskAffinity属性来指定任务栈,这个属性值不能和包名相同,否则就没有意义 。

随时随地退出活动
public class ActivityCollector{
//指定泛型
public static List activities=new ArrayList();
public static void addActivity(){
activities.add(activity);
}
public static void removeActivity(){
activities.remove(activity);
}
public static void removeAll(){
for(Activity activity:activities){
if(!activity.isFinishing()){
activity.finish();
}
}
}
}

//List暂存活动,removeAll()将活动全部销毁掉

使用在onCreate(){
ActivityCollector.addActivity(this);
}
在protected void onDestory(){
ActivityCollector.removeActivity(this);
}
switch(v.getId()){
case R.id.button:
ActivityCollector.removeAll();
break;
default:
break;
}

Service的生命周期 (bind和start的区别) Service是专门在后台处理长时间耗时任务的Android组件,没有UI,有两种启动方式
startService 只是startService,启动serivce的组件(如Activity)与Service并没有关联,只要调用service的stopSelf或者组件调用StopService才会停止服务
生命周期
onCreate()->onStartComand()->service is running->onDestory()
bindService方法创建Service,其他组件通过回调Service代理对象与Service交互,两者也进行绑定,启动方被销毁时service自动调用unBind方法解除绑定,当所有绑定被解除时,Service才被销毁

Service的onCreate是在主线程中调用的,不能进行耗时操作会阻塞UI线程

Service 使用时一定要注册 Android的四大组件都需要注册(Activity Service BroadCastReceive广播 ContentProvider内容提供器 )

是否知道IntentService,在什么场景下使用IntentService?

Service应用场景:如果一个应用要从网络上下载MP3文件,并在Activity上展示进度条,这个Activity要求是可以转屏的。那么在转屏时Actvitiy会重启,如何保证下载的进度条能正确展示进度呢?
解决:通过Service,不过涉及到Service与Activity交互

适合执行那些不需要和用户交互而且还要长时间执行的任务,服务默认运行于主线程,需要手动创建子线程执行具体的任务,否则出现主线程被阻塞

要针对实际场景具体分析是否使用Service,因为Service也是在主线程中调用,还是要开线程才能处理长时间操作,Service与UI交互也不简单,如果只是进行一些耗时操作而且在界面退出改变时,工作也停止,这样直接使用Thread,Asynctask,ThreadHandler更加简单

碎片生命周期和回调
生命周期的四个状态:运行状态,暂停状态,停止状态,销毁状态
运行状态:碎片可见而且与它关联的活动也处于运行状态
暂停状态:活动进入暂停状态(比如一个未占满屏幕的活动被添加到栈顶),与它相关联的可见碎片也进入暂停状态
停止状态:当相关联的活动进入停止状态,或者通过调用FragmentTransaction的remove(),replace(),将碎片从活动中移除,停止状态的碎片完全不可见可能被系统回收
销毁状态:相关联的活动呗销毁,或者调用FragmentTrasaction的remove(),replace(),将碎片从活动中移除,或者事务提交之前调用addToBackStack()方法,碎片都会进入停止状态

Fragemnt定义了一系列回调方法覆盖碎片生命周期的每一个环节
除了活动中的回调方法,Fragment还有附加的回调方法
1.onAttach()
在碎片和活动建立关联的时候调用
2.onCreateView()
为碎片加载布局时候调用
3.onActivityCreated()
与碎片相关联的活动创建完毕时候调用
4.onDestoryView()
与碎片相关联的视图被移除时候调用
5.onDetech()
当碎片与活动解除关联时候调用
onAttach()->onCreate()->onCreateView()->onActivityCreated()->onStart()->onResume()->
碎片已经激活->(用户点击返回键,或者碎片被移除/替换,或者碎片被添加到返回栈)->onPause()->onStop()->onDestoryView()(从返回栈中回到上一个碎片)->onDestory()->onDetech->碎片被销毁

Fragment和Activity生命周期的关系
创建过程
F onAttach()
F onCreate()
F onCreateView()
A onCrete()
F onActivityCreated()
A onStart()
F onStart()
A onResume()
F onResume()
销毁过程
F onPause()
A onPause()
F onStop()
A onStop()
F onDestroyView()
F onDestory()
F onDetach()
A onDestory()

Activity 与meun创建顺序
在Activity回调onResume()后创建menu,再调用onCreateOptionMenu()

重要
面试官要看的点,真正的项目需要一个开发人员对某个问题有一定的深度,也需要对整个Android的知识点有一定的广度。深度代表这个人对问题认真对待有钻研的精神,广度代表这个人在面对同一个问题时,会更容易从多种可行的方案中选出最合适的一种。

AIDL(Android Interface Defination Language) Android进程间通信(IPC) 也就是两个应用程序间通信

AsyncTask(原理也是基于异步消息处理机制) Android做了很好的封装
AsyncTask是一个抽象类,需要创建一个子类去继承他,需要指定三个泛型参数
1.params 执行AsyncTask需要传入的参数,可以在后台任务中使用
2.Process 后台执行任务,需要在界面上显示当前进度,把该 泛型 作为进度单位
3.Result 任务执行完毕,指定泛型作为返回值
class DownloanTask extends AsyncTask

需要重写四个方法
1.onPreExecute()
任务执行之前调用,进行一些初始化操作,比如显示一条进度条对话框
2.doInBackground(Params...)
所有代码都在子线程中运行,在这里处理一些耗时操作,通过return将任务执行结果返回,
如果第三个泛型参数为Void不用返回结果
注意:不能更新UI,如果需要调用publish(Progress...)方法来完成
3.onProgressUpdate(Progress...) OP
如果后台调用publishProgress()方法,就会执行该方法,通过后台传入的值更新UI
4.onPostExecute(Result)
当后台执行任务结束,并通过return 语句返回时调用,返回的数据作为参数,可以进行一些UI操作比如提醒任务执行结果、关闭掉进度条等

内存泄露(内存溢出)和OOM(Out of Memory 内存溢出)
1.Android(Java)中常见的容易引起内存泄漏的不良代码
Android应用于嵌入式设备,由于内存和配置的限制,如果代码有太多的对内存使用不当的地方,会照成设备运行缓慢或者死机。为了使应用程序安全且快速的运行,Android 的每个应用程序都会使用一个专有的Dalvik虚拟机实例来运行,也就是说每个应用程序都是在属于自己的进程中运行的。一方面,如果程序在运行过程中出现了内存泄漏的问题,仅仅会使得自己的进程被kill掉,而不会影响其他进程(如果是system_process 等系统进程出问题的话,则会引起系统重启)。另一方面Android为不同类型的进程分配了不同的内存使用上限,如果应用进程使用的内存超过了这个上限,则会被系统视为内存泄漏,从而被kill掉。

  1. 内存不够用就叫oom.
    Android设备内存一般比较小,容易引起oom.
    Android每个应用程序在专有的Dalvik虚拟机实例中运行,Android分配固定内存。超出分配的内存,引起oom,系统KILL掉,程序结束。
    Android系统进程OOM,机器重启。
    出现oom,无非主要是以下几个方面:
      一、加载对象过大
      二、相应资源过多,没有来不及释放。
      解决这样的问题,也有一下几个方面:
      一:在内存引用上做些处理,常用的有软引用、强化引用、弱引用
      二:在内存中加载图片时直接在内存中做处理,如:边界压缩.
      三:动态回收内存
      四:优化Dalvik虚拟机的堆内存分配
      五:自定义堆内存大小

SQLite 使用 SQL(Structured Query Language)
SQLite提供了一个SQLiteOpenHelper来操作数据库,新建一个子类继承
重写构造方法有四个参数 context,name,factory(查询数据返回自定义的Cursor一般为null),version
在重写onCrete() onUpdate()方法(如果数据库表中存在表,需要使用更新)

构建完SQLiteOpenHelper实例以后,调用getReadableDatabase()和getWritableDataBase()方法就会创建数据库 文件位置/data/data/package name/databases/目录下

数据库的操作 CURD()
getReadableDatabase()返回一个SQLiteDatabase对象,借助这个对象就可以进行CURD操作

insert()插入数据 三个参数 1.表名2.未指定数值赋值为null(null)3. ContentValues对象
提供了一系列方法向列中添加数据
ContentValues values=new ContentValues();
values.put("name","book");
values.put("pages",456);
db.insert("Book",null,values);

update() 更新数据 4个参数 1.表名 2. ContentValues 3.4.约定某一行或某几行(不指定更新所有行)
values.put("price",10.99);
db.update("Book",values,"name=?",new String[] {"My Real Story"});
第三个参数相当于where语句 where bookname="My Real Story"

delete() 删除数据 三个参数1.表名 2.3.约定某一行或某几行(不指定删除所有行数据)

db.delete("Book","page>?",new String[]{"500"})

query() 7/8个参数 1.表名 2.约定查询那几列(没有就所有列)3.4.参数约定那几行
5.指定groupby的列 6.group by 后进一步过滤7.设置排序方式

Cursor cursor=db.query("Book",null,null,null,null,null,null);
if(cursor.moveToFirst){//移动到第一行
do{
String name=cursor.getString(cursor.getColumnIdex("name"));//获取某一列的索引位置,再读取数据
int page=cursor.getInt(sursor.getColumnIndex("pages"));
}
cursor.close();
}

Android-universal-Image-Loader
实现了异步的网络图片加载,并且支持多线程异步加载

Fresco内存管理
Image Pipeline:从网络或者本地文件加载图片(设计三级缓存,2级内存,1级文件,最大节省内存和CPU)
Drawees模块:方便显示loadding图,图片不显示时及时释放内存
解压后的图片既Bitmap,占用大量的内存,大内存引发频繁的GC,Android5.0以下会造成卡顿,
Fresco将图片放到特殊的内存区域,图片不显示时内存自动释放,使APP运行更加流畅,减少因图片内存占用引发的OOM
Fresco会显示渐进式图片,就是显示图片的大致轮廓,随着图片的下载逐渐清晰(只需要提供URI)
支持加载gif,自定义居中,圆角图,加载失败点击重新加载,自定义占位图,自定义按压的占位图
从远端加载或者从缓存中读取,加载完成回调通知,缩放或旋转图片。

在setContentView前,进行初始化
Fresco.initialize(context);
2)layOut中加入simpleDraweeView
android:id="@+id/my_image_view"
android:layout_width="20dp"
android:layout_height="20dp"
fresco:placeholderImage="@drawable/my_drawable"
/>
3)加载图片
Uri uri=Uri.prase("http://");
SimpleDraweeView darwview=()findViewById();
drawview.setImageView(uri)


http访问网络


  1. 继承Service
  2. 在manifest中注册
  3. 调用context的bindService(intent,ServiceConnection,int)
  4. 不再使用时调用unbinService(ServiceConnection)方法停止服务
    Service生命周期
    onBind()方法返回一个IBinder接口,IBinder是组件与Service通信的桥梁
    组件获取IBinder接口就可以调用Service中的方法
    onCreate->onBind()->unBind()->onDestory()

IntentService是Service的子类,用来处理多个请求,因为Service通常处理一个请求,创建独立的线程处理请求,调用onHandlerIntent()方法处理按时间排序的请求队列。


BroadCastReceiver
广播分为两种类型,有序广播和无序广播,无序广播是异步的传播效率快,但是上一个接受者不能将处理结果传递给下一个接受者,无法中止Intent的传播,有序广播在Intent-filter的android:priority设置优先级
Context.sendBroadCast();
Context.sendOrderedBroadcast();

  • 有序:上一个接受者setResultExtras(Bundle)方法存入结果对象,
    下一个接受者调用Bundle bundle=getResultExtra(Bundle)获取上一个接受者存入结果中的对象的数据
    调用对象->实现onReceiver->结束

步奏

  1. 继承BroadcastReceiver
  2. 重写onReceive方法
  3. 在manifest.xml中注册
    BroadcastReceiver生命周期很短,执行完onReceiver以后就结束
  • xml注册
    //这里自定义一个广播动作
  • 动态注册
    registerReceiver(new MyBroadcastReceiver(),new IntentFilter("test"));
    一定要加上

发送广播
Intent intent=new Intent("test");
sendBroadCast(intent);

静态注册与动态注册的区别

  • 动态注册广播不是常驻型,广播跟随着Activiy的生命周期
    Activity结束前要移除广播接收器
  • 静态注册是常驻型,应用程序关闭如果有广播来,程序会被系统调用自动运行
    在AndroidManifest注册,应用程序关闭也会接收到广播

ContentProvider

ContentProvider是Android中跨程序共享数据的组件,为数据提供外部访问接口,被访问的数据主要以数据库的形式存在,可以选择共享那一部分数据,隐私数据不贡献从而更加安全。

  • 使用系统的ContentProvider(通话记录,短信,通讯录)
    步骤
  1. 获取ContentResolver实例。
  2. 确定Uri的内容,并解析成Uri实例
  3. 通过ContentResolver实例调用相应的方法,传递相应的参数,但第一个参数是Uri
  • 自定义ContentProvider
    resolver(内容操作器)操作数据的方法与sqlitedatabase的真删改查,因为他们操作数据都是以数据库格式存储的。
    resolver利用URI定位数据的位置,而sqlite方法第一个参数是数据库表名定位数据(resolver跨程序共享,不同程序可能有同名的表)
  1. Uri.prase(Uri字符串)->解析成Uri对象
  2. 传给resolver相应的真删改查方法

自定义时重写6个方法:

  • oncreate() contentprovider创建时提供的方法,负责数据库的创建和更新
  • query()
  • update()
  • insert()
  • delete()
  • gettype()

Intent对象保存了消息的内容,包括请求的操作名称和待操作数据的URI,
Intent不仅仅应用于程序之间,用于Service和Intent,Android根据Intent的描述找到对应的组件,将Intent传递给组件,完成组件的调用

后台Activity被回收如何恢复

重写onSaveInstanceState()方法,在此方法中保存需要的数据,在activity回收之前被调用,通过重写onRestoreInstanceState()提取保存的数据。

你可能感兴趣的:(Android 面试问题)