Application解析と内部のコールバック関数


このクラスはプログラムのタグとしてandroidを始めたばかりの頃、直接4つのコンポーネントの作成に着手した.その後、一般的には自分でクラスを書いて自分で継承し、onCreateでいくつかのタスクを限定的に処理し、グローバルな異常をキャプチャしていることがわかりました.今日apiを見ていると、とても役に立つ方法がいくつかあります.
まず継承構造を見ます.
彼はContextWrapperクラスを継承して、その名の通り、Contextの包装クラス、ContextWrapperの中でこの正真のContextを維持して、mBaseと定義されて、彼は抽象クラスContextの実現クラスで、ContextImplと呼ばれています.
ContextWrapperのmBaseにContextImplオブジェクトを添付するには、次の2つの方法があります.
アプリケーションの作成:
やはりApplicationの作成から言えば、Applicationがなくて、ContextImplを設定する方法はありません(ここではデスクトップのiconをクリックして新しいアプリケーションを起動する方法からApplicationの作成について話します).
デスクトップ上のアプリケーションをクリックすると、最終的にActivity ThreadのperformLaunchActivityメソッドが呼び出されます.前の手順は「羅さんのブログ」を参考にすることができます.
2128:ここでアプリケーションの作成の開始です
ActivityThread.class
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
ここのr.packageInfoはLoadedApkオブジェクトで、彼はLoadedApkの中を呼び出しました.
LoadedApk.class
 public Application makeApplication(boolean forceDefaultAppClass,
            Instrumentation instrumentation) {
        if (mApplication != null) {
            return mApplication;
        }

        Application app = null;

        String appClass = mApplicationInfo.className;
        if (forceDefaultAppClass || (appClass == null)) {
            appClass = "android.app.Application";
        }

        try {
            java.lang.ClassLoader cl = getClassLoader();
            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);
            }
        }
        mActivityThread.mAllApplications.add(app);
        mApplication = app;

        if (instrumentation != null) {
            try {
                instrumentation.callApplicationOnCreate(app);
            } catch (Exception e) {
                if (!instrumentation.onException(app, e)) {
                    throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                        + ": " + e.toString(), e);
                }
            }
        }
        
        return app;
    }
ここではContextImpl appContext=new ContextImpl()を利用する.をクリックしてContextImplオブジェクトを作成します.これは、後でApplicationに設定するmBaseオブジェクトです.
そしてappContextを利用する.init(this, null, mActivityThread);をクリックしてContextImplのオブジェクトを初期化します.
ContextImpl .class
 final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread) {
        init(packageInfo, activityToken, mainThread, null, null, Process.myUserHandle());
    }

    final void init(LoadedApk packageInfo, IBinder activityToken, ActivityThread mainThread,
            Resources container, String basePackageName, UserHandle user) {
        mPackageInfo = packageInfo;
        if (basePackageName != null) {
            mBasePackageName = mOpPackageName = basePackageName;
        } else {
            mBasePackageName = packageInfo.mPackageName;
            ApplicationInfo ainfo = packageInfo.getApplicationInfo();
            if (ainfo.uid == Process.SYSTEM_UID && ainfo.uid != Process.myUid()) {
                // Special case: system components allow themselves to be loaded in to other
                // processes.  For purposes of app ops, we must then consider the context as
                // belonging to the package of this process, not the system itself, otherwise
                // the package+uid verifications in app ops will fail.
                mOpPackageName = ActivityThread.currentPackageName();
            } else {
                mOpPackageName = mBasePackageName;
            }
        }
        mResources = mPackageInfo.getResources(mainThread);
        mResourcesManager = ResourcesManager.getInstance();

        CompatibilityInfo compatInfo =
                container == null ? null : container.getCompatibilityInfo();
        if (mResources != null &&
                ((compatInfo != null && compatInfo.applicationScale !=
                        mResources.getCompatibilityInfo().applicationScale)
                || activityToken != null)) {
            if (DEBUG) {
                Log.d(TAG, "loaded context has different scaling. Using container's" +
                        " compatiblity info:" + container.getDisplayMetrics());
            }
            if (compatInfo == null) {
                compatInfo = packageInfo.getCompatibilityInfo();
            }
            mDisplayAdjustments.setCompatibilityInfo(compatInfo);
            mDisplayAdjustments.setActivityToken(activityToken);
            mResources = mResourcesManager.getTopLevelResources(mPackageInfo.getResDir(),
                    Display.DEFAULT_DISPLAY, null, compatInfo, activityToken);
        } else {
            mDisplayAdjustments.setCompatibilityInfo(packageInfo.getCompatibilityInfo());
            mDisplayAdjustments.setActivityToken(activityToken);
        }
        mMainThread = mainThread;
        mActivityToken = activityToken;
        mContentResolver = new ApplicationContentResolver(this, mainThread, user);
        mUser = user;
    }
