安卓编程技巧总结(3) 进程与线程处理


  1. 多进程
安卓支持多进程
好处:
-- 减轻APP内存压力;
-- 逻辑相对独立;例如我们可以使Service在独立进程中执行数据计算处理操作,
   处理的结果与主进程通过AIDL通信;
缺点:
-- 首次进入新启动进程的页面时会有延时的现象,如闪屏,一般与Activity 的主题有关;
-- Application 被实例化多次,应考虑其中定义的一些参数是否合理;
   可以在Application中加进程的逻辑判断,在指定的进程中,才初始化对应的数据;  
-- 多进程间通过 SharedPreferences 共享数据时不稳定;
-- APP退出操作不能完全退出,仅退出了当前进程;
  1. 线程的管理
单个线程的创建,比较简单,通用的方法是:实现Runnable接口或者继承Thread类。
但是,这种方式创建的线程,不应该任意使用,应对创建的线程做好管理,
例如:线程缓存、顺序执行、最大并发数等等;

Java提供的一套线程管理的方案:线程池(ThreadPoolExecutor),
关于线程池的使用,Java默认给封装了几个类型,都可以通过Executors直接创建,例如:
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
线程池类型:
FixedThreadPool:固定大小的线程池,可解决最大并发数;
SingleThreadPool:单一线程池,可保证线程一个个执行;
CachedThreadPool:缓存线程池,减少了线程的重用性,提高了性能;
ScheduledThreadPool:定时线程池,可定时、延时执行线程;

具体的使用,我们需要根据特定场合,如果使用不当,也会有一些问题:
FixedThreadPool 和 SingleThreadPool : 
允许的请求队列长度过大,可能会堆积大量的请求,从而导致 OOM;
CachedThreadPool 和 ScheduledThreadPool : 
允许的创建线程数量过大,可能会创建大量的线程,从而导致 OOM。

其实,我们还可以通过new ThreadPoolExecutor(),构建自定义的线程池,自己定义的规则,
如何管控线程,自己也清楚,如下:
int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();//最大可用的处理器数量
int KEEP_ALIVE_TIME = 1;//  设置线程存活时间(setKeepAliveTime),确保空闲时线程能被释放
TimeUnit KEEP_ALIVE_TIME_UNIT = TimeUnit.SECONDS;
BlockingQueue taskQueue = new LinkedBlockingQueue();//线程队列,存放线程
ExecutorService executorService = new ThreadPoolExecutor(NUMBER_OF_CORES,NUMBER_OF_CORES*2, 
KEEP_ALIVE_TIME, KEEP_ALIVE_TIME_UNIT,taskQueue, new BackgroundThreadFactory(), new DefaultRejectedExecutionHandler());

Android中和线程、线程池相关的还有IntentService、AsyncTask、SurfaceView等,此处不再累述;
  1. 线程使用
子线程中不能直接更新界面,更新界面必须在UI线程中进行。

线程间通信Handler,可以自定义接口回调,也可使用EventBus等代替,
各有优缺点,自己根据情况合理使用。

注意:Handler不要通过 Msg 传递大的对象,会导致内存问题。
  1. 线程锁
作用:确保数据同步执行,保证数据的原子性。
线程锁类型,按使用方式可分为:可重入锁、不可重入锁(自旋锁)、公平锁、非公平锁、读写锁。
显示锁用Lock来定义、内置锁用syschronized
Lock接口,主要定义方法:
lock();
unLock();
tryLock();
lockInterrupt();
newCondition();

可重入锁(已获得某对象、代码库的锁后,可直接访问它的其他同步方法):
syschronized、reentraintLock
-- synchronized编码较简单;
-- ReentrantLock功能更丰富一些,如时间锁等候,可中断锁等候,锁投票等,还可以定义多个条件;
-- ReentrantLock必须在finally中释放锁;
-- ReentrantLock 的性能比synchronized会好点;

