多线程在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
原创粉丝点击