Android中进程与线程
常說的主線程(UI線程)是什么?
當一個Android程序剛啟動的時候,我們的android系統就會啟動一個帶有一個單一線程的linux進程。默認情況下,所有的組件比如Activity都運行在同樣的一個進程和線程當中,這個線程就叫做主線程或者UI線程。也就是說,默認情況下,app啟動的時候會創建一個線程,這個線程就叫做主線程。因為大部分功能是進行UI上的操作,所有也叫做UI線程。
關于為什么叫主線程請參考:Android 主線程之旅——PSVM(public static void main。
讓你的組件運行在一個新的進程
一般情況下,同一個Android程序里的所有應用都運行在一個進程當中。但是如果你有需要,你可以在manifest文件當中組件入口配置處進行設置,activity, service, receiver, provider都支持用android:process來設置運行的進程。你也在application里設置全局的android:process屬性。
<activity android:name=".MainActivity"android:label="@string/app_name"android:process="com.example.yangqiangyu.processandthread1"android:theme="@style/AppTheme.NoActionBar" ><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter> </activity><activity android:name=".Main2Activity"android:label="@string/title_activity_main2"android:theme="@style/AppTheme.NoActionBar" android:process="com.example.yangqiangyu.processandthread2"> </activity> public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);setSupportActionBar(toolbar);FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);fab.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG).setAction("Action", new View.OnClickListener() {@Overridepublic void onClick(View view) {startActivity(new Intent(MainActivity.this,Main2Activity.class));}}).show();}});} public class Main2Activity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main2);Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);setSupportActionBar(toolbar);ActivityManager mActivityManager =(ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);List<ActivityManager.RunningAppProcessInfo> list = mActivityManager.getRunningAppProcesses();for (ActivityManager.RunningAppProcessInfo info:list){Log.d("process", info.processName);} }指定了兩個Activity的process屬性,Main進入Main2,在Main2中輸出了運行的所有進程信息:
11-15 23:35:23.454 22628-22628/com.example.yangqiangyu.processandthread2 D/process: com.example.yangqiangyu.processandthread2 11-15 23:35:23.454 22628-22628/com.example.yangqiangyu.processandthread2 D/process: com.example.yangqiangyu.processandthread1 11-15 23:35:23.454 22628-22628/com.example.yangqiangyu.processandthread2 D/process: com.android.launcher可以看到的確存在了我們設置的進程。
進程的優先級
我們知道,Android系統會根據需要移除一些進程。比如當系統內存不足的時候,會把運行的某些Android程序干掉。那它又是按什么規則去干掉合適的程序呢?這就是進程的優先級。根據進程中的應用組件的重要性要判斷干掉哪些程序,讓哪些程序繼續運行。
下面是官網列出的5個級別,越往后優先級越低,越容易被干掉:
1、前臺進程
一般來說,在同一時刻只有一個前臺進程存在,前臺進程擁有最高的優先級,所以除非在特殊的情況下,比如內存不足完全不能運行程序的時候才會被干掉。(如果你沒有遇到過,說明你不是和我一樣用的百元Android機,哈哈)下面的情況都被認為是一個前臺進程:
進程中有一個正在和用戶交互的Activity時,也就是該Activity調用了onResume方法。
進程中含有一個與正在和用戶交互的Activity綁定了的Service
進程中含有一個運行”在前臺的” Service —–即該Service調用了startForeground()方法。
進程中含有一個調用了 onCreate(), onStart(), 或者onDestroy()方法中的任意一個方法的Service。
進程中含有一個調用了onReceive()方法的BroadcastReceiver。
2、可見的進程
即使一個進程沒有前臺組件,但是如果它能夠影響到用戶所看到的界面,它就是可見的進程。可見的進程依然很重要,它只有在必需干掉它才能維持前臺進程存在的情況下才會被干掉。老大需要資源,做小弟的能不讓嗎?下面的情況都被認為是一個可見進程:
當我們學習生命周期的時候打開一個dialog的情況。此時之前的Activity所在的進程就是可見的進程。
進程中含有一個綁定到一個可見的、或者前臺Activity的Service。
3、Service進程
和名字一樣,就是一個進程當中有Service在運行的情況,也就是調用了startService() 方法。進程Service沒有關聯任何東西,用戶看不見摸不著。但是它們所做的事情仍然重要(比如在后臺放音樂,下數據)。所有系統會在只有內存不足以維持以上兩個進程的時候干掉它。那就是老三。
4、后臺進程
進程中含有當前不可見的Activity(即調用了onStop()方法),這些進程對用戶沒太大影響,所以系統可以在任何系統資源不足、前三種需要資源的時候干掉它。但是一般情況下,后臺進程都會存在,并且維持著不可見的Activity信息。
5、空進程
一個沒有任何活躍的組件的進程就是空進程,它存在的目的是為了緩存。比如讓下次啟動組件的需要的時間更快一點。最低優先級,沒人權,不說了。
當上面的列舉的情況存在多個的時候,以情況優先級最高的為準。也就是說,當一個進程中存在前臺Activity,又有一個運行在后臺的Service時,就是前臺進程。
線程
當我們的Android應用啟動的時候,系統就會創建一個默認的線程,就叫做主線程。關于為什么叫主線程請參考:Android 主線程之旅——PSVM(public static void main。
它管理著我們用戶界面怎么‘畫‘出來,當我們點擊屏幕的時候,事件如何分發。所以主線程也叫做UI線程(以前雖然這么叫,但是我不知道為啥)。
由于我們的主線程要做那么多事情,如果此時我們又在它當中做耗時任務,比如網絡請求或者數據庫查詢。就可能會導致主線程阻塞。當主線程阻塞之后,就不能進行事件分發等它本來應該做的事情了。如果阻塞超過5秒,就會出現彈ANR(“application not responding” ) dialog的情況了。
Android Ui線程并不是線程安全的,所以不能在其他線程(工作線程)里操作Ui,你只能在主線程是操作你的UI。
總結:
不能阻塞UI線程
不能非主線程的其他外部線程進行UI操作。
工作線程
由于上面描述的原因,我們在Android中耗時任務必需放在工作線程當中,下面參照一個官網的寫個類似的例子,關鍵代碼如下
public void loadImage(View view) {switch (view.getId()){case R.id.loadImage:new Thread(new Runnable() {@Overridepublic void run() {Bitmap bitmap = loadImageFromNetwork("http://e.hiphotos.baidu.com/image/pic/item/b2de9c82d158ccbf0881c1d01dd8bc3eb135411e.jpg");imageView.setBitmap(bitmap);break;}}private Bitmap loadImageFromNetwork(String imageUrl) {URL imgUrl = null;Bitmap bitmap = null;try {imgUrl = new URL(imageUrl);HttpURLConnection conn = (HttpURLConnection)imgUrl.openConnection();conn.setDoInput(true);conn.connect();InputStream is = conn.getInputStream();bitmap = BitmapFactory.decodeStream(is);is.close();} catch (MalformedURLException e) {e.printStackTrace();}catch(IOException e){e.printStackTrace();}return bitmap;}布局文件很簡單,一個按鈕一個ImageView,在點擊按鈕的時候在一個新的線程中去執行網絡加載,這符合了上面總結的第一點不能阻塞UI線程,我們運行項目后點擊按鈕發現閃退了,報錯信息如下:
Only the original thread that created a view hierarchy can touch its views.
也就是我們之前總結的的第二點,由于主線程不是線程安全的,不能非主線程的其他外部線程進行UI操作。
為了解決上面的問題,Android給我們提供了幾種在其他線程中獲取主線程的方式:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runnable, long)
我們可以將上面的imageView.setImageBitmap(bitmap)設置圖片改成
image.post(Runnable)
image.postDelayed(Runnable, long)
runOnUiThread(Runnable)的任何一種方式。
修改之后運行,你發現可以正在的獲取圖片并且顯示在ui 界面上了。比如:
這樣就實現了網絡在其他線程,而UI操作在主線程了。
Handler和AsyncTask的作用
在上面的代碼中,用一個ImageView的 View.post(Runnable)方法,雖然實現了功能,但是如果每個View的操作都要這么寫的話,那我們的代碼不就太多太難維護了。然而Handler卻可以讓復雜的UI線程與主線程的交互變得簡單。你只需要簡單的在其他線程當中用handler發送消息。而AsyncTask也讓你能夠在合適的地方進行耗時操作,在合適的地方進行UI操作。比如我們可以將上面的代碼用handler來處理,整個類的代碼如下:
public class MainActivity extends AppCompatActivity {private ImageView imageView;private Handler handler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(Message message) {switch (message.what){case 1000:imageView.setImageBitmap((Bitmap) message.obj);break;}return true;}});@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);setSupportActionBar(toolbar);imageView = (ImageView) findViewById(R.id.imageView);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.menu_main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {// Handle action bar item clicks here. The action bar will// automatically handle clicks on the Home/Up button, so long// as you specify a parent activity in AndroidManifest.xml.int id = item.getItemId();//noinspection SimplifiableIfStatementif (id == R.id.action_settings) {return true;}return super.onOptionsItemSelected(item);}private Bitmap loadImageFromNetwork(String imageUrl) {URL imgUrl = null;Bitmap bitmap = null;try {imgUrl = new URL(imageUrl);HttpURLConnection conn = (HttpURLConnection)imgUrl.openConnection();conn.setDoInput(true);conn.connect();InputStream is = conn.getInputStream();bitmap = BitmapFactory.decodeStream(is);is.close();} catch (MalformedURLException e) {e.printStackTrace();}catch(IOException e){e.printStackTrace();}return bitmap;}public void loadImage(View view) {switch (view.getId()){case R.id.loadImage:new Thread(new Runnable() {@Overridepublic void run() {Bitmap bitmap = loadImageFromNetwork("http://e.hiphotos.baidu.com/image/pic/item/b2de9c82d158ccbf0881c1d01dd8bc3eb135411e.jpg");Message message = new Message();message.what = 1000;message.obj = bitmap;handler.sendMessage(message);}}).start();break;}} }在執行耗時任務的線程中用handler發送了一條消息,然后在handler的handleMessage方法里面進行了UI操作。
相信你到這里對進程與線程、主線程是什么,為什么不能在其他線程進行UI操作,為什么不能在主線程進行耗時任務等等,關于Handler和AsyncTask的將會在之后具體介紹!
如果覺得對你有用,點個贊或者留個言支持一下,如果有錯誤請提出,因為我也是一個正在學習的菜鳥。
轉載于:https://www.cnblogs.com/yangqiangyu/p/5143078.html
總結
以上是生活随笔為你收集整理的Android中进程与线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【翻译】WPF应用程序模块化开发快速入门
- 下一篇: 【开源框架】Android之史上最全最简