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

来源:互联网 发布:淘宝账号怎么转化卖家 编辑:程序博客网 时间:2024/05/21 17:31

不同于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;@Overridepublic 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 pageview.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 transitionview.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;

以下主线程的代码

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();initItem();initDoc();mHandler = new Handler() {@Overridepublic 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 {@Overridepublic 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 };@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);init();initItem();initDoc();mHandler = new Handler() {@Overridepublic 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;}@Overridepublic void onPageScrollStateChanged(int arg0) {// TODO Auto-generated method stub}@Overridepublic void onPageScrolled(int arg0, float arg1, int arg2) {// TODO Auto-generated method stub}@Overridepublic void onPageSelected(int position) {setcurrentDoc(position);}@Overridepublic 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 {@Overridepublic 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
原创粉丝点击