【笔记】【从Android Guide温习Android 一】进程和线程(Process And thread)

 

【笔记】【从Android Guide温习Android 一】进程和线程(Process And thread)

前言

  • 最近半年多的时间都在写iOS,Android很多东西都在忘却中。脑容量不足哇。

  • 虽然做了2年多的android开发,但基础知识总感觉不是很牢固。

基于以上两点,打算重新整理一下Android知识。从Android Guide入手。

对于读者,非常感谢抽出时间来看我的笔记,若有什么错误请指正。有什么意见可以私信我。

虽然从官方的Android Guide开始。

但说实话。我觉得官方文档并不适合新人(作为新人,我更喜欢iOS的文档,当然也有可能我有android经验。思路会类比一下感觉很简单)。

若需要明白个8 9分的东西需要你具备一定的Android项目开发经验才行。

一切从这里开始

当我们启动应用的最开始,先创建了一个linux进程和一个主线程,根据应用需要在创建了其他进程和线程。然后所有的操作化作指令,逐一交付给线程执行。

来自Android Guide - Processes and Threads
When an application component starts and the application does not have any other components running, the Android system starts a new Linux process for the application with a single thread of execution. By default, all components of the same application run in the same process and thread (called the "main" thread). If an application component starts and there already exists a process for that application (because another component from the application exists), then the component is started within that process and uses the same thread of execution. However, you can arrange for different components in your application to run in separate processes, and you can create additional threads for any process.
大致如下
  • 当启动app是,若当前没有该app的组建(components)运行,那么会创建一个linux进程。

  • 默认所有的组建都运行在同一个进程和线程(主线程)。

  • 如果这个组建启动时,要启动的进程已经存在,则执行这个组建在这个进程的主线程中。

  • 你可以创建不同的进程和线程。

组建(components)是指: activity, service, receiver, provider

进程(Process)

可以通过设置AndroidManifest.xml中的组建属性"android:process"来确定运行在其他进程。

<service android:name=".demo.service.remind.TYRemindServiceImpl"
         android:process="remindservice" /> <!-- remindservice -->
<service android:name=".demo.service.remind.TYRemindServiceImpl"
         android:process=":remindservice" /> <!-- :的作用:使得该线程名为包名:remindservice -->

生命周期

  • 进程等级

    以下是由高等级到低等级的顺序列出所有的进程等级以及他们的判定条件。

    • Foreground process -- 前台进程

      • Activity: 已经调用onResume()之后

      • Service: 已经绑定, 或设置foreground, 或正在执行生命周期的回调函数(onCreate,onStart,onDestory).

        这里官方文档没提及onStartCommand,但是在Service中却有体提到.

      • BroadcastReceiver: 正在执行onReceive

    • Visible process -- 可见进程

      当一个进程没有前台进程,但又正显示在屏幕上:

      • Activity: 当前Activity是前台进程并执行onPause方法。

      • Service: 已与前台Activity绑定.

    • Service process -- 服务进程

      正在运行的Service,在通过 startService() 方法启动的Service(由Service可知,并不符合前两个等级)。

    • Background process -- 后台进程

      Activity: 已执行onStop()后.该进程等级的判定降低为后台进程等级。

      后台进程对于用户来说是不可见的,终结进程也不会影响用户体验。

      被终结之后,若正确使用状态恢复(详见Activity),用户想要通过"最近访问"重启的时将会和之前的一样。

    • Empty process -- 空进程

      没有任何活动组建时。该进程等级为最低。

      存在的意义仅仅是加速再次启动的时间。

      这类进程会经常被终结。

  • 如何判定进程的等级

    该进程的等级判定受下面两点影响

    • 由所包含的组建的最高等级而定。例如:

      一个进程中:Activity为前台进程等级,并且也运行了一个Service,该Service符合服务进程等级。那该进程的等级为前台进程等级。

    • 依赖进程而定。例如:

      ContentProvider正在为另一进程服务,或Service与另一进程绑定,那前者的等级总是比后者高。

  • 进程终结时机及策略

    • 当系统内存紧张时,系统选择终结进程的顺序是根据等级由低到高执行。

    • 空进程会经常被回收,Service process以上等级的系统会尽量保证存活

耗时工作的选择

  • 由于服务进程等级(Service process)要高于后台进程(Background process)等级。

    所以当Activity进入后台时,选择Service处理耗时操作要比使用工作线程要好(如AsyncTask)。

    此时至少为Service的进程等级。而且不用考虑Activity是否被销毁。

  • 当BoardCastReceiver执行onReceive的时候,若有耗时操作。则应开启Service而不是选择创建其他线程(如AsyncTask)。

