IPCのToastにおける応用
20731 ワード
1、Toast概念と問題の引き出し
Toast中国語名「土司」は、Androidの使用頻度が高いwidgetと言えるでしょう.一般的には、タオバオでコレクションをクリックすると、底に「コレクション成功」というヒントが表示されます.これがToastの機能です.
まず1つの問題を投げ出します:それは複数のアプリケーションあるいは複数のスレッドがToastを表示する時システムがどのようにこの問題を処理しますか?シリアル実行ですか、パラレル実行ですか.
2、Toastの使い方
Toastの使用は非常に簡単で、一言で1つのToastを表示することができます.以下は具体的なコードの体現です.
3、Toastオブジェクトを構築する
次に、上記のコードに基づいて、Toastが携帯電話の画面に表示される過程を分析します.
メイン・エントリは2つです. public static Toast makeText(Context context, CharSequence text, int duration) public void show()
3.1、makeTextソース分析
makeTextメソッドの内部では主に次のようなことが行われています. Toastオブジェクトを作成します. は、このToastを表示するシステムレイアウトをロードします. 表示するテキストの内容を設定するのは、私たちが上に設定した「hello Toast」と、このToastが表示する時間を設定することです.
実はmakeTextの内部に伝わるいくつかの属性に基づいてToastオブジェクトを構築します.
3.2、Toast構造方法
Toastの構造方法は内部が簡単で、主にTNオブジェクトを作成し、mTNに値を付与する.このTNオブジェクトは非常に重要であり、以下の分析でその役割をゆっくり説明する.TNオブジェクトはToast構造で作成されるので、1つのToastオブジェクトの作成がTNオブジェクトの作成に対応することも重要です.
4、TN類の分析
TN類はまずその声明を見て、AIDLを学んだことがある学生はすべて知っているはずで、TNはITransientNotificationを受け継いだ.Stubクラスは、リモートサービスが本当に働いているクラスであることを示しています.これはどういう意味ですか.実際には、クライアントがトーストを表示する必要がある場合、この要件はリモート・サービスNotificationManagerServiceに渡されて統一的にスケジューリングされます.【ここでは、IPCプロセスが初めてです.ユーザー・プロセスがサービス・プロセスNotificationManagerServiceにアクセスします】.
NotificationManagerServiceのスケジューリングが完了したら、show/hideトーストに行くようにユーザーにどのように通知しますか?リモート・サービス・プロセスは、mTNを介してユーザー・プロセス(現在のプロセス)とインタラクティブに(show,hideなどの操作)【ここでは2回目のIPCプロセス:サービス・プロセスNotificationManagerServiceがユーザー・プロセスTNにアクセスする】.
5、Toastを表示する showメソッドではまずmNextViewが空かどうかを判断します.このmNextViewはロードしてToastのレイアウトを示す必要があります.レイアウトさえなければ、どんな土司を表示するか、直接throwになります. パケット名pkgを取得するには、なぜパケット名が必要なのか、リモートサービスで表示する必要があるToastが同じプロセス(1つのプロセスが1つのパケット名に対応する)に来たかどうかを判断するため、具体的な判断プロセスは再分析される. はtnにmTNを割り当てて、まだこのmTNを覚えていますか?彼はトーストにいるmakeTextの内部メソッドで作成された(つまりToast構造で作成された)もので、前に述べたように、1つのToastオブジェクトがmTNオブジェクトに対応している. IPCを呼び出してリモート・サービスNotificationServiceManagerにenqueueToastに行くように通知するプロセスは非常に重要です.tnをパラメータとして入力するのを見ましたが、これは何に使いますか?TNというクラスを紹介する前に、このクラスはプロセス間通信に使用されると述べた.Toastの表示と非表示はリモートのNMSによって統一的にスケジューリングされる必要があるため、内部では1つの集合を維持して1つのToastを格納し、指定された時間になってこのToastを表示することができるようになり、NMSはTNというエージェントクラスを通じてクライアントに本当にToastを表示するように通知することができる.
5.1、INotificationManager.Stub.Proxy.enqueueToast
service.EnqueueToast(pkg,tn,mDuration)というメソッドの呼び出しは、実際にはINotificationManagerのエージェントオブジェクトによって実行されます.具体的な実行コードは、以下のとおりです.主にParcel_にデータを書き込むことです.DataでmRemoteを呼び出します.TransactはRPCリモートコール操作を行い、その後_replyは、返されたデータを取得します.
6、リモートサービス呼び出しToastの開始
[NMSのソース](https://android.googlesource.com/platform/frameworks/base/+/f76a50c/services/java/com/android/server/NotificationManagerService.JAva)あるサイトgrepcodeはソースコードを調べることができ、必要に応じてここでソースコードを見ることができます.
enqueueToastメソッドの内部で主にこのいくつかのことをしました. synchronizeロック判断、ここでなぜロックを追加するのですか? indexOfToastLocked検査操作; indexOfToastLockedの検証結果に従って後続の操作を行う.
6.1、enqueueToastロック判定
ここではなぜここに鍵をかけるのか考えてみましょう.ロックは、同時アクセスによるデータの非同期化の問題を回避するためであり、NMSはシステムサービスであり、そのオブジェクトは1つしかなく、複数のプロセス(複数のAPP)または複数のスレッドがこのサービスを取得することができ、複数のスレッドが同じオブジェクトの共有変数を操作するデータの非同期化の問題のように、そのサービスのenqueueToastメソッドを同時に呼び出すことができることを知っています.そのため、ここにはロックが必要です.またenqueueToastメソッドは、Binderスレッドプールで呼び出されるサービス側のメソッドです.
6.2、indexOfToastLockedの検査操作
現在のpagパッケージ名とcallbackがmToastQueueにすでに存在するかどうかを検出します.分析する前にまずcallbackが何なのかを知っておきましょう.以前にToast構造を作成して説明したように、1つのToastオブジェクトはmTNオブジェクトに対応します.このcallbackはこのmTNオブジェクトのエージェントオブジェクトです.なぜですか.プロセス間通信が必要であるため、mTNオブジェクトをBinderに変換して転送する必要があり、NMSでこのBinderを取得するとITransientNotificationに変換する.Proxyエージェントオブジェクト.
このメソッドは整数を返します.-1は見つかりません.他の値は見つかりました.
6.3、indexOfToastLockedの検査結果に基づいて後続の操作を行う.
1.見つかった場合、戻り値が-1でない場合は、durationを直接更新し、そのToastRecordがmToastQueueの位置であることを変更しません.どういう意味ですか.ここで栗を挙げます:ここforは500回循環して、毎回toastを呼び出します.show()メソッドは、その実行結果が一度しか表示されませんが、なぜですか?前述したように、indexOfToastLockedの内部はpkgとcallbackで判断されるが、この2つの値は同じであるため、この方法で返される値は-1ではないため、show 500回でもそのmToastQueueの中にはToastRecordオブジェクトが1つしかない.
2.値が-1の場合、新しいToastRecordを作成し、mToastQueueに追加する必要があります.前に得るindexは-1であるため、ここではindex=mToastQueueを再計算する必要がある.size()-1、このindexは現在新しく作成されたToastRecordのキュー内の位置を表し、この位置が0の場合はキュー内にToastRecordが1つしかないことを示し、showNextToastLockedを直接実行すればよい.
3.ここで、知っていなければ、同じpkgでのToastRecordの数がMAX_を超えていることに注意するPACKAGE_NOTIFICATIONSはそのままreturnし、DOS attacksを避けるためだと公式に明らかにした.
4.ここで次のようなことに注意してください.実行結果はtoastが50回実行され、余計なことは捨てられます.理由は上の3つ目です.しかし、このような書き方と第1点の書き方の違いは、Toastの作成タイミングが異なることです.ここではループごとに1つ作成されます.つまり、Toastの構造は1回歩きます.mTNはToast構造で作成されたもので、makeTextを呼び出すたびに新しいToastオブジェクトが作成されます.つまり、mTNも新しいオブジェクトです.NMSに渡されるmTNが異なると、indexOfToastLockedを行うと-1が返されるので、ここではこのコードの違いです.
7、リモートサービス側のスケジューリング表示と非表示操作
7.1、showNextToastLocked()
この方法は主にいくつかのことをしました.は、キューからToastRecordを取り出し、IPCによってユーザプロセスを呼び出すTNのshowメソッドによって、実際にToastを表示する. Handlerを介して、durationのための遅延タスクが送信され、この時間が経過すると、このToastの表示がキャンセルされます.
7.2、record.callback.show();
record.callbackは、ユーザプロセスがサービスプロセスに伝達するITransientNotificationのエージェントオブジェクトであり、このエージェントオブジェクトのshow()メソッドを呼び出すことによって、IPCのプロシージャであり、このプロシージャは最終的にユーザプロセスのTNにおけるshow()メソッドを呼び出す.
7.3、 scheduleTimeoutLocked
Handlerを使用して、Toastが設定したdurationプロパティである遅延メッセージを送信します.
7.4、 handleTimeout
7.5、cancelToastLocked
Handlerを介して送信される遅延タスクの時間が来た場合、アプリケーションプロセスにhideというToastを通知する必要がある.具体的な操作はやはりIPC過程を通じて本当にTNのhide方法を呼び出して本当の隠蔽操作を実現する.
操作が完了した後、キューにToastRecordがまだ実行されていないかどうかを判断し、もしあればshowNextToastLockedメソッドを呼び出して再びToastRecordを取り出して実行する.
Toast中国語名「土司」は、Androidの使用頻度が高いwidgetと言えるでしょう.一般的には、タオバオでコレクションをクリックすると、底に「コレクション成功」というヒントが表示されます.これがToastの機能です.
まず1つの問題を投げ出します:それは複数のアプリケーションあるいは複数のスレッドがToastを表示する時システムがどのようにこの問題を処理しますか?シリアル実行ですか、パラレル実行ですか.
2、Toastの使い方
Toastの使用は非常に簡単で、一言で1つのToastを表示することができます.以下は具体的なコードの体現です.
Toast.makeText(this, "hello Toast", Toast.LENGTH_SHORT).show();
3、Toastオブジェクトを構築する
次に、上記のコードに基づいて、Toastが携帯電話の画面に表示される過程を分析します.
メイン・エントリは2つです.
3.1、makeTextソース分析
makeTextメソッドの内部では主に次のようなことが行われています.
実はmakeTextの内部に伝わるいくつかの属性に基づいてToastオブジェクトを構築します.
public static Toast makeText(Context context, CharSequence text, @Duration int duration) {
Toast result = new Toast(context);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView = v;
result.mDuration = duration;
return result;
}
3.2、Toast構造方法
Toastの構造方法は内部が簡単で、主にTNオブジェクトを作成し、mTNに値を付与する.このTNオブジェクトは非常に重要であり、以下の分析でその役割をゆっくり説明する.TNオブジェクトはToast構造で作成されるので、1つのToastオブジェクトの作成がTNオブジェクトの作成に対応することも重要です.
public Toast(Context context) {
mContext = context;
// TN
mTN = new TN();
//
mTN.mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
mTN.mGravity = context.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity);
}
4、TN類の分析
TN類はまずその声明を見て、AIDLを学んだことがある学生はすべて知っているはずで、TNはITransientNotificationを受け継いだ.Stubクラスは、リモートサービスが本当に働いているクラスであることを示しています.これはどういう意味ですか.実際には、クライアントがトーストを表示する必要がある場合、この要件はリモート・サービスNotificationManagerServiceに渡されて統一的にスケジューリングされます.【ここでは、IPCプロセスが初めてです.ユーザー・プロセスがサービス・プロセスNotificationManagerServiceにアクセスします】.
NotificationManagerServiceのスケジューリングが完了したら、show/hideトーストに行くようにユーザーにどのように通知しますか?リモート・サービス・プロセスは、mTNを介してユーザー・プロセス(現在のプロセス)とインタラクティブに(show,hideなどの操作)【ここでは2回目のIPCプロセス:サービス・プロセスNotificationManagerServiceがユーザー・プロセスTNにアクセスする】.
private static class TN extends ITransientNotification.Stub {
final Runnable mHide = new Runnable() {
@Override
public void run() {
handleHide();
// Don't do this in handleHide() because it is also invoked by handleShow()
mNextView = null;
}
};
private final WindowManager.LayoutParams mParams = new WindowManager.LayoutParams()
final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
IBinder token = (IBinder) msg.obj;
handleShow(token);
}
};
int mGravity;
int mX, mY;
float mHorizontalMargin;
float mVerticalMargin;
View mView;
View mNextView;
int mDuration;
WindowManager mWM;
static final long SHORT_DURATION_TIMEOUT = 5000;
static final long LONG_DURATION_TIMEOUT = 1000;
TN() {
// XXX This should be changed to use a Dialog, with a Theme.Toast
// defined that sets up the layout params appropriately.
final WindowManager.LayoutParams params = mParams;
params.height = WindowManager.LayoutParams.WRAP_CONTENT;
params.width = WindowManager.LayoutParams.WRAP_CONTENT;
params.format = PixelFormat.TRANSLUCENT;
params.windowAnimations = com.android.internal.R.style.Animation_Toast;
params.type = WindowManager.LayoutParams.TYPE_TOAST;
params.setTitle("Toast");
params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
}
/**
* schedule handleShow into the right thread
*/
@Override
public void show(IBinder windowToken) {
if (localLOGV) Log.v(TAG, "SHOW: " + this);
mHandler.obtainMessage(0, windowToken).sendToTarget();
}
/**
* schedule handleHide into the right thread
*/
@Override
public void hide() {
if (localLOGV) Log.v(TAG, "HIDE: " + this);
mHandler.post(mHide);
}
public void handleShow(IBinder windowToken) {
if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ " mNextView=" + mNextView);
if (mView != mNextView) {
// remove the old view if necessary
handleHide();
mView = mNextView;
Context context = mView.getContext().getApplicationContext();
String packageName = mView.getContext().getOpPackageName();
if (context == null) {
context = mView.getContext();
}
mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
// We can resolve the Gravity here by using the Locale for getting
// the layout direction
final Configuration config = mView.getContext().getResources().getConfigura
final int gravity = Gravity.getAbsoluteGravity(mGravity, config.getLayoutDi
mParams.gravity = gravity;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL)
mParams.horizontalWeight = 1.0f;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
mParams.verticalWeight = 1.0f;
}
mParams.x = mX;
mParams.y = mY;
mParams.verticalMargin = mVerticalMargin;
mParams.horizontalMargin = mHorizontalMargin;
mParams.packageName = packageName;
mParams.hideTimeoutMilliseconds = mDuration ==
Toast.LENGTH_LONG ? LONG_DURATION_TIMEOUT : SHORT_DURATION_TIMEOUT;
mParams.token = windowToken;
if (mView.getParent() != null) {
if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
}
if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
mWM.addView(mView, mParams);
trySendAccessibilityEvent();
}
}
private void trySendAccessibilityEvent() {
AccessibilityManager accessibilityManager =
AccessibilityManager.getInstance(mView.getContext());
if (!accessibilityManager.isEnabled()) {
return;
}
// treat toasts as notifications since they are used to
// announce a transient piece of information to the user
AccessibilityEvent event = AccessibilityEvent.obtain(
AccessibilityEvent.TYPE_NOTIFICATION_STATE_CHANGED);
event.setClassName(getClass().getName());
event.setPackageName(mView.getContext().getPackageName());
mView.dispatchPopulateAccessibilityEvent(event);
accessibilityManager.sendAccessibilityEvent(event);
}
public void handleHide() {
if (localLOGV) Log.v(TAG, "HANDLE HIDE: " + this + " mView=" + mView);
if (mView != null) {
// note: checking parent() just to make sure the view has
// been added... i have seen cases where we get here when
// the view isn't yet added, so let's try not to crash.
if (mView.getParent() != null) {
if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
mWM.removeViewImmediate(mView);
}
mView = null;
}
}
}
5、Toastを表示する
public void show() {
if (mNextView == null) {
throw new RuntimeException("setView must have been called");
}
// INotificationManager
INotificationManager service = getService();
// pkg
String pkg = mContext.getOpPackageName();
TN tn = mTN;
tn.mNextView = mNextView;
try {
service.enqueueToast(pkg, tn, mDuration);
} catch (RemoteException e) {
// Empty
}
}
5.1、INotificationManager.Stub.Proxy.enqueueToast
service.EnqueueToast(pkg,tn,mDuration)というメソッドの呼び出しは、実際にはINotificationManagerのエージェントオブジェクトによって実行されます.具体的な実行コードは、以下のとおりです.主にParcel_にデータを書き込むことです.DataでmRemoteを呼び出します.TransactはRPCリモートコール操作を行い、その後_replyは、返されたデータを取得します.
//INotificationManager.Stub.Proxy.enqueueToast
public void enqueueToast(java.lang.String pkg, android.app.ITransientNotification callback, int duration) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(pkg);
//callback tn , , Bidner
_data.writeStrongBinder((((callback!=null))?(callback.asBinder()):(null)));
_data.writeInt(duration);
mRemote.transact(Stub.TRANSACTION_enqueueToast, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
6、リモートサービス呼び出しToastの開始
[NMSのソース](https://android.googlesource.com/platform/frameworks/base/+/f76a50c/services/java/com/android/server/NotificationManagerService.JAva)あるサイトgrepcodeはソースコードを調べることができ、必要に応じてここでソースコードを見ることができます.
enqueueToastメソッドの内部で主にこのいくつかのことをしました.
public void enqueueToast(String pkg, ITransientNotification callback, int duration) {
//pkg callback ...
//
synchronized (mToastQueue) {
int callingPid = Binder.getCallingPid();
long callingId = Binder.clearCallingIdentity();
try {
ToastRecord record;
// ToastRecord
int index = indexOfToastLocked(pkg, callback);
// If it's already in the queue, we update it in place, we don't
// move it to the end of the queue.
// ToastRecord
if (index >= 0) {
// ToastRecord , , Toast , show , 。
record = mToastQueue.get(index);
record.update(duration);
} else {
//
// Limit the number of toasts that any given package except the android
// package can enqueue. Prevents DOS attacks and deals with leaks.
if (!"android".equals(pkg)) {//
int count = 0;
final int N = mToastQueue.size();
for (int i=0; i= MAX_PACKAGE_NOTIFICATIONS) {
Slog.e(TAG, "Package has already posted " + count
+ " toasts. Not showing more. Package=" + pkg);
return;
}
}
}
}
// ToastReord 。
record = new ToastRecord(callingPid, pkg, callback, duration);
// 。
mToastQueue.add(record);
index = mToastQueue.size() - 1;
keepProcessAliveLocked(callingPid);
}
// If it's at index 0, it's the current toast. It doesn't matter if it's
// new or just been updated. Call back and tell it to show itself.
// If the callback fails, this will remove it from the list, so don't
// assume that it's valid after this.
if (index == 0) {
showNextToastLocked();
}
} finally {
Binder.restoreCallingIdentity(callingId);
}
}
}
6.1、enqueueToastロック判定
ここではなぜここに鍵をかけるのか考えてみましょう.ロックは、同時アクセスによるデータの非同期化の問題を回避するためであり、NMSはシステムサービスであり、そのオブジェクトは1つしかなく、複数のプロセス(複数のAPP)または複数のスレッドがこのサービスを取得することができ、複数のスレッドが同じオブジェクトの共有変数を操作するデータの非同期化の問題のように、そのサービスのenqueueToastメソッドを同時に呼び出すことができることを知っています.そのため、ここにはロックが必要です.またenqueueToastメソッドは、Binderスレッドプールで呼び出されるサービス側のメソッドです.
6.2、indexOfToastLockedの検査操作
現在のpagパッケージ名とcallbackがmToastQueueにすでに存在するかどうかを検出します.分析する前にまずcallbackが何なのかを知っておきましょう.以前にToast構造を作成して説明したように、1つのToastオブジェクトはmTNオブジェクトに対応します.このcallbackはこのmTNオブジェクトのエージェントオブジェクトです.なぜですか.プロセス間通信が必要であるため、mTNオブジェクトをBinderに変換して転送する必要があり、NMSでこのBinderを取得するとITransientNotificationに変換する.Proxyエージェントオブジェクト.
このメソッドは整数を返します.-1は見つかりません.他の値は見つかりました.
private int indexOfToastLocked(String pkg, ITransientNotification callback)
{
IBinder cbak = callback.asBinder();
ArrayList list = mToastQueue;
int len = list.size();
for (int i=0; i
6.3、indexOfToastLockedの検査結果に基づいて後続の操作を行う.
1.見つかった場合、戻り値が-1でない場合は、durationを直接更新し、そのToastRecordがmToastQueueの位置であることを変更しません.どういう意味ですか.ここで栗を挙げます:ここforは500回循環して、毎回toastを呼び出します.show()メソッドは、その実行結果が一度しか表示されませんが、なぜですか?前述したように、indexOfToastLockedの内部はpkgとcallbackで判断されるが、この2つの値は同じであるため、この方法で返される値は-1ではないため、show 500回でもそのmToastQueueの中にはToastRecordオブジェクトが1つしかない.
Toast toast = Toast.makeText(this, "Toast", Toast.LENGTH_SHORT);
for (int i = 0; i < 500; i++) {
toast.show();
}
2.値が-1の場合、新しいToastRecordを作成し、mToastQueueに追加する必要があります.前に得るindexは-1であるため、ここではindex=mToastQueueを再計算する必要がある.size()-1、このindexは現在新しく作成されたToastRecordのキュー内の位置を表し、この位置が0の場合はキュー内にToastRecordが1つしかないことを示し、showNextToastLockedを直接実行すればよい.
3.ここで、知っていなければ、同じpkgでのToastRecordの数がMAX_を超えていることに注意するPACKAGE_NOTIFICATIONSはそのままreturnし、DOS attacksを避けるためだと公式に明らかにした.
4.ここで次のようなことに注意してください.実行結果はtoastが50回実行され、余計なことは捨てられます.理由は上の3つ目です.しかし、このような書き方と第1点の書き方の違いは、Toastの作成タイミングが異なることです.ここではループごとに1つ作成されます.つまり、Toastの構造は1回歩きます.mTNはToast構造で作成されたもので、makeTextを呼び出すたびに新しいToastオブジェクトが作成されます.つまり、mTNも新しいオブジェクトです.NMSに渡されるmTNが異なると、indexOfToastLockedを行うと-1が返されるので、ここではこのコードの違いです.
for (int i = 0; i < 500; i++) {
Toast toast = Toast.makeText(this, "Toast", Toast.LENGTH_SHORT).show();
}
7、リモートサービス側のスケジューリング表示と非表示操作
7.1、showNextToastLocked()
この方法は主にいくつかのことをしました.
private void showNextToastLocked() {
// ToastRecord
ToastRecord record = mToastQueue.get(0);
//while RemoteException
while (record != null) {
if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);
try {
// , TN show() 。
record.callback.show();
// Toast
scheduleTimeoutLocked(record, false);
return;
} catch (RemoteException e) {
// ,
// remove it from the list and let the process die
int index = mToastQueue.indexOf(record);
if (index >= 0) {
mToastQueue.remove(index);
}
keepProcessAliveLocked(record.pid);
// ToastRecord 。
if (mToastQueue.size() > 0) {
record = mToastQueue.get(0);
} else {
// null,
record = null;
}
}
}
}
7.2、record.callback.show();
record.callbackは、ユーザプロセスがサービスプロセスに伝達するITransientNotificationのエージェントオブジェクトであり、このエージェントオブジェクトのshow()メソッドを呼び出すことによって、IPCのプロシージャであり、このプロシージャは最終的にユーザプロセスのTNにおけるshow()メソッドを呼び出す.
public void show() {
// handler post
mHandler.post(mShow);
}
final Runnable mShow = new Runnable() {
public void run() {
handleShow();
}
};
// WindowManager View
public void handleShow() {
if (localLOGV) Log.v(TAG, "HANDLE SHOW: " + this + " mView=" + mView
+ " mNextView=" + mNextView);
if (mView != mNextView) {
// remove the old view if necessary
handleHide();
mView = mNextView;
mWM = WindowManagerImpl.getDefault();
final int gravity = mGravity;
mParams.gravity = gravity;
if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
mParams.horizontalWeight = 1.0f;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
mParams.verticalWeight = 1.0f;
}
mParams.x = mX;
mParams.y = mY;
mParams.verticalMargin = mVerticalMargin;
mParams.horizontalMargin = mHorizontalMargin;
if (mView.getParent() != null) {
if (localLOGV) Log.v(TAG, "REMOVE! " + mView + " in " + this);
mWM.removeView(mView);
}
if (localLOGV) Log.v(TAG, "ADD! " + mView + " in " + this);
mWM.addView(mView, mParams);
trySendAccessibilityEvent();
}
}
7.3、 scheduleTimeoutLocked
Handlerを使用して、Toastが設定したdurationプロパティである遅延メッセージを送信します.
private void scheduleTimeoutLocked(ToastRecord r, boolean immediate)
{
Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);
long delay = immediate ? 0 : (r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY);
mHandler.removeCallbacksAndMessages(r);
mHandler.sendMessageDelayed(m, delay);
}
7.4、 handleTimeout
private final class WorkerHandler extends Handler
{
@Override
public void handleMessage(Message msg)
{
switch (msg.what)
{
case MESSAGE_TIMEOUT:
handleTimeout((ToastRecord)msg.obj);
break;
}
}
}
private void handleTimeout(ToastRecord record)
{
synchronized (mToastQueue) {
int index = indexOfToastLocked(record.pkg, record.callback);
if (index >= 0) {
cancelToastLocked(index);
}
}
}
7.5、cancelToastLocked
Handlerを介して送信される遅延タスクの時間が来た場合、アプリケーションプロセスにhideというToastを通知する必要がある.具体的な操作はやはりIPC過程を通じて本当にTNのhide方法を呼び出して本当の隠蔽操作を実現する.
操作が完了した後、キューにToastRecordがまだ実行されていないかどうかを判断し、もしあればshowNextToastLockedメソッドを呼び出して再びToastRecordを取り出して実行する.
private void cancelToastLocked(int index) {
ToastRecord record = mToastQueue.get(index);
try {
// TN hide() toast
record.callback.hide();
} catch (RemoteException e) {
Slog.w(TAG, "Object died trying to hide notification " + record.callback
+ " in package " + record.pkg);
// don't worry about this, we're about to remove it from
// the list anyway
}
mToastQueue.remove(index);
keepProcessAliveLocked(record.pid);
// showNextToastLocked 。
if (mToastQueue.size() > 0) {
// Show the next one. If the callback fails, this will remove
// it from the list, so don't assume that the list hasn't changed
// after this point.
showNextToastLocked();
}
}