今天在做一个运动的小球的安卓项目的时候,我尝试着在子线程中刷新ui。众所周知,子线程是线程中不安全的,这个不安全不是说不能在子线程里面更新UI,这个很复杂,取决于你的主线程处理的时间,子线程就是子线程,主线程就是主线程,不必太纠结,碰到一个问题,要善于去寻找他有价值的地方。在子线程中也可以用handler,不过这需要开启一个循环队列looper,并且用looperloop()方法,这主要是为了能够在子线程中创建handler,但是问题来了,loop()里面是一个循环,但是为了刷新,线程里面又是一个循环,两者不可同时进行,所以这样的方法handleMessage不会被执行,逻辑上不是很好。逻辑上出现了问题。
package wust.edu.ball;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.View;
public class MoveActivity extends Activity {
Handler handler=null;
Thread t;//放在外面。这样比较好。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MyView mv=new MyView(MoveActivity.this);
setContentView(mv);
}
@Override
protected void onStop() {
// TODO Auto-generated method stu
super.onStop();
System.out.println("OnDestroy有没有被执行");
t.stop();
t.interrupt();
t=null;
}
class MyView extends View implements Runnable{
private int x=20,y=20;
public MyView(Context context) {//应用的上下文。
super(context);
setFocusable(true);
t=new Thread(this);
t.start();
// TODO Auto-generated constructor stub
}
@Override
public void run() {
System.out.println("线程有没有被执行");
Looper.prepare();
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
System.out.println("handler方法有没有执行");
if(msg.what==0x11){
MyView.this.update();//不要局限于这个,你可以取得他的引用的
MyView.this.invalidate();
}
super.handleMessage(msg);
}
};
while(!Thread.currentThread().isInterrupted()){
System.out.println("线程里面的方法有没有被执行");
Message m=Message.obtain();
m.what=0x11;
handler.sendMessage(m);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}//已经进入循环
}
Looper.loop();//监视循环队列。这里又是一个循环
}
protected void update() {
// TODO Auto-generated method stub
System.out.println("update方法被执行了");
int h=MyView.this.getHeight();
y+=5;
if(y>=h){
y=20;
}
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
Paint p=new Paint();
p.setColor(Color.RED);
canvas.drawCircle(x,y,15,p);
}
}
}
接下来。在一个网友的帮助下寻找到了正确的方法,就是最常规的方法,在主线程中更新ui,接下来看代码。
Handler mHandler = new Handler() {//主线程里面
@Override
public void handleMessage(Message msg) {
// TODO Auto-generated method stub
super.handleMessage(msg);
if (msg.what == 0x11) {
mv.update();//交给主线程去处理。
mv.invalidate();
}
}
};
private MyView mv;//持有类的(也可以是内部类的一个)对象。我老是不知道invalidate在那里面执行
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mv = new MyView(MoveActivity.this);
setContentView(mv);
new Thread() {//就是子线程,不要去纠结。
public void run() {
while (!Thread.currentThread().isInterrupted()) {
Message m = new Message();
m.what = 0x11;
mHandler.sendMessage(m);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
};
}.start();
}
class MyView extends View {
private int x = 20, y = 20;
public MyView(Context context) {// 应用的上下文。
super(context);
setFocusable(true);
// TODO Auto-generated constructor stub
}
protected void update() {
// TODO Auto-generated method stub
int h = MyView.this.getHeight();
int w = MyView.this.getWidth();
y += 5;
x += 5;
if (y >= h||x>=w) {
y = 20;
x = 20;
}
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
System.out.println("这个方法被执行了几次");
super.onDraw(canvas);
Paint p = new Paint();
p.setColor(Color.RED);
canvas.drawCircle(x, y, 15, p);
}
}
}
这里要注意它的一个方法,也是在面向对象中比较常见的一个方法,持有类的引用,这里最关键的是,他吧内部类Myview的引用申明为了全局变量,这样的话就可以访问它的update和invalidate方法了,这个思路比较好的,面向对象的核心。
总之还是那句话,在安卓里面,主线程是线程中不安全的,一般来说不允许在子线程中更新ui。但是还是要灵活解决。