안드로이드에서 액티비티를 실행시키려면 인텐트를 통해서 액티비티를 실행시켜야 한다.
인텐트를 이용하는 방법에는 묵시적, 명시적 방법이 있는데
명시적 방법은 인텐트에 아예 실행할 액티비티 이름을 적어 놓는 것이고
묵시적 방법은 인텐트에 어떤 일을 해야 하는지 적어 놓아
안드로이드 플랫폼이 알아서 액티비티를 처리하도록 한다.
액티비티를 실행하는 함수에는 startActivity 와 startActivityForResult 가 있는데
startActivity 는 단순히 액티비티를 실행시키는 함수이고 startActivityForResult 는 호출된 액티비티가 종료될 때
자신의 상태를 호출한 액티비티에 알려줄 수 있다.
startActivity 는 startActivityForResult 를 한번 더 감싼것으로 알 수 있다.
- public void startActivity(Intent intent) {
startActivityForResult(intent, -1);
}
- public void startActivityForResult(Intent intent, int requestCode) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
}
} else {
mParent.startActivityFromChild(this, intent, requestCode);
}
}
mParent 에 의해 분기가 갈리게 되는데 mParent 의 정체는 Activity 로써 액티비티의 뷰 안에 또 다른 액티비티가 있을 경우
mParent 는 액티비티를 포함하는 액티비티이다.
startActivityFromChild 메소드도 결국엔 같은 코드를 호출한다.
- public void startActivityFromChild(Activity child, Intent intent,
int requestCode) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, child,
intent, requestCode);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, child.mEmbeddedID, requestCode,
ar.getResultCode(), ar.getResultData());
}
}
Instrumentation 클래스는 시스템과 어플리케이션과의 동작을 모니터링할 수 있게 도와주는 클래스이다.
Instrumenataion execStartActivity
- public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,
Intent intent, int requestCode) {
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 {
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
null, 0, token, target != null ? target.mEmbeddedID : null,
requestCode, false, false);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
}
return null;
}
리턴되는 result 값이 호출한 액티비티에서 알 수 있게되는 호출된 액티비티의 결과값이다.
ActivityManagerNative.getDefault() 의 리턴값은 IActivityManager 로써 대체로 서비스와 IPC 를 하기 위한
서비스측의 인터페이스 클래스이다.
자신이 gDefault 객체를 가지고 있으면 그것을 리턴하고 없으면 ServiceManager 에 요청에 리턴한다.
- static public IActivityManager getDefault()
{
if (gDefault != null) {
//if (Config.LOGV) Log.v(
// "ActivityManager", "returning cur default = " + gDefault);
return gDefault;
}
IBinder b = ServiceManager.getService("activity");
if (Config.LOGV) Log.v(
"ActivityManager", "default service binder = " + b);
gDefault = asInterface(b);
if (Config.LOGV) Log.v(
"ActivityManager", "default service = " + gDefault);
return gDefault;
}
IActivityManagerService 는 인터페이스이기 때문에 어떤 동작을 하는지 알려면 이것을 구현한 클래스를 찾아야 하는데
보통 aidl 에서는 stub 클래스로 구현된다
- public interface IActivityManager extends IInterface
하지만 안드로이드 의 중요 서비스들은 aidl 을 사용하지 않고 직접 구현되었는데 ActivityManagerProxy 가 IActivityManager 를 구현하였다.
- class ActivityManagerProxy implements IActivityManager
asInterface 를 살펴보면 proxy 객체를 리턴한다는 것을 알 수 있다.
- 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);
}
Proxy 클래스는 바인더를 통하여 ActivityManagerService 에 startActivity 를 할 수 있도록 해준다.
- public int startActivity(IApplicationThread caller, Intent intent,
String resolvedType, Uri[] grantedUriPermissions, int grantedMode,
IBinder resultTo, String resultWho,
int requestCode, boolean onlyIfNeeded,
boolean debug) throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
intent.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeTypedArray(grantedUriPermissions, 0);
data.writeInt(grantedMode);
data.writeStrongBinder(resultTo);
data.writeString(resultWho);
data.writeInt(requestCode);
data.writeInt(onlyIfNeeded ? 1 : 0);
data.writeInt(debug ? 1 : 0);
mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);
reply.readException();
int result = reply.readInt();
reply.recycle();
data.recycle();
return result;
}
ActivityManagerService 의 startActivity 에서는 넘어온 Intent 를 분석한 다음 다시 startActivityLocked 를 호출한다.
- public final int startActivity(IApplicationThread caller,
Intent intent, String resolvedType, Uri[] grantedUriPermissions,
int grantedMode, IBinder resultTo,
String resultWho, int requestCode, boolean onlyIfNeeded,
boolean debug) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
final boolean componentSpecified = intent.getComponent() != null;
// Don't modify the client's object!
intent = new Intent(intent);
// Collect information about the target of the Intent.
ActivityInfo aInfo;
try {
ResolveInfo rInfo =
ActivityThread.getPackageManager().resolveIntent(
intent, resolvedType,
PackageManager.MATCH_DEFAULT_ONLY
| STOCK_PM_FLAGS);
aInfo = rInfo != null ? rInfo.activityInfo : null;
} catch (RemoteException e) {
aInfo = null;
}
if (aInfo != null) {
// Store the found target back into the intent, because now that
// we have it we never want to do this again. For example, if the
// user navigates back to this point in the history, we should
// always restart the exact same activity.
intent.setComponent(new ComponentName(
aInfo.applicationInfo.packageName, aInfo.name));
// Don't debug things in the system process
if (debug) {
if (!aInfo.processName.equals("system")) {
setDebugApp(aInfo.processName, true, false);
}
}
}
synchronized(this) {
final long origId = Binder.clearCallingIdentity();
int res = startActivityLocked(caller, intent, resolvedType,
grantedUriPermissions, grantedMode, aInfo,
resultTo, resultWho, requestCode, -1, -1,
onlyIfNeeded, componentSpecified);
Binder.restoreCallingIdentity(origId);
return res;
}
}
startActivityLocked 는 다시 startActivityUncheckedLocked 를 호출한다.
startActivityUncheckedLocked 에서 호출한 액티비티를 테스크에 추가하고 실행한다.
댓글 없음:
댓글 쓰기