以下是某家金融證券類公司的Android筆試題,及自己整理的答案,如有錯(cuò)誤請(qǐng)諸位看官指正,小生在此先行謝過。
基礎(chǔ)篇:
1、請(qǐng)描述下Activity的生命周期,說明下出現(xiàn)的條件和順序。
按照順序分別是:onCreate(),onStart(),onRestart(),onResume(),onPause(),onStop(),onDestory();
完整生命周期:一個(gè)activity從出現(xiàn)到消失,對(duì)應(yīng)的周期方法是從onCreate()到onDestory();
可見生命周期:當(dāng)activity處于用戶可見狀態(tài),將多次執(zhí)行從onStart()到onStop();
前景生命周期:當(dāng)activity可以與用戶交互時(shí),將多次執(zhí)行從onResume()到onPause();
2、如果后臺(tái)的Activity被系統(tǒng)回收了,如何在被回收之前保存當(dāng)前狀態(tài)。
調(diào)用onSaveInstanceState() 方法保存數(shù)據(jù):
@Override
protected void onSaveInstanceState(Bundle outState) {
// TODO Auto-generated method stub
super.onSaveInstanceState(outState);
String statetext=tView1.getText().toString();
outState.putString("state", statetext);
}
在onCreate()中恢復(fù)保存的數(shù)據(jù):
if(savedInstanceState!=null){
String statetextString=savedInstanceState.getString("state");
}
3、如何將activity設(shè)置成窗口化。
第一種方法,在styles.xml文件中,可以新建如下的類似Dialog的style。
<style name=“Theme.FloatActivity” parent=“android:style/Theme.Dialog”> </style>。然后在AndroidManifest.xml中在需要顯示為窗口的Activity中添加如下屬性: android: theme=“@style/Theme.FloatActivity”即可。
第二種方法,也可以直接添加對(duì)應(yīng)需要展示為Dialog style的Activity的android: theme屬性為android: theme=“@ android: style/Theme.Dialog”
4、如何退出activity?如何安全退出已調(diào)用多個(gè)activity的application?
對(duì)于單一的activity,直接finish()即可,或者可以使用killProcess()和System.exit();
對(duì)于多個(gè)activity,可以定義一個(gè)list<activity>,實(shí)現(xiàn)創(chuàng)建的時(shí)候?qū)ctivity緩存在list中,銷毀的時(shí)候從list中移除,按退出按鈕,則遍歷整個(gè)List將Activity一一銷毀;
5、請(qǐng)介紹下Android的數(shù)據(jù)存儲(chǔ)方式。
1)SharePreference:采用XML格式將數(shù)據(jù)存儲(chǔ)到設(shè)備中,只能在同一包名內(nèi)使用;
2)文件存儲(chǔ):存儲(chǔ)文件到 /data/data/包名/files 內(nèi)存里面,默認(rèn)是私有的訪問權(quán)限;
3)SQLite數(shù)據(jù)庫(kù):Android自帶的輕量級(jí)嵌入式數(shù)據(jù)庫(kù),支持SQL語句;
4)ContentProvider:主要用于應(yīng)用程序之間的數(shù)據(jù)交換;
5)網(wǎng)絡(luò)存儲(chǔ):通過網(wǎng)絡(luò)上的存儲(chǔ)空間上傳下載數(shù)據(jù);
6、請(qǐng)介紹下ContentProvider時(shí)如何實(shí)現(xiàn)數(shù)據(jù)共享。
ContentProvider是以Uri的形式對(duì)外提供數(shù)據(jù),ContenrResolver是根據(jù)Uri來訪問數(shù)據(jù)。
步驟:
1)定義自己的ContentProvider類,繼承系統(tǒng)的ContentProvider類
2)在Mainifest.xml文件中注冊(cè)ContentProvider,注冊(cè)時(shí)需要綁定一個(gè)URL
<manifest .... >
<application android:icon="@drawable/icon" android:label="@string/app_name">
<provider android:name=".PersonContentProvider"
android:authorities="com.faith.providers.personprovider" />
</application>
</manifest>
(注:authorities就相當(dāng)于為該ContentProvider指定URL)
3)其他程序調(diào)用ContentReslover的insert(),delete(),update(),query()進(jìn)行增刪改查。
7、請(qǐng)介紹下Android的五種常用的布局。
FrameLayout(幀布局):從屏幕左上角開始布局,疊加顯示
LinearLayout(線性布局):可分為垂直、水平布局
AbsoluteLayout(絕對(duì)布局):用X,Y坐標(biāo)制定元素位置
RelativeLatout(相對(duì)布局):以一個(gè)元素為參照物,來定位布局方式
TableLayout(表格布局):行和列的表格布局
8、如何啟動(dòng)service,如何停用service。
1)調(diào)用Context.startService()方法啟動(dòng)服務(wù),在服務(wù)為創(chuàng)建時(shí)系統(tǒng)會(huì)先調(diào)用onCreate()方法,接著調(diào)用onStart()方法。調(diào)用Context.stopService方法結(jié)束服務(wù),服務(wù)結(jié)束時(shí)會(huì)調(diào)用onDestroy()方法;
2)調(diào)用Context.bindService()法啟動(dòng)服務(wù),在服務(wù)為創(chuàng)建時(shí)系統(tǒng)會(huì)先調(diào)用onCreate()方法,接著調(diào)用onBind()方法,這個(gè)時(shí)候調(diào)用者與服務(wù)綁定在一起了,當(dāng)調(diào)用者退出時(shí)系統(tǒng)會(huì)調(diào)用服務(wù)的onUnbind()-->onDestroy()方法。解除綁定可調(diào)用unvindService()方法,調(diào)用該方法也會(huì)導(dǎo)致系統(tǒng)調(diào)用服務(wù)的onUnbind()-->onDestroy()方法;
9、注冊(cè)廣播的有幾種方式,有何優(yōu)缺點(diǎn)?請(qǐng)談?wù)凙ndroid引入廣播機(jī)制的用意。
1)靜態(tài)注冊(cè),只要是設(shè)備開啟狀態(tài),廣播接受器就是打開著的,無需擔(dān)心接收器是否被關(guān)閉;
2)動(dòng)態(tài)注冊(cè),廣播跟隨程序的生命周期,優(yōu)先級(jí)高于靜態(tài)注冊(cè);
引入廣播機(jī)制的用意:
a:從MVC的角度考慮(應(yīng)用程序內(nèi))
b:程序間互通消息(例如在自己的應(yīng)用程序內(nèi)監(jiān)聽系統(tǒng)來電)
c:效率上(參考UDP的廣播協(xié)議在局域網(wǎng)的方便性)
d:設(shè)計(jì)模式上(反轉(zhuǎn)控制的一種應(yīng)用,類似監(jiān)聽者模式)
10、請(qǐng)?jiān)O(shè)計(jì)一段斷點(diǎn)續(xù)傳的代碼,說明關(guān)鍵幾點(diǎn)即可。
在本地下載過程中要使用數(shù)據(jù)庫(kù)實(shí)時(shí)存儲(chǔ)到底存儲(chǔ)到文件的哪個(gè)位置了,這樣點(diǎn)擊開始繼續(xù)傳遞時(shí),才能通過HTTP的GET請(qǐng)求中的setRequestProperty()方法可以告訴服務(wù)器,數(shù)據(jù)從哪里開始,到哪里結(jié)束。同時(shí)在本地的文件寫入時(shí),RandomAccessFile的seek()方法也支持在文件中的任意位置進(jìn)行寫入操作。同時(shí)通過廣播將子線程的進(jìn)度告訴Activity的ProcessBar。
11、關(guān)于Android應(yīng)用程序自動(dòng)更新方式,請(qǐng)闡述一種合理的方式。
1)獲取服務(wù)器版本,對(duì)比當(dāng)前版本,判斷是否需要更新程序;
2)彈出是否下載新版本對(duì)話框,點(diǎn)擊下載
3)在通知欄顯示下載進(jìn)度
4)下載完成后,調(diào)用系統(tǒng)安裝軟件服務(wù),安裝軟件
12、Intent的幾種有關(guān)activity啟動(dòng)方式有哪些,說明下每種方式的含義。
Activity有如下兩種啟動(dòng)模式:
1)通過AndroidMainifest.xml給Activity 指定啟動(dòng)模式
2)通過intent中設(shè)置標(biāo)志位為Activity 指定啟動(dòng)模式
常用的Activity 的Flags有:
FLAG_ACTIVITY_NEW_TASK :為 Activity 指定 “singleTask” 啟動(dòng)模式
FLAG_ACTIVITY_SINGLE_TOP :為 Activity 指定 “singleTop” 啟動(dòng)模式
FLAG_ACTIVITY_CLEAR_TOP:具有此標(biāo)志位的 Activity ,當(dāng)它啟動(dòng)時(shí),在同一個(gè)任務(wù)棧中所有位于它上面的 Activity 都要出棧。這個(gè)模式一般需要和 FLAG_ACTIVITY_NEW_TASK 配合使用。singleTask 啟動(dòng)模式默認(rèn)具有此標(biāo)志位的效果。
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:
具有這個(gè)標(biāo)記的 Activity 不會(huì)出現(xiàn)在歷史 Activity 的列表中,它等同于在 XML 中指定 Activity 的屬性 android:excludeFromRecents="true" 。
13、說說你對(duì)自定義控件的想法,介紹下你做過的某些自定義控件。
隨著UI越來越絢麗,Android自帶控件已無法滿足產(chǎn)品的腦洞了,這時(shí)候就需要我們自己來實(shí)現(xiàn)滿足項(xiàng)目需求的控件。常用的自定義控件有如下幾種:
1)基于原聲控件的擴(kuò)展(如:實(shí)現(xiàn)可滾動(dòng)字體的TextView)
2)基于組合控件的擴(kuò)展(如:可以圖文混排的LinearLayout)
3)自定義View(如:波浪加載動(dòng)畫)
自定義View的繪制主要有三種方法onMeasure(),onLayout(),onDraw();
14、你如何評(píng)價(jià)Android系統(tǒng),有何優(yōu)缺點(diǎn)。
優(yōu)點(diǎn):
開源性,市場(chǎng)占有率大;
無縫結(jié)合Google應(yīng)用
缺點(diǎn):
安全與隱私
運(yùn)行商可定制系統(tǒng)
缺乏標(biāo)準(zhǔn)配置
15、請(qǐng)解釋下在單線程模型中Message、Handler、Message Queue、Looper之間的關(guān)系。
簡(jiǎn)單的說,Handler獲取當(dāng)前線程中的 looper對(duì)象,looper 用來從存放 Message 的 MessageQueue中取出 Message,再有 Handler 進(jìn)行 Message 的分發(fā)和處理.
Message Queue(消息隊(duì)列): 用來存放通過 Handler 發(fā)布的消息, 通常附屬于某一個(gè)創(chuàng)建它的線程,可以通過 Looper.myQueue()得到當(dāng)前線程的消息隊(duì)列
Handler:可以發(fā)布或者處理一個(gè)消息或者操作一個(gè) Runnable,通過 Handler發(fā)布消息, 消息將只會(huì)發(fā)送到與它關(guān)聯(lián)的消息隊(duì)列,然也只能處理該消息隊(duì)列中的消息
Looper:是 Handler 和消息隊(duì)列之間通訊橋梁,程序組件首先通過 Handler 把消息傳遞給 Looper,Looper 把消息放入隊(duì)列。Looper 也把消息隊(duì)列里的消息廣播給所有的 Handler,Handler接受到消息后調(diào)用 handleMessage進(jìn)行處理
Message:消息的類型,在 Handler 類中的 handleMessage 方法中得到單個(gè)的消息進(jìn)行處理
在單線程模型下, 為了線程通信問題, Android 設(shè)計(jì)了一個(gè) Message Queue(消息隊(duì)列), 線程間可以通過該 Message Queue 并結(jié)合 Handler 和 Looper 組件進(jìn)行信息交換。
16、Android 中線程與線程,進(jìn)程與進(jìn)程之間如何通信。
一個(gè)Android程序開始運(yùn)行時(shí),會(huì)單獨(dú)啟動(dòng)一個(gè)process,這個(gè)process呈現(xiàn)的是單線程模型,即mianthread,所有的任務(wù)都在一個(gè)線程中運(yùn)行。所以mainthread所調(diào)用的每一個(gè)函數(shù)耗時(shí)越短越好,對(duì)于比較耗時(shí)的工作應(yīng)該交給子線程去做,以避免主線程阻塞。Android提供了Handler和Looper來滿足線程之間的通信。
Android的IPC(進(jìn)程間通信)機(jī)制是為了讓activity和service之間可以隨時(shí)進(jìn)行交互,所以該機(jī)制只適用于activity和service之間的通信。 要實(shí)現(xiàn)跨進(jìn)程通信,需要借助AIDL(Android Interface Definition Language)。Android中的跨進(jìn)程服務(wù)其實(shí)是采用C/S的架構(gòu),因而AIDL的目的就是實(shí)現(xiàn)通信接口。簡(jiǎn)單來說就是通過定義 AIDL 接口文件來定義 IPC 接口。Servier 端實(shí)現(xiàn) IPC接口,Client 端調(diào)用 IPC接口本地代理。
17、 AIDL的全稱是什么?如何工作?
AIDL 全稱 Android Interface Definition Language(Android 接口描述語言)是一種接口描述語言 ; 編譯器可以通過 aidl文件生成一段代碼,通過預(yù)先定義的接口達(dá)到兩個(gè)進(jìn)程內(nèi)部通信進(jìn)程跨界對(duì)象訪問的目的。
AIDL的IPC的機(jī)制和COM 或 CORBA 類似,是基于接口的,但它是輕量級(jí)的。它使用代理類在客戶端和實(shí)現(xiàn)層間傳遞值。
如果要使用 AIDL, 需要完成2件事情 :
- 引入AIDL的相關(guān)類;
- 調(diào)用aidl產(chǎn)生的 class;(理論上 , 參數(shù)可以傳遞基本數(shù)據(jù)類型和String, 還有就是Bundle的派生類 )
進(jìn)階篇
1、請(qǐng)闡述之前使用過的網(wǎng)絡(luò)通信框架,包含了調(diào)用方式,封裝數(shù)據(jù)對(duì)象,傳輸,返回?cái)?shù)據(jù)解析,以及全局的一場(chǎng)捕獲和處理。
這一題不知道具體怎么回答,就簡(jiǎn)單列出一個(gè)OKhttp的GET請(qǐng)求代碼:
OkHttpClient okHttpClient=new OkHttpClient();
Request request=new Request.Builder()
.url("http://www.baidu.com")
.build();
Call call=okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.d(LOG,"body"+response.body().toString());
}
});
2、有4個(gè)Activity A、B、C、D在當(dāng)前應(yīng)用的頁(yè)面棧中,請(qǐng)描述如何保證在互相隨意跳轉(zhuǎn),打開的是原來的界面,并且4個(gè)activity都不會(huì)被回收或者重新打開一個(gè)新的對(duì)象。
這個(gè)問題也不知道怎么回答才好,先簡(jiǎn)單列一下Android的四種啟動(dòng)模式,再做分析吧。
1)standard:標(biāo)準(zhǔn)模式,也就是Android默認(rèn)的啟動(dòng)方式。在該模式下沒啟動(dòng)一個(gè)activity就會(huì)重新創(chuàng)建一個(gè)新的實(shí)例,不管這個(gè)實(shí)例在任務(wù)中是否存在。
2)singleTop:棧頂復(fù)用模式。假如A啟動(dòng)B,就會(huì)判斷A所在的棧頂是否是B實(shí)例,如果是則直接引用這個(gè)棧頂實(shí)例,如果不是則重新創(chuàng)建的B實(shí)例加載到棧頂。
3)singleTask:站內(nèi)復(fù)用模式。如果要啟動(dòng)的activity不存在,則創(chuàng)建新的的實(shí)例并加載到棧頂;如果位于棧中,則銷毀上面的實(shí)例,使該實(shí)例位于棧頂;如果已位于棧頂,則與singleTop模式相同。
4)singleInstance:?jiǎn)卫J?。這個(gè)是 singleTask 模式的加強(qiáng)版,它除了具有 singleTask 模式的所有特性外,它還有一點(diǎn)獨(dú)特的特性,那就是此模式的 Activity 只能單獨(dú)地位于一個(gè)任務(wù)棧,不與其他 Activity 共存于同一個(gè)任務(wù)棧。
經(jīng)過上面的描述,可以看出滿足題中條件只能使用singleTop,即棧頂復(fù)用模式,具體方法就是通過AndroidMenifest 給 Activity 指定啟動(dòng)模式:
<activity android:name=".SecondActivity"
android:launchMode="singleTask"></activity>
3、描述下ANR錯(cuò)誤,哪些情況會(huì)發(fā)生ANR錯(cuò)誤,如何避免?
Application Not Responding 程序無響應(yīng),出現(xiàn)下列情況,會(huì)顯示ANR框:
1)輸入事件的響應(yīng)時(shí)間超過5秒
2)BroadcastReceiver在10秒內(nèi)沒有執(zhí)行完畢
造成以上兩點(diǎn)的原因有很多,比如在主線程中進(jìn)行耗時(shí)操作,如網(wǎng)絡(luò)請(qǐng)求、IO等。避免方法就是不要在主線程中做耗時(shí)操作,而是采用handler+message的方式放在子線程中去實(shí)現(xiàn)。
4、如何理解application?你的項(xiàng)目中,application用來管理哪些數(shù)據(jù)?
Application是單例模式的類,android系統(tǒng)為每個(gè)應(yīng)用程序創(chuàng)建一個(gè)Application類的對(duì)象且只創(chuàng)建一個(gè)。
啟動(dòng)Application時(shí),系統(tǒng)會(huì)創(chuàng)建一個(gè)PID,即進(jìn)程ID,所有的Activity都會(huì)在此進(jìn)程上運(yùn)行。
Application創(chuàng)建的時(shí)候初始化全局變量,同一個(gè)應(yīng)用的所有Activity都可以取到這些全局變量的值
Application對(duì)象的生命周期是整個(gè)程序中最長(zhǎng)的,它的生命周期就等于這個(gè)程序的生命周期
Application全局的單例的,所以在不同的Activity,Service中獲得的對(duì)象都是同一個(gè)對(duì)象。
應(yīng)用場(chǎng)景:
在Android中,可以通過繼承Application類來實(shí)現(xiàn)應(yīng)用程序級(jí)的全局變量,這種全局變量方法相對(duì)靜態(tài)類更有保障,直到應(yīng)用的所有Activity全部被destory掉之后才會(huì)被釋放掉。
5、說說activity的affinity屬性及其應(yīng)用場(chǎng)景。
每個(gè)Activity都有taskAffinity屬性,這個(gè)屬性指出了它希望進(jìn)入的Task。如果一個(gè)Activity沒有顯式的指明該Activity的taskAffinity,那么它的這個(gè)屬性就等于Application指明的taskAffinity,如果Application也沒有指明,那么該taskAffinity的值就等于包名。而Task也有自己的affinity屬性,它的值等于它的根Activity的taskAffinity的值。
6、如何理解DVM的進(jìn)程和Linux的進(jìn)程
Dvm的進(jìn)程是dalivk虛擬機(jī)進(jìn)程,每個(gè)Android程序都運(yùn)行在自己的進(jìn)程里面,每個(gè)android程序系統(tǒng)都會(huì)給他分配一個(gè)單獨(dú)的liunx uid(user id),每個(gè)dvm都是Linux里面的一個(gè)進(jìn)程.所以說這兩個(gè)進(jìn)程是一個(gè)進(jìn)程.
7、描述下JNI的編譯和工作原理,你的項(xiàng)目中使用過JNI嗎?
編譯過程:
1)下載和配置NDK
2)新建項(xiàng)目MyJniDemo,然后再新建一個(gè)類JniUtils,在其內(nèi)部聲明native方法
3)將JNI的內(nèi)容通過TextView顯示在UI上
4)執(zhí)行Build->Make Project,生成的.class文件目錄
5)根據(jù)生成的class文件,利用javah生成對(duì)應(yīng)的 .h頭文件
6)在main目錄下新建一個(gè)名為jni的目錄,講剛才的.h文件剪切過來,然后新建一個(gè)jniutils.c文件
7)在app module目錄下的build.gradle中設(shè)置庫(kù)文件名
8)在JniUtils類中添加靜態(tài)初始化load代碼
原理:
在JNI中,本地函數(shù)是通過一個(gè)獨(dú)立的.c或.cpp文件來實(shí)現(xiàn)的(C++為JNI提供的界面會(huì)更簡(jiǎn)潔一些)。當(dāng)JVM調(diào)用該函數(shù)時(shí),它傳遞了一個(gè)JNIEnv指針、一個(gè)jobject指針和通過Java方法定義的Java參數(shù)
8、你理解的Android安全性可以分為哪幾塊?分別說說你的的一些安全解決方案,也可以單獨(dú)舉出一個(gè)場(chǎng)景來說明,如“登錄”,“支付”流程。
1)數(shù)據(jù)傳輸過程遭劫持 解決:數(shù)據(jù)加密
2)APP反編譯 解決:應(yīng)用程序簽名,混淆打包,應(yīng)用加固
3)手機(jī)ROOT,進(jìn)程被劫持 解決:應(yīng)用權(quán)限控制
4)本地?cái)?shù)據(jù)存儲(chǔ)的安全性 解決:數(shù)據(jù)存儲(chǔ)加密
9、如APP運(yùn)行所需內(nèi)存較大,在一些低端手機(jī)上經(jīng)常會(huì)無法正常工作。請(qǐng)給出定位、解決內(nèi)存問題的方案思路,可以結(jié)合你曾經(jīng)解決過的場(chǎng)景說明。
本應(yīng)該被回收的對(duì)象沒有被GC回收,這些無用的對(duì)象會(huì)占用大量?jī)?nèi)存,這就是我們常說的內(nèi)存泄漏。下面說一下怎么避免內(nèi)存泄漏:
1)Application Context代替Activity Context
2)使用靜態(tài)內(nèi)部類和弱引用
3)資源對(duì)象必須關(guān)閉
4)注銷監(jiān)聽器
5)不要加載過大的圖片
6)對(duì)批量加載進(jìn)行緩存設(shè)計(jì)