鱼鱼Chen之学写自己的apk(五)使用Handler完成Android里的多线程操作实例

不同于java的多线程,安卓提供了一套自己的多线程操作。主要是通过Handler类和Message类来完成。天天吃外卖,突然看到美团外卖的listview上有这么个可以自动切换的广告,于是就做这么个效果。(虽然我不明白为什么它是按照1-3-2的顺序的。。。。。。)

一、分析一下结构

这里用到的ViewPager组件就不多讲了,详细的参考

鱼鱼Chen之学写自己的apk(一) ViewPager实现第一次使用的引导

http://blog.csdn.net/zerolovesc1993/article/details/45066993

MyTransformer是一个自定义的类,满足接口ViewPager.PageTransformer


二、关于ViewPager的切换动画

这个在之前没有提到(因为鄙人的疏忽)。这个是ViewPager中实现切换动画的方法,只要在主类中加一句

viewPager.setPageTransformer(true, new MyTransformer());即可

然后是我们的自定义类

public class MyTransformer implements ViewPager.PageTransformer {
	private final static float MIN_SCALE = 0.75f;

	@Override
	public void transformPage(View view, float position) {
		int pageWidth = view.getWidth();

		if (position < -1) { // [-Infinity,-1)
			// This page is way off-screen to the left.
			view.setAlpha(0);

		} else if (position <= 0) { // [-1,0]
			// Use the default slide transition when moving to the left page
			view.setAlpha(1);
			view.setTranslationX(0);
			view.setScaleX(1);
			view.setScaleY(1);

		} else if (position <= 1) { // (0,1]
			// Fade the page out.
			view.setAlpha(1 - position);

			// Counteract the default slide transition
			view.setTranslationX(pageWidth * -position);

			// Scale the page down (between MIN_SCALE and 1)
			float scaleFactor = MIN_SCALE + (1 - MIN_SCALE)
					* (1 - Math.abs(position));
			view.setScaleX(scaleFactor);
			view.setScaleY(scaleFactor);

		} else { // (1,+Infinity]
			// This page is way off-screen to the right.
			view.setAlpha(0);
		}
	}

}
这边我是直接复制了一个Google提供好的。动画类不是很熟悉,没怎么写过。但是几个属性还是可以提一下的

Scale是缩放,Translation是位移,Alpha是透明度。这些都很好理解,关于float  position这个变量我想提一提。

[-1,1]其实就是你滑动时,显示的两个图片,右边为(0,1],左边为[-1,0),而左边的完全不可视的就是<-1。同理,右边的不可视的为>1
举个例子,如果此时2显示着,我们右滑,2对应的就是[-1,0),而3对应的是(0,1]。1,4分别对应<-1,>1的情况。此时float  position随右滑变大。反之,左滑的同理,只不过float position随左滑变小。


三、引入Handler类

首先,简单讲一下知识点。因为纯粹是个人的理解,所以讲的可能不够专业化,但是肯定很形象~~最好是再百度将理论补全。首先,你的所有耗时操作都应该新建一个子线程去执行,放在主线程里会造成ANR(应用无响应),我相信大家的手机都出过这个情况。不过,请记住,在支线是不能更新UI的。比如,你在子线程里写textview.settext("");是会报错的,因为这是不安全的(google规定的,虽然我不懂为什么不安全)。所以要想通过子线程来更新UI怎么办呢?

主线程提供一个Handler类,接受Message类的对象,通过辨识对象的what属性来选择行为。而你需要耗时的行为,放在子线程中执行,完成后发送一个对应what的消息即可。还有一个就是Message发送后会放在一个looper里(消息队列)handler会按照顺序处理

好,简单提了一下相关的知识点,让我们来看一下代码。这边为了方便使用,我直接使用了匿名类。

先上一些声明

private int index = 0;
	public Handler mHandler;
	private Thread mThread;
	private final static int START_UPDATA = 1;

以下主线程的代码

@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		init();
		initItem();
		initDoc();
		mHandler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				super.handleMessage(msg);
				switch (msg.what) {
				case START_UPDATA:
					viewPager.setCurrentItem(index);
					break;

				default:
					break;
				}
			}
		};
	}
接着是自定义的子线程
class ChangeThread extends Thread implements Runnable {
		@Override
		public void run() {
			super.run();
			synchronized (MainActivity.class) {
				try {
					Log.i("Tips", "开始了!");
					do {
						Thread.sleep(2000);
						index++;
						if (index == 3) {
							index = 0;
						}
						Message message = new Message();
						message.what = START_UPDATA;
						mHandler.sendMessage(message);
					} while (MainActivity.ChangeThread.interrupted() == false);
				} catch (Exception e) {
					// TODO: handle exception
				} finally {
					Log.i("Tips", "结束了");
				}
			}
		}
	}
好了,我来解释一下。首先,我的作用是每隔2s切换ViewPager来实现循环播放。过2s就封装一个Message对象,并把what设为我们指定的值,并设置好index。接着我们在匿名类里重写handemessage方法,处理消息,接受到对应what消息时将viewpager的page设为指定index。

这边有个要注意的地方,我们循环的条件用(MainActivity.ChangeThread.interrupted() == false)是一种合理的选择,为什么呢?如果要停止线程,thread对象的stop是不安全的,用interrupt方法才是最佳,所以这边这么写。

写到这边自然还没有结束,我们还要启动线程呢!

mThread = new ChangeThread();
			mThread.start();
写一个Button,把这两句放入就可以了!


四、思考

