Androidアプリケーションの高度な使い方とソース分析

19233 ワード

Androidが開発した人にとって、Applicationというクラスはよく知られているに違いありません.私たちは一般的にこのクラスの中でActivityが起動する前に初期化する必要があるリソースを初期化したり、アプリケーション全体に必要なものを構成したりします.多く使われていますが、このクラスをよく知っているとは限りません.この文章はApplicationについて新しい認識を持つに違いありません.
ここではまず2つの実現が必要な要求を提出して、あなたが考えているかどうかを見てみましょう.
1、各Activityのライフサイクルをリスニングする(BaseActivityを使用してリスニングすることを考えるかもしれません).
2、アプリケーションのメモリがきつい時、どのようにメモリがきつい等級によって相応のActivityの資源を解放して、アプリケーションが破壊されないようにします;
上記の3つのニーズについて、Applicationについてよく知っていれば、これはあなたにとって簡単なはずです.ニーズは実現しています.では、Applicationがどのように実現されているかを知っていますか.理解していなければ、遊び続けてみてください.
まずAppcationがいつどこで作成されたかを見てみましょう.これを言う前に、Activity Threadというクラスがあります.Activityライフサイクルの各方法はここから伝わります.アプリケーションの起動が最初に実行されるmain方法もここにあります.もちろん、Activityライフサイクルを制御するのもリモートのActivity Management Serviceです.ここでは触れません.ここでは主にActivity Threadから始め、アプリケーションが最初に呼び出しを開始するのもstartActivity()であり、一連の呼び出しを経てActivity ThreadのscheduleLaunchActivity()という方法が実行されるので、ここでは私たちの切り込み点を見てみましょう.
 
@Override
public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                                         ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                                         CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                                         int procState, Bundle state, PersistableBundle persistentState,
                                         List pendingResults, List pendingNewIntents,
                                         boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

    ......//              

    sendMessage(H.LAUNCH_ACTIVITY, r);
}
private void sendMessage(int what, Object obj) {
    sendMessage(what, obj, 0, 0, false);
}
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    ......
    mH.sendMessage(msg);
}

 
主に送信されたものを受信し、Handlerを使用してメッセージを送信します.次に、このメッセージを処理する場所を見つけます.
 
public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        case LAUNCH_ACTIVITY: {
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
            final ActivityClientRecord r = (ActivityClientRecord) msg.obj;

            r.packageInfo = getPackageInfoNoCheck(
                    r.activityInfo.applicationInfo, r.compatInfo);
            handleLaunchActivity(r, null);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        }
        break;
        ......
    }
}

ここにはActivityを起動する方法があります.これまでApplicationを作成する場所が見えなかったので、下を探し続けます.
 
private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ......
    //           activity   
    Activity a = performLaunchActivity(r, customIntent);
    ......
}

performLaunchActivity()という方法も見なければなりません.
 
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // System.out.println("##### [" + System.currentTimeMillis() + "] ActivityThread.performLaunchActivity(" + r + ")");

    ActivityInfo aInfo = r.activityInfo;
    if (r.packageInfo == null) {
        r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
                Context.CONTEXT_INCLUDE_CODE);
    }

    ComponentName component = r.intent.getComponent();
    if (component == null) {
        component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
        r.intent.setComponent(component);
    }

    if (r.activityInfo.targetActivity != null) {
        component = new ComponentName(r.activityInfo.packageName,
                r.activityInfo.targetActivity);
    }

    Activity activity = null;
    try {
        //            Activity,component            Activity   
        java.lang.ClassLoader cl = r.packageInfo.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                    "Unable to instantiate activity " + component
                            + ": " + e.toString(), e);
        }
    }

    try {
        //         Activity  ,      Activity     ,
        //           Application
        Application app = r.packageInfo.makeApplication(false, mInstrumentation);

        if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
        if (localLOGV) Slog.v(
                TAG, r + ": app=" + app
                        + ", appName=" + app.getPackageName()
                        + ", pkg=" + r.packageInfo.getPackageName()
                        + ", comp=" + r.intent.getComponent().toShortString()
                        + ", dir=" + r.packageInfo.getAppDir());
        //              activity,      null
        if (activity != null) {
            Context appContext = createBaseContextForActivity(r, activity);
            CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
            Configuration config = new Configuration(mCompatConfiguration);
            if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
                    + r.activityInfo.name + " with config " + config);
            //     Activity attach()  ,    application   ,     
            //  activity   Application  
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor);

            if (customIntent != null) {
                activity.mIntent = customIntent;
            }
            r.lastNonConfigurationInstances = null;
            activity.mStartedActivity = false;
            int theme = r.activityInfo.getThemeResource();
            if (theme != 0) {
                activity.setTheme(theme);
            }

            activity.mCalled = false;
            if (r.isPersistable()) {
                mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
            } else {
                //     ,        Activity onCreate()  
                mInstrumentation.callActivityOnCreate(activity, r.state);
            }
            if (!activity.mCalled) {
                throw new SuperNotCalledException(
                        "Activity " + r.intent.getComponent().toShortString() +
                                " did not call through to super.onCreate()");
            }
            r.activity = activity;
            r.stopped = true;
            if (!r.activity.mFinished) {
                activity.performStart();
                r.stopped = false;
            }
            if (!r.activity.mFinished) {
                if (r.isPersistable()) {
                    if (r.state != null || r.persistentState != null) {
                        mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state,
                                r.persistentState);
                    }
                } else if (r.state != null) {
                    mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
                }
            }
            if (!r.activity.mFinished) {
                activity.mCalled = false;
                if (r.isPersistable()) {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state,
                            r.persistentState);
                } else {
                    mInstrumentation.callActivityOnPostCreate(activity, r.state);
                }
                if (!activity.mCalled) {
                    throw new SuperNotCalledException(
                            "Activity " + r.intent.getComponent().toShortString() +
                                    " did not call through to super.onPostCreate()");
                }
            }
        }
        r.paused = true;

        mActivities.put(r.token, r);//       Activity    ,          

    } catch (SuperNotCalledException e) {
        throw e;

    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                    "Unable to start activity " + component
                            + ": " + e.toString(), e);
        }
    }

    return activity;
}

