Sajjad
Sajjad
خواندن ۴ دقیقه·۴ سال پیش

کار با View های اندروید در غیر Main Thread یا همون Ui Thread


همه میدونیم که اپ های اندروید با یه threadی میان بالا که بهش میگیم Main Thread یا Ui Thread و هر کاری هم با ویو ها داشته باشیم باید روی این ترد انجام بدیم

مثل setText برای TextView یا setVisibility برای هر Viewیی، که اگه ما روی یه ترد دیگه انجامشون بدیم، برنامه کرش میکنه

حالا فرض میکنیم که بنا به هر دلیلی نیاز داریم که یه سری از کارهای View رو یه ترد دیگه هندل کنه، اما چطور؟ اصلا مگه میشه؟

خبر خوب اینجاس که بعله که میشه ?



بدین صورت:

ابتدا یه ترد واسه این Viewمون میسازیم و استارت میکنیمش:

Kotlin:

thread { }

Java:

new Thread(() -> { }).start();

سپس ویویی که میخوایم رو میسازیم، یا مثلا از یه xml میتونیم inflateش کنیم:

Kotlin:

val myImageView = ImageView(this).apply { setImageResource(R.mipmap.ic_launcher) }

Java:

new Thread(() -> { ImageView myImageView = new ImageView(this); myImageView.setImageResource(R.mipmap.ic_launcher); }).start();

حالا Window manager رو از سیستم میگیریم:

Kotlin:

val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager

Java:

WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

و به این Window manager میگیم که این ویوی مارو نمایش بده:

پارامتر اولش اون Viewمون هست و پارامتر دوم هم LayoutParam هست که میشه باهاش پارامتر های یه Window رو ست کرد که ما اینجا فقط یه Object ازش میسازیم و چیزیشو تغییر نمیدیم

Kotlin:

windowManager.addView(myImageView, WindowManager.LayoutParams())

Java:

windowManager.addView(myImageView, new WindowManager.LayoutParams());

اما نکته‌ای که اینجا باهاش مواجهیم اینه که این تابع addView برا این که این view رو نمایش بده باید اول رو همین ترد Looper.prepare رو صدا بزنیم و در انتهای تابع هم Looper.loop رو که در نهایت کد ما اینشکلی میشه:

Kotlin:

import android.content.Context import android.os.Bundle import android.os.Looper import android.view.WindowManager import android.widget.ImageView import androidx.appcompat.app.AppCompatActivity import kotlin.concurrent.thread class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) thread { Looper.prepare() val myImageView = ImageView(this).apply { setImageResource(R.mipmap.ic_launcher) } val windowManager = getSystemService(Context.WINDOW_SERVICE) as WindowManager windowManager.addView(myImageView, WindowManager.LayoutParams()) Looper.loop() } } }

Java:

import android.content.Context; import android.os.Bundle; import android.os.Looper; import android.view.WindowManager; import android.widget.ImageView; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; class ActivityMain extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(() -> { Looper.prepare(); ImageView myImageView = new ImageView(this); myImageView.setImageResource(R.mipmap.ic_launcher); WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE); windowManager.addView(myImageView, new WindowManager.LayoutParams()); Looper.loop(); }).start(); } }

اما اینکه چرا اصن لوپر باید باشه و اینا یه بحث دیگس



حالا چه کاریه خب؟ چرا باید اصن هیچوقت همچین کاری کنیم؟

یه نمونه‌ی بارزش GlSurfaceه که Rendererی که بهش میدیم تا Frame هارو draw کنه، رو یه ترد مستقل میاد بالا و کار میکنه چون این view یه ماهیت مستقل داره و از سمت native داره ترسیم ها انجام میشه مثل بخش map خود waze، که به خاطر performance واقعا میطلبه این رو

یه همچین use case هایی هستن که باعث میشن این کارو کنیم



با تشکر از دوست عزیزمون که زحمت تحقیق این مطالب رو کشیدن: https://gist.github.com/DHosseiny

از نظرات و پیشنهاداتون هم استقبال میکنیم، با تشکر از این که وقت گذاشتید و مطالعه کردید!

androidmultithreadingjavaperformance
Programmer
شاید از این پست‌ها خوشتان بیاید