之前写自己的app的时候也就用到了这么多,当时可能就一带而过了吧。现在会用了就想到了许多别的问题,花了一些时间解决了。在这里也分享一下,希望给对遇到同样问题的朋友点参考。

1·、多个子线程开启

多次点击,发现刚刚写好的代码会有bug,切换速度在变快。很简单,每次点击都会有一个新的ChangeThread对象执行了!于是,想到了用synchronized,用来保证同时只有一个运行,剩下的会排队,直到前一个完成。

	synchronized (MainActivity.class)
把子线程的方法封入其中即可,意思为一次只能让MainActivity类同时运行一个ChangeThread。一开始我尝试填入

synchronized (this),然后出现ANR了。仔细想了想,我也不确定对不对。前一种写法的话,thread会生成,只是去排队了。所以不会造成阻塞。而后一种写法,只有在前一个thread完成后,才开始生成,所以会阻塞!

2、停止多个线程

停止线程的话,直接用thread.interrupt即可。但是我这边又遇到了别的问题,当我多次点击开始,启动了多个线程对象时,没法关掉了。。。。。怎么办呢?我想了个办法。

新建一个list集合,放入线程对象

private List<Thread> threads;
每次thread.start时,将其放入集合。这样,多次点击,我只要遍历集合,并interrupt即可

添加部分:

mThread = new ChangeThread();
			mThread.start();
			threads.add(mThread);
去除部分:
if (threads.size() > 0) {
				Thread currentThread = threads.get(0);
				currentThread.interrupt();
				threads.remove(currentThread);
稍微加了一个修改text的代码。完整的MainActivity如下:

public class MainActivity extends Activity implements OnPageChangeListener,
		OnClickListener {
	private ViewPager viewPager;
	private List<View> picsList;
	private List<Thread> threads;
	private ImageView[] point;
	private MyPageAdapter adapter;
	private int currentIndex = 0;
	private Button btnStart, btnEnd;
	private int index = 0;
	public Handler mHandler;
	private Thread mThread;
	private final static int START_UPDATA = 1;
	private int[] pics = { R.drawable.no280, R.drawable.no281, R.drawable.no282 };

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		init();
		initItem();
		initDoc();
		mHandler = new Handler() {
			@Override
			public void handleMessage(Message msg) {
				super.handleMessage(msg);
				switch (msg.what) {
				case START_UPDATA:
					viewPager.setCurrentItem(index);
					break;

				default:
					break;
				}
			}
		};
	}

	private void init() {
		viewPager = (ViewPager) findViewById(R.id.viewPager);
		picsList = new ArrayList<View>();
		threads = new ArrayList<Thread>();
		adapter = new MyPageAdapter(picsList);
		btnStart = (Button) findViewById(R.id.btnStart);
		btnEnd = (Button) findViewById(R.id.btnEnd);
		btnStart.setOnClickListener(this);
		btnEnd.setOnClickListener(this);
	}

	private void initItem() {
		LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
				LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		for (int i = 0; i < pics.length; i++) {
			ImageView imageView = new ImageView(this);
			imageView.setLayoutParams(params);
			imageView.setImageResource(pics[i]);
			picsList.add(imageView);
		}
		viewPager.setAdapter(adapter);
		viewPager.setPageTransformer(true, new MyTransformer());
		viewPager.setOnPageChangeListener(this);
	}

	private void initDoc() {
		LinearLayout linearLayout = (LinearLayout) findViewById(R.id.linearLayout_point);
		point = new ImageView[pics.length];
		for (int i = 0; i < pics.length; i++) {
			point[i] = (ImageView) linearLayout.getChildAt(i);
			point[i].setEnabled(true);
		}
		point[currentIndex].setEnabled(false);
	}

	private void setcurrentDoc(int position) {
		point[position].setEnabled(false);
		point[currentIndex].setEnabled(true);
		currentIndex = position;
	}

	@Override
	public void onPageScrollStateChanged(int arg0) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onPageScrolled(int arg0, float arg1, int arg2) {
		// TODO Auto-generated method stub

	}

	@Override
	public void onPageSelected(int position) {
		setcurrentDoc(position);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.btnStart:
			mThread = new ChangeThread();
			mThread.start();
			threads.add(mThread);
			if (threads.size() > 1) {
				btnEnd.setText("结束(还有" + (threads.size() - 1) + "条线程等待开始)");
			} else {
				btnEnd.setText("结束");
			}
			break;
		case R.id.btnEnd:
			if (threads.size() > 0) {
				Thread currentThread = threads.get(0);
				currentThread.interrupt();
				threads.remove(currentThread);
				if (threads.size() > 1) {
					btnEnd.setText("结束(还有" + (threads.size() - 1) + "条线程等待开始)");
				} else {
					btnEnd.setText("结束");
				}
			}
			break;

		default:
			break;
		}
	}

	class ChangeThread extends Thread implements Runnable {
		@Override
		public void run() {
			super.run();
			synchronized (MainActivity.class) {
				try {
					Log.i("Tips", "开始了!");
					do {
						Thread.sleep(2000);
						index++;
						if (index == 3) {
							index = 0;
						}
						Message message = new Message();
						message.what = START_UPDATA;
						mHandler.sendMessage(message);
					} while (MainActivity.ChangeThread.interrupted() == false);
				} catch (Exception e) {
					// TODO: handle exception
				} finally {
					Log.i("Tips", "结束了");
				}
			}
		}
	}
}
老样子,百度云链接:http://pan.baidu.com/s/1bn96GUV






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值