目录
  
  Android限制只能在主线程中进行UI访问
  
  Thread的实现
  
  Android Thread 的构造方法
  
  Android Thread 的start()方法
  
  如何在我们自己的代码中去检测当前Thread是不是UI线程呢?
  
  Android限制只能在主线程中进行UI访问
  
  我们知道,Android中规定了访问UI只能在主线程中进行,如果在子线程中访问UI的话,程序就会抛出异常Only the original thread that created a view hierarchy can touch its views.
  
  查看源码后可以发现,这个验证工作是由ViewRootImpl的checkThread()方法来完成的
  
  void checkThread() {
  
  if (mThread != Thread.currentThread()) {
  
  throw new CalledFromWrongThreadException(
  
  "Only the original thread that created a view hierarchy can touch its views.");
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  我们来看下这个方法,其中mThread是一个final类型,赋值是在ViewRootImpl的构造方法中,指向mThread = Thread.currentThread();
  
  final Thread mThread;
  
  public ViewRootImpl(Context context, Display display) {
  
  ...
  
  mThread = Thread.currentThread();
  
  ...
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  即mThread和当前调用的Thread.currentThread()不是一个Thread的话,即可判定当前不是UI线程中执行。
  
  这里再多说一点,系统为什么不允许在子线程中访问UI呢?这是因为Android的UI空间不是线程安全的,如果在多线程中并发访问可能会导致UI空间处于不可预期的状态。
  
  Thread的实现
  
  Android Thread 的构造方法
  
  涉及到的 Android 源码路径:
  
  libcore/luni/src/main/java/java/lang/Runnable.java
  
  libcore/luni/src/main/java/java/lang/Thread.java
  
  libcore/luni/src/main/java/java/lang/ThreadGroup.java
  
  libcore/luni/src/main/java/java/lang/VMThread.java
  
  dalvik/vm/native/java_lang_VMThread.cpp
  
  dalvik/vm/Thread.cpp
  
  首先来分析 Android Thread,它实现了 Runnable 接口
  
  // libcore/luni/src/main/java/java/lang/Thread.java
  
  public class Thread implements Runnable {
  
  ...
  
  }
  
  1
  
  2
  
  3
  
  4
  
  而Runnable 只有一个无参无返回值的 run() 接口:
  
  // libcore/luni/src/main/java/java/lang/Runnable.java
  
  /**
  
  * Represents a command that can be executed. Often used to run code in a
  
  * different {@link Thread}.
  
  */
  
  public interface Runnable {
  
  /**
  
  * Starts executing the active part of the class' code. This method is
  
  * called when a thread is started that has been created with a class which
  
  * implements {@code Runnable}.
  
  */
  
  public void run();
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  Android Thread存在六种状态,这些状态定义在枚举 State 中,源码注释写的很清晰
  
  // libcore/luni/src/main/java/java/lang/Thread.java
  
  /**
  
  * A representation of a thread's state. A given thread may only be in one
  
  * state at a time.
  
  */
  
  public enum State {
  
  /**
  
  * The thread has been created, but has never been started.
  
  */
  
  NEW,
  
  /**
  
  * The thread may be run.
  
  */
  
  RUNNABLE,
  
  /**
  
  * The thread is blocked and waiting for a lock.
  
  */
  
  BLOCKED,
  
  /**
  
  * The thread is waiting.
  
  */
  
  WAITING,
  
  /**
  
  * The thread is waiting for a specified amount of time.
  
  */
  
  TIMED_WAITING,
  
  /**
  
  * The thread has been terminated.
  
  */
  
  TERMINATED
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  Android Thread 类中一些关键成员变量如下:
  
  // libcore/luni/src/main/java/java/lang/Thread.java
  
  volatile VMThread vmThread;
  
  volatile ThreadGroup group;
  
  volatile String name;
  
  volatile int priority;
  
  volatile long stackSize;
  
  Runnable target;
  
  private static int count = 0;
  
  private long id;
  
  ThreadLocal.Values localValues;
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  vmThread:可视为对 dalvik thread 的简单封装,Thread 类通过 VMThread 里面的 JNI 方法来调用 dalvik 中操作线程的方法,通过它的成员变量 thread 和 vmata,我们可以将 Android Thread 和 dalvik Thread 的关联起来;
  
  group:每一个线程都属于一个group,当线程被创建时就会加入一个特定的group,当线程运行结束,会从这个 group 中移除;
  
  priority:线程优先级;
  
  stackSize:线程栈大小;
  
  target:一个 Runnable 对象,Thread 的 run() 方法中会转调该 target 的 run() 方法,这是线程真正处理事务的地方;
  
  id:线程 id,通过递增 count 得到该id,如果没有显式给线程设置名字,那么就会使用 Thread+id 当作线程的名字。注意这不是真正意义上的线程 id,即在 logcat 中打印的 tid 并不是这个 id,那 tid 是指 dalvik 线程的 id;
  
  localValues:线程本地存储(TLS)数据,而TLS的作用是能将数据和执行的特定的线程联系起来。
  
  接下来,我们来看Android Thread 的构造函数,大部分构造函数都是通过转调静态函数 create 实现的
  
  // libcore/luni/src/main/java/java/lang/Thread.java
  
  public Thread() {
  
  create(null, null, null, 0);
  
  }
  
  1
  
  2
  
  3
  
  4
  
  下面来详细分析 create 这个关键函数:
  
  // libcore/luni/src/main/java/java/lang/Thread.java
  
  private void create(ThreadGroup group, Runnable runnable, String threadName, long stackSize) {
  
  Thread currentThread = Thread.currentThread(); ❶
  
  if (group == null) {
  
  group = currentThread.getThreadGroup();
  
  }
  
  ...
  
  this.group = group;
  
  synchronized (Thread.class) {
  
  id = ++Thread.count;
  
  }
  
  if (threadName == null) {
  
  this.name = "Thread-" + id;
  
  } else {
  
  this.name = threadName;
  
  }
  
  this.target = runnable;
  
  this.stackSize = stackSize;
  
  this.priority = currentThread.getPriority();
  
  this.contextClassLoader = currentThread.contextClassLoader;
  
  // Transfer over InheritableThreadLocals.
  
  if (currentThread.inheritableValues != null) {
  
  inheritableValues = new ThreadLocal.Values(currentThread.inheritableValues);
  
  }
  
  // add ourselves to our ThreadGroup of choice
  
  this.group.addThread(this); ❷
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  首先看下[create]❶部分的代码,通过静态函数 currentThread 获取创建线程所在的当前线程,然后将当前线程的一些属性传递给即将创建的新线程。这是通过 VMThread 转调 dalvik 中的代码实现的。
  
  // android/libcore/luni/src/main/java/java/lang/Thread.java
  
  public static Thread currentThread() {
  
  return VMThread.currentThread();
  
  }
  
  1
  
  2
  
  3
  
  4
  
  VMThread 的 currentThread 是一个 native 方法,其 JNI 实现为
  
  // dalvik/vm/native/java_lang_VMThread.cpp
  
  static void Dalvik_java_lang_VMThread_currentThread(const u4* args,
  
  JValue* pResult)
  
  {
  
  ...
  
  RETURN_PTR(dvmThreadSelf()->threadObj);
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  来看下 dvmThreadSelf() 方法,每一个 dalvik 线程都会将自身存放在key 为 pthreadKeySelf 的线程本地存储中,获取当前线程时,只需要根据这个 key 查询获取即可
  
  // dalvik/vm/Thread.cpp 中:
  
  Thread* dvmThreadSelf()
  
  {
  
  return (Thread*) pthread_getspecific(gDvm.pthreadKeySelf);
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  dalvik Thread 有一个名为 threadObj 的成员变量:
  
  // dalvik/vm/Thread.h
  
  /* the java/lang/Thread that we are associated with */
  
  Object* threadObj;
  
  1
  
  2
  
  3
  
  在之后的分析中我们可以看到,dalvik Thread 这个成员变量 threadObj 关联的就是对应的 Android Thread 对象,所以通过 native 方法 VMThread.currentThread() 返回的是存储在 TLS 中的当前 dalvik 线程对应的 Android Thread。
  
  接着分析上面[create]❷部分的代码,如果没有给新线程指定 group ,那么就会指定 group 为当前线程所在的 group 中,然后给新线程设置 name,priority 等。最后通过调用 ThreadGroup 的 addThread 方法将新线程添加到 group 中:
  
  // libcore/libart/src/main/java/java/lang/ThreadGroup.java
  
  /**
  
  * Called by the Thread constructor.
  
  */
  
  final void addThread(Thread thread) throws IllegalThreadStateException {
  
  synchronized (threadRefs) {
  
  ...
  
  threadRefs.add(new WeakReference<Thread>(thread));
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  ThreadGroup 的代码相对简单,它有一个名为 threadRefs 的列表,持有属于同一组的 thread 引用,可以对一组 thread 进行一些线程操作。
  
  上面分析的是 Android Thread 的构造过程,从上面的分析可以看出,Android Thread 的构造方法仅仅是设置了一些线程属性,并没有真正去创建一个新的 dalvik Thread,dalvik Thread 创建过程要等到客户代码调用 Android Thread 的 start() 方法才会进行。
  
  Android Thread 的start()方法
  
  下面我们来分析 Java Thread 的 start() 方法:
  
  // libcore/luni/src/main/java/java/lang/Thread.java
  
  public synchronized void start() {
  
  checkNotStarted();
  
  hasBeenStarted = true;
  
  VMThread.create(this, stackSize);
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  Android Thread 的 start 方法很简单,仅仅是转调 VMThread 的 native 方法
  
  // dalvik/vm/native/java_lang_VMThread.cpp
  
  static void Dalvik_java_lang_VMThread_create(const u4* args, JValue* pResult)
  
  {
  
  Object* threadObj = (Object*) args[0];
  
  s8 stackSize = GET_ARG_LONG(args, 1);
  
  /* copying collector will pin threadObj for us since it was an argument */
  
  dvmCreateInterpThread(threadObj, (int) stackSize);
  
  RETURN_VOID();
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  dvmCreateInterpThread 的实现
  
  // dalvik/vm/Thread.cpp
  
  bool dvmCreateInterpThread(Object* threadObj, int reqStackSize)
  
  {
  
  Thread* self = dvmThreadSelf();
  
  ...
  
  Thread* newThread = allocThread(stackSize);
  
  newThread->threadObj = threadObj;
  
  ...
  
  Object* vmThreadObj = dvmAllocObject(gDvm.classJavaLangVMThread, ALLOC_DEFAULT);
  
  dvmSetFieldInt(vmThreadObj, gDvm.offJavaLangVMThread_vmData, (u4)newThread);
  
  dvmSetFieldObject(threadObj, gDvm.offJavaLangThread_vmThread, vmThreadObj);
  
  ...
  
  pthread_t threadHandle;
  
  int cc = pthread_create(&threadHandle, &threadAttr, interpThreadStart, newThread);
  
  /*
  
  * Tell the new thread to start.
  
  *
  
  * We must hold the thread list lock before messing with another thread.
  
  * In the general case we would also need to verify that newThread was
  
  * still in the thread list, but in our case the thread has not started
  
  * executing user code and therefore has not had a chance to exit.
  
  *
  
  * We move it to VMWAIT, and it then shifts itself to RUNNING, which
  
  * comes with a suspend-pending check.
  
  */
  
  dvmLockThreadList(self);
  
  assert(newThread->status == THREAD_STARTING);
  
  newThread->status = THREAD_VMWAIT;
  
  pthread_cond_broadcast(&gDvm.threadStartCond);
  
  dvmUnlockThreadList();
  
  ...
  
  }
  
  /*
  
  * Alloc and initialize a Thread struct.
  
  *
  
  * Does not create any objects, just stuff on the system (malloc) heap.
  
  */
  
  static Thread* allocThread(int interpStackSize)
  
  {
  
  Thread* thread;
  
  thread = (Thread*) calloc(1, sizeof(Thread));
  
  ...
  
  thread->status = THREAD_INITIALIZING;
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  首先,通过调用 allocThread 创建一个名为 newThread 的 dalvik Thread 并设置一些属性,将设置其成员变量 threadObj 为传入的 Android Thread,这样 dalvik Thread 就与Android Thread 关联起来了;
  
  然后创建一个名为 vmThreadObj 的 VMThread 对象,设置其成员变量 vmData 为 newThread,设置 Android Thread threadObj 的成员变量 vmThread 为这个 vmThreadObj,这样 Android Thread 通过 VMThread 的成员变量 vmData 就和 dalvik Thread 关联起来了。
  
  最后,通过 pthread_create 创建 pthread 线程,并让这个线程 start,这样就会进入该线程的 thread entry 运行,下来我们来看新线程的 thread entry 方法 interpThreadStart,同样只列出关键的地方:
  
  // dalvik/vm/Thread.cpp
  
  /*
  
  * pthread entry function for threads started from interpreted code.
  
  */
  
  static void* interpThreadStart(void* arg)
  
  {
  
  Thread* self = (Thread*) arg;
  
  ...
  
  /*
  
  * Finish initializing the Thread struct.
  
  */
  
  dvmLockThreadList(self);
  
  prepareThread(self);
  
  ...
  
  /*
  
  * Change our state so the GC will wait for us from now on. If a GC is
  
  * in progress this call will suspend us.
  
  */
  
  dvmChangeStatus(self, THREAD_RUNNING);
  
  /*
  
  * Execute the "run" method.
  
  *
  
  * At this point our stack is empty, so somebody who comes looking for
  
  * stack traces right now won't have much to look at. This is normal.
  
  */
  
  Method* run = self->threadObj->clazz->vtable[gDvm.voffJavaLangThread_run];
  
  JValue unused;
  
  ALOGV("threadid=%d: calling run()", self->threadId);
  
  assert(strcmp(run->name, "run") == 0);
  
  dvmCallMethod(self, run, self->threadObj, &unused);
  
  ALOGV("threadid=%d: exiting", self->threadId);
  
  /*
  
  * Remove the thread from various lists, report its death, and free
  
  * its resources.
  
  */
  
  dvmDetachCurrentThread();
  
  return NULL;
  
  }
  
  /*
  
  * Finish initialization of a Thread struct.
  
  *
  
  * This must be called while executing in the new thread, but before the
  
  * thread is added to the thread list.
  
  *
  
  * NOTE: The threadListLock must be held by the caller (needed for
  
  * assignThreadId()).
  
  */
  
  static bool prepareThread(Thread* thread)
  
  {
  
  assignThreadId(thread);
  
  thread->handle = pthread_self();
  
  thread->systemTid = dvmGetSysThreadId();
  
  setThreadSelf(thread);
  
  ...
  
  return true;
  
  }
  
  /*
  
  * Explore our sense of self. Stuffs the thread pointer into TLS.
  
  */
  
  static void setThreadSelf(Thread* thread)
  
  {
  
  int cc;
  
  cc = pthread_setspecific(gDvm.pthreadKeySelf, thread);
  
  ...
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  52
  
  53
  
  54
  
  55
  
  56
  
  57
  
  58
  
  59
  
  60
  
  61
  
  62
  
  63
  
  64
  
  65
  
  66
  
  67
  
  68
  
  69
  
  70
  
  71
  
  72
  
  73
  
  74
  
  75
  
  76
  
  77
  
  78
  
  在新线程的 thread entry 方法 interpThreadStart 中,首先设置线程的名字,然后通过调用 prepareThread 设置线程 id 以及其它一些属性,并调用 setThreadSelf 将新 dalvik Thread 自身保存在 TLS 中,这样之后就能通过 dvmThreadSelf 方法从 TLS 中获取它。然后修改状态为 THREAD_RUNNING,并调用对应 Android Thread 的 run 方法,运行客户代码:
  
  // libcore/luni/src/main/java/java/lang/Thread.java
  
  public void run() {
  
  if (target != null) {
  
  target.run();
  
  }
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  target 在前面已经做了介绍,它是线程真正处理逻辑事务的地方。一旦逻辑事务处理完毕从 run 中返回,线程就会回到 interpThreadStart 方法中,继续执行 dvmDetachCurrentThread 方法:
  
  // dalvik/vm/Thread.cpp
  
  /*
  
  * Detach the thread from the various data structures, notify other threads
  
  * that are waiting to "join" it, and free up all heap-allocated storage.
  
  * /
  
  void dvmDetachCurrentThread()
  
  {
  
  Thread* self = dvmThreadSelf();
  
  Object* vmThread;
  
  Object* group;
  
  ...
  
  group = dvmGetFieldObject(self->threadObj, gDvm.offJavaLangThread_group);
  
  /*
  
  * Remove the thread from the thread group.
  
  */
  
  if (group != NULL) {
  
  Method* removeThread =
  
  group->clazz->vtable[gDvm.voffJavaLangThreadGroup_removeThread];
  
  JValue unused;
  
  dvmCallMethod(self, removeThread, group, &unused, self->threadObj);
  
  }
  
  /*
  
  * Clear the vmThread reference in the Thread object. Interpreted code
  
  * will now see that this Thread is not running. As this may be the
  
  * only reference to the VMThread object that the VM knows about, we
  
  * have to create an internal reference to it first.
  
  */
  
  vmThread = dvmGetFieldObject(self->threadObj,
  
  gDvm.offJavaLangThread_vmThread);
  
  dvmAddTrackedAlloc(vmThread, self);
  
  dvmSetFieldObject(self->threadObj, gDvm.offJavaLangThread_vmThread, NULL);
  
  /* clear out our struct Thread pointer, since it's going away */
  
  dvmSetFieldObject(vmThread, gDvm.offJavaLangVMThread_vmData, NULL);
  
  ...
  
  /*
  
  * Thread.join() is implemented as an Object.wait() on the VMThread
  
  * object. Signal anyone who is waiting.
  
  */
  
  dvmLockObject(self, vmThread);
  
  dvmObjectNotifyAll(self, vmThread);
  
  dvmUnlockObject(self, vmThread);
  
  dvmReleaseTrackedAlloc(vmThread, self);
  
  vmThread = NULL;
  
  ...
  
  dvmLockThreadList(self);
  
  /*
  
  * Lose the JNI context.
  
  */
  
  dvmDestroyJNIEnv(self->jniEnv);
  
  self->jniEnv = NULL;
  
  self->status = THREAD_ZOMBIE;
  
  /*
  
  * Remove ourselves from the internal thread list.
  
  */
  
  unlinkThread(self);
  
  ...
  
  releaseThreadId(self);
  
  dvmUnlockThreadList();
  
  setThreadSelf(NULL);
  
  freeThread(self);
  
  }
  
  /*
  
  * Free a Thread struct, and all the stuff allocated within.
  
  */
  
  static void freeThread(Thread* thread)
  
  {
  
  ...
  
  free(thread);
  
  }
  
  1
  
  2
  
  3
  
  4
  
  5
  
  6
  
  7
  
  8
  
  9
  
  10
  
  11
  
  12
  
  13
  
  14
  
  15
  
  16
  
  17
  
  18
  
  19
  
  20
  
  21
  
  22
  
  23
  
  24
  
  25
  
  26
  
  27
  
  28
  
  29
  
  30
  
  31
  
  32
  
  33
  
  34
  
  35
  
  36
  
  37
  
  38
  
  39
  
  40
  
  41
  
  42
  
  43
  
  44
  
  45
  
  46
  
  47
  
  48
  
  49
  
  50
  
  51
  
  52
  
  53
  
  54
  
  55
  
  56
  
  57
  
  58
  
  59
  
  60
  
  61
  
  62
  
  63
  
  64
  
  65
  
  66
  
  67
  
  68
  
  69
  
  70
  
  71
  
  72
  
  73
  
  74
  
  75
  
  76
  
  77
  
  78
  
  79
  
  80
  
  81
  
  82
  
  83
  
  84
  
  在 dvmDetachCurrentThread 函数里,首先获取当前线程 self,这里获得的就是当前执行 thread entry 的新线程,然后通过其对应的 Android Thread 对象 threadObj 获取该对象所在 group,然后将 threadObj 这个 Android Thread 对象从 group 中移除;
  
  接着清除 Android 与 dalvik 线程之间的关联关系,并通知 join 该线程的其它线程;
  
  最后,设置线程状态为 THREAD_ZOMBIE,清除 TLS 中存储的线程值,并通过调用 freeThread 释放内存,至此线程就终结了。
  
  如何在我们自己的代码中去检测当前Thread是不是UI线程呢?
  
  最后来说下在app中检测当前Thread是不是UI线程的方法:
  
  if(Looper.myLooper() == Looper.getMainLooper()) {
  
  // Current Thread is Main Thread.
  
  }
  
  1
  
  2
  
  3
  
  或者
  
  if(Looper.getMainLooper().getThread() == Thread.currentThread()) {
  
  // Current Thread is Main Thread.
  
  }
  
  1
  
  2

转载于:https://www.cnblogs.com/hj558558/p/10082344.html

[转]Android限制只能在主线程中进行UI访问的实现原理相关推荐

  1. 【Android 异步操作】Handler ( 主线程中的 Handler 与 Looper | Handler 原理简介 )

    文章目录 一.主线程中的 Handler 与 Looper 二.Handler 原理简介 一.主线程中的 Handler 与 Looper Android 系统中 , 点击图标启动一个应用进程 , 就 ...

  2. android view只能在主线程操作

    接触android时间不长,今天在写程序的时候想要根据数据变化改变RecyclerView列表的程序,在改变数据后,更新界面时报如下的错误:Only the original thread that ...

  3. UnityThread子线程使用只能在主线程中调用的函数或Unity API

    Unity的Socket网络编程中,为了防止程序卡死,一般使用多线程来监听端口,当收到来自客户端的消息时,需要显示在界面上.但是如果直接在子线程中操作Unity的界面或物体会报错.国外一个大神写了一个 ...

  4. 【Android 异步操作】Android 线程切换 ( 判定当前线程是否是主线程 | 子线程中执行主线程方法 | 主线程中执行子线程方法 )

    文章目录 一.判定当前线程是否是主线程 二.子线程中执行主线程方法 三.主线程中执行子线程方法 一.判定当前线程是否是主线程 在 Android 中 , 如果要判定当前线程是否是主线程 , 可以使用如 ...

  5. 既然android service是运行在主线程中的,那service还有什么用?

    既然android service是运行在主线程中的,那service还有什么用? 对于Android,每一个进程都有一个主线程,四大组件的处理任务都是在这个线程中进行的.每个线程都有一个Messag ...

  6. 【Android面试】主线程中的Looper.loop()一直无限循环为什么不会造成ANR?

    Android面试中,你也许会被问到题目中的问题,这里我们基于以下几点来延伸解读其中原因: 1.什么是ANR?ANR发生的原因是什么? 2.Looper为什么要无限循环? 3.线程的几种状态 4.主线 ...

  7. 用Handler的post()方法来传递线程中的代码段到主线程中执行

    自定义的线程中是不能更新UI的,但是如果遇到更新UI的事情,我们可以用handler的post()方法来将更新UI的方法体,直接传送到主线程中,这样就能直接更新UI了.Handler的post()方法 ...

  8. 在非主线程中创建窗口

    很多朋友都会有过这样的经历,为什么在主线程中创建窗口且窗口工作很正常,但一移到非主线程(有的朋友喜欢叫它为工作线程),却无法正常工作.本文就这个问题和各位探讨,可能无法做到尽善尽美,但能抛砖引玉也算是 ...

  9. python主线程执行_在Django vi中的主线程中执行Python函数

    我创建了Django视图"graph",目的是显示从matplotlib.pyplot模块.我编写了我的函数plot\u bubbles(返回amatplotlib.figure. ...

最新文章

  1. Centos中的时钟面板月份混乱解决办法
  2. JVM-05垃圾收集Garbage Collection(中)【垃圾收集算法】
  3. [6] 测试用例管理工具的需求整理
  4. Linux文件atime ctime mtime
  5. 面向对象也可以搞单片机!
  6. java awt区域_java的awt包中有没有表示区域的类或者方法,可以传递一个Rectangle
  7. php如何做水仙花数,使用PHP实现水仙花数及各种特殊有趣数的输出
  8. R语言转换并保存json文件--使用jsonlite包
  9. 如何解决git冲突,出现更新失败向下的箭头?
  10. wpf 怎样判断是否选中 checkbox_怎样判断一个人是否缺爱?
  11. pythontransform详解_Python自定义聚合函数merge与transform区别详解
  12. Spring Quartz 框架结构概述(一)[转]
  13. Mvc 前台 匿名对象
  14. 安天工程师解读CryptKeeper通用密码事件
  15. 汇编语言是一种什么程序设计语言?
  16. Android如何制作本地音乐播放器,简单实现Android本地音乐播放器
  17. 网线 绿灯长亮 黄灯不亮
  18. RSHELIOS速腾32线激光配置记录
  19. 数据报和字节流的区别
  20. JQuery学习22篇(事件委托)

热门文章

  1. VTK中导入并显示STL、3DS文件
  2. django+uwsgi+nginx部署
  3. 从C# 到 Java 点滴
  4. Python——模拟轮盘抽奖游戏
  5. string字符串数字自增_常见的字符串操作
  6. Linux之文件系统介绍
  7. Postman系列之基本操作及设置
  8. 请允许我用2019年11种主流编程语言,祝祖国70华诞快乐!
  9. 深度学习VS机器学习——到底什么区别
  10. 报告:上周比特币基金流出9800万美元,ETH、ADA等投资需求上升