この方法は少し長いですが、主な論理はここにあります.すべて省略されていません.興味のあるものはよく見てください.私たちがついている主線はApplicationです.だから、次はLoadedApkというクラスのmakeApplication()を見て、名前を見るとここがApplicationを作成していることがわかります.
 
public Application makeApplication(boolean forceDefaultAppClass,
                                   Instrumentation instrumentation) {
    //               Application,          ,
    // application      ,      activity          Application,
    //    activity             
    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();
        if (!mPackageName.equals("android")) {
            initializeJavaContextClassLoader();
        }
        //  context     activity     application   ,        ,
        //    application         ContextImpl,
        // newApplication()        application,        application attach()  
        ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
        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);
        }
    }
    //    application   ActivityThread      
    mActivityThread.mAllApplications.add(app);
    mApplication = app;

    if (instrumentation != null) {
        try {
            //            Application onCreate()  
            instrumentation.callApplicationOnCreate(app);
        } catch (Exception e) {
            if (!instrumentation.onException(app, e)) {
                throw new RuntimeException(
                        "Unable to create application " + app.getClass().getName()
                                + ": " + e.toString(), e);
            }
        }
    }

    // Rewrite the R 'constants' for all library apks.
    SparseArray packageIdentifiers = getAssets(mActivityThread)
            .getAssignedPackageIdentifiers();
    final int N = packageIdentifiers.size();
    for (int i = 0; i < N; i++) {
        final int id = packageIdentifiers.keyAt(i);
        if (id == 0x01 || id == 0x7f) {
            continue;
        }

        rewriteRValues(getClassLoader(), packageIdentifiers.valueAt(i), id);
    }

    return app;
}

makeApplication()は主にApplicationを作成することであり、Applicationが存在する場合はそのまま戻し、最初のActivityを起動するときにApplicationを作成するのが一般的であり、その他の場合は直接返しているので、ここではアプリケーションを単例として理解することができる.
ここで、Applicationの作成は分析済みです.次に、上記のニーズをどのように実現するかを分析します.ここでは、まず最初のニーズを推測することができます.
1、各Activityのライフサイクルをリスニングする(BaseActivityを使用してリスニングすることを考えるかもしれません).各activityの対応するライフサイクルをリスニングする以上、Activityライフサイクルに対応するメソッドをApplicationで維持し、Activityライフサイクルメソッドを実行するときにApplication対応のメソッドを呼び出す必要があるのではないでしょうか.では、ソースを見てみましょう.
(1)上記の分析によれば、まず検証するのは、アプリケーションで対応するライフサイクル方法があるかどうかを確認することです.
 
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);
}

一見、Activityのライフサイクルに対応しています.
(2)次にActivityのライフサイクルメソッドに呼び出しアプリケーションに対応するメソッドがあるかどうかを確認します.
 
protected void onCreate(@Nullable Bundle savedInstanceState) {
    ......
    //      Application dispatchActivityCreated()
    getApplication().dispatchActivityCreated(this, savedInstanceState);
    ......
}
public final Application getApplication() {
    return mApplication;
}
// ActivityThread    Application    Activity attach()    Application    
final void attach(Context context, ActivityThread aThread,
                  Instrumentation instr, IBinder token, int ident,
                  Application application, Intent intent, ActivityInfo info,
                  CharSequence title, Activity parent, String id,
                  NonConfigurationInstances lastNonConfigurationInstances,
                  Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
    ......
    mApplication = application;
    ......
}

各ActivityのonCreate()メソッドでは、ApplicationのdispatchActivity Create()メソッドが呼び出されます.ここでは、このメソッドの実装を見てみましょう.
 
