多线程在Android中的应用以及线程间的通信
来源:互联网 发布:硬盘文件夹加密软件 编辑:程序博客网 时间:2024/05/08 12:42
多线程在安卓中的应用还是挺多的,很多耗时操作,为了避免防止影响用户体验和阻塞线程,我们一般要把这些操作放在线程里面,很多人会把线程和进程弄混,其实只是两个区别很大的概念:
1.进程:安卓系统会给一个程序开启一个进程,而这个进程在程序中有一个组件开启的时候进行分配,当有组件被激活就会被分配一个进程,在已经分配了进程的情况下,再开启其他的组件是不会继续分配进程的,一个程序占有一个进程。
2.线程:线程是程序中的调度单位,进程通过调度线程来完成一系列的事情,在一个进程中可以有多个线程,每一个线程内部都跟其他并列的线程间独立分享各自的栈内存,但是所有的线程之间又共享这个大进程的堆内存。
通常我们在代码中实现多线程有两种方式:
1.实现Runnable接口。
2.继承Thread类。
//通过继承Runnable方式实现一个子线程public static class BRunnable implements Runnable{ @Override public void run() { for( int i = 0 ; i < 10 ; i ++){ System.out.println("BRunnable : "+ i); } } }
但是开启线程的唯一方式就是Thread.start();所以我们在拿到了Runnable对象之后还要把他创建Thread对象
Thread BThread = new Thread(new BRunnable()) ; BThread.start();
这样就开启了一个Runnable实现的子线程了。接下来就看看看继承Thread方式开启线程。
public static class AThread extends Thread{ String name ; public AThread(String name){ this.name = name ; } @Override public void run() { for( int i = 0 ; i < 10 ; i ++){ System.out.println(name + " "+ i); } } }//测试下运行效果public static void main(String[] args) { AThread threadA = new AThread("A") ; AThread threadB = new AThread("B") ; Thread BThread = new Thread(new BRunnable()) ; threadA.start() ;// threadB.start() ; BThread.start() ; }
从运行结果来看,虽然A线程在前面,但是B线程并没有等A执行完毕再执行自己的任务。线程之间是个并列的竞争关系,并不是阻塞进程的,他们运行在自己的进程里面。已经了解完一些基本的线程知识了,要了解安卓中的应用,我们还需要对安卓中的handler来了解一下。
handler:消息处理机制,线程间通过handler来进行消息的传递和处理。在主线程中创建一个handler对象,在线程中触发事件的时候,可以通过handler.post(runnable , delay)来处理事件,也可以用通过handler发送一个message到message queen消息队列中,handler的looper轮询器不断的轮询这个消息队列,扫描到message之后将会发送给handler的handlemessage方法进行处理,之所以需要这样是因为我们在子线程中不能修改主线程的控件,我们经常在完成一个操作之后,需要对控件进行刷新,但是UI控件都是存在于主线程的,子线程无法修改,但是用handler.post(runnable , delay)来处理是代码过于复杂且不易维护,因此我们大多数情况下都是通过handler对象的帮助进行消息的传递和处理的。
下面通过之前写过的一个歌曲扫描的例子来说明
/** * The columns choose to get from your phone */ private String[] projection = {Media._ID, Media.DISPLAY_NAME, Media.DATA, Media.ALBUM, Media.ARTIST, Media.DURATION, Media.SIZE, Media.ALBUM_ID}; private Uri contentUri = Media.EXTERNAL_CONTENT_URI; /** * A thread to scan the music in your phone */ class ScanMusicThread extends Thread { @Override public void run() { super.run(); MusicInfo musicInfo; Cursor cursor = resolver.query(contentUri, projection, where, null, sortOrder); if (cursor != null) { while (cursor.moveToNext() && IS_CONTINUE_SCAN) { String title = cursor.getString(cursor.getColumnIndex(Media.DISPLAY_NAME)); String album = cursor.getString(cursor.getColumnIndex(Media.ALBUM)); long albumID = cursor.getLong(cursor.getColumnIndex(Media.ALBUM_ID)); long id = cursor.getLong(cursor.getColumnIndex(Media._ID)); int duration = cursor.getInt(cursor.getColumnIndex(Media.DURATION)); long size = cursor.getLong(cursor.getColumnIndex(Media.SIZE)); String artist = cursor.getString(cursor.getColumnIndex(Media.ARTIST)); String path = cursor.getString(cursor.getColumnIndex(Media.DATA)); musicInfo = new MusicInfo(id, albumID, title, artist, duration, path, IS_FAVORITE); musicList.add(musicInfo); if (mHandler != null && SongScanActivity.this != null) { try { Thread.sleep(UPDATE_MUSIC_INFO_DURATION); } catch (InterruptedException e) { e.printStackTrace(); } Message msg = Message.obtain(); msg.obj = musicInfo; msg.what = UPDATE_MESSAGE; mHandler.sendMessage(msg); } } } } }
通过一个内容解析者来访问手机自身暴露数来的音乐数据,在这个访问音乐的过程中,是一个比较耗时的过程,是一个将会阻塞主线程的操作,所以我把他放在了一个线程里面,在扫描到音乐的时候,我们要通过界面来显示歌曲名和歌手来显示当前的扫描情况,所以这个涉及到了主线程UI的修改,我们在主线程创建了一个handler对象,并且重写了里面的handleMessage方法来处理。
private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case UPDATE_MESSAGE: int insertCount = 0; MusicInfo tempMusic = (MusicInfo) msg.obj; if (SongScanActivity.this != null) { songCount++; scancount_tv.setText(String.valueOf(songCount)); scannow_tv.setText(tempMusic.getTitle() + "--" + tempMusic.getPath()); //insert the music that not into database into database if (mifDao.load(tempMusic.getSongId()) == null) { daoSession.insert(tempMusic); insertCount++; } if (SweetApplication.DEBUG) { Log.i("com.cvil.debug", String.valueOf(insertCount)); } } break; } } };
还有一种比较危险的情况就是在你退出当前界面之后,这个线程并不会停止它的工作,它将继续扫描你手机里面的音乐,而如果这时候你退出了这个界面,它继续发送消息给handler,handler在进行UI的改变的时候无法找到控件导致控指针异常,这时候又涉及到了线程的控制,我们在扫描的时候, while (cursor.moveToNext() && IS_CONTINUE_SCAN)可以看到在循环的时候有这个全局变量IS_CONTINUE_SCAN的控制,只有在true的时候才继续循环,否则就退出循环,一旦推出循环,那么这个线程将结束它的工作,从而进入一种睡眠或者说杀死的状态,这时候就不会继续消息的发送以及悲剧的导致。这个变量我在onDestroy方法中进行了控制。
@Override protected void onDestroy() { super.onDestroy(); IS_CONTINUE_SCAN = false; try { Thread.sleep(UPDATE_MUSIC_INFO_DURATION);//wait for the thread stop } catch (InterruptedException e) { e.printStackTrace(); } scanMusicThread.interrupt(); //interrupt the thread to stop the work of the thread }
当界面被销毁的时候,这个成员变量将会被复制false,停止子线程中的循环。从而保证界面的安全刷新。
以下附上完整代码:
package com.huwei.sweetmusicplayer;import android.content.ContentResolver;import android.database.Cursor;import android.net.Uri;import android.os.Bundle;import android.os.Handler;import android.os.Message;import android.provider.MediaStore.Audio.Media;import android.support.v7.widget.Toolbar;import android.util.Log;import android.view.View;import android.widget.Button;import android.widget.TextView;import com.huwei.sweetmusicplayer.dao.DaoSession;import com.huwei.sweetmusicplayer.dao.MusicInfoDao;import com.huwei.sweetmusicplayer.models.MusicInfo;import org.androidannotations.annotations.AfterViews;import org.androidannotations.annotations.EActivity;import org.androidannotations.annotations.ViewById;import java.util.ArrayList;import java.util.List;@EActivity(R.layout.activity_songscan)public class SongScanActivity extends BaseActivity { private static final boolean IS_FAVORITE = false; private List<MusicInfo> musicList = new ArrayList<>(); private Uri contentUri = Media.EXTERNAL_CONTENT_URI; private ContentResolver resolver; private ScanMusicThread scanMusicThread; private int songCount = 0; /** * The columns choose to get from your phone */ private String[] projection = {Media._ID, Media.DISPLAY_NAME, Media.DATA, Media.ALBUM, Media.ARTIST, Media.DURATION, Media.SIZE, Media.ALBUM_ID}; /** * filter condition */ String where = "mime_type in ('audio/mpeg','audio/x-ms-wma') and is_music > 0"; /** * sort order */ private String sortOrder = Media.DATA; private DaoSession daoSession; private MusicInfoDao mifDao; private static final int UPDATE_MESSAGE = 0; private static final int UPDATE_MUSIC_INFO_DURATION = 200; private boolean IS_CONTINUE_SCAN = true; @ViewById TextView scannow_tv; @ViewById TextView scancount_tv; @ViewById Button scanfinish_btn; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case UPDATE_MESSAGE: int insertCount = 0; MusicInfo tempMusic = (MusicInfo) msg.obj; if (SongScanActivity.this != null) { songCount++; scancount_tv.setText(String.valueOf(songCount)); scannow_tv.setText(tempMusic.getTitle() + "--" + tempMusic.getPath()); //insert the music that not into database into database if (mifDao.load(tempMusic.getSongId()) == null) { daoSession.insert(tempMusic); insertCount++; } if (SweetApplication.DEBUG) { Log.i("com.cvil.debug", String.valueOf(insertCount)); } } break; } } }; @ViewById Toolbar toolbar; @AfterViews void init() { toolbar.setVisibility(View.VISIBLE); toolbar.setTitle("歌曲扫描"); toolbar.setNavigationIcon(R.drawable.mc_back); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { onBackPressed(); } }); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); resolver = getContentResolver(); daoSession = SweetApplication.getDaoSession(); mifDao = daoSession.getMusicInfoDao(); scanMusicThread = new ScanMusicThread(); scanMusicThread.start(); } @Override protected void onDestroy() { super.onDestroy(); IS_CONTINUE_SCAN = false; try { Thread.sleep(UPDATE_MUSIC_INFO_DURATION);//wait for the thread stop } catch (InterruptedException e) { e.printStackTrace(); } scanMusicThread.interrupt(); //interrupt the thread to stop the work of the thread } /** * A thread to scan the music in your phone */ class ScanMusicThread extends Thread { @Override public void run() { super.run(); MusicInfo musicInfo; Cursor cursor = resolver.query(contentUri, projection, where, null, sortOrder); if (cursor != null) { while (cursor.moveToNext() && IS_CONTINUE_SCAN) { String title = cursor.getString(cursor.getColumnIndex(Media.DISPLAY_NAME)); String album = cursor.getString(cursor.getColumnIndex(Media.ALBUM)); long albumID = cursor.getLong(cursor.getColumnIndex(Media.ALBUM_ID)); long id = cursor.getLong(cursor.getColumnIndex(Media._ID)); int duration = cursor.getInt(cursor.getColumnIndex(Media.DURATION)); long size = cursor.getLong(cursor.getColumnIndex(Media.SIZE)); String artist = cursor.getString(cursor.getColumnIndex(Media.ARTIST)); String path = cursor.getString(cursor.getColumnIndex(Media.DATA)); musicInfo = new MusicInfo(id, albumID, title, artist, duration, path, IS_FAVORITE); musicList.add(musicInfo); if (mHandler != null && SongScanActivity.this != null) { try { Thread.sleep(UPDATE_MUSIC_INFO_DURATION); } catch (InterruptedException e) { e.printStackTrace(); } Message msg = new Message(); msg.obj = musicInfo; msg.what = UPDATE_MESSAGE; mHandler.sendMessage(msg); } } } } }}
0 0
- 多线程在Android中的应用以及线程间的通信
- android系统中的多线程(一): 关于在android中启动线程以及线程间的交互
- android系统中的多线程(一): 关于在android中启动线程以及线程间的交互
- android系统中的多线程(一): 关于在android中启动线程以及线程间的交互
- 多线程中的线程通信以及死锁问题
- 线程池在socket通信中的应用
- Java中的多线程(三)之线程间的通信
- 【多线程】线程间的通信
- 多线程-线程间的通信
- Java笔记3 多线程<2>线程间通信-代码分析以及多线程常见方法的运用
- Android的Looper,Handler以及线程间的通信
- Android的Looper,Handler以及线程间的通信
- Android的Looper,Handler以及线程间的通信
- HttpClient在Android网络通信中的应用
- Android中的多线程的应用
- 多线程技术在VC++串口通信程序中的应用研究
- 多线程技术在VC++串口通信程序中的应用研究
- 多线程技术在VC++串口通信程序中的应用研究
- git学习(1)错误提示:fatal: remote origin already exists.
- [前端] nodejs之express框架和ejs模板引擎的入门
- Selenium WebDriver---weiwan
- 课堂笔记
- 网页的加载渲染速度分析及网页支持程度和兼容性
- 多线程在Android中的应用以及线程间的通信
- 虚数的图解
- c++字符串操作函数
- HDU 5433(Xiao Ming climbing)(费用最短路:BFS+优先队列)
- 原子操作实现
- 可扩展性的解法关于数组里面的数分类
- TCP协议三次握手
- 仿网易新闻框架
- linux线程属性、共享属性