一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務(wù)器之家:專(zhuān)注于服務(wù)器技術(shù)及軟件下載分享
分類(lèi)導(dǎo)航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術(shù)|正則表達(dá)式|C/C++|IOS|C#|Swift|Android|VB|R語(yǔ)言|JavaScript|易語(yǔ)言|vb.net|

服務(wù)器之家 - 編程語(yǔ)言 - Android - 深入解析Android App開(kāi)發(fā)中Context的用法

深入解析Android App開(kāi)發(fā)中Context的用法

2021-06-19 17:21安卓吧 Android

這篇文章主要介紹了深入解析Android App開(kāi)發(fā)中Context的用法,包括Context的創(chuàng)建場(chǎng)景和Context對(duì)資源的訪(fǎng)問(wèn)等內(nèi)容,需要的朋友可以參考下

context在開(kāi)發(fā)android應(yīng)用的過(guò)程中扮演著非常重要的角色,比如啟動(dòng)一個(gè)activity需要使用context.startactivity方法,將一個(gè)xml文件轉(zhuǎn)換為一個(gè)view對(duì)象也需要使用context對(duì)象,可以這么說(shuō),離開(kāi)了這個(gè)類(lèi),android開(kāi)發(fā)寸步難行,對(duì)于這樣一個(gè)類(lèi),我們又對(duì)他了解多少呢。我就說(shuō)說(shuō)我的感受吧,在剛開(kāi)始學(xué)習(xí)android開(kāi)發(fā)時(shí),感覺(jué)使用context的地方一直就是傳入一個(gè)activity對(duì)象,久而久之感覺(jué)只要是context的地方就傳入一個(gè)activity就行了,那么我們現(xiàn)在就來(lái)詳細(xì)的分析一下context和activity的關(guān)系吧!
在開(kāi)始本文之前我們先放置一個(gè)問(wèn)題在這里:
我們平時(shí)在獲取項(xiàng)目資源時(shí)使用context.getresources()的時(shí)候?yàn)槭裁捶呕氐氖峭粋€(gè)值,明明是使用不同的activity調(diào)用getresources返回結(jié)果卻是一樣的。

深入解析Android App開(kāi)發(fā)中Context的用法

context本身是一個(gè)純的abstract類(lèi),contextwrapper是對(duì)context的一個(gè)包裝而已,它的內(nèi)部包含了一個(gè)context對(duì)象,其實(shí)對(duì)contextwrapper的方法調(diào)用最終都是調(diào)用其中的context對(duì)象完成的,至于contextthremewrapper,很明顯和theme有關(guān),所以activity從contextthemmwrapper繼承,而service從contextwrapper繼承,contextimpl是唯一一個(gè)真正實(shí)現(xiàn)了context中方法的類(lèi)。
 
從上面的繼承關(guān)系來(lái)看,每一個(gè)activity就是一個(gè)context,每一個(gè)service就是一個(gè)context,這也就是為什么使用context的地方可以被activity或者service替換了。
 

創(chuàng)建context
根據(jù)前面所說(shuō),由于實(shí)現(xiàn)了context的只有contextimpl類(lèi),activity和service本沒(méi)有真正的實(shí)現(xiàn),他們只是內(nèi)部包含了一個(gè)真實(shí)的context對(duì)象而已,也就是在在創(chuàng)建activity或者service的時(shí)候肯定要?jiǎng)?chuàng)建愛(ài)你一個(gè)contextimpl對(duì)象,并賦值到activity中的context類(lèi)型變量中。那我們就來(lái)看看andorid源碼中有哪些地方創(chuàng)建了contextimpl.
據(jù)統(tǒng)計(jì)android中創(chuàng)建contextimpl的地方一共有7處:

  • 在packageinfo.makeapplication()中
  • 在performlaunchactivity()中
  • 在handlecreatebackupagent()中
  • 在handlecreateservice()中
  • 2次在hanldbinderappplication()中
  • 在attach()方法中