线程(Thread)

主线程(Main Thread or UI Thread)

  • 启动应用时,会创建及启动主线程(Main Thread),或称UI线程(UI Thread).

  • 主线程用于分发用户事件(Touch event, drawing event)等。

  • 所有的组建都运行在主线程中。

  • Andoid UI toolkit(属于android.widget 和 android.view包下的所有类) 均非线程安全。必须在主线程中使用。

  • 主线程中不要耗时操作,会阻塞用户行为的响应(点击,刷新页面),从而导致ANR的出现

工作线程(Worker Thread or Background Thread)

工作线程这个名字一开始让我很困扰。以为是什么特殊的线程。其实没什么,只是相对于主线程的一种说法。

  • 耗时操作要在这里进行。

  • 若需要刷新UI控件等涉及影响主线程的地方。可以通过一下策略解决

    • View.post(Runnable) 和 View.postDelayed(Runnable, long) //仅针对View

    • Activity.runOnUiThread(Runnable) //随时使用

    • Handler //由于Handler依赖于创建时的线程,所以若主线程刷新。需要在主线程创建Handler,并在工作线程中发消息。

    • AsyncTask // 这个不多说。详见官方文档。

    • IntentService // 见其他文档。

下面是例子,均完成相同的操作,工作线程下载图片,通知主线程显示图片。来自官方文档

// 错误例子
new Thread(new Runnable() {
    public void run() {
        Bitmap b = loadImageFromNetwork("http://example.com/image.png");

        mImageView.setImageBitmap(b); // 这是错误的,违反"综述"中的第4条
       }
}).start();

// 使用Activity
Activity activity = this;// 假设获取Activity实例

new Thread(new Runnable() {
    public void run() {
        Bitmap b = loadImageFromNetwork("http://example.com/image.png");

        activity.runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mImageView.setImageBitmap(b); // 这是错误的,违反"综述"中的第4条
            }
        });
    }
}).start();   

// 使用Post
public void onClick(View v) {
    new Thread(new Runnable() {
        public void run() {
            final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
            mImageView.post(new Runnable() {
                public void run() {
                    mImageView.setImageBitmap(bitmap);
                }
            });
        }
    }).start();
}

// 使用AsyncTask

public void onClick(View v) {
    new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
    /** The system calls this to perform work in a worker thread and
      * delivers it the parameters given to AsyncTask.execute() */
    protected Bitmap doInBackground(String... urls) {
        return loadImageFromNetwork(urls[0]);
    }

    /** The system calls this to perform work in the UI thread and delivers
      * the result from doInBackground() */
    protected void onPostExecute(Bitmap result) {
        mImageView.setImageBitmap(result);
    }
}

// 使用Handler
// 在主线程创建Handler实例
private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        mImageView.setImageBitmap(msg.obj);
    }
};

new Thread(new Runnable() {
    public void run() {
        final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
        Message msg = handler.obtainMessage(0, bitmap);
        handler.sendMessage(msg);
    }
}).start();

线程安全

总的来说,当你使用多线程或多进程时,数据通讯是比较繁琐且易出错的工作。

但你可以使用Android的一些本地化特性,可以帮你省去很多工作,比如官网举两点的例子。

  • Service的onBind()方法,此方法执行在主线程,而返回的对象将在线程池中被调用。

  • ContentProvider和ContentResolver同样隐藏了如何管理进程间通讯(IPC),入口看上去仅仅是insert(),delete()等操作。但你可以在任意线程里使用。

IPC(进程间通讯)

官网仅提及了Service 通过绑定进行通讯。使用Messenger.这部分以后在做介绍。

总结

  • 了解进程的生命周期很重要,比如重要的耗时工作放在Service中要比AsyncTask要安全。

  • 对于耗时操作,Android提供了多重特性可以应对。而且使用简单。

  • 涉及多线程,多进程的通讯,利用特性(Handler,IBinder) 可以简单安全的解决。

  • 数据存储。使用ContentProvider要优于存文件,不用考虑线程安全。

【笔记】【从Android Guide温习Android 一】进程和线程(Process And thread)

上一篇:小程序里使用async和await变异步为同步,解决回调地狱问题


下一篇:微信小程序传参 查询数据库,显示在小程序上