/* package */ void dispatchActivityCreated(Activity activity, Bundle savedInstanceState) {
    Object[] callbacks = collectActivityLifecycleCallbacks();
    if (callbacks != null) {
        for (int i=0; i

ここで実行するのは(1)のインタフェースの方法で、次にこのインタフェースがどのように集合に追加されたのかを考えるべきで、collectActivity LifcycleCallback()を見てみましょう.
 
private Object[] collectActivityLifecycleCallbacks() {
    Object[] callbacks = null;
    synchronized (mActivityLifecycleCallbacks) {
        if (mActivityLifecycleCallbacks.size() > 0) {
            callbacks = mActivityLifecycleCallbacks.toArray();
        }
    }
    return callbacks;
}

この方法は簡単ですが、実際に戻ったのはmActivity LifecycleCallbackという集合の内容で、次はこの集合にデータを追加する方法を探しています.
 
public void registerActivityLifecycleCallbacks(ActivityLifecycleCallbacks callback) {
    synchronized (mActivityLifecycleCallbacks) {
        mActivityLifecycleCallbacks.add(callback);
    }
}

ここまですべて接続して、上のインタフェースを実現して、このメソッドを呼び出すと、Activityの対応するライフサイクルごとにこのインタフェースの対応メソッドが実行されます.ここではonCreate()のメソッドについて一度行っただけで、他のメソッドクラスのように、ここでは繰り返しません.
この方法はいろいろなところで使えるので、あなたがどのように使うかによって決まります.ここの1つの簡単な使用はActivityのスタック管理について、興味のあるのは自動的にActivityスタック管理を維持することを見ることができて、このようにして、第1点はここまで話しました.
2、アプリケーションのメモリがきつい時、どのようにメモリがきつい等級によって相応のActivityの資源を解放して、アプリケーションが破壊されないようにします;
システム内のメモリが不足している場合、多くのリソースを占有しているアプリケーションを優先的に殺す必要があります.この場合、アプリケーションが不足していることを防止するためにリソースを解放する必要があります.メモリが不足している場合、Activity ThreadのscheduleTrimMemory()が実行され、アプリケーションに解放リソースを通知します.
 
public void scheduleTrimMemory(int level) {
    sendMessage(H.TRIM_MEMORY, null, level);
}
public void handleMessage(Message msg) {
    if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
    switch (msg.what) {
        ......
        case TRIM_MEMORY:
            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "trimMemory");
            handleTrimMemory(msg.arg1);
            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
            break;
        .......
    }
}
final void handleTrimMemory(int level) {
    if (DEBUG_MEMORY_TRIM) Slog.v(TAG, "Trimming memory to level: " + level);
    //      Activity    ComponentCallbacks2     ,        Application、Activity Service
    ArrayList callbacks = collectComponentCallbacks(true, null);
    
    final int N = callbacks.size();
    for (int i = 0; i < N; i++) {
        //      Activity Application onTrimMemory()  
        callbacks.get(i).onTrimMemory(level);
    }

    WindowManagerGlobal.getInstance().trimMemory(level);
}

ここではまずActivityのonTrimMemory()の方法を見てみましょう.
 
public void onTrimMemory(int level) {
    if (DEBUG_LIFECYCLE) Slog.v(TAG, "onTrimMemory " + this + ": " + level);
    mCalled = true;
    //    activity    Fragment       Fragment onTrimMemory()  
    mFragments.dispatchTrimMemory(level);
}

次に、すべてのActivity、Service、Applicationを収集する方法を見てみましょう.
 
ArrayList collectComponentCallbacks(
        boolean allActivities, Configuration newConfig) {
    ArrayList callbacks
            = new ArrayList();

    synchronized (mResourcesManager) {
        //      Application
        //     makeApplication()  Application      ,
        //    Application          
        final int NAPP = mAllApplications.size();
        for (int i=0; ipublic interface ComponentCallbacks2 extends ComponentCallbacks {

    //          
    static final int TRIM_MEMORY_COMPLETE = 80;

    //             
    static final int TRIM_MEMORY_MODERATE = 60;

    //                ,               
    static final int TRIM_MEMORY_BACKGROUND = 40;

    //             ,                  
    static final int TRIM_MEMORY_UI_HIDDEN = 20;

    //             ,        
    //      ,            
    static final int TRIM_MEMORY_RUNNING_CRITICAL = 15;

    //     ,            
    static final int TRIM_MEMORY_RUNNING_LOW = 10;
    
    //         ,             
    static final int TRIM_MEMORY_RUNNING_MODERATE = 5;
    
    void onTrimMemory(int level);
}

 
 
ここまでApplicationでは全体的に整理するところでしたが、ここでまとめてみます.
1、アプリケーションはActivity反射インスタンス化後、ライフサイクルメソッドが実行される前にインスタンス化される.
2、アプリケーションのインスタンスを持つ前にインスタンスが存在しているかどうかを判断し、存在している場合はそのまま返し、単列と見なすことができる.
3、Activityのattach()を実行すると、Applicationインスタンスが渡されます.つまり、Activityごとに同じApplicationインスタンスがあります.
4、1つのアプリケーションに複数のプロセスが存在する場合、このときアプリケーションには複数のインスタンスが存在する.
5、システムメモリが不足している場合は、システムから送られてきた状態に応じて、対応するリソースを解放する必要があります.