由于創(chuàng)建contextimpl的基本原理類(lèi)似,所以這里只會(huì)分析幾個(gè)比較有代表性的地方:
1、  application對(duì)應(yīng)的context
在應(yīng)用程序啟動(dòng)時(shí),都會(huì)創(chuàng)建一個(gè)application對(duì)象,所以輾轉(zhuǎn)調(diào)用到handlebindapplication()方法。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
private final void handlebindapplication(appbinddata data) {
    mboundapplication = data;
    mconfiguration = new configuration(data.config);
 
    ....
    data.info = getpackageinfonocheck(data.appinfo);
 
      ...
     
    application app = data.info.makeapplication(data.restrictedbackupmode, null);
    minitialapplication = app;
 
   ....
 }

其中data.info是loadedapk類(lèi)型的,到getpackageinfonocheck中看看源碼

?
1
2
3
public final loadedapk getpackageinfonocheck(applicationinfo ai) {
    return getpackageinfo(ai, null, false, true);
}

里面其實(shí)調(diào)用的是getpackageinfo,繼續(xù)跟進(jìn):

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
if (includecode) {
        ref = mpackages.get(ainfo.packagename);
      } else {
        ref = mresourcepackages.get(ainfo.packagename);
      }
      loadedapk packageinfo = ref != null ? ref.get() : null;
      if (packageinfo == null || (packageinfo.mresources != null
          && !packageinfo.mresources.getassets().isuptodate())) {
        if (locallogv) slog.v(tag, (includecode ? "loading code package "
            : "loading resource-only package ") + ainfo.packagename
            + " (in " + (mboundapplication != null
                ? mboundapplication.processname : null)
            + ")");
        packageinfo =
          new loadedapk(this, ainfo, this, baseloader,
              securityviolation, includecode &&
              (ainfo.flags&applicationinfo.flag_has_code) != 0);
if (includecode) {
          mpackages.put(ainfo.packagename,
              new weakreference<loadedapk>(packageinfo));
        } else {
          mresourcepackages.put(ainfo.packagename,
              new weakreference<loadedapk>(packageinfo));
        }

由于includecode傳入的是true,所以首先從mpackages中獲取,如果沒(méi)有,則new一個(gè)出來(lái),并放入mpackages里面去,注意,這里的mpackages是activitythread中的屬性。
下面繼續(xù)分析一下loadedapk這個(gè)類(lèi)中的makeapplication函數(shù)

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
try {
      java.lang.classloader cl = getclassloader();
      //創(chuàng)建一個(gè)contextimpl對(duì)象
      contextimpl appcontext = new contextimpl();
      appcontext.init(this, null, mactivitythread);
      app = mactivitythread.minstrumentation.newapplication(
          cl, appclass, appcontext);
      appcontext.setoutercontext(app);
    } catch (exception e) {
      if (!mactivitythread.minstrumentation.onexception(app, e)) {
        throw new runtimeexception(
          "unable to instantiate application " + appclass
          + ": " + e.tostring(), e);
      }
    }

這里創(chuàng)建了一個(gè)contextimpl對(duì)象,并調(diào)用了它的init方法,現(xiàn)在進(jìn)入init方法。 

?
1
2
mpackageinfo = packageinfo;
mresources = mpackageinfo.getresources(mainthread);

對(duì)mpackageinof和mresources兩個(gè)變量初始化
回到makeapplication中,創(chuàng)建了一個(gè)application對(duì)象,并將appcontext傳進(jìn)去,其實(shí)就是將appcontext傳遞給contextwrapper中的context類(lèi)型變量(application也是繼承contextwrapper)
2、activity中的context
在創(chuàng)建一個(gè)activity時(shí),經(jīng)過(guò)輾轉(zhuǎn)調(diào)用,會(huì)執(zhí)行handlelaunchactivity(),然后調(diào)用performlaunchactivity(),該方法創(chuàng)建contextimpl代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
r.packageinfo= getpackageinfo(ainfo.applicationinfo,
          context.context_include_code);
 
contextimplappcontext = new contextimpl();
        appcontext.init(r.packageinfo,r.token, this);
        appcontext.setoutercontext(activity);
 
activity.attach(appcontext,this, getinstrumentation(), r.token,
            r.ident, app, r.intent,r.activityinfo, title, r.parent,
            r.embeddedid,r.lastnonconfigurationinstance,
            r.lastnonconfigurationchildinstances, config);

由于getpackageinfo函數(shù)之前已經(jīng)分析過(guò)了,稍微有點(diǎn)區(qū)別,但是大致流程是差不多的,所以此處的appcontext執(zhí)行init之后,其中的mpackages變量和mresources變量時(shí)一樣的,activity通過(guò)attach函數(shù)將該appcontext賦值到contextwrapper中的context類(lèi)型變量。

3、service中的context
同樣 在創(chuàng)建一個(gè)service時(shí),經(jīng)過(guò)輾轉(zhuǎn)調(diào)用會(huì)調(diào)用到schedulecreateservice方法,之后會(huì)巧用handlecreateservice

?
1
2
3
4
5
6
7
8
9
10
loadedapkpackageinfo = getpackageinfonocheck(
        data.info.applicationinfo);
 
contextimplcontext = new contextimpl();
      context.init(packageinfo, null,this);
 
      application app =packageinfo.makeapplication(false, minstrumentation);
      context.setoutercontext(service);
      service.attach(context, this,data.info.name, data.token, app,
          activitymanagernative.getdefault());

其思路和上面兩個(gè)基本一樣,在此就不再詳述。

context對(duì)資源的訪(fǎng)問(wèn)
很明確,不同的context得到的都是同一份資源。這是很好理解的,請(qǐng)看下面的分析

得到資源的方式為context.getresources,而真正的實(shí)現(xiàn)位于contextimpl中的getresources方法,在contextimpl中有一個(gè)成員 private resources mresources,它就是getresources方法返回的結(jié)果,mresources的賦值代碼為:

?
1
2
mresources = mresourcesmanager.gettoplevelresources(mpackageinfo.getresdir(),
          display.default_display, null, compatinfo, activitytoken);

下面看一下resourcesmanager的gettoplevelresources方法,這個(gè)方法的思想是這樣的:在resourcesmanager中,所有的資源對(duì)象都被存儲(chǔ)在arraymap中,首先根據(jù)當(dāng)前的請(qǐng)求參數(shù)去查找資源,如果找到了就返回,否則就創(chuàng)建一個(gè)資源對(duì)象放到arraymap中。有一點(diǎn)需要說(shuō)明的是為什么會(huì)有多個(gè)資源對(duì)象,原因很簡(jiǎn)單,因?yàn)閞es下可能存在多個(gè)適配不同設(shè)備、不同分辨率、不同系統(tǒng)版本的目錄,按照android系統(tǒng)的設(shè)計(jì),不同設(shè)備在訪(fǎng)問(wèn)同一個(gè)應(yīng)用的時(shí)候訪(fǎng)問(wèn)的資源可以不同,比如drawable-hdpi和drawable-xhdpi就是典型的例子。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
public resources gettoplevelresources(string resdir, int displayid,
    configuration overrideconfiguration, compatibilityinfo compatinfo, ibinder token) {
  final float scale = compatinfo.applicationscale;
  resourceskey key = new resourceskey(resdir, displayid, overrideconfiguration, scale,
      token);
  resources r;
  synchronized (this) {
    // resources is app scale dependent.
    if (false) {
      slog.w(tag, "gettoplevelresources: " + resdir + " / " + scale);
    }
    weakreference<resources> wr = mactiveresources.get(key);
    r = wr != null ? wr.get() : null;
    //if (r != null) slog.i(tag, "isuptodate " + resdir + ": " + r.getassets().isuptodate());
    if (r != null && r.getassets().isuptodate()) {
      if (false) {
        slog.w(tag, "returning cached resources " + r + " " + resdir
            + ": appscale=" + r.getcompatibilityinfo().applicationscale);
      }
      return r;
    }
  }
 
  //if (r != null) {
  //  slog.w(tag, "throwing away out-of-date resources!!!! "
  //      + r + " " + resdir);
  //}
 
  assetmanager assets = new assetmanager();
  if (assets.addassetpath(resdir) == 0) {
    return null;
  }
 
  //slog.i(tag, "resource: key=" + key + ", display metrics=" + metrics);
  displaymetrics dm = getdisplaymetricslocked(displayid);
  configuration config;
  boolean isdefaultdisplay = (displayid == display.default_display);
  final boolean hasoverrideconfig = key.hasoverrideconfiguration();
  if (!isdefaultdisplay || hasoverrideconfig) {
    config = new configuration(getconfiguration());
    if (!isdefaultdisplay) {
      applynondefaultdisplaymetricstoconfigurationlocked(dm, config);
    }
    if (hasoverrideconfig) {
      config.updatefrom(key.moverrideconfiguration);
    }
  } else {
    config = getconfiguration();
  }
  r = new resources(assets, dm, config, compatinfo, token);
  if (false) {
    slog.i(tag, "created app resources " + resdir + " " + r + ": "
        + r.getconfiguration() + " appscale="
        + r.getcompatibilityinfo().applicationscale);
  }
 
  synchronized (this) {
    weakreference<resources> wr = mactiveresources.get(key);
    resources existing = wr != null ? wr.get() : null;
    if (existing != null && existing.getassets().isuptodate()) {
      // someone else already created the resources while we were
      // unlocked; go ahead and use theirs.
      r.getassets().close();
      return existing;
    }
 
    // xxx need to remove entries when weak references go away
    mactiveresources.put(key, new weakreference<resources>(r));
    return r;
  }
}

