原文(转载自:http://my.oschina.net/shaorongjie/blog/146566):最近遇到一个问题是如何在Android Framework中显示一个提示信息Toast。 从网上查了一下资料,但提供的都是有Activity或者Service的情况。但我的需求是要在一个没有Activity或者Service的Java文件中去显示一个Toast。怎么办呢?因为要创建一个Toast就需要Context,怎样获取一个Context呢?苦思冥想没有找到方法。无奈之下,就先找了一种临时方案。
临时方案的做法是写一个Service,在这个Service中加个提供显示Toast的接口。将这个Service注册到ServiceManager中。然后通过ServiceManager去获取这个Service,调用这个Service的接口去显示Toast。这种写法虽然有效,但感觉很不方便。因为后面有人又遇到这个问题。
后来在Android的源代码中发现ActivityThread.currentApplication()可以返回一个Application。通过Application.getApplicationContext()可以获取一个Context。看起来是可行的。但实际试的时候发现ActivityThread.currentApplication()返回为null。怎么办呢?通过分析ActivityThread的代码发现如果是在非UI thread里面调用,因为这个时候ActivityThread调用currentActivityThread返回为null.因此为null.
1 public static Application currentApplication() {
2 ActivityThread am = currentActivityThread();
3 return am != null ? am.mInitialApplication : null;
4 }
悲剧啊,刚好那支Java文件的API都是运行在非UI thread里面的。那有没有办法让这段代码运行在UI thread里面呢?
于是苦逼的继续查找,终于找到一种方法:
1.通过Looper.getMainLooper()获取到main looper。在创建一个Handler,在创建Handler的时候将main looper传递给Handler.这样就可以使这个Handler运行在UI thread中。
2.在Handler的handleMessages()中去调用ActivityThread.currentApplication()获取Application。再通过Application.getApplicationContext()获取到Context。然后创建Toast,显示。
3.通过Handler.sendMessage的方式去通知Handler显示一个Toast。
测试,大功告成。
附代码:
01 public void connect() {
02 Looper looper = Looper.getMainLooper();
03 mHandler = new Handler(looper);
04 Message message = mHandler.obtainMessage(SHOW_TOAST);
05 mHandler.sendMessage(m);
06 }
07
08 public void handleMessage(Message msg) {
09 switch(msg.what) {
10 case SHOW_TOAST:
11 showToast();
12 return;
13 }
14
15 public void showToast() {
16 Application application = ActivityThread.currentApplication();
17 Toast.makeText(application.getApplicationContext(), "test test test .....", Toast.LENGTH_SHORT).show();
18 }
由于这种做法有点小技巧,不大容易想到,因此记录在此,供大家查看。
=========================================================================================================
参考上文的方法,我修改了\android\frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindowManager.java
确实可以实现Toat显示。但直接使用上文中的showToast()方法会报错。原因是application.getApplicationContext()获得空指针。
解决只要加一下判断就可以了。
public void showToast(int i) {
Application application = ActivityThread.currentApplication();
if(i == 0){
Toast.makeText(application.getApplicationContext() == null ? mContext : application.getApplicationContext(), "AUDIO MODE change to AUDIO_HDMI", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(application.getApplicationContext() == null? mContext : application.getApplicationContext(), "AUDIO MODE change to AUDIO_CODEC", Toast.LENGTH_SHORT).show();
}
}
mContext是类中的Context变量。
临时方案的做法是写一个Service,在这个Service中加个提供显示Toast的接口。将这个Service注册到ServiceManager中。然后通过ServiceManager去获取这个Service,调用这个Service的接口去显示Toast。这种写法虽然有效,但感觉很不方便。因为后面有人又遇到这个问题。
后来在Android的源代码中发现ActivityThread.currentApplication()可以返回一个Application。通过Application.getApplicationContext()可以获取一个Context。看起来是可行的。但实际试的时候发现ActivityThread.currentApplication()返回为null。怎么办呢?通过分析ActivityThread的代码发现如果是在非UI thread里面调用,因为这个时候ActivityThread调用currentActivityThread返回为null.因此为null.
1 public static Application currentApplication() {
2 ActivityThread am = currentActivityThread();
3 return am != null ? am.mInitialApplication : null;
4 }
悲剧啊,刚好那支Java文件的API都是运行在非UI thread里面的。那有没有办法让这段代码运行在UI thread里面呢?
于是苦逼的继续查找,终于找到一种方法:
1.通过Looper.getMainLooper()获取到main looper。在创建一个Handler,在创建Handler的时候将main looper传递给Handler.这样就可以使这个Handler运行在UI thread中。
2.在Handler的handleMessages()中去调用ActivityThread.currentApplication()获取Application。再通过Application.getApplicationContext()获取到Context。然后创建Toast,显示。
3.通过Handler.sendMessage的方式去通知Handler显示一个Toast。
测试,大功告成。
附代码:
01 public void connect() {
02 Looper looper = Looper.getMainLooper();
03 mHandler = new Handler(looper);
04 Message message = mHandler.obtainMessage(SHOW_TOAST);
05 mHandler.sendMessage(m);
06 }
07
08 public void handleMessage(Message msg) {
09 switch(msg.what) {
10 case SHOW_TOAST:
11 showToast();
12 return;
13 }
14
15 public void showToast() {
16 Application application = ActivityThread.currentApplication();
17 Toast.makeText(application.getApplicationContext(), "test test test .....", Toast.LENGTH_SHORT).show();
18 }
由于这种做法有点小技巧,不大容易想到,因此记录在此,供大家查看。
=========================================================================================================
参考上文的方法,我修改了\android\frameworks\base\policy\src\com\android\internal\policy\impl\PhoneWindowManager.java
确实可以实现Toat显示。但直接使用上文中的showToast()方法会报错。原因是application.getApplicationContext()获得空指针。
解决只要加一下判断就可以了。
public void showToast(int i) {
Application application = ActivityThread.currentApplication();
if(i == 0){
Toast.makeText(application.getApplicationContext() == null ? mContext : application.getApplicationContext(), "AUDIO MODE change to AUDIO_HDMI", Toast.LENGTH_SHORT).show();
}else{
Toast.makeText(application.getApplicationContext() == null? mContext : application.getApplicationContext(), "AUDIO MODE change to AUDIO_CODEC", Toast.LENGTH_SHORT).show();
}
}
mContext是类中的Context变量。