この中には多くのメンバー変数が維持されており、他の方法で間接的または直接呼び出される必要があります.ここで初期化を行い、次のようにします.
mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);
ここでmActivity Threadが呼び出されました.mInstrumentationクラスであるオブジェクトのメソッド:
Instrumentation.class
 /**
     * Perform instantiation of the process's {@link Application} object.  The
     * default implementation provides the normal system behavior.
     * 
     * @param cl The ClassLoader with which to instantiate the object.
     * @param className The name of the class implementing the Application
     *                  object.
     * @param context The context to initialize the application with
     * 
     * @return The newly instantiated Application object.
     */
    public Application newApplication(ClassLoader cl, String className, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        return newApplication(cl.loadClass(className), context);
    }
    
    /**
     * Perform instantiation of the process's {@link Application} object.  The
     * default implementation provides the normal system behavior.
     * 
     * @param clazz The class used to create an Application object from.
     * @param context The context to initialize the application with
     * 
     * @return The newly instantiated Application object.
     */
    static public Application newApplication(Class<?> clazz, Context context)
            throws InstantiationException, IllegalAccessException, 
            ClassNotFoundException {
        Application app = (Application)clazz.newInstance();
        app.attach(context);
        return app;
    }
上から分かるように、最後にApplicationが作成すると、appが呼び出される.attach(context);この方法はApplicationでhideのものだったので、ideでは見つかりませんでしたが、ソースコードを見ると見つかりました.
Application.class
 /**
     * @hide
     */
    /* package */ final void attach(Context context) {
        attachBaseContext(context);
        mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;
    }
ここでやっとattachBaseContext(context)が呼び出されたのを見た.の方法で、ここまでContextWrapperの中のmBaseはやっと本当にオブジェクトを獲得しました.アプリケーションはContextWrapperから継承する方法を使用できます.
方法が多すぎると一つ一つリストされません.次に、Applicationが登録できる3つのコールバックを見てみましょう.
これは4.0後に入ったのでしょうか?
1.最初のコールバックはComponentCallbacksタイプであり、ComponentCallbacksまたはComponentCallbacks 2であってもよい.ComponentCallbacksコールバックインタフェースは2つの方法しか提供していない.
 void onConfigurationChanged(Configuration newConfig);

void onLowMemory();
ComponentCallbacks 2 ComponentCallbacksを継承するには、次の方法があります.
void onTrimMemory(int level);

Applicationでは、ComponentCallbacks 2インタフェースが実装されているため、この3つの方法が実装されています.
  public void onConfigurationChanged(Configuration newConfig) {
        Object[] callbacks = collectComponentCallbacks();
        if (callbacks != null) {
            for (int i=0; i<callbacks.length; i++) {
                ((ComponentCallbacks)callbacks[i]).onConfigurationChanged(newConfig);
            }
        }
    }

    public void onLowMemory() {
        Object[] callbacks = collectComponentCallbacks();
        if (callbacks != null) {
            for (int i=0; i<callbacks.length; i++) {
                ((ComponentCallbacks)callbacks[i]).onLowMemory();
            }
        }
    }

    public void onTrimMemory(int level) {
        Object[] callbacks = collectComponentCallbacks();
        if (callbacks != null) {
            for (int i=0; i<callbacks.length; i++) {
                Object c = callbacks[i];
                if (c instanceof ComponentCallbacks2) {
                    ((ComponentCallbacks2)c).onTrimMemory(level);
                }
            }
        }
    }