根據(jù)上述代碼中資源的請(qǐng)求機(jī)制,再加上resourcesmanager采用單例模式,這樣就保證了不同的contextimpl訪(fǎng)問(wèn)的是同一套資源,注意,這里說(shuō)的同一套資源未必是同一個(gè)資源,因?yàn)橘Y源可能位于不同的目錄,但它一定是我們的應(yīng)用的資源,或許這樣來(lái)描述更準(zhǔn)確,在設(shè)備參數(shù)和顯示參數(shù)不變的情況下,不同的contextimpl訪(fǎng)問(wèn)到的是同一份資源。設(shè)備參數(shù)不變是指手機(jī)的屏幕和android版本不變,顯示參數(shù)不變是指手機(jī)的分辨率和橫豎屏狀態(tài)。也就是說(shuō),盡管application、activity、service都有自己的contextimpl,并且每個(gè)contextimpl都有自己的mresources成員,但是由于它們的mresources成員都來(lái)自于唯一的resourcesmanager實(shí)例,所以它們看似不同的mresources其實(shí)都指向的是同一塊內(nèi)存(c語(yǔ)言的概念),因此,它們的mresources都是同一個(gè)對(duì)象(在設(shè)備參數(shù)和顯示參數(shù)不變的情況下)。在橫豎屏切換的情況下且應(yīng)用中為橫豎屏狀態(tài)提供了不同的資源,處在橫屏狀態(tài)下的contextimpl和處在豎屏狀態(tài)下的contextimpl訪(fǎng)問(wèn)的資源不是同一個(gè)資源對(duì)象。

 