自旋锁(自旋锁可以使线程在没有取得锁的时候,不被挂起,而转去执行一个空循环,
(即所谓的自旋,就是自己执行空循环),若在若干个空循环后,线程如果可以获得锁,则继续执行。)

公平锁(排队竞争)
-- 构造ReentrantLock (true)时,表示为公平锁;
非公平锁(非排队竞争)
-- 构造ReentrantLock ()时,表示为非公平锁;

读写锁
允许多个读操作同时进行,但每次只允许一个写操作。
读写锁是一种性能优化的策略。
--ReentrantReadWriteLock
  1. 进程使用
-- 官方不推荐 在多 进 程 之 间 用 SharedPreferences 共 享数 据 ;
-- 尽量减少不同 APP 之间的进程间通信及拉起行为。拉起导致占用系统资源,影响用户体验。
  1. 进程间通信时注意URI传递
7.0之后,安卓安全性提高,APP之间传递文件路径时,应注意适配,
防止出现FileUriExposedException异常;

具体请参考:
https://www.jianshu.com/p/bec4497c2a63

7.带返回值的异步任务Callable

其实,除Runnable接口外,Callable泛型参数化接口也可以实现异步任务。

Runnable与Callable不同点:
1. Runnable不返回任务执行结果,Callable可返回任务执行结果;
2. Callable在任务无法计算结果时抛出异常,而Runnable不能;
3. Runnable任务可直接由Thread的start方法或ExecutorService的submit方法去执行;
Callable只通过ExecutorService的submit方法去执行;

ExecutorService的submit方法返回FutureTask对象,要获得Callable接口返回的执行结果,
需要调用FutureTask.get()方法实现,此方法会阻塞主线程直到获取‘将来’结果;
当不调用此方法时,主线程不会阻塞!

Callable示例:查找文件包含某关键字总个数:每个文件启动一个线程查找关键字

public class FileSearchTask {
    public static void main(String[] args) throws ExecutionException, 
InterruptedException {
        String path = args[0];
        String keyword = args[1];
        int c = 0;
        File[] files = new File(path).listFiles();
        ArrayList> rs = new ArrayList<>();
        for(File file: files){  //每个文件启动一个task去查找
            MatchCount count = new MatchCount();
            count.file = file;
            count.keyword = keyword;
            FutureTask task = new FutureTask(count);
            rs.add(task); //将任务返回的结果添加到集合中
            Thread thread = new Thread(task);
            thread.start();
        }

        for(Future f: rs){
            c += f.get(); //迭代返回结果并累加,会阻塞线程,直到结果返回;
        }
        System.out.println("包含关键字的总文件数为:" + c);
    }
}

class  MatchCount implements Callable{
    public File file;
    public String keyword;
    private  Integer count = 0;

    public Integer call() throws Exception {   //call封装线程所需做的任务
        if(search(file))
              count ++;
        return count;
    }

    public boolean search(File file){
        boolean founded = false;
        try(Scanner scanner = new Scanner(new FileInputStream(file))){
            while(!founded && scanner.hasNextLine()){
                if (scanner.nextLine().contains(keyword))
                    founded = true;
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
        return  founded;
    }
}

第一篇: 安卓编程技巧总结(1) 资源与UI布局处理

https://www.jianshu.com/p/ff97b15d5c9d

第二篇: 安卓编程技巧总结(2) 基础组件开发

https://www.jianshu.com/p/b05752377887

第三篇:安卓编程技巧总结(3) 进程与线程处理

https://www.jianshu.com/p/7d05c8a368bd

第四篇:安卓编程技巧总结(4) 数据文件处理

https://www.jianshu.com/p/0515df3b697d

第五篇:安卓编程技巧总结(5) 图片处理

https://www.jianshu.com/p/76690b2ba310

第六篇:安卓编程技巧总结(6) APP安全分析

https://www.jianshu.com/p/4347ff392122

第七篇:安卓编程技巧总结(7) 性能检测代码分析

https://www.jianshu.com/p/687f3c641408

你可能感兴趣的:(安卓编程技巧总结(3) 进程与线程处理)