线程在 Android 中是如何工作的?
当应用程序在 Android 中启动时,它会创建主执行线程,称为“主”线程。大多数线程负责将事件分派到可接受的界面小部件,同时与来自 Android UI 工具包的组件进行通信。为了使您的应用程序保持响应,必须避免使用最多的线程来执行任何会发现您将其阻塞的操作。
网络操作和数据库调用,也作为某些组件的加载,是在主线程中应该避免的常见操作示例。一旦它们在主线程中被调用,它们就会被同步调用,这表明 UI 将保持完全无响应,直到操作完成。因此,需要调用的任务通常在不同的线程中执行,从而避免阻塞 UI 并在执行任务时保持响应。 (即,它们从 UI 异步执行)。
Android 提供了一些创建和管理线程的方法,并且存在许多第三方库使线程管理更加愉快。然而,手头有多种方法,选择合适的方法通常会令人困惑。在本文中,您将学习Android 开发中线程变得必不可少的一些常见场景,以及一些将应用于这些场景的简单解决方案等等。
Android 中的线程
在 Android 中,您将所有线程组件分为两个基本类别:
- 附加到活动/片段的线程:这些线程与活动/片段的生命周期相关联,并且由于活动/片段被破坏而立即终止。
- 未附加到任何活动/片段的线程:这些线程仍然可以运行超过产生它们的活动/片段(如果有)的生命周期。
类型 1:附加到 Activity/Fragment 的线程组件
1. 异步任务
AsyncTask 是最基本的 Android 线程组件。使用起来超级简单简单 它也适用于一些基本场景
Java
public class GeeksActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
// Adding Task to the List (Async)
new MyTask().execute(url);
}
private class MyTask
extends AsyncTask {
@Override
protected String doInBackground(String... params)
{
String url = params[0];
return doSomeWork(url);
}
@Override
protected void onPostExecute(String result)
{
super.onPostExecute(result);
// do something with the result
}
}
}
Java
public class GeeksActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Getting instance of loader manager
getLoaderManager().initLoader(1, null, new MyLoaderCallbacks());
}
private class MyGeekyLoaderCallbacks implements LoaderManager.LoaderCallbacks {
// Overriding the method
@Override
public Loader onCreateLoader(int id, Bundle args) {
return new MyLoader(GeeksforGeeks.this);
}
@Override
public void onLoadFinished(Loader loader, Object data) {
}
@Override
public void onLoaderReset(Loader loader) {
}
}
private class MyLoader extends AsyncTaskLoader {
public MyLoader(Context context) {
super(context);
}
@Override
public Object loadInBackground() {
return someWorkToDo();
}
}
}
Java
public class ExampleServiceGeeks extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
doSomeLongProccesingWork();
stopSelf();
// Self stopping the service
// by calling stopSelf();
return START_NOT_STICKY;
}
@Nullable
@Override
// Binding the service to the Method calls
public IBinder onBind(Intent intent) {
return null;
}
}
Java
public class GeeksforGeeks extends Activity {
// ...
public class MyAsyncTask extends AsyncTask {
@Override protected void onPostExecute(String result) {...}
@Override protected String doInBackground(Void... params) {...}
}
}
Kotlin
class GeeksforGeeks : Activity() {
// ...
inner class MyAsyncTask : AsyncTask() {
override fun onPostExecute(result: String) {...}
override fun doInBackground(vararg params: Unit): String {...}
}
}
的AsyncTask,但达不到,如果你想你的递延任务超出了活动/片段的寿命运行。即使是最轻微的屏幕旋转也会导致 Activity 被破坏,这一事实简直太糟糕了!所以我们来:
2. 装载机
装载机是上述可怕噩梦的答案。加载器非常擅长在这种情况下执行,并且它们会在 Activity 被销毁时自动停止,更重要的是,它们还会在重新创建 Activity 后重新启动。
Java
public class GeeksActivity extends Activity{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Getting instance of loader manager
getLoaderManager().initLoader(1, null, new MyLoaderCallbacks());
}
private class MyGeekyLoaderCallbacks implements LoaderManager.LoaderCallbacks {
// Overriding the method
@Override
public Loader onCreateLoader(int id, Bundle args) {
return new MyLoader(GeeksforGeeks.this);
}
@Override
public void onLoadFinished(Loader loader, Object data) {
}
@Override
public void onLoaderReset(Loader loader) {
}
}
private class MyLoader extends AsyncTaskLoader {
public MyLoader(Context context) {
super(context);
}
@Override
public Object loadInBackground() {
return someWorkToDo();
}
}
}
输入#2。不附加到 Activity/Fragment 的线程组件
1. 服务
可以将服务视为一个组件,可用于在没有 UI 的情况下执行长(或可能长)的操作。是的,你听到的是对的!服务没有他们的任何用户界面!服务在其托管进程的主线程内运行;除非您另行指定,否则该服务不会创建自己的线程并且不会在单独的进程中运行。
Java
public class ExampleServiceGeeks extends Service {
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
doSomeLongProccesingWork();
stopSelf();
// Self stopping the service
// by calling stopSelf();
return START_NOT_STICKY;
}
@Nullable
@Override
// Binding the service to the Method calls
public IBinder onBind(Intent intent) {
return null;
}
}
典型的设计错误
看看下面的代码片段:
Java
public class GeeksforGeeks extends Activity {
// ...
public class MyAsyncTask extends AsyncTask {
@Override protected void onPostExecute(String result) {...}
@Override protected String doInBackground(Void... params) {...}
}
}
科特林
class GeeksforGeeks : Activity() {
// ...
inner class MyAsyncTask : AsyncTask() {
override fun onPostExecute(result: String) {...}
override fun doInBackground(vararg params: Unit): String {...}
}
}
好像有什么不对?
在这段代码中发生的错误是代码将线程对象 MyAsyncTask 声明为某个活动的非静态内部类(或 Kotlin 中的内部类)。此声明创建了对封闭 Activity 实例的隐式关注。因此,在线程工作完成之前,事物包含对活动的关注,从而导致引用活动的销毁过程中出现延迟。因此,我们得到了延迟,这反过来会损害系统并给内存带来沉重的负担。当前问题的直接解决方案是将重载的类实例定义为静态类或在它们自己的文件中,从而删除隐式引用。
另一种解决方案是在适当的 Activity 生命周期回调中始终取消和打包后台任务,例如 onDestroy。然而,这种方法通常很乏味且容易出错。作为一般规则,您不应将复杂的、非 UI 逻辑直接放入活动中。此外, AsyncTask现在已弃用,但不建议在新代码中使用。
线程优先级
如进程以及应用程序生命周期中所述,您的应用程序线程获得的优先级部分取决于应用程序在应用程序生命周期中的位置。当您在应用程序中创建和管理线程时,重要的是排列它们的优先级,以便正确的线程在正确的时间获得正确的优先级。
如果优先级设置得太高,那么该线程可能会中断 UI 线程,甚至在某些不利情况下甚至阻塞 Render 线程,从而导致应用程序性能问题,如丢帧、延迟、应用程序 UI 缓慢等。
每次创建线程时,都应该调用setThreadPriority() 。系统的线程调度程序优先考虑具有高优先级的线程,平衡这些优先级与最终完成所有工作的必要性。通常,前台组中的线程从设备获得整个执行时间的大约 95%,而后台组获得大约 5%。
因此,如果您通过对像素进行长时间运行的工作而大发雷霆,这对您来说可能是一个更好的解决方案。当您的应用程序使用HandlerThread创建线程时,请不要忘记将线程的优先级排列在它正在执行的工作类型所支持的范围内。请记住,CPU 只能并行处理少量线程。当所有其他线程都在争取注意力时,设置优先级有助于系统知道安排这项工作的正确方法。
可以在此处找到有关“服务绑定和线程”的详细讨论以供参考。