代碼:?jiǎn)卫J降膔esourcesmanager類(lèi)

?
1
2
3
4
5
6
7
8
public static resourcesmanager getinstance() {
  synchronized (resourcesmanager.class) {
    if (sresourcesmanager == null) {
      sresourcesmanager = new resourcesmanager();
    }
    return sresourcesmanager;
  }
}

getapplication和getapplicationcontext的區(qū)別
 

getapplication返回結(jié)果為application,且不同的activity和service返回的application均為同一個(gè)全局對(duì)象,在activitythread內(nèi)部有一個(gè)列表專(zhuān)門(mén)用于維護(hù)所有應(yīng)用的application

 

?
1
final arraylist<application> mallapplications = new arraylist<application>();

getapplicationcontext返回的也是application對(duì)象,只不過(guò)返回類(lèi)型為context,看看它的實(shí)現(xiàn)

?
1
2
3
4
5
@override
public context getapplicationcontext() {
  return (mpackageinfo != null) ?
      mpackageinfo.getapplication() : mmainthread.getapplication();
}

上面代碼中mpackageinfo是包含當(dāng)前應(yīng)用的包信息、比如包名、應(yīng)用的安裝目錄等,原則上來(lái)說(shuō),作為第三方應(yīng)用,包信息mpackageinfo不可能為空,在這種情況下,getapplicationcontext返回的對(duì)象和getapplication是同一個(gè)。但是對(duì)于系統(tǒng)應(yīng)用,包信息有可能為空,具體就不深入研究了。從這種角度來(lái)說(shuō),對(duì)于第三方應(yīng)用,一個(gè)應(yīng)用只存在一個(gè)application對(duì)象,且通過(guò)getapplication和getapplicationcontext得到的是同一個(gè)對(duì)象,兩者的區(qū)別僅僅是返回類(lèi)型不同。