自分のアプリケーションまたは4つのコンポーネントで傍受するだけで、この3つのメソッドを書き換えるだけで、superの3つのメソッドを呼び出すことが望ましい.他の場所で傍受する必要がある場合は、アプリケーションオブジェクトのregisterComponentCallback(ComponentCallback)メソッドでコールバックを登録すればよい.
onLowMemory():システムのメモリが不足し、すべてのバックグラウンドプログラム(優先度backgroundのプロセス、バックグラウンドで実行されるプロセスではない)が殺された場合、システムはOnLowMemoryを呼び出す.
onTrimMemory(int level):OnTrimMemoryはAndroid 4.0以降に提供するAPIであり、システムは異なるメモリ状態に応じてコールバックする.
コールバックのパラメータの意味:
この3つは、現在実行中のプログラムです.
TRIM_MEMORY_RUNNING_MODERATE(5):アプリケーションが正常に動作していることを示しますが、現在は携帯電話のメモリが少し低くなっています.実行中のプロセスでは、他の場所で不要なリソースを解放する必要がある場合があります.LRUキャッシュ規則に従ってプロセスを殺し始める可能性があります
TRIM_MEMORY_RUNNING_LOW(10):アプリケーションが正常に動作していることを示しますが、現在、携帯電話のメモリは少し低くなっています.実行中のプロセスは、他の場所で使用できるメモリを必要としないリソースを空ける必要があります.
TRIM_MEMORY_RUNNING_CRITICAL(15):アプリケーションが正常に動作しているが、デバイスで非常に低いメモリが動作し、バックグラウンドプロセスの実行を維持できないことを示します.実行中のプロセスは、他の場所で使用できるメモリを使用できるため、できるだけ多くの非キーリソースを空ける必要があります.この場合,ユーザに顕著な影響を及ぼし始めることができ,その後,onLowMemory()レポートが保持されていないバックグラウンドプロセスの後を呼び出す.
この3つは、現在のプログラムがバックグラウンドにあります.
TRIM_MEMORY_UI_HIDDEN(20):ユーザが再びデスクトップに戻って呼び出す
TRIM_MEMORY_BACKGROUND(40):携帯電話のメモリが低くなっていることを示し、LRUキャッシュに基づいてプロセスをクリーンアップする準備ができています.このとき、私たちのプログラムはLRUキャッシュリストの最近の位置では、クリーンアップされることはできませんが、回復しやすいリソースを解放することで、携帯電話のメモリを十分にすることができ、私たちのプログラムをキャッシュに長く残すことができます.これにより、ユーザーが私たちのプログラムに戻ると、再起動のプロセスを経験するのではなく、非常にスムーズに感じることができます.
TRIM_MEMORY_MODERATE(60):携帯電話のメモリが低く、私たちのプログラムがLRUキャッシュリストの中間位置にあることを示しています.もし携帯電話のメモリがさらに解放されなければ、私たちのプログラムはシステムに殺されるリスクがあります.
TRIM_MEMORY_COMPLETE(80):携帯電話のメモリが低く、私たちのプログラムがLRUキャッシュリストの最端にあることを示しています.システムは私たちのアプリケーションを殺すことを最優先しています.この時、できるだけ解放できるものを解放しなければなりません.
onConfigurationChanged(Configuration newConfig):デバイスの情報が変更されると呼び出され、システムの構成情報が変更されるとactivityが再onCreate()を避ける必要がある場合があります.android:configChangesで、ある属性が宣言されていることを宣言することができます.この属性の情報が変更されると、onConfigurationChanged(Configuration newConfig)メソッドのみがコールバックされます.
Configuration.fontScale:ユーザーがフォントのサイズを選択
Configuration.mcc:simをmccに交換
Configuration.mnc:simはmncに交換されました
Configuration.locale:システム言語設定
Configuration.screenLayout:
Configuration.touchscreen:
Configuration.keyboard:
Configuration.keyboardHidden:
Configuration.hardKeyboardHidden:
Configuration.navigation:
Configuration.navigationHidden:
Configuration.orientation:画面が回転しました
Configuration.uiMode:
Configuration.screenWidthDp:
Configuration.screenHeightDp:
Configuration.smallestScreenWidthDp:
Configuration.densityDpi:
Configuration.orientation:
2.2番目のコールバックは、Activity LifecycleCallbacksタイプ(4.0+)です.
従来我々はactivityのライフサイクルを検出するが,いずれも単独のactivityにおけるライフサイクル関数にLogを書く.
アプリケーションでこのインタフェースを実装して登録すれば、次のようになります.
 public interface ActivityLifecycleCallbacks {
        void onActivityCreated(Activity activity, Bundle savedInstanceState);
        void onActivityStarted(Activity activity);
        void onActivityResumed(Activity activity);
        void onActivityPaused(Activity activity);
        void onActivityStopped(Activity activity);
        void onActivitySaveInstanceState(Activity activity, Bundle outState);
        void onActivityDestroyed(Activity activity);
    }
は、その後、r e g i s t e r A c t i vityLifecycleCallbacks(A t i v ityLifecycleCallbacks callback)によって登録され、アプリケーション内のすべてのactivityライフサイクルのリスニングを実現することができる.
3.3番目のコールバックは、OnProvideAssistDataListenerタイプです.まだ理解されていない