我的全部文章整理在此篇,將陸續更新收錄:知無涯,行者之路莫言終(個人編程之路)html
[1].Intent的簡介及[隱式調用]和[顯示調用]
[2].對象的序列化與反序列化:[Parcelable(簡)]和`Serializable]
[3].[Bundle類]的及其在intent的數據傳遞
[4].Android源碼中[intent-filter]的解析流程
複製代碼
類名:Intent 父類:Object
實現的接口:[Parcelable, Cloneable]
包名:android.content' 依賴類個數:52 內部類/接口個數:3 源碼行數:10086 源碼行數(除註釋):3407 屬性個數:24 方法個數:164 複製代碼
自從接觸安卓的第一天就接觸到了這個類:
Intent
上面可見Intent挺普通的,就是比較大,看起來10086行,感受挺大的
除註釋和空行,裸碼3407,註釋比率之高,家庭背景繼承Object
,接口平平,可謂白手起家
他是Android四大組件的忠實夥伴,跳轉Activity
,發送BroadcastReceiver
,開啓Service
組件之間經過Intent互相聯繫,而且傳遞數據,可謂名副其實的"外交官"java
源碼上來看一共有8個構造函數,上面兩個是空參和隱藏的,不用管
左邊兩個經過拷貝來生成Intent對象,兩參的拷貝是似有的
右邊兩個經過設置匹配信息
方法來生成Intent對象(隱式)
下面兩個加入了ComponentName
來生成Intent對象 (顯式)node
component(組件):目的組件(應用包名+組件全類名)
action(動做):意圖的行爲action
category(類別):行爲action的類別
data(數據):表示與動做要操縱的數據
type(數據類型):對於data範例的描寫
extras(擴展信息):擴展信息
Flags(標誌位):指望這個意圖的運行模式
複製代碼
即不指定組件名,經過
action,category,data,type
等信息打開組件
系統中內置了不少應用,咱們能夠經過這些信息來匹配打開須要的應用android
ActivityJustAction
很是簡單,清單中爲該Activity設置
intent-filter
自定義action:www.toly1994.com.ActivityJustAction
這個名字隨便起,只要使用時對應就好了(通常是惟一的),固然也能夠不惟一
沒有category會崩掉,這裏給個默認的category,也就是action的類別程序員
class ActivityJustAction : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(LinearLayout(this))
title = "ActivityJustAction"
}
}
---->[AndroidManifest.xml配置]------------
<activity android:name=".activity.ActivityJustAction">
<intent-filter>
<action android:name="www.toly1994.com.ActivityJustAction"></action>
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</activity>
複製代碼
就像一我的在喊,我要找
旺財
,而後旺財
就來了web
---->[IntentActivity]--------------
id_btn_just_action.setOnClickListener { v ->
val intent = Intent("www.toly1994.com.ActivityJustAction")
startActivity(intent)
}
複製代碼
旺財
怎麼辦?新建一個ActivityJustAction2,intent-filter設置的同樣
既然兩個都叫旺財
,就把兩個都帶來,讓你選一個唄(你應該常常遇到)編程
class ActivityJustAction2 : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(LinearLayout(this))
title = "ActivityJustAction2"
}
}
<activity android:name=".activity.ActivityJustAction2">
<intent-filter>
<action android:name="www.toly1994.com.ActivityJustAction2"></action>
<category android:name="android.intent.category.DEFAULT"></category>
</intent-filter>
</activity>
複製代碼
action+category
名字加類別,一個
intent-filter
能夠加多個類別,就像一件事物能夠劃分在多個領域
如人、程序員、中國公民能夠指同一人,添加category以後,至關於你喊了句:
我要找一個叫旺財的程序員
,這樣就能更精確匹配,縮小撞名的可能,方便管理bash
---->[AndroidManifest.xml配置]------------
<activity android:name=".activity.ActivityJustAction">
<intent-filter>
<action android:name="www.toly1994.com.ActivityJustAction"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="www.toly1994.com.people"></category>
<category android:name="www.toly1994.com.coder"></category>
</intent-filter>
</activity>
<activity android:name=".activity.ActivityJustAction2">
<intent-filter>
<action android:name="www.toly1994.com.ActivityJustAction"></action>
<category android:name="android.intent.category.DEFAULT"></category>
<category android:name="www.toly1994.com.dog"></category>
<category android:name="www.toly1994.com.erha"></category>
</intent-filter>
</activity>
---->[IntentActivity]--------------
id_btn_just_action.setOnClickListener { v ->
val intent = Intent("www.toly1994.com.ActivityJustAction")
//intent.addCategory("www.toly1994.com.coder")//開1
//intent.addCategory("www.toly1994.com.people")//開1
//intent.addCategory("www.toly1994.com.dog")//開2
intent.addCategory("www.toly1994.com.erha")//開2
startActivity(intent)
}
複製代碼
action + data
提及Uri(Uniform Resource Identifier),統一資源標識符
形式爲:<scheme>://<authority><path>?<query>
微信
id_btn_open_web.setOnClickListener { v ->
val intent = Intent(Intent.ACTION_VIEW)
intent.data = Uri.parse("https://juejin.im/user/5b42c0656fb9a04fe727eb37")
startActivity(intent)
}
複製代碼
/**
* 發送短信
* @param number 號碼
* @param body 內容
*/
private fun sendMsg(number: String, body: String) {
val intent = Intent(Intent.ACTION_SENDTO, Uri.parse("smsto:$number"))
intent.putExtra("sms_body", body)
startActivity(intent)
}
複製代碼
action+type
)根據action打一個應用,附加
MIME類型
cookie
/**
* 打開圖庫
*/
private fun openGallery() {
val intent = Intent(Intent.ACTION_PICK)
intent.type = "image/*";
startActivity(intent)
}
複製代碼
看一下圖庫的源碼是如何配置
action+type+data
Android API 24 及以上對file的Uri作了限制,須要適配一下
/**
* 做者:張風捷特烈<br/>
* 時間:2018/10/30 0030:18:38<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:適配類
*/
public class Compat {
public static void fileUri(Context context, Intent intent, File file, String type) {
//判斷是不是AndroidN以及更高的版本
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri contentUri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".fileProvider", file);
intent.setDataAndType(contentUri, type);
} else {
intent.setDataAndType(Uri.fromFile(file), type);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
}
}
---->[AndroidManifest.xml配置provider]------
<!--android:authorities="本應用包名.fileProvider"-->
<provider android:name="android.support.v4.content.FileProvider"
android:authorities="com.toly1994.tolyservice.fileProvider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
---->[xml/file_paths.xml]-----------
<?xml version="1.0" encoding="utf-8"?>
<paths>
<!--Android/data/本應用包名/-->
<external-path path="Android/data/com.toly1994.tolyservice/" name="files_root" />
<external-path path="." name="external_storage_root" />
</paths>
複製代碼
須要加文件讀權限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
id_btn_music.setOnClickListener { v ->//音頻
val intent = Intent(Intent.ACTION_VIEW)
val file = File("/sdcard/toly/勇氣-梁靜茹-1772728608-1.mp3")
Compat.fileUri(this, intent, file, "audio/mp3")
startActivity(intent)
}
id_btn_video.setOnClickListener { v ->//視頻
val intent = Intent(Intent.ACTION_VIEW)
val file = File("/sdcard/toly/cy3d.mp4")
Compat.fileUri(this, intent, file, "video/mp4")
startActivity(intent)
}
id_btn_txt.setOnClickListener { v ->//文本
val intent = Intent(Intent.ACTION_VIEW)
val file = File("/sdcard/toly/應龍.txt")
Compat.fileUri(this, intent, file, "text/*")
startActivity(intent)
}
id_btn_pic.setOnClickListener { v ->//圖片
val intent = Intent(Intent.ACTION_VIEW)
val file = File("/sdcard/toly/touch.jpg.png")
Compat.fileUri(this, intent, file, "image/*")
startActivity(intent)
}
複製代碼
圖庫源碼中對於打開一張圖片的配置:
隱式的intent抓住action、category、data、type
四個要點就好了
即已經明確須要開啓的組件
本組件上下文+目標組件字節碼
這個是咱們最經常使用的,打開
Activity,Service,BroadcastReceiver
private fun openComponent() {
val intent = Intent(this, MainActivity::class.java)
startActivity(intent)
}
複製代碼
一直用Intent打開Activity,貌似沒有分析過,如今進源碼裏看看吧
---->[Intent#Intent(Context, Class<?>)]-------
public Intent(Context packageContext, Class<?> cls) {
mComponent = new ComponentName(packageContext, cls);
}
可見該方法核心是ComponentName,顧名思義"組件名稱"
源碼首行註釋說:特定應用程序組件的標識符
---->[ComponentName#ComponentName(Context, lang.Class<?>)]--------
public ComponentName(@NonNull Context pkg, @NonNull Class<?> cls) {
mPackage = pkg.getPackageName();
mClass = cls.getName();
}
-------------------------------------------------------------------
ComponentName是一個比較簡單的類,核心是兩個成員變量mPackage和mClass
這個兩參構造中,mPackage是傳入的context的包名,mClass是目標組件的類名
複製代碼
看一下兩個String的ComponentName構造,更能表達出它們的做用
也可以實現打開組件的功能,因此知道項目的包名,和組件的全類名,就能開啓組件
val intent = Intent()
val compName = ComponentName(
"com.toly1994.tolyservice",//項目的包名
"com.toly1994.tolyservice.activity.MainActivity")//要打開的組件全類名
intent.component = compName
startActivity(intent)
複製代碼
組件包名+目標組件全類名+flag
private fun openComponent() {
val intent = Intent()
intent.flags=Intent.FLAG_ACTIVITY_NEW_TASK
val compName = ComponentName(
"com.tencent.mm",//本組件的包名
"com.tencent.mm.ui.LauncherUI")//要打開的組件全類名
intent.component = compName
startActivity(intent)
}
複製代碼
---->[Intent拷貝構造]---------
public Intent(Intent o) {
this(o, COPY_MODE_ALL);
}
//|--使用兩參的[COPY_MODE_ALL]模式
---->[Intent兩參拷貝]---------
private Intent(Intent o, @CopyMode int copyMode) {
this.mAction = o.mAction;
this.mData = o.mData;
this.mType = o.mType;
this.mPackage = o.mPackage;
this.mComponent = o.mComponent;
if (o.mCategories != null) {
this.mCategories = new ArraySet<>(o.mCategories);
}
//|--至此把category,action,data, type,component,package 的字段拷貝了
//|--COPY_MODE_ALL顧名思義,把全部的內容都拷貝
if (copyMode != COPY_MODE_FILTER) {
this.mFlags = o.mFlags;
this.mContentUserHint = o.mContentUserHint;
this.mLaunchToken = o.mLaunchToken;
if (o.mSourceBounds != null) {
this.mSourceBounds = new Rect(o.mSourceBounds);
}
if (o.mSelector != null) {
this.mSelector = new Intent(o.mSelector);
}
if (copyMode != COPY_MODE_HISTORY) {
if (o.mExtras != null) {
this.mExtras = new Bundle(o.mExtras);
}
if (o.mClipData != null) {
this.mClipData = new ClipData(o.mClipData);
}
} else {
if (o.mExtras != null && !o.mExtras.maybeIsEmpty()) {
this.mExtras = Bundle.STRIPPED;
}
// Also set "stripped" clip data when we ever log mClipData in the (broadcast)
// history.
}
}
}
---->[還有個clone方法]------------------
@Override
public Object clone() {
return new Intent(this);
}
|----根據調用的intent對象,直接返回了一個新的實例,本質上合拷貝構造並沒有區別
複製代碼
序列化有什麼用?
1.永久的保存對象數據(保存在文件當中,或者是磁盤中),須要時反序列化生成對象
2.將對象數據轉換成字節流進行網絡傳輸
3.使用Intent時傳遞序列化對象
複製代碼
Serializable
//類的可序列化,只要實現Serializable便可,很是簡單
class Person(var name: String?, var age: Int) : Serializable {
override fun toString(): String {
return "Person{" +
"name='" + name + '\''.toString() + ", age=" + age + '}'.toString() } } 複製代碼
Serializable
序列化保存到磁盤val toly = Person("toly", 24)
val file = File(cacheDir, "toly.obj")
val oos = ObjectOutputStream(FileOutputStream(file))
oos.writeObject(toly)
oos.close()
複製代碼
val ois = ObjectInputStream(FileInputStream(file))
val toly = ois.readObject() as Person
ois.close()
複製代碼
當某些字段不須要序列化時,可以使用
@Transient(kotlin)
或transient(Java)關鍵字
好比我不想讓name字段序列化。(由於字段越多,消耗的資源越多)
class Person(@Transient var name: String?, var age: Int) : Serializable {
override fun toString(): String {
return "Person{" +
"name='" + name + '\''.toString() + ", age=" + age + '}'.toString() } } 複製代碼
serialVersionUID
看一下Android源碼,實現Serializable的類都有一個`serialVersionUID`的常量
Java的序列化機制是經過判斷類的serialVersionUID來驗證版本一致性的。
在進行反序列化時,JVM會把傳來的字節流和當前類中的serialVersionUID進行對比,
是一致的則進行反序列化,不然拋序列化版本不一致的異常(InvalidCastException)
複製代碼
Parcelable
實現對象的序列化(Java版)當一個實現Parcelable接口時必須實現
describeContents和writeToParcel
方法
感受怪麻煩的,還好AndroidStudio有快捷生成方式
/**
* 做者:張風捷特烈<br/>
* 時間:2019/1/21/021:22:30<br/>
* 郵箱:1981462002@qq.com<br/>
* 說明:Parcelable序列化
*/
public class Book implements Parcelable {
private String name;
private int price;
public Book(String name, int price) {
this.name = name;
this.price = price;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(this.name);
dest.writeInt(this.price);
}
protected Book(Parcel in) {
this.name = in.readString();
this.price = in.readInt();
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
@Override
public Book createFromParcel(Parcel source) {
return new Book(source);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
@Override
public String toString() {
return "Book{" +
"name='" + name + '\'' + ", price=" + price + '}'; } } 複製代碼
Parcelable
與Serializable
的比較Parcelable 所屬包android.os
Serializable 所屬包java.io
|---所屬包說明了Parcelable只能在Android中使用
P以Ibinder做爲信息載體的,在內存上的開銷比較小,P在性能方面要強於S
S在序列化操做的時候會產生大量的臨時變量,(反射機制)從而致使GC的頻繁調用
|---Parcelable的性能要強於Serializable
在讀寫數據的時候,Parcelable是在內存中直接進行讀寫
而Serializable是經過使用IO流的形式將數據讀寫入在硬盤上
Parcelable沒法將數據進行持久化(磁盤存儲),Serializable能夠
(在不一樣的Android版本當中,Parcelable可能會不)
複製代碼
Intent 除來一大堆對屬性的set以外,還有一大堆的putExtra來盛放數據
Intent不只傳遞"命令"還能攜帶數據傳達,put數據的方法躲到使人髮指
能夠說應有盡有,有put,固然對應有get,下面僅列舉出put數據的方法
因爲常見類型不少,這裏選三個表明,其餘的用法相似,怎麼放怎麼取
---->[FromActivity 點擊時]--------
val intent = Intent(this, ToActivity::class.java)
//String類型數據
intent.putExtra("stringData", "張風捷特烈")
//int類型數據
intent.putExtra("intData", 100)
//容器類型數據
val arr = arrayListOf(1, 2, 3, 4, 5)
intent.putExtra("arrData", arr)
startActivity(intent)
---->[ToActivity#onCreate]--------
var result = ""
val stringData = intent.getStringExtra("stringData")
val intData = intent.getIntExtra("intData", 10)
val arrData = intent.getIntegerArrayListExtra("arrData")
result+=intData.toString()+"\n"
if (stringData != null) {
result+=stringData+"\n"
}
if (arrData != null) {
result+=arrData.toString()+"\n"
}
id_tv_result.append(result)
複製代碼
簡單來看就是鍵值對,並無什麼很是神奇的。也有一堆的put,get
其中最重要的是有put序列化對象(Parcelable/Serializable
)的方法
A mapping from String keys to various {@link Parcelable} values.
字符串型的鍵到不一樣值得映射(link 到 Parcelable)
複製代碼
---->[FromActivity 點擊時]--------
val intent = Intent(this, ToActivity::class.java)
val bundle = Bundle()
//存放Serializable序列化對象
val toly = Person("toly", 24)
bundle.putSerializable("person", toly)
//存放Parcelable序列化對象
val book = Book("《幻將錄》", 10000)
bundle.putParcelable("book", book)
intent.putExtra("bean", bundle)
startActivity(intent)
---->[ToActivity#onCreate]--------
val bundle = intent.getBundleExtra("bean")
if (bundle != null) {
val personBean = bundle.get("person") as Person
val bookBean = bundle.get("book") as Book
}
複製代碼
intent-filter
的解析流程PackageManagerService在啓動後會掃描系統和第三方的app信息,
在scanPackageLI方法中實例化PackageParser對象pp,使用pp對包進行解析
PackageParser的parseBaseApk在調用以後解析AndroidManifest.xml,返回一個Package對象
將手機中全部的app的AndroidManifest.xml解析完畢,構建出一個手機中全部app的信息樹
從這顆棵樹上
複製代碼
---->[PackageParser#parseMonolithicPackage]------------
@Deprecated
public Package parseMonolithicPackage(File apkFile, int flags) throws PackageParserException {
//略...
final AssetManager assets = new AssetManager();
try {
final Package pkg = parseBaseApk(apkFile, assets, flags);
pkg.codePath = apkFile.getAbsolutePath();
return pkg;
} finally {
IoUtils.closeQuietly(assets);
}
}
private static final String ANDROID_MANIFEST_FILENAME = "AndroidManifest.xml";
---->[PackageParser#parseBaseApk 3參]------------
private Package parseBaseApk(File apkFile, AssetManager assets, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
//略...
Resources res = null;
XmlResourceParser parser = null;//構建Xml的解析器
try {
res = new Resources(assets, mMetrics, null);
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);//打開`AndroidManifest.xml`文件
final String[] outError = new String[1];
final Package pkg = parseBaseApk(res, parser, flags, outError);
if (pkg == null) {
throw new PackageParserException(mParseError,
apkPath + " (at " + parser.getPositionDescription() + "): " + outError[0]);
}
pkg.volumeUuid = volumeUuid;
pkg.applicationInfo.volumeUuid = volumeUuid;
pkg.baseCodePath = apkPath;
pkg.mSignatures = null;
return pkg;
//略...
}
---->[PackageParser#parseBaseApk 4參]------------
|--------核心的解析xml邏輯全在這個方法裏,很是長,----------
----------這裏從application的解析開始看------------------
private Package parseBaseApk(Resources res, XmlResourceParser parser, int flags,
String[] outError) throws XmlPullParserException, IOException {
//略...
String tagName = parser.getName();
if (tagName.equals("application")) {//下面開始解析application
//略...
//這裏調用了parseBaseApplication方法,activity的解析就在其中
if (!parseBaseApplication(pkg, res, parser, attrs, flags, outError)) {
return null;
}
---->[PackageParser#parseBaseApplication]------------
private boolean parseBaseApplication(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError)
//略...
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > innerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("activity")) {//這裏開始解析activity
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, false,
owner.baseHardwareAccelerated);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.activities.add(a);
} else if (tagName.equals("receiver")) {//這裏開始解析receiver
Activity a = parseActivity(owner, res, parser, attrs, flags, outError, true, false);
if (a == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.receivers.add(a);
} else if (tagName.equals("service")) {//這裏開始解析service
Service s = parseService(owner, res, parser, attrs, flags, outError);
if (s == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.services.add(s);
} else if (tagName.equals("provider")) {//這裏開始解析provider
Provider p = parseProvider(owner, res, parser, attrs, flags, outError);
if (p == null) {
mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
return false;
}
owner.providers.add(p);
//略...還有不少解析的東西
return true;
}
---->[PackageParser#parseActivity]------------
private Activity parseActivity(Package owner, Resources res,
XmlPullParser parser, AttributeSet attrs, int flags, String[] outError,
boolean receiver, boolean hardwareAccelerated)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(attrs, R.styleable.AndroidManifestActivity);
//略...
//下面開始解析:intent-filter
if (parser.getName().equals("intent-filter")) {
//建立ActivityIntentInfo
ActivityIntentInfo intent = new ActivityIntentInfo(a);
//調用parseIntent方法
if (!parseIntent(res, parser, attrs, true, true, intent, outError)) {
return null;
}
if (intent.countActions() == 0) {
} else {
a.intents.add(intent);
//略...
return a;
}
---->[PackageParser#parseIntent]------------
private boolean parseIntent(Resources res, XmlPullParser parser, AttributeSet attrs,
boolean allowGlobs, boolean allowAutoVerify, IntentInfo outInfo, String[] outError)
throws XmlPullParserException, IOException {
TypedArray sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestIntentFilter);
//略...
int outerDepth = parser.getDepth();
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String nodeName = parser.getName();
if (nodeName.equals("action")) {//解析action
String value = attrs.getAttributeValue(
ANDROID_RESOURCES, "name");
if (value == null || value == "") {
outError[0] = "No value supplied for <android:name>";
return false;
}
XmlUtils.skipCurrentTag(parser);
outInfo.addAction(value);
} else if (nodeName.equals("category")) {//解析category
String value = attrs.getAttributeValue(
ANDROID_RESOURCES, "name");
if (value == null || value == "") {
outError[0] = "No value supplied for <android:name>";
return false;
}
XmlUtils.skipCurrentTag(parser);
outInfo.addCategory(value);
} else if (nodeName.equals("data")) {//解析data
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestData);
String str = sa.getNonConfigurationString( com.android.internal.R.styleable.AndroidManifestData_mimeType, 0);
if (str != null) {
try {
outInfo.addDataType(str);
} catch (IntentFilter.MalformedMimeTypeException e) {
outError[0] = e.toString();
sa.recycle();
return false;
}
}
return true;
}
複製代碼
startActivity(intent)
作了什麼?startActivity一連串的調用以後,最終核心是下面的方法
前一篇已經涉及過Instrumentation類,它真可謂Activity的忠實僕人
------>[Activity#startActivityForResult]----------------------
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
//略...
}
}
------>[Instrumentation#execStartActivity]----------------------
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, String target,
Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i=0; i<N; i++) {
final ActivityMonitor am = mActivityMonitors.get(i);
if (am.match(who, null, intent)) {
am.mHits++;
if (am.isBlocking()) {
return requestCode >= 0 ? am.getResult() : null;
}
break;
}
}
}
}
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target, requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}
------>[ActivityManagerNative#getDefault]----------------------
static public IActivityManager getDefault() {
return gDefault.get();
}
------>[ActivityManagerNative#Singleton]----------------------
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);//IActivityManager的建立
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
------>[ActivityManagerNative#asInterface]----------------------
|--------這裏能夠看出get的IActivityManager對象是一個ActivityManagerProxy對象
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ActivityManagerProxy(obj);
}
>如今焦點在ActivityManagerProxy的身上
複製代碼
先看IActivityManager,他是一個接口定義了不少關於Activity管理的方法
ActivityManagerProxy做爲它的實現類,固然也就實現了這些方法
---->[ActivityStackSupervisor#startActivity]--------
@Override
public final int startActivity(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options) {
return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
---->[ActivityStackSupervisor#startActivityAsUser]--------
@Override
public final int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle options, int userId) {
enforceNotIsolatedCaller("startActivity");
userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(), userId,
false, ALLOW_FULL_ONLY, "startActivity", null);
// TODO: Switch to user app stacks here.
return mStackSupervisor.startActivityMayWait(caller, -1, callingPackage, intent,
resolvedType, null, null, resultTo, resultWho, requestCode, startFlags,
profilerInfo, null, null, options, false, userId, null, null);
---->[ActivityStackSupervisor#resolveActivity]--------
ActivityInfo resolveActivity(Intent intent, String resolvedType, int startFlags,
ProfilerInfo profilerInfo, int userId) {
// Collect information about the target of the Intent.
ActivityInfo aInfo;
try {
ResolveInfo rInfo =
//這裏經過AppGlobals獲取了getPackageManager,也就是包管理器
AppGlobals.getPackageManager().resolveIntent(
intent, resolvedType,
PackageManager.MATCH_DEFAULT_ONLY
| ActivityManagerService.STOCK_PM_FLAGS, userId);
aInfo = rInfo != null ? rInfo.activityInfo : null;
---->[ActivityStackSupervisor#resolveActivity]--------
public static IPackageManager getPackageManager() {
//經過ActivityThread獲取PackageManager
return ActivityThread.getPackageManager();
}
---->[ActivityThread#getPackageManager]--------
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
//經過ServiceManager獲取包管理器的IBinder
IBinder b = ServiceManager.getService("package");
//Slog.v("PackageManager", "default service binder = " + b);
//生成的IPackageManager對象
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}
//接下來的焦點集中到了PackageManager和IPackageManager身上
複製代碼
IPackageManager.aidl
的描述中有這個方法PackageManagerService做爲IPackageManager.Stub的實現類
確定也實現了queryIntentActivities方法,就是他查看intent是否匹配
其中aidl的相關知識,會寫一篇進行詳述
---->[PackageManagerService#queryIntentActivities]------------
@Override
public List<ResolveInfo> queryIntentActivities(Intent intent,
String resolvedType, int flags, int userId) {
//略...
synchronized (mPackages) {//有包名
final String pkgName = intent.getPackage();
if (pkgName == null) {
//略...
//ActivityIntentResolver#queryIntent進行查詢
List<ResolveInfo> result = mActivities.queryIntent(
intent, resolvedType, flags, userId);
//略...
}
return result;
}
final PackageParser.Package pkg = mPackages.get(pkgName);
if (pkg != null) {
return filterIfNotPrimaryUser(
mActivities.queryIntentForPackage(
intent, resolvedType, flags, pkg.activities, userId),
userId);
}
return new ArrayList<ResolveInfo>();
}
}
---->[PackageManagerService$ActivityIntentResolver#queryIntent]------------
final class ActivityIntentResolver
extends IntentResolver<PackageParser.ActivityIntentInfo, ResolveInfo> {
public List<ResolveInfo> queryIntent(Intent intent, String resolvedType,
boolean defaultOnly, int userId) {
if (!sUserManager.exists(userId)) return null;
mFlags = defaultOnly ? PackageManager.MATCH_DEFAULT_ONLY : 0;
//這裏調用了父類的queryIntent方法
return super.queryIntent(intent, resolvedType, defaultOnly, userId);
}
---->[IntentResolver#queryIntent]------------
public List<R> queryIntent(Intent intent, String resolvedType, boolean defaultOnly,int userId) {
String scheme = intent.getScheme();
ArrayList<R> finalList = new ArrayList<R>();
final boolean debug = localLOGV ||
((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
if (debug) Slog.v(
TAG, "Resolving type=" + resolvedType + " scheme=" + scheme
+ " defaultOnly=" + defaultOnly + " userId=" + userId + " of " + intent);
F[] firstTypeCut = null;
F[] secondTypeCut = null;
F[] thirdTypeCut = null;
F[] schemeCut = null;
// If the intent includes a MIME type, then we want to collect all of
// the filters that match that MIME type.
if (resolvedType != null) {
int slashpos = resolvedType.indexOf('/');
if (slashpos > 0) {
final String baseType = resolvedType.substring(0, slashpos);
if (!baseType.equals("*")) {
if (resolvedType.length() != slashpos+2
|| resolvedType.charAt(slashpos+1) != '*') {
// Not a wild card, so we can just look for all filters that
// completely match or wildcards whose base type matches.
firstTypeCut = mTypeToFilter.get(resolvedType);
if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTyp
secondTypeCut = mWildTypeToFilter.get(baseType);
if (debug) Slog.v(TAG, "Second type cut: "
+ Arrays.toString(secondTypeCut));
} else {
// We can match anything with our base type.
firstTypeCut = mBaseTypeToFilter.get(baseType);
if (debug) Slog.v(TAG, "First type cut: " + Arrays.toString(firstTyp
secondTypeCut = mWildTypeToFilter.get(baseType);
if (debug) Slog.v(TAG, "Second type cut: "
+ Arrays.toString(secondTypeCut));
}
// Any */* types always apply, but we only need to do this
// if the intent type was not already */*.
thirdTypeCut = mWildTypeToFilter.get("*");
if (debug) Slog.v(TAG, "Third type cut: " + Arrays.toString(thirdTypeCut
} else if (intent.getAction() != null) {
// The intent specified any type ({@literal *}/*). This
// can be a whole heck of a lot of things, so as a first
// cut let's use the action instead. firstTypeCut = mTypedActionToFilter.get(intent.getAction()); if (debug) Slog.v(TAG, "Typed Action list: " + Arrays.toString(firstType } } } // If the intent includes a data URI, then we want to collect all of // the filters that match its scheme (we will further refine matches // on the authority and path by directly matching each resulting filter). if (scheme != null) { schemeCut = mSchemeToFilter.get(scheme); if (debug) Slog.v(TAG, "Scheme list: " + Arrays.toString(schemeCut)); } // If the intent does not specify any data -- either a MIME type or // a URI -- then we will only be looking for matches against empty // data. if (resolvedType == null && scheme == null && intent.getAction() != null) { firstTypeCut = mActionToFilter.get(intent.getAction()); if (debug) Slog.v(TAG, "Action list: " + Arrays.toString(firstTypeCut)); } FastImmutableArraySet<String> categories = getFastIntentCategories(intent); if (firstTypeCut != null) { buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, firstTypeCut, finalList, userId); } if (secondTypeCut != null) { buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, secondTypeCut, finalList, userId); } if (thirdTypeCut != null) { buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, thirdTypeCut, finalList, userId); } if (schemeCut != null) { buildResolveList(intent, categories, debug, defaultOnly, resolvedType, scheme, schemeCut, finalList, userId); } sortResults(finalList); if (debug) { Slog.v(TAG, "Final result list:"); for (int i=0; i<finalList.size(); i++) { Slog.v(TAG, " " + finalList.get(i)); } } return finalList; } 複製代碼