在此總結(jié)一下:
(1)context是一個(gè)抽象類(lèi),contextwrapper是對(duì)context的封裝,它包含一個(gè)context類(lèi)型的變量,contextwrapper的功能函數(shù)內(nèi)部其實(shí)都是調(diào)用里面的context類(lèi)型變量完成的。application,service,activity等都是直接或者間接繼承自contextwrapper,但是并沒(méi)有真正的實(shí)現(xiàn)其中的功能,application,service,activity中關(guān)于context的功能都是通過(guò)其內(nèi)部的context類(lèi)型變量完成的,而這個(gè)變量的真實(shí)對(duì)象必定是contextimpl,所以沒(méi)創(chuàng)建一個(gè)application,activity,servcice便會(huì)創(chuàng)建一個(gè)contextimpl,并且這些contextimpl中的mpackages和mresources變量都是一樣的,所以不管使用acitivty還是service調(diào)用getresources得到相同的結(jié)果
(2)在一個(gè)apk中,context的數(shù)量等于activity個(gè)數(shù)+service個(gè)數(shù)+1.

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲成av人片在线观看天堂无码 | 国产在视频 | 欧美精品一区二区三区免费播放 | 五月色天在线视频综合观看 | 国产亚洲精品久久yy5099 | 日本www视频在线观看 | 久久亚洲精品AV无码四区 | 成人小视频在线免费观看 | 爽好舒服把腿张小说 | 日本高清视频在线观看 | 日韩一级生活片 | 教师波多野结衣在线播放 | 久久精品一区 | 91精品手机国产在线观 | 色倩网站| 成年人免费在线看 | 男人天堂bt | 精品国产国产综合精品 | 亚洲精品一 | 亚洲色图第四页 | 91亚洲精品国产自在现线 | 2021海角社区最新版 | 日本免费在线观看视频 | 精品视频一区二区三区免费 | 国产私人影院 | 出差上的少妇20p | 国产精品第2页 | 日韩欧美推理片免费看完整版 | 美女脱了内裤张开腿亲吻男生 | 日本一道本中文字幕 | 偷拍自拍校园春色 | 短篇同学新婚h系列小说 | chinese男性厕所撒尿合集 | 国产精品亚欧美一区二区三区 | 被老外操 | 调教女高中生第3部分 | 我被男人下药添得好爽 | 亚洲热图 | 天天操精品 | 99精品在线视频 | 国产区最新 |