刚开始学习android的同学们,都应该知道Log,和Toast这两个类,因为这两个类经常用来,给我查看代码的运行情况,所以有些时候,就会导致用起来混淆的情况,Log的话,没什么限制,在任何的方法下都可以使用,当然使用Log的时候,记得标记TAG,不要直接写“TAG”,一般情况下,直接下类名的,这样的话,就方便将来去找相应的Log,当然,你也可以给Log加一个开关,当你的项目完成,log开关一关,那么所有的Log就消失的无影无踪了。
Toast,一般在主线程使用的时候,没有多大的问题,可是一旦到子线程中,就有可能报错。那么这是什么原因呢?
那么咱们就来看看Toast的源码
public Toast(Context context) {
mContext = context;
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);
}
咋一看,什么玩意,就弄俩对象,第一个没啥可讲的,第二TN类,什么破类,咱们接着点。。。
private static class TN extends ITransientNotification.Stub {
....
看这里 ***final Handler mHandler = new Handler();***
....
}
Handler!眼前一亮,我们都知道Handler的使用,需要和Loop配合,假如在主线程中,那么Handler是直接可以new的,因为主线程中自带的有Loop,而普通的线程是没有Loop的(如果你不知道,那么你现在应该知道了),所以Toast在主线程使用的时候,就不会报错,那么为啥子线程中就会报错呢?
咱么继续点Handler
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
有木有,大家看到了这个没有
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
如果Looper为null的时候,就会抛出这个异常,前面咱们也说了,普通线程是不会创建Loop的(除了主线程),所有Loop自然就是null的,所以子线程中使用Toast就会报错!
既然找到了原因,那么就有了解决的方法:
1、既然在主线程中使用,不会报错,那么咱们就在主线程使用呗!
public class MainActivity extends Activity {
private static final int TAG_TOAST = 0;
private Handler mHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case TAG_TOAST:
Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onClick(View view){
new Thread(){
public void run() {
Message _Message = mHandler.obtainMessage();
_Message.what = TAG_TOAST;
_Message.obj = "在子线程中Toast!";
mHandler.sendMessage(_Message);
};
}.start();
}
}
2、既然子线程中没有Loop,那么咱们给他创建一个不就得了嘛,开始干!
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onClick(View view){
new Thread(){
public void run() {
Looper.prepare();
Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show();
Looper.loop();
Toast.makeText(MainActivity.this, msg.obj.toString(), Toast.LENGTH_SHORT).show();
};
}.start();
}
}
你猜猜上面的子线程中会Toast的几次,两次,哈哈,错了,一次,因为第二种方法有个缺点就是Looper.loop();后面的方法得不到执行,当然也可以调用Looper.myLooper().quit();推出Looper,那么后面的方法就会继续执行了。