鱼鱼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
- 鱼鱼Chen之学写自己的apk(五)使用Handler完成Android里的多线程操作实例
- 鱼鱼Chen之学写自己的apk(一) ViewPager实现第一次使用的引导
- 鱼鱼Chen之学写自己的apk(二) 使用SlidingPaneLayout实现漂亮的布局
- 鱼鱼Chen之学写自己的apk(八)使用ViewPager和Fragment实现流行的底部导航
- 鱼鱼Chen之学写自己的apk(三) 自定义漂亮的Spinner
- 鱼鱼Chen之学写自己的apk(七) 数据库的实际运用
- 鱼鱼Chen之学写自己的apk(六)ListView带动画图标
- 鱼鱼Chen之学写自己的apk(四)灵活使用EditText的hint属性以及使用自定义的美观AlertDialog
- android多线程使用实例之AsynTask和handler的使用
- 处女男学Android(五)---Handler完结篇之Handler的post方法详解
- 06 Android里的多线程及Handler通信
- Android学习笔记(五)——handler的使用
- java 多线程实例,自己写的第一个例子,理解什么是多线程,怎么写多线程
- Android之handler的使用
- android之Handler的使用
- Android之Handler的使用
- 获取Android自己写好了的apk以及反编译
- 获取Android自己写好了的apk以及反编译
- PL/SQL -->隐式游标(SQL%FOUND)
- PageAdmin手机网站制作教程
- Lua笔记:播放粒子特效应用
- 安装 MariaDB gelera cluster
- 以太网没有有效IP配置问题
- 鱼鱼Chen之学写自己的apk(五)使用Handler完成Android里的多线程操作实例
- 类与类之间的几种关系
- 二叉树的深度优先遍历以及广度优先遍历
- 设置session失效时间的三种方法
- DWR搭建以及使用教程
- Lua笔记:帧动画及CocosStudio动画应用
- Java多线程基础(二)定时器类:Timer类和TimerTask类
- drupal 简介
- 黑马程序员_JAVA IO 流 基本概述