很全面的Android面试题
这些有些来源于网站、有些来源于自己的思考。可能有些问题网上有、可能有些问题对应的答案也有,有可能有些问题讲的不对,能指正的希望大家不吝指教。
Activity
什么是Activity
四大组件之一,一个和用户交的互界面就是一个activity,是所有 View 的容器
Activity 生命周期
生命周期描述的是一个类 从创建(new出来)到死亡(垃圾回收)的过程中会执行的方法.
在这个过程中会针对不同的生命阶段会调用不同的方法
Activity从创建到销毁有多种状态,从一种状态到另一种状态时会激发相应的回调方法,这些回调方法包括:
- oncreate:Activity对象创建完毕,但此时不可见
- onstart:Activity在屏幕可见,但是此时没有焦点
- onResume:Activity在屏幕可见,并且获得焦点
- onPause:Activity此时在屏幕依然可见,但是已经没有焦点
- onStop:Activity已经不可见了,但此时Activity的对象还在内存中
- onDestroy:Activity对象被销毁
其实这些方法都是两两对应的,onCreate创建与onDestroy销毁;onStart可见与onStop不可见;onResume可编辑(即焦点)与onPause;
还有一个onRestart方法了,在Activity被onStop后,但是没有被onDestroy,在再次启动此Activity时就调用onRestart(而不再调用onCreate)方法;如果被onDestroy了,则是调用onCreate方法。
两个Activity之间跳转时必然会执行的是哪几个方法。
一般情况比如说有两个activity,分别叫A,B ,当在A里面激活B组件的时候, A 会调用 onPause()方法,然后B 调用onCreate() ,onStart(), OnResume() ,这个时候B覆盖了窗体, A会调用onStop()方法. 如果B呢 是个透明的,或者是对话框的样式, 就不会调用onStop()方法。
因此,我们在两个activities中传递数据,或者共享资源时(如数据库连接),需要在前一个activity的onPause()方法而不是onStop()方法中进行
横竖屏切换时候Activity的生命周期。
这个生命周期跟清单文件里的配置有关系
1、不设置Activity的android:configChanges时,切屏会重新调用各个生命周期,默认首先销毁当前activity,然后重新加载
2、设置Activity的android:configChanges="orientation|keyboardHidden|screenSize"时,切屏不会重新调用各个生命周期,只会执行onConfigurationChanged方法,游戏开发中, 屏幕的朝向都是写死的.
如何将一个Activity设置成窗口的样式。
可以自定义一个activity的样式
android:theme="@android:style/Theme.Dialog"
你后台的Activity被系统 回收怎么办?如果后台的Activity由于某原因被系统回收了,如何在被系统回收之前保存当前状态?
除了在栈顶的activity,其他的activity都有可能在内存不足的时候被系统回收,一个activity越处于栈底,被回收的可能性越大.如果有多个后台进程,在选择杀死的目标时,采用最近最少使用算法(LRU)。
Activity中提供了一个 onSaveInstanceState()回调方法,这个方法会保证一定在活动被回收之前调用, 可以通过这个方法来解决活动被回收时临时数据得不到保存的问题。onSaveInstanceState()方法会携带一个 Bundle类型的参数,Bundle提供了一系列的方法用于保存数据,比如可以使用 putString()方法保存字符串,使用 putInt()方法保存整型数据。每个保存方法需要传入两个参数,第一个参数是键,用于后面从 Bundle中取值,第二个参数是真正要保存的内容。在 MainActivity中添加如下代码就可以将临时数据进行保存:
protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); String tempData = "Something you just typed"; outState.putString("data_key", tempData); }
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (savedInstanceState != null) { String tempData = savedInstanceState.getString("data_key"); }
也可以通过onRestoreInstanceState来存储和恢复数据,区别是不需要判断空了,onRestoreInstanceState调用一定是有值的
Activity的 onSaveInstanceState() 和 onRestoreInstanceState()并不是生命周期方法,它们不同于 onCreate()、onPause()等生命周期方法,它们并不一定会被触发。当应用遇到意外情况(如:内存不足、用户直接按Home键)由系统销毁一个Activity,onSaveInstanceState() 会被调用。但是当用户主动去销毁一个Activity时,例如在应用中按返回键,onSaveInstanceState()就不会被调用。除非该activity是被用户主动销毁的,通常onSaveInstanceState()只适合用于保存一些临时性的状态,而onPause()适合用于数据的持久化保存。
onSaveInstanceState()被执行的场景有哪些:
系统不知道你按下HOME后要运行多少其他的程序,自然也不知道activity A是否会被销毁,因此系统都会调用onSaveInstanceState(),让用户有机会保存某些非永久性的数据。以下几种情况的分析都遵循该原则
- 当用户按下HOME键时
- 长按HOME键,选择运行其他的程序时
- 锁屏时
- 从activity A中启动一个新的activity时
- 屏幕方向切换时
如何退出Activity?
退出activity 直接调用 finish () 方法
用户点击back键 就是退出一个activity ,退出activity 会执行 onDestroy()方法 。
1、抛异常强制退出:
该方法通过抛异常,使程序Force Close。不推荐使用
验证可以,但是,需要解决的问题是,如何使程序结束掉,而不弹出Force Close的窗口。
安全结束进程 android.os.Process.killProcess(android.os.Process.myPid());
2、记录打开的Activity:
每打开一个Activity,就用集合记录下来。在需要退出时,关闭每一个Activity即可。
可以写在Application里,直接getApplication.list.add,在需要退出时遍历集合里的Activity,finish掉
也可以定义一个baseactivity里面进行操作,记得一般不用的话最后都需要把list=null
3、发送特定广播:
//在baseactivity里注册广播
registerReceiver(receiver, filter)
//想退出的时候就在onRecriver方法里finish()。
4、可以通过 intent的flag 来实现.. intent.setFlag(FLAG_ACTIVITY_CLEAR_TOP)激活一个新的activity,然后在新的activity的oncreate方法里面就可以finish掉.
讲一讲你对activity的理解
把上面的几点用自己的心得写出来
两个Activity之间怎么传递数据?
- 基本数据类型可以通过Intent 传递数据
//把数据封装至intent对象中
startActivity(intent);
- 传递对象
怎么让在启动一个Activity是就启动一个service?
在activity的onCreate()方法里面 startService();
如何返回数据
基本流程:
- 使用 startActivityForResult(Intent intent, int requestCode) 方法打开 Activity;
- 新 Activity 中调用 setResult(int resultCode, Intent data) 设置返回数据之后,关闭 Activity 就会调用onActivityResult 方法;
- 在原来的activity里重写 onActivityResult(int requestCode, int resultCode, Intent data) 方法;
- 注意:新的 Activity 的启动模式不能设置成 singleTask(如果已创建,会使用以前创建的)与 singleInstance(单
独的任务栈) ,不能被摧毁(执行不到 finish 方法) ,父 Activity 中的 onActivityResult 方法将不会执行;
请描述一下Intent 和 Intent Filter。
Android 中通过 Intent 对象来表示一条消息,一个 Intent 对象不仅包含有这个消息的目的地,还可以包含消息的内容,这好比一封 Email,其中不仅应该包含收件地址,还可以包含具体的内容。对于一个 Intent 对象,消息“目的地”是必须的,而内容则是可选项。
通过Intent 可以实现各种系统组件的调用与激活.
Intent filter:是传递的信息,这些信息不是必须的,有:
Action: 动作 view
Data: 数据uri uri
Category : 而外的附加信息
- 隐式意图跳转至指定Activity
<intent-filter > <action android:name="com.itheima.second"/> <data android:scheme="asd" android:mimeType="aa/bb"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> Intent intent = new Intent("com.example.activitytest.ACTION_START");
PendingIntent和Intent区别
它们都可以去指明某一个“意图”,都可以用于启动活动、启动服务以及发送广播等。不同的是,Intent更加倾向于去立即执行某个动作,而 PendingIntent更加倾向于在某个合适的时机去执行某个动作。所以,也可以把 PendingIntent简单地理解为延迟执行的 Intent。
PendingIntent的用法同样很简单,它主要提供了几个静态方法用于获取 PendingIntent的实例,可以根据需求来选择是使用 getActivity()方法、getBroadcast()方法、还是 getService()方法。
每8小时开启广播
AlarmManager manager = (AlarmManager) getSystemService(ALARM_SERVICE); int anHour = 8 * 60 * 60 * 1000; // 这是8小时的毫秒数 long triggerAtTime = SystemClock.elapsedRealtime() + anHour; Intent i = new Intent(this, AutoUpdateReceiver.class); PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0); manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
Intent传递数据和Bundle传递数据是一回事,
Intent传递时内部还是调用了Bundle。
以下为源码:
public Intent putExtra(String name, boolean value) { if (mExtras == null) { mExtras = new Bundle(); } mExtras.putBoolean(name, value); return this; }
启动一个Activity有哪几种方法
startActivity
startActivityforresult
launcher的activity
widget
同一个程序,但不同的Activity是否可以放在不同的Task任务栈中?
启动模式里有个Singleinstance,可以运行在另外的单独的任务栈里面。用这个模式启动的activity,在内存中只有一份,这样就不会重复的开启。
也可以在激活一个新的activity时候, 给intent设置flag,Intent的flag添加FLAG_ACTIVITY_NEW_TASK,这个被激活的activity就会在新的task栈里面
Android中Task任务栈的分配。
1)任务栈的概念
问:一个手机里面有多少个任务栈?
答:一般情况下,有多少个应用正在运行,就对应开启多少个任务栈;每开启一个应用程序就会创建一个与之对应的任务栈;
栈:后进先出,最先进栈,就会最后出栈。Activity的启动模式就是修改任务栈的排列情况
2)任务栈的作用:
它是存放 Activity 的引用的,Activity不同的启动模式,对应不同的任务栈的存放;
可通过 getTaskId()来获取任务栈的 ID,如果前面的任务栈已经清空,新开的任务栈 ID+1,是自动增长的;
首先来看下Task的定义,Google是这样定义Task的:Task实际上是一个Activity栈,通常用户感受的一个Application就是一个Task。从这个定义来看,Task跟Service或者其他Components是没有任何联系的,它只是针对Activity而言的。
Activity的启动模式
singleTop 单一顶部模式
-
如果任务栈的栈顶存在这个要开启的activity,不会重新的创建activity,而是复用已经存在的activity。如果栈顶没有或者不在栈顶,会重新创建
-
会调用 onNewInstance(),复用已经存在的实例
-
应用场景:singleTop适合接收通知启动的内容显示页面。例如,某个新闻客户端的新闻内容页面,如果收到10个新闻推送,每次都打开一个新闻内容页面是很耗内存的。
-
当开启activity的时候,就去检查在任务栈里面是否有实例已经存在,如果有实例存在就复用这个已经存在的activity,并且把这个activity上面的所有的别的activity都清空,复用这个已经存在的activity。保证整个任务栈里面只有一个实例存在
-
会调用 onNewInstance(),复用已经存在的实例
- 应用场景:
singleInstance:activity会运行在自己的任务栈里面,并且这个任务栈里面只有一个实例存在
-
如果你要保证一个activity在整个手机操作系统里面只有一个实例存在,使用singleInstance
-
应用场景: 电话拨打界面
Android下的进程
进程是被系统创建的,当内存不足的时候,又会被系统回收
内存管理:Android 系统在运行多个进程时,如果系统资源不足,会强制结束一些进程,优先选择哪个进程来结束是有优先级的。
会按照以下顺序杀死(进程级别):
①、空: 进程中没有任何组件(无组件启动,做进程缓存使用,恢复速度快),任务栈清空,意味着程序退出了,但进程留着,这个就是空进程,容易被系统回收;
②、后台:进程中只有停止状态(onStop)的 Activity;
③、服务:进程中有正在运行的服务;
④、可见:进程中有一个暂停状态(onPause)的 Activity;
⑤、前台:进程中正在运行一个 Activity;
Activity 在退出的时候进程不会销毁, 会保留一个空进程方便以后启动. 但在内存不足时进程会被销毁;
Activity 中不要在 Activity 做耗时的操作, 因为 Activity 切换到后台之后(Activity 停止了), 内存不足时, 也容易被销毁;
Service
什么是Service以及描述下它的生命周期。Service有哪些启动方法,有什么区别,怎样停用Service?
在Service的生命周期中,被回调的方法比Activity少一些,只有onCreate, onStartCommand, onDestroy,onBind和onUnbind。
通常有两种方式启动一个Service,他们对Service生命周期的影响是不一样的。
1 通过startService
Service会经历 onCreate 到onStartCommand,然后处于运行状态,stopService的时候调用onDestroy方法。
如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。
2 通过bindService
Service会运行onCreate,然后是调用onBind, 这个时候调用者和Service绑定在一起。调用者退出了,Srevice就会调用onUnbind->onDestroyed方法。所谓绑定在一起就共存亡了。调用者也可以通过调用unbindService方法来停止服务,这时候Srevice就会调用onUnbind->onDestroyed方法。
3.当调用了startService()方法后,又去调用 stopService()方法,这时服务中的 onDestroy()方法就会执行,表示服务已经销毁了。类似地,当调用了 bindService()方法后,又去调用unbindService()方法,onDestroy()方法也会执行,这两种情况都很好理解。但是需要注意,我们是完全有可能对一个服务既调用了startService()方法,又调用了bindService()方法的,这种情况下该如何才能让服务销毁掉呢?根据Android系统的机制,一个服务只要被启动或者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被销毁。所以,这种情况下要同时调用stopService()和unbindService()方法,onDestroy()方法才会执行这样就把服务的生命周期完整地走了一遍。
start –> bind -> unbind -> stop 经常使用服务长期后台运行,又可以调用服务中的方法
service如何杀不死?
1.onStartCommand方法,返回START_STICKY(粘性)当service因内存不足被kill,当内存又有的时候,service又被重新创建
2.设置优先级,在服务里的ondestory里发送广播 在广播里再次开启这个服务,双进程守护
service是否在main thread中执行, service里面是否能执行耗时的操作?
默认情况,如果没有显示的指定service所运行的进程, Service和Activity是运行在当前app所在进程的main thread(UI主线程)里面
service里面不能执行耗时的操作(网络请求,拷贝数据库,大文件 ),需要在子线程中执行 new Thread(){}.start();
service里面可以弹土司吗
service里面弹toast需要添加到主线程里执行
@Override public void onCreate(){ handler = new Handler(Looper.getMainLooper()); System.out.println("service started"); handler.post(new Runnable() { @Override public void run() { Toast.makeText(getApplicationContext(), "Test",Toast.LENGTH_SHORT).show(); }); }
Activity怎么和service绑定,怎么在activity中启动自己对应的service?
startService() 一旦被创建 调用着无关,没法使用service里面的方法
bindService () 把service 与调用者绑定 ,如果调用者被销毁, service会销毁,可以使用service 里面的方法
Service与Activity怎么实现通信
方法一:
- 添加一个继承Binder的内部类,并添加相应的逻辑方法
- 重写Service的onBind方法,返回我们刚刚定义的那个内部类实例
- Activity中创建一个ServiceConnection的匿名内部类,并且重写里面的onServiceConnected方法和onServiceDisconnected方法,这两个方法分别会在活动与服务成功绑定以及解除绑定的时候调用,在onServiceConnected方法中,我们可以得到一个刚才那个service的binder对象,通过对这个binder对象进行向下转型,得到我们那个自定义的Binder实例,有了这个实例,做可以调用这个实例里面的具体方法进行需要的操作了
方法二
通过BroadCast(广播)的形式
当我们的进度发生变化的时候我们发送一条广播,然后在Activity的注册广播接收器,接收到广播之后更新视图
什么是IntentService?有何优点?
普通的service ,默认运行在ui main 主线程,Sdk给我们提供的方便的,带有异步处理的service类
OnHandleIntent() 处理耗时的操作,不需要开启子线程,这个方法已经在子线程中运行了
Intentservice若未执行完成上一次的任务,将不会新开一个线程,是等待之前的任务完成后,再执行新的任务,等任务完成后再次调用stopService()
startForeground(id, notification)
拥有service的进程具有较高的优先级。当内存不足时,拥有service的进程具有较高的优先级。
1. 如果service正在调用onCreate, onStartCommand或者onDestory方法,那么用于当前service的进程相当于前台进程以避免被killed。
2. 如果当前service已经被启动(start),拥有它的进程则比那些用户可见的进程优先级低一些,但是比那些不可见的进程更重要,这就意味着service一般不会被killed.
3. 如果客户端已经连接到service (bindService),那么拥有Service的进程则拥有最高的优先级,可以认为service是可见的。
4. 如果service可以使用startForeground(int, Notification)方法来将service设置为前台状态,那么系统就认为是对用户可见的,并不会在内存不足时killed。
什么时候使用Service?
如果有其他的应用组件作为Service,Activity等运行在相同的进程中,那么将会增加该进程的重要性。
1.Service的特点可以让他在后台一直运行,可以在service里面创建线程去完成耗时的操作.
2.Broadcast receiver捕获到一个事件之后,可以起一个service来完成一个耗时的操作.
广播
请描述一下Broadcast Receiver。
Android中:系统在运行过程中,会产生会多事件,那么某些事件产生时,比如:电量改变、收发短信、拨打电话、屏幕解锁、开机,系统会发送广播,只要应用程序接收到这条广播,就知道系统发生了相应的事件,从而执行相应的代码。使用广播接收者,就可以收听广播
广播分两种:有序广播、无序广播
无序广播:无序广播不可中断,不能互相传递数据;
有序广播:一个接一个的传递,广播可中断,通过调用 abortBroadcast()方法;接收者之间可以传递数据(intent);
无序广播(标准广播)
- 所有与广播中的action匹配的广播接收者都可以收到这条广播,并且是没有先后顺序,视为同时收到
有序广播
- 所有与广播中的action匹配的广播接收者都可以收到这条广播,但是是有先后顺序的,按照广播接收者的优先级排序
- 优先级的定义:-1000~1000
-
<intent-filter android:priority="100" > <action android:name="com.example.broadcasttest.MY_BROADCAST"/> </intent-filter>
- 最终接收者:所有广播接收者都接收到广播之后,它才接收,并且一定会接收
- abortBroadCast:阻止其他接收者接收这条广播,类似拦截,只有有序广播可以被拦截
如何注册广播
- 安卓中有一些广播接收者,必须使用代码注册,清单文件注册是无效的
- 屏幕锁屏和解锁
- 电量改变
public class MainActivity extends Activity { private IntentFilter intentFilter; private NetworkChangeReceiver networkChangeReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intentFilter = new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); networkChangeReceiver = new NetworkChangeReceiver(); registerReceiver(networkChangeReceiver, intentFilter); } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(networkChangeReceiver); } class NetworkChangeReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context, "network changes",Toast.LENGTH_SHORT).show(); } } }
- 广播一旦发出,系统就会去所有清单文件中寻找,哪个广播接收者的action和广播的action是匹配的,如果找到了,就把该广播接收者的进程启动起来
四大组件其中比较特殊的是广播接收者,可以不在清单文件中配置,可以通过代码进行注册。其他组件全部在清单文件中注册
Intent intent = new Intent("my-sensitive-event"); intent.putExtra("event", "this is a test event"); LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
广播的生命周期
a. 广播接收者的生命周期非常短暂的,在接收到广播的时候创建,onReceive()方法结束之后销毁;
b. 广播接收者中不要做一些耗时的工作,否则会弹出 Application No Response错误对话框;
c. 最好也不要在广播接收者中创建子线程做耗时的工作,因为广播接收者被销毁后进程就成为了空进程,很容易被系统杀掉;
d. 耗时的较长的工作最好放在服务中完成;
内容提供者
请介绍下ContentProvider是如何实现数据共享的。
是四大组件之一,内容提供者的作用:把私有数据暴露给其他应用,通常,是把私有数据库的数据暴露给其他应用
在清单文件中定义内容提供者的标签,注意必须要有authorities属性,这是内容提供者的主机名,功能类似地址
<provider android:name="com.itheima.contentprovider.PersonProvider" android:authorities="com.itheima.person" android:exported="true" ></provider>
把自己的数据通过uri的形式共享出去,android 系统下不同程序,数据默认是不能共享访问
需要去实现一个类去继承ContentProvider
为什么要用ContentProvider?它和sql的实现上有什么差别?
屏蔽数据存储的细节,对用户透明,用户只需要关心操作数据的uri就可以了
不同app之间共享,操作数据
Sql也有增删改查的方法.
但是contentprovider 还可以去增删改查本地文件. xml文件的读取,更改,网络数据读取更改
请介绍下Android的数据存储方式
1.文件储存,在内部文件和SD卡
getCacheDir(),在data/data/包名/cache
getFilesDir(),在data/data/包名/files
SD卡:首先通过File file = new File(Environment.getExternalStorageDirectory(), "info.txt"),然后通过io存储
2.SharedPreference
3.SQLite数据库:
当应用程序需要处理的数据量比较大时,为了更加合理地存储、管理、查询数据,往往使用关系数据库来存储数据。Android系统的很多用户数据,如联系人信息,通话记录,短信息等,都是存储在SQLite数据库当中的,所以利用操作SQLite数据库的API可以同样方便的访问和修改这些数据。
4.ContentProvider:
主要用于在不同的应用程序之间实现数据共享的功能,不同于sharepreference和文件存储中的两种全局可读写操作模式,内容提供其可以选择只对哪一部分数据进行共享,从而保证我们程序中的隐私数据不会有泄漏的风险
Fragment
简单说说Fragment?
用途:在一个Activity里切换界面,切换界面时只切换Fragment里面的内容
生命周期方法跟Activity一致,可以理解把其为就是一个Activity,与Activity同存亡,Activity的XX方法调用,Fragment的XX方法就调用
他们是怎么进行传递数据的?
活动传递给Fragment:为了方便碎片和活动之间进行通信, FragmentManager提供了一个类似于findViewById()的方法,专门用于从布局文件中获取碎片的实例,前提是自己在布局文件中定义fragment这个标签,代码如下所示:
RightFragment rightFragment = (RightFragment) getFragmentManager()
.findFragmentById(R.id.right_fragment);
调用 FragmentManager的 findFragmentById()方法,可以在活动中得到相应碎片的实例,然后就能轻松地调用碎片里的方法了。还有findViewByTag,在replace 的时候设置tag
或者在fragment里声明接口,然后activity获得fragment对象调用接口里的方法
fragment数据传递给活动,直接getActivity就可以调用活动里的方法了
activity给fragment传递数据一般不通过fragment的构造方法来传递,会通过setArguments来传递,因为当横竖屏会调用fragment的空参构造函数,数据丢失。
fragment和fragment数据传递
首先在一个fragment可以得到与它相关联的活动,然后再通过这个活动去获取另外一个fragment的实例,这样也就实现了不同fragment之间的通信功能
FragmentManager , add 和 replace 有什么区别?
- 使用 FragmentTransaction 的时候,它提供了这样两个方法,一个 add , 一个 replace ,add 和 replace 影响的只是界面,而控制回退的,是事务。
- add 是把一个fragment添加到一个容器 container 里。replace 是先remove掉相同id的所有fragment,然后在add当前的这个fragment。
- 在大部分情况下,这两个的表现基本相同。因为,一般,咱们会使用一个FrameLayout来当容器,而每个Fragment被add 或者 replace 到这个FrameLayout的时候,都是显示在最上层的。所以你看到的界面都是一样的。但是, 使用add的情况下,这个FrameLayout其实有2层,多层肯定要比一层的来得浪费,所以还是推荐使用replace。 当然有时候还是需要使用add的。比如要实现轮播图的效果,每个轮播图都是一个独立的Fragment,而他的容器FrameLayout需要add多个Fragment,这样他就可以根据提供的逻辑进行轮播了。而至于返回键的时候,这个跟事务有关,跟使用add还是replace没有任何关系。
- replace()方法会将被替换掉的那个Fragment彻底地移除掉,因此最好的解决方案就是使用hide()和show()方法来隐藏和显示Fragment,这就不会让Fragment的生命周期重走一遍了。
Fragment和view的区别
Fragment可以有效的对 view进行管理(增删和替换)而且结构更加清晰,有模块化的实现思想。
用view 很多逻辑代码可能都需要写在Activity里,如果view很多, 耦合度会很高。用Fragment则可以各自管理,起了解耦的作用。
一般软件简单的话直接view,复杂的用Fragment。
viewpager是一个滑动切换的控件,Fragment是一个轻量级的Activity,这个Fragment可以放到这个Viewpager里面去运行。
例如QQ或微信那样,可以来回切换不同的选项卡,即切换了不同的Fragment。通常Viewpager 会放fargment或者view
Fragment和Activity的区别
因为现在的手机屏幕都比较大了,Activity会把内容拉的很长,用Fragment的话可以左侧是列表,右侧是内容。在一个Activity里切换界面,切换界面时只切换Fragment里面的内容。Fragment通常用来作为一个activity界面的一部分。
view
请介绍下Android中常用的五种布局。
FrameLayout(帧布局),LinearLayout (线性布局),AbsoluteLayout(绝对布局),RelativeLayout(相对布局),TableLayout(表格布局)
- FrameLayout:从屏幕的左上角开始布局,叠加显示, 实际应用 播放器的暂停按钮.
- LinearLayout:线性布局,他首先是一个一个从上往下罗列在屏幕上。每一个LinearLayout里面又可分为垂直布局、水平布局。当垂直布局时,每一行就只有一个元素,多个元素依次垂直往下;水平布局时,只有一行,每一个元素依次向右排列。
- AbsoluteLayout:用X,Y坐标来指定元素的位置android:layout_x="20px" ,android:layout_y="12px" ,指定平板机型的游戏开发、机顶盒开发中经常用到绝对布局
- RelativeLayout:在相对的布局中主要就进行避免覆盖的问题,就是组件1可能会覆盖在组件2上(屏幕适配),在相对的布局中主要就进行避免覆盖的问题,就是组件1可能会覆盖在组件2上
- TableLayout:有几行,就有几个<TableRow/>,有几列,那么在<TableRow>中就有几个<TestView>,TableRow的子节点的宽和高是包裹内容,不需要指定宽高
TextView 、ImageView ,Button,ImageButton他们之间的联系和区别
ImageView 控件负责显示图片,其图片来源既可以是资源文件的id,也可以是Drawable对象或 Bitmap 对象,还可以是 内容提供者(Content Provider)的Uri.
ImageButton 控件 继承自 ImageView。与Button相同之处:都用于响应按钮的点击事件
不同之处:ImageButton只能显示图片;Button用于显示文字
屏幕适配
- 开发时选取主流屏幕 1280*720,用相对布局和线性布局
- 用dp sp不用px,dp单位动态匹配
- 开发后期在不同的分辨率上测试,没有太大问题可以上线
- 权重适配:weight 只有线性布局有
- 代码适配:getWindowManager().getDefaultDisplay().getWidth();获得屏幕的宽高
- 如果屏幕放不下了,可以使用 ScrollView(可以上下拖动)
- 布局适配:layout-800x180 针对某一种屏幕 工作量大
- 尺寸适配:dp=px/设备密度=getResource().getDisplayMetrice().dsnsity;
- 根据不同分辨率的屏幕建立不同的valuse,比如valuse-1280x720,values里的dimens里算出dp,最后引用系统会自动匹配。
- 在进行开发的时候,我们需要把合适大小的图片放在合适的文件夹里面。大分辨率图片(单维度超过 1000)大分辨率图片建议统一放在 xxhdpi 目录下管理,否则将导致占用内存成倍数增加。
RelativeLayout和FrameLayout的区别
Padding和Margin有什么区别?
Padding 是控件内容的距离margin是控件和控件间的距离
Listview如何显示不同条目?
第一种:在getview方法里根据position 进行判断,比如,position等于0是,显示标题,否则显示数据,当然相应的setOnItemClickListener也要去判断
第二种:根据listview里的getItemViewType,getViewTypeCount方法显示不同条目
/** 根据位置 判断当前条目是什么类型 */ @Override public int getItemViewType(int position) { //20 if (position == datas.size()) { // 当前是最后一个条目 return MORE_ITEM; } return getInnerItemViewType(position); // 如果不是最后一个条目 返回默认类型 } private int getInnerItemViewType(int position) { return DEFAULT_ITEM; } /** 当前ListView 有几种不同的条目类型 */ @Override public int getViewTypeCount() { return super.getViewTypeCount() + 1; // 2 有两种不同的类型 }
ScrowView 使用的注意
在不同的屏幕上显示内容不同的情况,其实这个问题我们往往是用滚动视图来解决的,也就是 ScrowView,
需要注意的是 ScrowView 中使用 layout_weight 是无效的,既然使用 ScrowView 了,就把它里面的控件的大小都设成固定的吧。
Android UI中的View如何刷新
在主线程中 拿到view调用Invalide()方法,在子线程里面可以通过postInvalide()方法;
invalidate();//主线程,刷新当前视图 导致 执行onDraw执行
postInvalidate();//子线程
什么是ANR(异步加载) 如何避免它?
android在主线程是不能加载网络数据或图片、数据库查询、复杂业务逻辑处理以及费时任务操作,因为Android的UI操作并不是线程安全的,并且所有涉及UI的操作必须在UI线程中完成。Android应用在5s内无响应的话会导致ANR(Application Not Response),这就要求开发者必须遵循两条法则:1、不能阻塞UI线程,2、确保只在UI线程中访问Android UI工具包。
Activity 5秒 broadcast10秒,服务20秒,耗时的操作在主thread里面完成
解决办法:Thread + Handler + Message ,Thread + Handler + post,AsyncTask,intentservice
runOnUiThread(Runnable)在子线程中直接使用该方法,可以更新UI
实现侧边栏、和指示器效果、页面滑动有几种方式
侧边栏:自定义、slidingmenu、DrawerLayout 、SlidingDrawer
指示器效果:自定义、viewpager里面的PagerTabStrip、ActionBar Tab标签、viewpagerindicate、FragmentTabHost、TabActivity、radiobutton
页面滑动:自定义、viewpager、手势识别器,其实就是onTouchEvent提供的简单工具类,onTouchEvent将触摸事件委托给了手势识别器
其他
Gradle中buildToolsVersion和TargetSdkVersion的区别是什么
compileSdkVersion, minSdkVersion 和 targetSdkVersion 的作用:他们分别控制可以使用哪些 API ,要求的 API 级别是什么,以及应用的兼容模式。TargetSdkVersion 设为23那么是按6.0设置的(运行时权限),小于23是按6.0以前的方式(安装时默认获得权限,且用户无法在安装App之后取消权限)
进程间怎么通信
binder是安卓中的一个类,它实现了IBinder接口,是安卓中跨进程通信的方式。当绑定服务的时候会返回一个binder对象,然后通过他进行多进程间的通信。
其实进程间通信就是为了实现数据共享。一个程序不同组件在不同进程也叫多进程,和俩个应用没有本质区别。使用process属性可以实现多进程,但是会带来很多麻烦,主要原因是共享数据会失败,弊端有:静态和单利失效,同步失效,sharedprefer可靠性减低等问题
Intent,Binder(AIDL),sharedpre、Messenger
- 使用intent的附加信息extras来传递,通过bundle,传递的是bundle支持的类型,比如基本数据类型、实现pracellable或serializeable的对象
- 使用文件共享,序列化或是sharedpre,不过不适用于读写并发的操作
- 通过message进行传递,在远程服务里创建message对像,在onbind里返回(message.getbinder)。在客户端绑定服务,拿着message对象发消息(可以用bundle)。在远程服务的handlermessage方法就会收到。他是一个个处理的,如果大量并发请求用aidl,mrssage底层就是aidl
- AIDL
- socket可以实现俩个终端通信,也可以在一个设备的俩个进程通信。需要在服务里创建服务端
- ContentProvider(进程间数据共享)和message一样,底层也是binder,除了oncreate方法其他方法(crud)都是运行在bindler线程里。所以在oncerate里不能做耗时操作。在其他应用访问通过uri(主机名),例如gercontentResolver.query(uri,null,null....)
- 有以上6种
使用多进程显而易见的好处就是分担主进程的内存压力。我们的应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的内存空间了。当然还有其他好处,有心人会发现Android后台进程里有很多应用是多个进程的,因为它们要常驻后台,特别是即时通讯或者社交应用,不过现在多进程已经被用烂了。典型用法是在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程,然后做监听等。还有就是防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方被杀就重新启动它。
假设手机本地需要缓存数据,如何保证和服务器的数据统一?
-
比如有个网络更新的功能,activity可以每隔半小时开启service去访问服务器,获取最新的数据。
-
在缓存文件里面加入时间戳,根据实际情况在一定的时间差内再次访问网络数据、判断URL
在缓存的第一行写一个上当前时间,读的时候判断是不是过期,根据需求看需要多久跟新
分页怎么做的?
分页根据服务器接口参数决定每次加载多少,getviewtype,getitemview
分批处理 解决的是时间等待的问题,不能解决内存占用的问题。
要想解决内存占用问题,可以采用分页方式
什么Context
Android工程环境中像Activity、Service、BroadcastReceiver等系统组件,而这些组件并不是像一个普通的Java对象new一下就能创建实例的了,而是要有它们各自的上下文环境,这就是Context。可以这样讲,Context是维持Android程序中各组件能够正常工作的一个核心功能类。
Context功能很多,可以弹出Toast、启动Activity、启动Service、发送广播、操作数据库等等等等都需要用到Context。
Context一共有Application、Activity和Service三种类型:Context数量 = Activity数量 + Service数量 + 1 。1代表着Application的数量,因为一个应用程序中可以有多个Activity和多个Service,但是只能有一个Application。
Android程序与Java程序的区别?
Android程序用android sdk开发,java程序用javasdk开发.
Android SDK引用了大部分的Java SDK,少数部分被Android SDK抛弃,比如说界面部分,java.awt swing package除了java.awt.font被引用外,其他都被抛弃,在Android平台开发中不能使用。 Android sdk 添加工具jar httpclient , pull opengl
Android中的动画有哪几类,它们的特点和区别是什么?
-
如果动画中的图像变换比较有规律时,例如图像的移动(TranslateAnimation)、旋转(RotateAnimation)、缩放(ScaleAnimation)、透明度渐变(AlphaAnimation),这些图像变化过程中的图像都可以根据一定的算法自动生成,我们只需要指定动画的第一帧和最后一帧图像即可,这种自动生成中间图像的动画就是补间动画。
-
补间动画,只是一个动画效果,组件其实还在原来的位置上,xy没有改变
属性动画:动画的对象除了传统的View对象,还可以是Object对象,动画结束后,Object对象的属性值被实实在在的改变了
Android工程的目录结构
src:项目的java代码
assets:资源文件夹,存放视频或者音乐等较大的资源文件
bin:存放应用打包编译后的文件
res:资源文件夹,在这个文件夹中的所有资源,都会有资源id,读取时通过资源id就可以读取
资源id不能出现中文
coverview原理:
这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用,在 getView()方法中进行了判断,如果 convertView为空,则使用
LayoutInflater去加载布局,如果不为空则直接对 convertView进行重用
android的启动流程
当程序启动Linux内核后,会加载各种驱动和数据结构,当有了驱动以后,开始启动Android系统同时会加载用户级别的第一个进程init(system\core\init.c),加载init.rc文件,会启动一个Zygote进程,此进程是Android系统的一个母进程,用来启动Android的其他服务进程,然后接着在里面启动各种硬件服务和activity。Android系统启动完成,打开了Luncher应用的Home界面。
Logcat
1. Log.v()
这个方法用于打印那些最为琐碎的,意义最小的日志信息。对应级别 verbose,是Android日志里面级别最低的一种。
2. Log.d()
这个方法用于打印一些调试信息, 这些信息对你调试程序和分析问题应该是有帮助的。对应级别 debug,比 verbose高一级。
3. Log.i()
这个方法用于打印一些比较重要的数据,这些数据应该是你非常想看到的,可以帮你分析用户行为的那种。对应级别 info,比 debug高一级。
4. Log.w()
这个方法用于打印一些警告信息,提示程序在这个地方可能会有潜在的风险,最好去修复一下这些出现警告的地方。对应级别 warn,比 info高一级。
5. Log.e()
这个方法用于打印程序中的错误信息,比如程序进入到了 catch语句当中。当有错误信息打印出来的时候,一般都代表你的程序出现严重问题了,必须尽快修复。对应级别 error,比 warn高一级.
推送
所有需要客户端被动接收信息的功能模块,都可以用推送实现,比如A想给B发消息,A调服务器接口,服务器只是存数据,它调推送的接口,推送去去找B。推送用的是xmpp协议, 是一种基于TCP/IP的协议, 这种协议更适合消息发送
- socket 套接字, 发送和接收网络请求
- 长连接 keep-alive, 服务器基于长连接找到设备,发送消息
- 心跳包 , 客户端会定时(30秒一次)向服务器发送一段极短的数据,作为心跳包, 服务器定时收到心跳,证明客户端或者,才会发消息.否则将消息保存起来,等客户端活了之后(重新连接),重新发送.
由于篇幅原因,附俩篇优化和自定义控件常见的面试题,希望对大家有所帮助: