Android面試題
http://topic.csdn.net/u/20101219/21/b6f83d6f-35b5-49ea-b281-d47ca662ea17.html?573241. 請(qǐng)描述下Activity的生命周期。
2. 如果后臺(tái)的Activity由于某原因被系統(tǒng)回收了,如何在被系統(tǒng)回收之前保存當(dāng)前狀態(tài)?
3. 如何將一個(gè)Activity設(shè)置成窗口的樣式。(Edited by Sodino)
4. 如何退出Activity?如何安全退出已調(diào)用多個(gè)Activity的Application?
5. 請(qǐng)介紹下Android中常用的五種布局。
6. 請(qǐng)介紹下Android的數(shù)據(jù)存儲(chǔ)方式。(Edited by Sodino)
7. 請(qǐng)介紹下ContentProvider是如何實(shí)現(xiàn)數(shù)據(jù)共享的。(Edited by Sodino)
8. 如何啟用Service,如何停用Service。(Edited by Sodino)
9. 注冊(cè)廣播有幾種方式,這些方式有何優(yōu)缺點(diǎn)?請(qǐng)談?wù)凙ndroid引入廣播機(jī)制的用意。
10. 請(qǐng)解釋下在單線程模型中Message、Handler、Message Queue、Looper之間的關(guān)系。
11. AIDL的全稱是什么?如何工作?能處理哪些類(lèi)型的數(shù)據(jù)?
12. 請(qǐng)解釋下Android程序運(yùn)行時(shí)權(quán)限與文件系統(tǒng)權(quán)限的區(qū)別。(Edited by Sodino)
13. 系統(tǒng)上安裝了多種瀏覽器,能否指定某瀏覽器訪問(wèn)指定頁(yè)面?請(qǐng)說(shuō)明原由。
14. 有一個(gè)一維整型數(shù)組int[]data保存的是一張寬為width,高為height的圖片像素值信息。請(qǐng)寫(xiě)一個(gè)算法,將該圖片所有的白色不透明(0xffffffff)像素點(diǎn)的透明度調(diào)整為50%。
15. 你如何評(píng)價(jià)Android系統(tǒng)??jī)?yōu)缺點(diǎn)。
1.activity的生命周期。
2.橫豎屏切換時(shí)候activity的生命周期
總結(jié):
1、不設(shè)置Activity的android:configChanges時(shí),切屏?xí)匦抡{(diào)用各個(gè)生命周期,切橫屏?xí)r會(huì)執(zhí)行一次,切豎屏?xí)r會(huì)執(zhí)行兩次
2、設(shè)置Activity的android:configChanges="orientation"時(shí),切屏還是會(huì)重新調(diào)用各個(gè)生命周期,切橫、豎屏?xí)r只會(huì)執(zhí)行一次
3、設(shè)置Activity的android:configChanges="orientation|keyboardHidden"時(shí),切屏不會(huì)重新調(diào)用各個(gè)生命周期,只會(huì)執(zhí)行onConfigurationChanged方法
3.android中的動(dòng)畫(huà)有哪幾類(lèi),它們的特點(diǎn)和區(qū)別是什么
4.handler機(jī)制的原理
5.說(shuō)說(shuō)activity,intent,service是什么關(guān)系
6.android中線程與線程,進(jìn)程與進(jìn)程之間如何通信
7.widget相對(duì)位置的完成在antivity的哪個(gè)生命周期階段實(shí)現(xiàn)
8.說(shuō)說(shuō)mvc模式的原理,它在android中的運(yùn)用
9.說(shuō)說(shuō)在android中有哪幾種數(shù)據(jù)存儲(chǔ)方式
10.android中有哪幾種解析xml的類(lèi),官方推薦哪種?以及它們的原理和區(qū)別
3. 如何將一個(gè)Activity設(shè)置成窗口的樣式。
設(shè)置Activity窗口化:
android:theme="@android:style/Theme.Dialog"
設(shè)置Activity透明化:
android:theme="@android:style/Theme.Translucent"
6. 請(qǐng)介紹下Android的數(shù)據(jù)存儲(chǔ)方式
㈠.文件存儲(chǔ)方式:
在Android中通常使用File存儲(chǔ)方式是用 Context.openFileOutput(String fileName, int mode)和Context.openFileInput(String fileName)。
Context.openFileOutput(String fileName, int mode)生成的文件自動(dòng)存儲(chǔ)在/data/data/PackageName/file目錄下,其全路徑是/data/data/Package Name/files/fileName 。注意下,這里的參數(shù)fileName不可以包含路徑分割符(如"/")。
通常來(lái)說(shuō),這種方式生成的文件只能在這個(gè)apk內(nèi)訪問(wèn)。但這個(gè)結(jié)論是指使用Context.openFileInput(String fileName)的方式。使用這種方式,每個(gè)apk只可以訪問(wèn)自己的/data/data/Package Name/files目錄下的文件,原因很簡(jiǎn)單,參數(shù)fileName中不可以包含路徑分割符,Android會(huì)自動(dòng)在/data/data /Package Name/files目錄下尋找文件名為fileName的文件。
具體實(shí)例如下:
String fn = “moandroid.log”;
FileInputStream fis = openFileInput(fn);
FileOutputStream fos = openFileOutput(fn.Context.MODE_PRIVATE);
㈡.使用SharedPreferences存儲(chǔ)數(shù)據(jù)
首先說(shuō)明SharedPreferences存儲(chǔ)方式,它是 Android提供的用來(lái)存儲(chǔ)一些簡(jiǎn)單配置信息的一種機(jī)制,例如:登錄用戶的用戶名與密碼。其采用了Map數(shù)據(jù)結(jié)構(gòu)來(lái)存儲(chǔ)數(shù)據(jù),以鍵值的方式存儲(chǔ),可以簡(jiǎn)單的讀取與寫(xiě)入,具體實(shí)例如下:
void ReadSharedPreferences(){
String strName,strPassword;
SharedPreferences user = getSharedPreferences(“user_info”,0);
strName = user.getString(“NAME”,””);
strPassword = user getString(“PASSWORD”,””);
}
void WriteSharedPreferences(String strName,String strPassword){
SharedPreferences user = getSharedPreferences(“user_info”,0);
uer.edit()//獲得編輯器;
user.putString(“NAME”, strName);
user.putString(“PASSWORD” ,strPassword);
user.commit();
}
數(shù)據(jù)讀取與寫(xiě)入的方法都非常簡(jiǎn)單,只是在寫(xiě)入的時(shí)候有些區(qū)別:先調(diào)用edit()使其處于編輯狀態(tài),然后才能修改數(shù)據(jù),最后使用commit()提交修改的數(shù)據(jù)。實(shí)際上SharedPreferences是采用了XML格式將數(shù)據(jù)存儲(chǔ)到設(shè)備中,在DDMS中的File Explorer中的/data/data/<package name>/shares_prefs下。以上面的數(shù)據(jù)存儲(chǔ)結(jié)果為例,打開(kāi)后可以看到一個(gè)user_info.xml的文件,打開(kāi)后可以看到:
<?xml version=”1.0″ encoding=”UTF-8″?>
<map>
<string name=”NAME”>moandroid</string>
<string name=” PASSWORD”>SharedPreferences</string>
</map>
使用SharedPreferences是有些限制的:只能在同一個(gè)包內(nèi)使用,不能在不同的包之間使用。
㈢:網(wǎng)絡(luò)存儲(chǔ)數(shù)據(jù)
網(wǎng)絡(luò)存儲(chǔ)方式,需要和Android網(wǎng)絡(luò)數(shù)據(jù)包打交道。
㈣.ContentProvider數(shù)據(jù)存儲(chǔ)
1、ContentProvider簡(jiǎn)介
當(dāng)應(yīng)用繼承ContentProvider類(lèi),并重寫(xiě)該類(lèi)用于提供數(shù)據(jù)和存儲(chǔ)數(shù)據(jù)的方法,就可以向其他應(yīng)用共享其數(shù)據(jù)。雖然使用其他方法也可以對(duì)外共享數(shù)據(jù),但數(shù)據(jù)訪問(wèn)方式會(huì)因數(shù)據(jù)存儲(chǔ)的方式而不同,如:采用文件方式對(duì)外共享數(shù)據(jù),需要進(jìn)行文件操作讀寫(xiě)數(shù)據(jù);采用sharedpreferences共享數(shù)據(jù),需要使用sharedpreferences API讀寫(xiě)數(shù)據(jù)。而使用ContentProvider共享數(shù)據(jù)的好處是統(tǒng)一了數(shù)據(jù)訪問(wèn)方式。與之相關(guān)聯(lián)的是uri,下面是uri的簡(jiǎn)介:
2、Uri類(lèi)簡(jiǎn)介
Uri代表了要操作的數(shù)據(jù),Uri主要包含了兩部分信息:需要操作的ContentProvider和對(duì)ContentProvider中的什么數(shù)據(jù)進(jìn)行操作,一個(gè)Uri由以下幾部分組成:
1).scheme:ContentProvider(內(nèi)容提供者)的scheme已經(jīng)由Android所規(guī)定為:content://。
2).主機(jī)名(或Authority):用于唯一標(biāo)識(shí)這個(gè)ContentProvider,外部調(diào)用者可以根據(jù)這個(gè)標(biāo)識(shí)來(lái)找到它。
3).路徑(path):可以用來(lái)表示我們要操作的數(shù)據(jù),路徑的構(gòu)建應(yīng)根據(jù)業(yè)務(wù)而定,如下:
要操作contact表中id為10的記錄,可以構(gòu)建這樣的路徑:/contact/10
要操作contact表中id為10的記錄的name字段, contact/10/name
要操作contact表中的所有記錄,可以構(gòu)建這樣的路徑:/contact?
要操作的數(shù)據(jù)不一定來(lái)自數(shù)據(jù)庫(kù),也可以是文件等他存儲(chǔ)方式,如下:
要操作xml文件中contact節(jié)點(diǎn)下的name節(jié)點(diǎn),可以構(gòu)建這樣的路徑:/contact/name
如果要把一個(gè)字符串轉(zhuǎn)換成Uri,可以使用Uri類(lèi)中的parse()方法,如下:
Uri uri = Uri.parse("content://com.changcheng.provider.contactprovider/contact")
3、UriMatcher、ContentUrist和ContentResolver簡(jiǎn)介
因?yàn)閁ri代表了要操作的數(shù)據(jù),所以我們很經(jīng)常需要解析Uri,并從 Uri中獲取數(shù)據(jù)。Android系統(tǒng)提供了兩個(gè)用于操作Uri的工具類(lèi),分別為UriMatcher 和ContentUris 。掌握它們的使用,會(huì)便于我們的開(kāi)發(fā)工作。
? UriMatcher:用于匹配Uri,它的用法如下:
1).首先把你需要匹配Uri路徑全部給注冊(cè)上,如下:
//常量UriMatcher.NO_MATCH表示不匹配任何路徑的返回碼(-1)。
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
//如果match()方法匹配content://com.changcheng.sqlite.provider.contactprovider /contact路徑,返回匹配碼為1
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1);//添加需要匹配uri,如果匹配就會(huì)返回匹配碼
//如果match()方法匹配 content://com.changcheng.sqlite.provider.contactprovider/contact/230路徑,返回匹配碼為2
uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);//#號(hào)為通配符
2).注冊(cè)完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法對(duì)輸入的Uri進(jìn)行匹配,如果匹配就返回匹配碼,匹配碼是調(diào)用 addURI()方法傳入的第三個(gè)參數(shù),假設(shè)匹配 content://com.changcheng.sqlite.provider.contactprovider/contact路徑,返回的匹配碼為1。
ContentUris:用于獲取Uri路徑后面的ID部分,它有兩個(gè)比較實(shí)用的方法:
withAppendedId(uri, id)用于為路徑加上ID部分
parseId(uri)方法用于從路徑中獲取ID部分
ContentResolver:當(dāng)外部應(yīng)用需要對(duì)ContentProvider中的數(shù)據(jù)進(jìn)行添加、刪除、修改和查詢操作時(shí),可以使用 ContentResolver 類(lèi)來(lái)完成,要獲取ContentResolver 對(duì)象,可以使用Activity提供的getContentResolver()方法。 ContentResolver使用insert、delete、update、query方法,來(lái)操作數(shù)據(jù)。
㈤.數(shù)據(jù)庫(kù)存儲(chǔ)方式?
數(shù)據(jù)庫(kù)存儲(chǔ)方式,最長(zhǎng)用的是SQLite
8. 如何啟用Service,如何停用Service
● 啟動(dòng)方式和停止方式:
㈠.Context.startService() / Context.stopService();
㈡. Context.bindService() / Context.unbindService();
9. 注冊(cè)廣播有幾種方式,這些方式有何優(yōu)缺點(diǎn)?請(qǐng)談?wù)凙ndroid引入廣播機(jī)制的用意。
在android下,要想接受廣播信息,那么這個(gè)廣播接收器就得我們自己來(lái)實(shí)現(xiàn)了,我們可以繼承BroadcastReceiver,就可以有一個(gè)廣播接受器了。有個(gè)接受器還不夠,我們還得重寫(xiě)B(tài)roadcastReceiver里面的onReceiver方法,當(dāng)來(lái)廣播的時(shí)候我們要干什么,這就要我們自己來(lái)實(shí)現(xiàn),不過(guò)我們可以搞一個(gè)信息防火墻。具體的代碼:
public class SmsBroadCastReceiver extends BroadcastReceiver
{
public void onReceive(Context context, Intent intent)
{
Bundle bundle = intent.getExtras();
Object[] object = (Object[])bundle.get("pdus");
SmsMessage sms[]=new SmsMessage[object.length];
for(int i=0;i<object.length;i++)
{
sms[0] = SmsMessage.createFromPdu((byte[])object[i]);
Toast.makeText(context, "來(lái)自 "+sms[i].getDisplayOriginatingAddress()+" 的消息是:"+sms[i].getDisplayMessageBody(), Toast.LENGTH_SHORT).show();
}
//終止廣播,在這里我們可以稍微處理,根據(jù)用戶輸入的號(hào)碼可以實(shí)現(xiàn)短信防火墻。
abortBroadcast();
}
}
當(dāng)實(shí)現(xiàn)了廣播接收器,還要設(shè)置廣播接收器接收廣播信息的類(lèi)型,這里是信息:android.provider.Telephony.SMS_RECEIVED
我們就可以把廣播接收器注冊(cè)到系統(tǒng)里面,可以讓系統(tǒng)知道我們有個(gè)廣播接收器。這里有兩種,
? 一種是代碼動(dòng)態(tài)注冊(cè):
//生成廣播處理
smsBroadCastReceiver = new SmsBroadCastReceiver();
//實(shí)例化過(guò)濾器并設(shè)置要過(guò)濾的廣播
IntentFilter intentFilter = new IntentFilter("android.provider.Telephony.SMS_RECEIVED");
//注冊(cè)廣播
BroadCastReceiverActivity.this.registerReceiver(smsBroadCastReceiver, intentFilter);
? 一種是在AndroidManifest.xml中配置廣播
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="spl.broadCastReceiver"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".BroadCastReceiverActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--廣播注冊(cè)-->
<receiver android:name=".SmsBroadCastReceiver">
<intent-filter android:priority="20">
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
</application>
<uses-sdk android:minSdkVersion="7" />
<!-- 權(quán)限申請(qǐng) -->
<uses-permission android:name="android.permission.RECEIVE_SMS"></uses-permission>
</manifest>
兩種注冊(cè)類(lèi)型的區(qū)別是:
1)第一種不是常駐型廣播,也就是說(shuō)廣播跟隨程序的生命周期。
2)第二種是常駐型,也就是說(shuō)當(dāng)應(yīng)用程序關(guān)閉后,如果有信息廣播來(lái),程序也會(huì)被系統(tǒng)調(diào)用自動(dòng)運(yùn)行
請(qǐng)解釋下在單線程模型中Message、Handler、Message Queue、Looper之間的關(guān)系。
2.2 Message Queue
在單線程模型下,為了解決類(lèi)似的問(wèn)題,Android設(shè)計(jì)了一個(gè)Message Queue(消息隊(duì)列), 線程間可以通過(guò)該Message Queue并結(jié)合Handler和Looper組件進(jìn)行信息交換。下面將對(duì)它們進(jìn)行分別介紹:
1. Message
Message消息,理解為線程間交流的信息,處理數(shù)據(jù)后臺(tái)線程需要更新UI,則發(fā)送Message內(nèi)含一些數(shù)據(jù)給UI線程。
2. Handler
Handler處理者,是Message的主要處理者,負(fù)責(zé)Message的發(fā)送,Message內(nèi)容的執(zhí)行處理。后臺(tái)線程就是通過(guò)傳進(jìn)來(lái)的 Handler對(duì)象引用來(lái)sendMessage(Message)。而使用Handler,需要implement 該類(lèi)的 handleMessage(Message)
方法,它是處理這些Message的操作內(nèi)容,例如Update UI。通常需要子類(lèi)化Handler來(lái)實(shí)現(xiàn)handleMessage方法。
3. Message Queue
Message Queue消息隊(duì)列,用來(lái)存放通過(guò)Handler發(fā)布的消息,按照先進(jìn)先出執(zhí)行。
每個(gè)message queue都會(huì)有一個(gè)對(duì)應(yīng)的Handler。Handler會(huì)向message queue通過(guò)兩種方法發(fā)送消息:sendMessage或post。這兩種消息都會(huì)插在message queue隊(duì)尾并按先進(jìn)先出執(zhí)行。但通過(guò)這兩種方法發(fā)送的消息執(zhí)行的方式略有不同:通過(guò)sendMessage發(fā)送的是一個(gè)message對(duì)象,會(huì)被 Handler的handleMessage()函數(shù)處理;而通過(guò)post方法發(fā)送的是一個(gè)runnable對(duì)象,則會(huì)自己執(zhí)行。
4. Looper
Looper是每條線程里的Message Queue的管家。Android沒(méi)有Global的Message Queue,而Android會(huì)自動(dòng)替主線程(UI線程)建立Message Queue,但在子線程里并沒(méi)有建立Message Queue。所以調(diào)用Looper.getMainLooper()得到的主線程的Looper不為NULL,但調(diào)用Looper.myLooper() 得到當(dāng)前線程的Looper就有可能為NULL。
對(duì)于子線程使用Looper,API Doc提供了正確的使用方法:
這個(gè)Message機(jī)制的大概流程:
1. 在Looper.loop()方法運(yùn)行開(kāi)始后,循環(huán)地按照接收順序取出Message Queue里面的非NULL的Message。
2. 一開(kāi)始Message Queue里面的Message都是NULL的。當(dāng)Handler.sendMessage(Message)到Message Queue,該函數(shù)里面設(shè)置了那個(gè)Message對(duì)象的target屬性是當(dāng)前的Handler對(duì)象。隨后Looper取出了那個(gè)Message,則調(diào)用該Message的target指向的Hander的dispatchMessage函數(shù)對(duì)Message進(jìn)行處理。
在dispatchMessage方法里,如何處理Message則由用戶指定,三個(gè)判斷,優(yōu)先級(jí)從高到低:
1) Message里面的Callback,一個(gè)實(shí)現(xiàn)了Runnable接口的對(duì)象,其中run函數(shù)做處理工作;
2) Handler里面的mCallback指向的一個(gè)實(shí)現(xiàn)了Callback接口的對(duì)象,由其handleMessage進(jìn)行處理;
3) 處理消息Handler對(duì)象對(duì)應(yīng)的類(lèi)繼承并實(shí)現(xiàn)了其中handleMessage函數(shù),通過(guò)這個(gè)實(shí)現(xiàn)的handleMessage函數(shù)處理消息。
由此可見(jiàn),我們實(shí)現(xiàn)的handleMessage方法是優(yōu)先級(jí)最低的!
3. Handler處理完該Message (update UI) 后,Looper則設(shè)置該Message為NULL,以便回收!
在網(wǎng)上有很多文章講述主線程和其他子線程如何交互,傳送信息,最終誰(shuí)來(lái)執(zhí)行處理信息之類(lèi)的,個(gè)人理解是最簡(jiǎn)單的方法——判斷Handler對(duì)象里面的 Looper對(duì)象是屬于哪條線程的,則由該線程來(lái)執(zhí)行!
1. 當(dāng)Handler對(duì)象的構(gòu)造函數(shù)的參數(shù)為空,則為當(dāng)前所在線程的Looper;
2. Looper.getMainLooper()得到的是主線程的Looper對(duì)象,Looper.myLooper()得到的是當(dāng)前線程的Looper對(duì)象。
11. AIDL的全稱是什么?如何工作?能處理哪些類(lèi)型的數(shù)據(jù)?
AIDL(AndRoid接口描述語(yǔ)言)是一種接口描述語(yǔ)言; 編譯器可以通過(guò)aidl文件生成一段代碼,通過(guò)預(yù)先定義的接口達(dá)到兩個(gè)進(jìn)程內(nèi)部通信進(jìn)程的目的. 如果需要在一個(gè)Activity中, 訪問(wèn)另一個(gè)Service中的某個(gè)對(duì)象, 需要先將對(duì)象轉(zhuǎn)化成AIDL可識(shí)別的參數(shù)(可能是多個(gè)參數(shù)), 然后使用AIDL來(lái)傳遞這些參數(shù), 在消息的接收端, 使用這些參數(shù)組裝成自己需要的對(duì)象.
AIDL的IPC的機(jī)制和COM或CORBA類(lèi)似, 是基于接口的,但它是輕量級(jí)的。它使用代理類(lèi)在客戶端和實(shí)現(xiàn)層間傳遞值. 如果要使用AIDL, 需要完成2件事情: 1. 引入AIDL的相關(guān)類(lèi).; 2. 調(diào)用aidl產(chǎn)生的class.
AIDL的創(chuàng)建方法:
AIDL語(yǔ)法很簡(jiǎn)單,可以用來(lái)聲明一個(gè)帶一個(gè)或多個(gè)方法的接口,也可以傳遞參數(shù)和返回值。由于遠(yuǎn)程調(diào)用的需要, 這些參數(shù)和返回值并不是任何類(lèi)型.
下面是些AIDL支持的數(shù)據(jù)類(lèi)型:
1. 不需要import聲明的簡(jiǎn)單Java編程語(yǔ)言類(lèi)型(int,boolean等)
2. String, CharSequence不需要特殊聲明
3. List, Map和Parcelables類(lèi)型, 這些類(lèi)型內(nèi)所包含的數(shù)據(jù)成員也只能是簡(jiǎn)單數(shù)據(jù)類(lèi)型, String等其他比支持的類(lèi)型.
12. 請(qǐng)解釋下Android程序運(yùn)行時(shí)權(quán)限與文件系統(tǒng)權(quán)限的區(qū)別
要區(qū)分apk運(yùn)行時(shí)的擁有的權(quán)限與在文件系統(tǒng)上被訪問(wèn)(讀寫(xiě)執(zhí)行)的權(quán)限兩個(gè)概念。
apk程序是運(yùn)行在虛擬機(jī)上的,對(duì)應(yīng)的是Android獨(dú)特的權(quán)限機(jī)制,只有體現(xiàn)到文件系統(tǒng)上時(shí)才使用linux的權(quán)限設(shè)置
(一)linux文件系統(tǒng)上的權(quán)限
-rwxr-x--x system system 4156 2010-04-30 16:13 test.apk
代表的是相應(yīng)的用戶/用戶組及其他人對(duì)此文件的訪問(wèn)權(quán)限,與此文件運(yùn)行起來(lái)具有的權(quán)限完全不相關(guān)。比如上面的例子只能說(shuō)明system用戶擁有對(duì)此文件的讀寫(xiě)執(zhí)行權(quán)限;system組的用戶對(duì)此文件擁有讀、執(zhí)行權(quán)限;其他人對(duì)此文件只具有執(zhí)行權(quán)限。
而test.apk運(yùn)行起來(lái)后可以干哪些事情,跟這個(gè)就不相關(guān)了。
千萬(wàn)不要看apk文件系統(tǒng)上屬于system/system用戶及用戶組,或者root/root用戶及用戶組,就認(rèn)為apk具有system或root權(quán)限
(二)Android的權(quán)限規(guī)則
(1)Android中的apk必須簽名
這種簽名不是基于權(quán)威證書(shū)的,不會(huì)決定某個(gè)應(yīng)用允不允許安裝,而是一種自簽名證書(shū)。重要的是,android系統(tǒng)有的權(quán)限是基于簽名的。比如:system等級(jí)的權(quán)限有專(zhuān)門(mén)對(duì)應(yīng)的簽名,簽名不對(duì),權(quán)限也就獲取不到。
默認(rèn)生成的APK文件是debug簽名的。
(2)基于UserID的進(jìn)程級(jí)別的安全機(jī)制
大家都知道,進(jìn)程有獨(dú)立的地址空間,進(jìn)程與進(jìn)程間默認(rèn)是不能互相訪問(wèn)的,是一種很可靠的保護(hù)機(jī)制。
Android通過(guò)為每一個(gè)安裝在設(shè)備上的包(apk)分配唯一的linux userID來(lái)實(shí)現(xiàn),名稱為"app_"加一個(gè)數(shù)字,比如app_43
不同的UserID,運(yùn)行在不同的進(jìn)程,所以apk之間默認(rèn)便不能相互訪問(wèn)。
Android提供了如下的一種機(jī)制,可以使兩個(gè)apk打破前面講的這種壁壘。
在AndroidManifest.xml中利用sharedUserId屬性給不同的package分配相同的userID,通過(guò)這樣做,兩個(gè)package可以被當(dāng)做同一個(gè)程序,
系統(tǒng)會(huì)分配給兩個(gè)程序相同的UserID。當(dāng)然,基于安全考慮,兩個(gè)package需要有相同的簽名,否則沒(méi)有驗(yàn)證也就沒(méi)有意義了。
(這里補(bǔ)充一點(diǎn):并不是說(shuō)分配了同樣的UserID,兩程序就運(yùn)行在同一進(jìn)程, 下面為PS指令摘取的,
顯然,system、app_2分別對(duì)應(yīng)的兩個(gè)進(jìn)程的PID都不同,不知Android到底是怎樣實(shí)現(xiàn)它的機(jī)制的)
User PID PPID
system 953 883 187340 55052 ffffffff afe0cbcc S system_server
app_2 1072 883 100264 19564 ffffffff afe0dcc4 S com.android.inputmethod.
system 1083 883 111808 23192 ffffffff afe0dcc4 S android.process.omsservi
app_2 1088 883 156464 45720 ffffffff afe0dcc4 S android.process.acore
(3)默認(rèn)apk生成的數(shù)據(jù)對(duì)外是不可見(jiàn)的
實(shí)現(xiàn)方法是:Android會(huì)為程序存儲(chǔ)的數(shù)據(jù)分配該程序的UserID。
借助于Linux嚴(yán)格的文件系統(tǒng)訪問(wèn)權(quán)限,便實(shí)現(xiàn)了apk之間不能相互訪問(wèn)似有數(shù)據(jù)的機(jī)制。
例:我的應(yīng)用創(chuàng)建的一個(gè)文件,默認(rèn)權(quán)限如下,可以看到只有UserID為app_21的程序才能讀寫(xiě)該文件。
-rw------- app_21 app_21 87650 2000-01-01 09:48 test.txt
如何對(duì)外開(kāi)放?
<1> 使用MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE 標(biāo)記。
When creating a new file with getSharedPreferences(String, int), openFileOutput(String, int), or openOrCreateDatabase(String, int, SQLiteDatabase.CursorFactory), you can use the MODE_WORLD_READABLE and/or MODE_WORLD_WRITEABLE flags to allow any other package to read/write the file. When setting these flags, the file is still owned by your application, but its global read and/or write permissions have been set appropriately so any other application can see it.
(4)AndroidManifest.xml中的顯式權(quán)限聲明
Android默認(rèn)應(yīng)用是沒(méi)有任何權(quán)限去操作其他應(yīng)用或系統(tǒng)相關(guān)特性的,應(yīng)用在進(jìn)行某些操作時(shí)都需要顯式地去申請(qǐng)相應(yīng)的權(quán)限。
一般以下動(dòng)作時(shí)都需要申請(qǐng)相應(yīng)的權(quán)限:
A particular permission may be enforced at a number of places during your program's operation:
• At the time of a call into the system, to prevent an application from executing certain functions.
• When starting an activity, to prevent applications from launching activities of other applications.
• Both sending and receiving broadcasts, to control who can receive your broadcast or who can send a broadcast to you.
• When accessing and operating on a content provider.
• Binding or starting a service.
在應(yīng)用安裝的時(shí)候,package installer會(huì)檢測(cè)該應(yīng)用請(qǐng)求的權(quán)限,根據(jù)該應(yīng)用的簽名或者提示用戶來(lái)分配相應(yīng)的權(quán)限。
在程序運(yùn)行期間是不檢測(cè)權(quán)限的。如果安裝時(shí)權(quán)限獲取失敗,那執(zhí)行就會(huì)出錯(cuò),不會(huì)提示用戶權(quán)限不夠。
大多數(shù)情況下,權(quán)限不足導(dǎo)致的失敗會(huì)引發(fā)一個(gè) SecurityException, 會(huì)在系統(tǒng)log(system log)中有相關(guān)記錄。
(5)權(quán)限繼承/UserID繼承
當(dāng)我們遇到apk權(quán)限不足時(shí),我們有時(shí)會(huì)考慮寫(xiě)一個(gè)linux程序,然后由apk調(diào)用它去完成某個(gè)它沒(méi)有權(quán)限完成的事情,很遺憾,這種方法是行不通的。
前面講過(guò),android權(quán)限是經(jīng)營(yíng)在進(jìn)程層面的,也就是說(shuō)一個(gè)apk應(yīng)用啟動(dòng)的子進(jìn)程的權(quán)限不可能超越其父進(jìn)程的權(quán)限(即apk的權(quán)限),
即使單獨(dú)運(yùn)行某個(gè)應(yīng)用有權(quán)限做某事,但如果它是由一個(gè)apk調(diào)用的,那權(quán)限就會(huì)被限制。
實(shí)際上,android是通過(guò)給子進(jìn)程分配父進(jìn)程的UserID實(shí)現(xiàn)這一機(jī)制的。
(三)常見(jiàn)權(quán)限不足問(wèn)題分析
首先要知道,普通apk程序是運(yùn)行在非root、非system層級(jí)的,也就是說(shuō)看要訪問(wèn)的文件的權(quán)限時(shí),看的是最后三位。
另外,通過(guò)system/app安裝的apk的權(quán)限一般比直接安裝或adb install安裝的apk的權(quán)限要高一些。
言歸正傳,運(yùn)行一個(gè)android應(yīng)用程序過(guò)程中遇到權(quán)限不足,一般分為兩種情況:
(1)Log中可明顯看到權(quán)限不足的提示。
此種情況一般是AndroidManifest.xml中缺少相應(yīng)的權(quán)限設(shè)置,好好查找一番權(quán)限列表,應(yīng)該就可解決,是最易處理的情況。
有時(shí)權(quán)限都加上了,但還是報(bào)權(quán)限不足,是什么情況呢?
Android系統(tǒng)有一些API及權(quán)限是需要apk具有一定的等級(jí)才能運(yùn)行的。
比如 SystemClock.setCurrentTimeMillis()修改系統(tǒng)時(shí)間,WRITE_SECURE_SETTINGS權(quán)限好像都是需要有system級(jí)的權(quán)限才行。
也就是說(shuō)UserID是system.
(2)Log里沒(méi)有報(bào)權(quán)限不足,而是一些其他Exception的提示,這也有可能是權(quán)限不足造成的。
比如:我們常會(huì)想讀/寫(xiě)一個(gè)配置文件或其他一些不是自己創(chuàng)建的文件,常會(huì)報(bào)java.io.FileNotFoundException錯(cuò)誤。
系統(tǒng)認(rèn)為比較重要的文件一般權(quán)限設(shè)置的也會(huì)比較嚴(yán)格,特別是一些很重要的(配置)文件或目錄。
如
-r--r----- bluetooth bluetooth 935 2010-07-09 20:21 dbus.conf
drwxrwx--x system system 2010-07-07 02:05 data
dbus.conf好像是藍(lán)牙的配置文件,從權(quán)限上來(lái)看,根本就不可能改動(dòng),非bluetooth用戶連讀的權(quán)利都沒(méi)有。
/data目錄下存的是所有程序的私有數(shù)據(jù),默認(rèn)情況下android是不允許普通apk訪問(wèn)/data目錄下內(nèi)容的,通過(guò)data目錄的權(quán)限設(shè)置可知,其他用戶沒(méi)有讀的權(quán)限。
所以adb普通權(quán)限下在data目錄下敲ls命令,會(huì)得到opendir failed, Permission denied的錯(cuò)誤,通過(guò)代碼file.listfiles()也無(wú)法獲得data目錄下的內(nèi)容。
13. 系統(tǒng)上安裝了多種瀏覽器,能否指定某瀏覽器訪問(wèn)指定頁(yè)面?請(qǐng)說(shuō)明原由。
如果在你的android系統(tǒng)上安裝了多種瀏覽器,能否指定某瀏覽器訪問(wèn)指定頁(yè)面?答案當(dāng)然是:肯定的。
問(wèn)題的關(guān)鍵在于我們?cè)O(shè)置了class name,也就是我們想要跳轉(zhuǎn)的pakcage的activity。如果你想要跳轉(zhuǎn)到其它的瀏覽器,只需要修改一下這個(gè)函數(shù)就OK了。
好,我們現(xiàn)在來(lái)讓剛剛的思路來(lái)指導(dǎo)我們的實(shí)踐。假如我們現(xiàn)在要直接啟動(dòng)UC瀏覽器,那么我們?cè)撛趺醋瞿兀孔屛覀僺tep by step吧。
1.下載UC apk:http://i-uc.net/read.php?2
2. 用7zip解壓apk文件,得到classes.dex文件
3.下載反編譯dex文件工具:http://nchc.dl.sourceforge.net/project/dedexer/dedexer/1.5/ddx1.5.jar(Dedexer 項(xiàng)目主頁(yè): http://dedexer.sourceforge.net/)
4.執(zhí)行命令:java -jar ddx1.5.jar -o -D -d c:\ c:\classes.dex
5. 得到package name是:com.uc.browser,啟動(dòng)的activity是:com.uc.browser.ActivityUpdate(補(bǔ)充:當(dāng)我在這里選擇采用ActivityBrowser的時(shí)候發(fā)覺(jué)權(quán)限不夠,報(bào)permiss denied 異常,而且也不是我們要的那個(gè)activity,幸運(yùn)的是在第二次嘗試用ActivityUpdate,剛好能滿足要求)
6.修改上面的代碼為intent.setClassName("com.uc.browser","com.uc.browser.ActivityUpdate");