- 北营
-
不知道你正在试图做。 您可以使用下面,以供参考。
如果你实现Thread or HandlerThread ,确保您的UI线程不会阻塞而等待工作线程完成不致电Thread.wait()或Thread.sleep()
检查developer.android.com/training/articles/perf-anr.html
你不应该阻止你的主UI线程。 而不是一个定时器,你可以使用一个"处理器"
您可以启动和停止定时器按钮点击。 您可以重新安排您的定时器计数值。 你需要照顾的方向变化的活动被销毁并重新创建。 count的值,我们将重新初始化。
MainActivity.java
public class MainActivity extends Activity implements OnClickListener{
Timer t;
int count = 0;
boolean isDone = true;
Button b;
TextView tv;
Thread thread;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
b = (Button) findViewById(R.id.button1);
tv = (TextView) findViewById(R.id.textView1);
t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
runOnUiThread(new Runnable() {
public void run() {
tv.setText(String.valueOf(count));
if(isDone)
count++;
}
});
}
}, 1000, 1000);
b.setOnClickListener(this);
}
@Override
public void onClick(View v) {
// TODO Auto-generated method stu
final Dialog d = new Dialog(MainActivity.this);
d.setTitle("Pause Menu");
d.setContentView(R.layout.pausemenu);
Button b1 = (Button) d.findViewById(R.id.button1);
Button b2 = (Button) d.findViewById(R.id.button2);
b1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
t.cancel();
d.dismiss();
}
});
b2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if(isDone)
{
t.cancel();
isDone=false;
}
t = new Timer();
t.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
runOnUiThread(new Runnable() {
public void run() {
tv.setText(String.valueOf(count));
count++;
}
});
}
}, 1000, 1000);
d.dismiss();
}
});
d.show();
}
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:textSize="20sp"
android:text="TextView" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignRight="@+id/textView1"
android:layout_marginBottom="21dp"
android:text="Button" />
</RelativeLayout>
pausemenu.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_marginBottom="118dp"
android:layout_marginLeft="51dp"
android:text="Stop" />
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@+id/button1"
android:layout_alignBottom="@+id/button1"
android:layout_marginLeft="32dp"
android:layout_toRightOf="@+id/button1"
android:text="Start" />
</RelativeLayout>
编辑:
使用处理程序
Handler m_handler;
Runnable m_handlerTask ;
m_handlerTask = new Runnable()
{
@Override
public void run() {
if(isDone)
{
tv.setText(""+count);
count++;
m_handler.postDelayed(m_handlerTask, 1000);
}
// m_handler.removeCallbacks(m_handlerTask);
}
};
m_handlerTask.run();
你应该停止定时器和处理程序时,不需要取消。
如果你t.cancel()使用定时器和使用m_handler.removeCallbacks(m_handlerTask)停止处理程序呼叫
相关推荐
HandleThread的用法
什么是HandleThread呢? 这个类的作用是创建一个包含looper的线程。 什么时候使用到它呢? 加入在应用程序当中为了实现同时完成多个任务,所以我们会在应用程序当中创建多个线程。为了让多个线程之间能够方便的通信,我们会使用Handler实现线程间的通信。这个时候我们手动实现的多线程+Handler的简化版就是我们HandlerThrea所要做的事了。 HandleThread的基本用法: 首先创建一个HandleThread: HandlerThread mHandlerThread = new HandlerThread("myHandlerThreand"); mHandlerThread.start();//调用run方法 其次: 通过HandleThread的Lopper来创建Handle final Handler mHandler = new Handler(mHandlerThread.getLooper()) { @Override public void handleMessage(Message msg) { Log.i("tag", "接收到消息:" + msg.obj.toString()); } }; 然后就可以使用了: mHandler.sendMessage(msg); 最后在不需要再用的时候记得手动收回: protected void onDestroy() { super.onDestroy(); mHandlerThread.quit(); //quitSafely();可以用这个 } 分析: 可以看出HandleThread的本质其实就是一个Thread.内部自己维护了一个消息队列和一个Looper; 总结: HandlerThread本质上是一个Thread对象,只不过其内部帮我们创建了该线程的Looper和MessageQueue; 通过HandlerThread我们不但可以实现UI线程与子线程的通信同样也可以实现子线程与子线程之间的通信; HandlerThread在不需要使用的时候需要手动的回收掉;2023-07-21 18:21:121
handlerthread与单线程池区别
区别就是:前者是指一种东西,一件事情,或一种状况令人觉得尴尬.后者是指某人觉得很尴尬.2023-07-21 18:21:292
Android 中Handler 如何通过Thread获得传来的数据?
先实例化一个Handler再重写handleMessage(Message msg)方法msg.getData().getXXX()获得Thread线程发送的数据2023-07-21 18:21:372
android.os.HandlerThread与java.lang.Thread的区别在哪?(最好详细点)
HandlerThread是继承Thread,主要的作用是建立了一个线程,并且创立了消息队列,有来自己的looper,可以让我们在自己的线程中分发和处理消息。android的消息处理通过handler和looper, HandlerThread不但能提供异步处理,Handler处理消息的方法也会在这个线程中执行,他最要的作用就是提供了一个线程。2023-07-21 18:21:441
android中如何退出程序停止服务
采用 android.os.Process.killProcess(android.os.Process.myPid());可以做到的,另外如果你程序中又service 在推出的额时候stop().2023-07-21 18:21:513
Android怎么结束一个线程
在Java语言中,安全结束线程的方法就是“让线程的自身流程执行完”。出现“僵尸线程”的原因:1.线程在死循环中执行。2.线程阻塞。很明显,你的ReadHandlerThread线程发生了线程阻塞。dis.readFully(b),你在这里调用了一个阻塞方法,由于socket的另一端一直没有发送数据,ReadHandlerThread线程一直阻塞在了这里,等待接收完136字节的数据。136字节没有接收完,readFully方法将一直处于阻塞状态。解决方法,设置socket的等待超时值,而不是无限等待。2023-07-21 18:21:581
android intentservice和service的区别
IntentService:异步处理服务,新开一个线程:handlerThread在线程中发消息,然后接受处理完成后,会清理线程,并且关掉服务。IntentService有以下特点:(1) 它创建了一个独立的工作线程来处理所有的通过onStartCommand()传递给服务的intents。(2) 创建了一个工作队列,来逐个发送intent给onHandleIntent()。(3) 不需要主动调用stopSelft()来结束服务。因为,在所有的intent被处理完后,系统会自动关闭服务。(4) 默认实现的onBind()返回null(5) 默认实现的onStartCommand()的目的是将intent插入到工作队列中2023-07-21 18:22:061
android平台通过串口发送AT命令
excel中,A1 A2 A3 ,如何实现这样合并A1A2(A3+A1的值)2023-07-21 18:22:146
如何理解android多线程里的getLooper方法
下面的代码是学习Handler.post方法的,第一种写法没有使用getLooper,结果运行的效果是一个线程,第二种写法使用了getLooper,运行的效果是多线程的: package com.WriteCode.AndroidHandlerPost; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.HandlerThread; import android.util.Log; public class AndroidHandlerPost extends Activity { private Handler mHandler = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //第一种写法beg,不使用getLooper,那么整个是一个线程(UI线程) //mHandler = new Handler(); //第一种写法end,不使用getLooper,那么整个是一个线程(UI线程) //第二种写法beg,不使用getLooper,那么整个是两个线程 HandlerThread handlerThread = new HandlerThread("myHandlerThread"); handlerThread.start(); mHandler = new Handler(handlerThread.getLooper()); //第二种写法end,不使用getLooper,那么整个是两个线程 mHandler.post(new MyRunnable()); Log.e("OnCreate", "The Thread is: " + Thread.currentThread().getId()); setContentView(R.layout.main); } private class MyRunnable implements Runnable { @Override public void run() { // TODO Auto-generated method stub Log.e("Runnable", "The Thread is running"); Log.e("Runnable", "The Thread is: " + Thread.currentThread().getId()); try { Thread.sleep(6000); } catch (InterruptedException e) { e.printStackTrace(); } } } }2023-07-21 18:22:281
qt中handler的定义
Handler的定义:主要接受子线程发送的数据, 并用此数据配合主线程更新UI.解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进行事件分发, 比如说, 你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作。 如果此时需要一个耗时的操作,例如: 联网读取数据, 或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,,如果你放在主线程中的话,界面会出现假死现象, 如果5秒钟还没有完成的话,会收到Android系统的一个错误提示 "强制关闭". 这个时候我们需要把这些耗时的操作,放在一个子线程中,因为子线程涉及到UI更新,Android主线程是线程不安全的,也就是说,更新UI只能在主线程中更新,子线程中操作是危险的. 这个时候,Handler就出现了.,来解决这个复杂的问题 , 由于Handler运行在主线程中(UI线程中), 它与子线程可以通过Message对象来传递数据, 这个时候,Handler就承担着接受子线程传过来的(子线程用sedMessage()方法传弟)Message对象,(里面包含数据) , 把这些消息放入主线程队列中,配合主线程进行更新UI。Handler 概念解释:handler类允许你发送消息和处理线程消息队列中的消息及runnable对象。handler实例都是与一个线程和该线程的消息队列一起使用,一旦创建了一个新的handler实例,系统就把该实例与一个线程和该线程的消息队列捆绑起来,这将可以发送消息和runnable对象给该消息队列,并在消息队列出口处处理它们。handler类有两种主要用途:1。按照时间计划,在未来某时刻,对处理一个消息或执行某个runnable实例。2。把一个对另外线程对象的操作请求放入消息队列中,从而避免线程间冲突。时间类消息通过如下方法使用: post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long), and sendMessageDelayed(Message, long)methods. post之类函数可以传输一个runnable对象给消息队列,并在到达消息队列后被调用。sendmessage之类函数可以传送一个包含数据的message对象,该message对象可以被Handler类的handleMessage(Message) 方法所处理。post之类函数和sendmessage之类的函数都可以指定消息的执行时机,是立即执行、稍后一段时间执行,还是在某个确定时刻执行。这可以用来实现超时、消息或其他时间相关的操作。当一个进程启动时,主线程独立执行一个消息队列,该队列管理着应用顶层的对象(如:activities、broadcast receivers等等)和所有创建的窗口。你可以创建自己的一个线程,并通过handler来与主线程进行通信。这可以通过在新的线程中调用主线程的handler的post和sendmessage操作来实现。HandlerThread/Looper & MessageQueue & Message的关系:handler 负责将需要传递的信息封装成Message,通过handler 对象的sendMessage()来将消息传递给Looper,由Looper将Message放入MessageQueue中。当Looper对象看到MessageQueue中含有Message,就将其广播出去。该handler 对象收到该消息后,调用相应的handler对象的handleMessage()方法对其进行处理。Handler一些特点handler可以分发Message对象和Runnable对象到主线程中, 每个Handler实例,都会绑定到创建他的线程中(一般是位于主线程),它有两个作用:(1): 安排消息或Runnable 在某个主线程中某个地方执行,(2)安排一个动作在不同的线程中执行Handler中分发消息的一些方法post(Runnable)postAtTime(Runnable,long)postDelayed(Runnable long)sendEmptyMessage(int)sendMessage(Message)sendMessageAtTime(Message,long)sendMessageDelayed(Message,long)以上post类方法允许你排列一个Runnable对象到主线程队列中,sendMessage类方法, 允许你安排一个带数据的Message对象到队列中,等待更新.2023-07-21 18:22:461
android线程出现错误!没有运行handleMassager(Message msg)这个方法!
有几个地方要修改的:1. msg.sendToTarget(); 改成 myhandler.sendMessage(msg);2. 你的handleMessage()方法 修改成如下: @Override public void handleMessage(Message msg) { super.handleMessage(msg); Log.i(TAG, "handeMessage."); Bundle b = msg.getData(); int age = b.getInt("age"); String name = b.getString("name"); System.out.println(age + name); System.out.println("handler--->" + Thread.currentThread().getId()); System.out.println("handleMessage"); }这时候你就可以看到运行结果了。不懂的HI我。2023-07-21 18:22:544
Android中Handler的使用方法
Handler在Android中主要是负责发送和处理消息。它的主要用途大致是下面两个: 1)按计划发送消息或执行某个Runnanble; 2)从其他线程中发送来的消息放入消息队列中,避免线程冲突(常见于更新UI线程) 学写一下,在UI线程中,系统已经有一个Activity来处理了,你可以再起若干个Handler来处理。在实例化Handler的时候,只要有Handler的指针,任何线程也都可以sendMessage。 Handler对于Message的处理是异步处理的。一个Looper 只有处理完一条Message才会读取下一条,所以消息的处理是阻塞形式的(handleMessage()方法里不应该有耗时操作,可以将耗时操作放在其他线程执行,操作完后发送Message(通过sendMessges方法),然后由handleMessage()更新UI)。 根据对视频的学习写了一个通过Button控件启动进度条(类似于下载等操作)的程序,简单介绍一下,有两个Button控件,一个是开始,点击之后会显示一个进度条以每次十分之一的进度进行(一开始是隐藏的),另一个是停止,可以中断进度。 java代码: 1 package zzl.handleactivity; 2 3 import android.app.Activity; 4 import android.os.Bundle; 5 import android.os.Handler; 6 import android.os.Message; 7 import android.view.Gravity; 8 import android.view.View; 9 import android.view.View.OnClickListener; 10 import android.widget.Button; 11 import android.widget.ProgressBar; 12 import android.widget.Toast; 13 14 public class Handler_01 extends Activity { 15 16 //声明变量 17 private Button startButton=null; 18 private Button endButton=null; 19 private ProgressBar firstBar=null; 20 private Toast toast=null; 21 @Override 22 protected void onCreate(Bundle savedInstanceState) { 23 super.onCreate(savedInstanceState); 24 setContentView(R.layout.main); 25 26 //根据ID获取对象 27 startButton =(Button)findViewById(R.id.startButton); 28 endButton=(Button)findViewById(R.id.endButton); 29 firstBar=(ProgressBar)findViewById(R.id.firstBar); 30 //给对象设置动作监听器 31 startButton.setOnClickListener(new StartButtonListener()); 32 endButton.setOnClickListener(new EndButtonListener()); 33 } 34 35 class StartButtonListener implements OnClickListener{ 36 37 @Override 38 public void onClick(View v) { 39 // TODO Auto-generated method stub 40 //一开始执行,加入到消息队列,不延迟, 41 //然后马上执行run方法 42 firstBar.setVisibility(View.VISIBLE); 43 firstBar.setMax(100); 44 handler.post(upRunnable); 45 toast=Toast.makeText(Handler_01.this, "运行开始", Toast.LENGTH_SHORT); 46 toast.setGravity(Gravity.CENTER, 0, 0); 47 toast.show(); 48 } 49 } 50 class EndButtonListener implements OnClickListener{ 51 52 @Override 53 public void onClick(View v) { 54 // TODO Auto-generated method stub 55 //停止 56 handler.removeCallbacks(upRunnable); 57 System.out.println("It"s time to stop..."); 58 } 59 } 60 61 //创建handler对象,在调用post方法 62 //异步消息处理:将下载或者处理大数据等等单独放到另一个线程 63 //更好的用户体验 64 Handler handler=new Handler(){ 65 66 @Override 67 public void handleMessage(Message msg){ 68 firstBar.setProgress(msg.arg1); 69 firstBar.setSecondaryProgress(msg.arg1+10); 70 //handler.post(upRunnable); 71 if(msg.arg1<=100) { 72 handler.post(upRunnable); //将要执行的线程放入到队列当中 73 }else { 74 handler.removeCallbacks(upRunnable); 75 } 76 } 77 }; 78 79 //声明线程类:实现Runnable的接口 80 Runnable upRunnable=new Runnable() { 81 82 int i=0; 83 @Override 84 public void run() {//程序的运行状态 85 // TODO Auto-generated method stub 86 //postDelayed方法:把线程对象加入到消息队列中 87 // 隔2000ms(延迟) 88 System.out.println("It"s time to start..."); 89 i=i+10; 90 //获取Message消息对象 91 Message msg=handler.obtainMessage(); 92 //将msg对象的arg1(还有arg2)对象的值设置 93 //使用这两个变量传递消息优点:系统消耗性能较少 94 msg.arg1=i; 95 try{ 96 //设置当前显示睡眠1秒 97 Thread.sleep(1000); 98 }catch(InterruptedException e){ 99 e.printStackTrace(); 100 } 101 //将msg对象加入到消息队列当中 102 handler.sendMessage(msg); 103 if(i==100){//当值满足时,将线程对象从handle中剔除 104 handler.removeCallbacks(upRunnable); 105 firstBar.setVisibility(View.GONE); 106 //临时弹出 107 108 toast=Toast.makeText(Handler_01.this, "运行结束", Toast.LENGTH_SHORT); 109 toast.setGravity(Gravity.CENTER, 0, 0); 110 toast.show(); 111 } 112 } 113 }; 114 } main.xml 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 android:orientation="vertical" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 tools:context=".Handler_01" > 7 8 <ProgressBar 9 android:id="@+id/firstBar" 10 style="?android:attr/progressBarStyleHorizontal" 11 android:layout_width="200dp" 12 android:layout_height="wrap_content" 13 android:visibility="gone"/> 14 15 <Button 16 android:id="@+id/startButton" 17 android:layout_width="wrap_content" 18 android:layout_height="wrap_content" 19 android:text="@string/start" /> 20 21 <Button 22 android:id="@+id/endButton" 23 android:layout_width="wrap_content" 24 android:layout_height="wrap_content" 25 android:text="@string/end" /> 26 27 </LinearLayout> 总结: 1)当点击开始或者运行结束的时候,都会通过调用Toas弹出临时窗口,Toast.makeText(Handler_01.this, "运行结束", Toast.LENGTH_SHORT),这一句一开始总是执行出错,原因在于必须调用它的show方法才可以显示出来,还可以通过设置它的位置来显示; 2)在xml中 android:text="@string/end",则需要在layout下的string文件中敲写相应的代码 3)原本代码有一些瑕疵,就是没有下面这一段代码: 1 if(msg.arg1<=100) { 2 handler.post(upRunnable); //将要执行的线程放入到队列当中 3 }else { 4 handler.removeCallbacks(upRunnable); 5 } 这样就导致了upRunnable的run方法出现了死循环,这样,虽然程序UI本身没有问题,但是内部却又很大的缺陷 这是因为 1 if(i==100){//当值满足时,将线程对象从handle中剔除 2 handler.removeCallbacks(upRunnable); 3 firstBar.setVisibility(View.GONE); 4 toast=Toast.makeText(Handler_01.this, "运行结束", Toast.LENGTH_SHORT); 5 toast.setGravity(Gravity.CENTER, 0, 0); 6 toast.show(); 7 } 这一段代码看似是把upRunnable线程从线程对象队列中移除,但是再次之前又前执行了handler.sendMessage(msg);这句代码 从而导致下面的代码又被执行到 1 public void handleMessage(Message msg){ 2 firstBar.setProgress(msg.arg1); 3 firstBar.setSecondaryProgress(msg.arg1+10); 4 5 } 这样肯定会使upRunnable线程重新加入到线程对象队列中,updateThread的run方法重复执行,这就导致了死循环。所以必须加上之前的那段代码,通过判断来控制循环终止。并且run方法中的if(i==100)的那段代码也是可以不用的,不过我是写了一些其他代码就懒得优化了,这是后话了。 4) 刚刚大家看到我们可以通过敲写System.out.println在logcat中显示,一开始eclipse编译器中是没有,这是如何显示出来的? 大家可以再window的show view中找到logCat(deprecated)通过下图中绿色的“+”添加出来 然后显示内容的时候,选择右上角“V D I W E ”的I就可以比较清晰的显示出来了,当然你也可以选择另外一个logCat来显示,方法类似。 5)实际上,Handler在默认情况下,和调用它的Activity是处于同一个线程的。 上述Handler的使用示例中,虽然声明了线程对象,但是在实际调用当中它并没有调用线程的start()方法,而是直接调用当前线程的run()方法。 如果要实现调用另一个新的线程,只要注释post方法,然后加上这样两段代码即可: Thread t = new Thread(r); t.start();2023-07-21 18:23:036
Android 中的“子线程”解析
Android 中线程可分为 主线程 和 子线程 两类,其中主线程也就是 UI线程 ,它的主要这作用就是运行四大组件、处理界面交互。子线程则主要是处理耗时任务,也是我们要重点分析的。 首先 Java 中的各种线程在 Android 里是通用的,Android 特有的线程形态也是基于 Java 的实现的,所以有必要先简单的了解下 Java 中的线程,本文主要包括以下内容: 在 Java 中要创建子线程可以直接继承 Thread 类,重写 run() 方法: 或者实现 Runnable 接口,然后用Thread执行Runnable,这种方式比较常用: 简单的总结下: Callable 和 Runnable 类似,都可以用来处理具体的耗时任务逻辑的,但是但具体的差别在哪里呢?看一个小例子: 定义 MyCallable 实现了 Callable 接口,和之前 Runnable 的 run() 方法对比下, call() 方法是有返回值的哦,泛型就是返回值的类型: 一般会通过线程池来执行 Callable (线程池相关内容后边会讲到),执行结果就是一个 Future 对象: 可以看到,通过线程池执行 MyCallable 对象返回了一个 Future 对象,取出执行结果。 Future 是一个接口,从其内部的方法可以看出它提供了取消任务(有坑!!!)、判断任务是否完成、获取任务结果的功能: Future 接口有一个 FutureTask 实现类,同时 FutureTask 也实现了 Runnable 接口,并提供了两个构造函数: 用 FutureTask 一个参数的构造函数来改造下上边的例子: FutureTask 内部有一个 done() 方法,代表 Callable 中的任务已经结束,可以用来获取执行结果: 所以 Future + Callable 的组合可以更方便的获取子线程任务的执行结果,更好的控制任务的执行,主要的用法先说这么多了,其实 AsyncTask 内部也是类似的实现! 注意, Future 并不能取消掉运行中的任务,这点在后边的 AsyncTask 解析中有提到。 Java 中线程池的具体的实现类是 ThreadPoolExecutor ,继承了 Executor 接口,这些线程池在 Android 中也是通用的。使用线程池的好处: 常用的构造函数如下: 一个常规线程池可以按照如下方式来实现: 执行任务: 基于 ThreadPoolExecutor ,系统扩展了几类具有新特性的线程池: 线程池可以通过 execute() 、 submit() 方法开始执行任务,主要差别从方法的声明就可以看出,由于 submit() 有返回值,可以方便得到任务的执行结果: 要关闭线程池可以使用如下方法: IntentService 是 Android 中一种特殊的 Service,可用于执行后台耗时任务,任务结束时会自动停止,由于属于系统的四大组件之一,相比一般线程具有较高的优先级,不容易被杀死。用法和普通 Service 基本一致,只需要在 onHandleIntent() 中处理耗时任务即可: 至于 HandlerThread,它是 IntentService 内部实现的重要部分,细节内容会在 IntentService 源码中说到。 IntentService 首次创建被启动的时候其生命周期方法 onCreate() 会先被调用,所以我们从这个方法开始分析: 这里出现了 HandlerThread 和 ServiceHandler 两个类,先搞明白它们的作用,以便后续的分析。 首先看 HandlerThread 的核心实现: 首先它继承了 Thread 类,可以当做子线程来使用,并在 run() 方法中创建了一个消息循环系统、开启消息循环。 ServiceHandler 是 IntentService 的内部类,继承了 Handler,具体内容后续分析: 现在回过头来看 onCreate() 方法主要是一些初始化的操作, 首先创建了一个 thread 对象,并启动线程,然后用其内部的 Looper 对象 创建一个 mServiceHandler 对象,将子线程的 Looper 和 ServiceHandler 建立了绑定关系,这样就可以使用 mServiceHandler 将消息发送到子线程去处理了。 生命周期方法 onStartCommand() 方法会在 IntentService 每次被启动时调用,一般会这里处理启动 IntentService 传递 Intent 解析携带的数据: 又调用了 start() 方法: 就是用 mServiceHandler 发送了一条包含 startId 和 intent 的消息,消息的发送还是在主线程进行的,接下来消息的接收、处理就是在子线程进行的: 当接收到消息时,通过 onHandleIntent() 方法在子线程处理 intent 对象, onHandleIntent() 方法执行结束后,通过 stopSelf(msg.arg1) 等待所有消息处理完毕后终止服务。 为什么消息的处理是在子线程呢?这里涉及到 Handler 的内部消息机制,简单的说,因为 ServiceHandler 使用的 Looper 对象就是在 HandlerThread 这个子线程类里创建的,并通过 Looper.loop() 开启消息循环,不断从消息队列(单链表)中取出消息,并执行,截取 loop() 的部分源码: dispatchMessage() 方法间接会调用 handleMessage() 方法,所以最终 onHandleIntent() 就在子线程中划线执行了,即 HandlerThread 的 run() 方法。 这就是 IntentService 实现的核心,通过 HandlerThread + Hanlder 把启动 IntentService 的 Intent 从主线程切换到子线程,实现让 Service 可以处理耗时任务的功能! AsyncTask 是 Android 中轻量级的异步任务抽象类,它的内部主要由线程池以及 Handler 实现,在线程池中执行耗时任务并把结果通过 Handler 机制中转到主线程以实现UI操作。典型的用法如下: 从 Android3.0 开始,AsyncTask 默认是串行执行的: 如果需要并行执行可以这么做: AsyncTask 的源码不多,还是比较容易理解的。根据上边的用法,可以从 execute() 方法开始我们的分析: 看到 @MainThread 注解了吗?所以 execute() 方法需要在主线程执行哦! 进而又调用了 executeOnExecutor() : 可以看到,当任务正在执行或者已经完成,如果又被执行会抛出异常!回调方法 onPreExecute() 最先被执行了。 传入的 sDefaultExecutor 参数,是一个自定义的串行线程池对象,所有任务在该线程池中排队执行: 可以看到 SerialExecutor 线程池仅用于任务的排队, THREAD_POOL_EXECUTOR 线程池才是用于执行真正的任务,就是我们线程池部分讲到的 ThreadPoolExecutor : 再回到 executeOnExecutor() 方法中,那么 exec.execute(mFuture) 就是触发线程池开始执行任务的操作了。 那 executeOnExecutor() 方法中的 mWorker 是什么? mFuture 是什么?答案在 AsyncTask 的构造函数中: 原来 mWorker 是一个 Callable 对象, mFuture 是一个 FutureTask 对象,继承了 Runnable 接口。所以 mWorker 的 call() 方法会在 mFuture 的 run() 方法中执行,所以 mWorker 的 call() 方法在线程池得到执行! 同时 doInBackground() 方法就在 call() 中方法,所以我们自定义的耗时任务逻辑得到执行,不就是我们第二部分讲的那一套吗! doInBackground() 的返回值会传递给 postResult() 方法: 就是通过 Handler 将最终的耗时任务结果从子线程发送到主线程,具体的过程是这样的, getHandler() 得到的就是 AsyncTask 构造函数中初始化的 mHandler , mHander 又是通过 getMainHandler() 赋值的: 可以在看到 sHandler 是一个 InternalHandler 类对象: 所以 getHandler() 就是在得到在主线程创建的 InternalHandler 对象,所以 就可以完成耗时任务结果从子线程到主线程的切换,进而可以进行相关UI操作了。 当消息是 MESSAGE_POST_RESULT 时,代表任务执行完成, finish() 方法被调用: 如果任务没有被取消的话执行 onPostExecute() ,否则执行 onCancelled() 。 如果消息是 MESSAGE_POST_PROGRESS , onProgressUpdate() 方法被执行,根据之前的用法可以 onProgressUpdate() 的执行需要我们手动调用 publishProgress() 方法,就是通过 Handler 来发送进度数据: 进行中的任务如何取消呢?AsyncTask 提供了一个 cancel(boolean mayInterruptIfRunning) ,参数代表是否中断正在执行的线程任务,但是呢并不靠谱, cancel() 的方法注释中有这么一段: 大致意思就是调用 cancel() 方法后, onCancelled(Object) 回调方法会在 doInBackground() 之后被执行而 onPostExecute() 将不会被执行,同时你应该 doInBackground() 回调方法中通过 isCancelled() 来检查任务是否已取消,进而去终止任务的执行! 所以只能自己动手了: AsyncTask 整体的实现流程就这些了,源码是最好的老师,自己跟着源码走一遍有些问题可能就豁然开朗了!2023-07-21 18:23:181
android 大量多线程怎么优化
在程序开发的实践当中,为了让程序表现得更加流畅,我们肯定会需要使用到多线程来提升程序的并发执行性能。但是编写多线程并发的代码一直以来都是一个相对棘手的问题,所以想要获得更佳的程序性能,我们非常有必要掌握多线程并发编程的基础技能。众所周知,Android 程序的大多数代码操作都必须执行在主线程,例如系统事件(例如设备屏幕发生旋转),输入事件(例如用户点击滑动等),程序回调服务,UI 绘制以及闹钟事件等等。那么我们在上述事件或者方法中插入的代码也将执行在主线程。一旦我们在主线程里面添加了操作复杂的代码,这些代码就很可能阻碍主线程去响应点击/滑动事件,阻碍主线程的 UI 绘制等等。我们知道,为了让屏幕的刷新帧率达到 60fps,我们需要确保 16ms 内完成单次刷新的操作。一旦我们在主线程里面执行的任务过于繁重就可能导致接收到刷新信号的时候因为资源被占用而无法完成这次刷新操作,这样就会产生掉帧的现象,刷新帧率自然也就跟着下降了(一旦刷新帧率降到 20fps 左右,用户就可以明显感知到卡顿不流畅了)。为了避免上面提到的掉帧问题,我们需要使用多线程的技术方案,把那些操作复杂的任务移动到其他线程当中执行,这样就不容易阻塞主线程的操作,也就减小了出现掉帧的可能性。那么问题来了,为主线程减轻负的多线程方案有哪些呢?这些方案分别适合在什么场景下使用?Android 系统为我们提供了若干组工具类来帮助解决这个问题。AsyncTask: 为 UI 线程与工作线程之间进行快速的切换提供一种简单便捷的机制。适用于当下立即需要启动,但是异步执行的生命周期短暂的使用场景。HandlerThread: 为某些回调方法或者等待某些任务的执行设置一个专属的线程,并提供线程任务的调度机制。ThreadPool: 把任务分解成不同的单元,分发到各个不同的线程上,进行同时并发处理。IntentService: 适合于执行由 UI 触发的后台 Service 任务,并可以把后台任务执行的情况通过一定的机制反馈给 UI。了解这些系统提供的多线程工具类分别适合在什么场景下,可以帮助我们选择合适的解决方案,避免出现不可预期的麻烦。虽然使用多线程可以提高程序的并发量,但是我们需要特别注意因为引入多线程而可能伴随而来的内存问题。举个例子,在 Activity 内部定义的一个 AsyncTask,它属于一个内部类,该类本身和外面的 Activity 是有引用关系的,如果 Activity 要销毁的时候,AsyncTask 还仍然在运行,这会导致 Activity 没有办法完全释放,从而引发内存泄漏。所以说,多线程是提升程序性能的有效手段之一,但是使用多线程却需要十分谨慎小心,如果不了解背后的执行机制以及使用的注意事项,很可能引起严重的问题。2023-07-21 18:23:262
Android进程和线程的区别
线程是执行代码的最小单元,线程是跑在进程上的。2023-07-21 18:23:341
如何分析解决Android ANR
android代码完全没有错误,但是有时候会发出“应用程序×××无法响应”的异常,这让很多做android开发的人员很是郁闷,所以我这里就专门来解析一下,以及谈谈它的解决方案。 android 应用程序运行起来都有一个UI主线程,如果你把一个耗时的操作放在主线程里,而用户在5秒内没做任何输入(触摸屏幕或按钮),则这时候android系统就会自动弹出“应用程序×××无法响应”的异常,而且这时候还存在一个问题,程序阻塞在那里,用户界面就无法及时更新,界面卡在那里,造成假死机的状态;还有就是广播接收器的生命周期只有10s,如果耗时操作超过了这个值,也会弹出这个异常窗口,但对于Android平台来说UI控件都没有设计成为线程安全类型,所以需要引入一些同步的机制来使其刷新,这点Google在设计Android时倒是参考了下Win32的消息处理机制。 1. 对于线程中的刷新一个View为基类的界面,可以使用postInvalidate()方法在线程中来处理,其中还提供了一些重写方法比如postInvalidate(int left,int top,int right,int bottom) 来刷新一个矩形区域,以及延时执行,比如postInvalidateDelayed(long delayMilliseconds)或postInvalidateDelayed(long delayMilliseconds,int left,int top,int right,int bottom) 方法,其中第一个参数为毫秒,如下: 2. 当然推荐的方法是通过一个Handler来处理这些,可以在一个线程的run方法中调用handler对象的 postMessage或sendMessage方法来实现,Android程序内部维护着一个消息队列,会轮训处理这些,如果你是Win32程序员可以很好理解这些消息处理,不过相对于Android来说没有提供 PreTranslateMessage这些干涉内部的方法。 3. Looper又是什么呢? ,其实Android中每一个Thread都跟着一个Looper,Looper可以帮助Thread维护一个消息队列,昨天的问题 Can"t create handler inside thread 错误 一文中提到这一概念,但是Looper和Handler没有什么关系,我们从开源的代码可以看到Android还提供了一个Thread继承类HanderThread可以帮助我们处理,在HandlerThread对象中可以通过getLooper方法获取一个Looper对象控制句柄,我们可以将其这个Looper对象映射到一个Handler中去来实现一个线程同步机制,Looper对象的执行需要初始化Looper.prepare方法就是昨天我们看到的问题,同时推出时还要释放资源,使用Looper.release方法。 4.Message 在Android是什么呢? 对于Android中Handler可以传递一些内容,通过Bundle对象可以封装String、Integer以及Blob二进制对象,我们通过在线程中使用Handler对象的 sendEmptyMessage或sendMessage方法来传递一个Bundle对象到Handler处理器。对于Handler类提供了重写方法handleMessage(Message msg) 来判断,通过msg.what来区分每条信息。将Bundle解包来实现Handler类更新UI线程中的内容实现控件的刷新操作。相关的Handler对象有关消息发送sendXXXX相关方法如下,同时还有postXXXX相关方法,这些和Win32中的道理基本一致,一个为发送后直接返回,一个为处理后才返回 : 5. java.util.concurrent对象分析,对于过去从事Java开发的程序员不会对Concurrent对象感到陌生吧,他是JDK 1.5以后新增的重要特性作为掌上设备,我们不提倡使用该类,考虑到Android为我们已经设计好的Task机制,我们这里Android开发网对其不做过多的赘述,相关原因参考下面的介绍: 6. 在Android中还提供了一种有别于线程的处理方式,就是Task以及AsyncTask,从开源代码中可以看到是针对Concurrent的封装,开发人员可以方便的处理这些异步任务,具体的Android123在以前的文章中有详细解释,可以使用站内搜索来了解更多。 解决办法,就是把这个耗时操作放在一个服务组件去执行,并创建一个新的子线程,而不是放在UI线程里,android的new Thread/new Runnalbe好像在这里无效,而是要新建一个线程,并放在Handler里进行处理,以进行耗时操作的同时,不停的去循环监听UI线程,以及时响应用户的操作,以免造成假死机的状态。具体实现代码如下: //服务启动 public int onStartCommand(Intent intent, int flags, int startId) { //先在主线程里新建(实例化)一个HandlerThread 对象 HandlerThread handlerThread = new HandlerThread("handler_thread"); //在使用HandlerThread的getLooper()方法之前,必须先调用该类的start(); handlerThread.start(); //利用HandlerThread .getLooper实例化一个handler对象,该HandlerThread 与该messageQueue关联 MyHandler myHandler = new MyHandler(handlerThread.getLooper()); //获得该handler里的消息 Message msg = myHandler.obtainMessage(); //将msg发送到目标对象,所谓的目标对象,就是生成该msg对象的handler对象 msg.sendToTarget(); stopSelf(); return super.onStartCommand(intent, flags, startId); } //Handler类 class MyHandler extends Handler{ public MyHandler(){ } public MyHandler(Looper looper){ super(looper); } @Override public void handleMessage(Message msg) {//接收message发过来的消息 //耗时的操作 } }2023-07-21 18:23:411
android获取surfaceview里面的每一帧
屏幕的显示机制和帧动画类似,也是一帧一帧的连环画,只不过刷新频率很高,感觉像连续的。为了显示一帧,需要经历计算和渲染两个过程,CPU 先计算出这一帧的图像数据并写入内存,然后调用 OpenGL 命令将内存中数据渲染成图像存放在 GPU Buffer 中,显示设备每隔一定时间从 Buffer 中获取图像并显示。上述过程中的计算,对于View来说,就好比在主线程遍历 View树 以决定视图画多大(measure),画在哪(layout),画些啥(draw),计算结果存放在内存中,SurfaceFlinger 会调用 OpenGL 命令将内存中的数据渲染成图像存放在 GPU Buffer 中。每隔16.6ms,显示器从 Buffer 中取出帧并显示。所以自定义 View 可以通过重载onMeasure()、onLayout()、onDraw()来定义帧内容,但不能定义帧刷新频率。SurfaceView可以突破这个限制。而且它可以将计算帧数据放到独立的线程中进行。下面是自定义SurfaceView的模版代码:public abstract class BaseSurfaceView extends SurfaceView implements SurfaceHolder.Callback {public static final int DEFAULT_FRAME_DURATION_MILLISECOND = 50;//用于计算帧数据的线程private HandlerThread handlerThread;private Handler handler;//帧刷新频率private int frameDuration = DEFAULT_FRAME_DURATION_MILLISECOND;//用于绘制帧的画布private Canvas canvas;private boolean isAlive;public BaseSurfaceView(Context context) {super(context);init();}protected void init() {getHolder().addCallback(this);//设置透明背景,否则SurfaceView背景是黑的setBackgroundTransparent();}private void setBackgroundTransparent() {getHolder().setFormat(PixelFormat.TRANSLUCENT);setZOrderOnTop(true);}@Overridepublic void surfaceCreated(SurfaceHolder holder) {isAlive = true;startDrawThread();}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {stopDrawThread();isAlive = false;}//停止帧绘制线程private void stopDrawThread() {handlerThread.quit();handler = null;}//启动帧绘制线程private void startDrawThread() {handlerThread = new HandlerThread(“SurfaceViewThread”);handlerThread.start();handler = new Handler(handlerThread.getLooper());handler.post(new DrawRunnable());}private class DrawRunnable implements Runnable {@Overridepublic void run() {if (!isAlive) {return;}try {//1.获取画布canvas = getHolder().lockCanvas();//2.绘制一帧onFrameDraw(canvas);} catch (Exception e) {e.printStackTrace();} finally {//3.将帧数据提交getHolder().unlockCanvasAndPost(canvas);//4.一帧绘制结束onFrameDrawFinish();}//不停的将自己推送到绘制线程的消息队列以实现帧刷新handler.postDelayed(this, frameDuration);}}protected abstract void onFrameDrawFinish();protected abstract void onFrameDraw(Canvas canvas);}用HandlerThread作为独立帧绘制线程,好处是可以通过与其绑定的Handler方便地实现“每隔一段时间刷新”,而且在Surface被销毁的时候可以方便的调用HandlerThread.quit()来结束线程执行的逻辑。DrawRunnable.run()运用模版方法模式定义了绘制算法框架,其中帧绘制逻辑的具体实现被定义成两个抽象方法,推迟到子类中实现,因为绘制的东西是多样的,对于本文来说,绘制的就是一张张图片,所以新建BaseSurfaceView的子类FrameSurfaceView:逐帧解析 & 及时回收public class FrameSurfaceView extends BaseSurfaceView {public static final int INVALID_BITMAP_INDEX = Integer.MAX_VALUE;private List bitmaps = new ArrayList<>();//帧图片private Bitmap frameBitmap;//帧索引private int bitmapIndex = INVALID_BITMAP_INDEX;private Paint paint = new Paint();private BitmapFactory.Options options = new BitmapFactory.Options();//帧图片原始大小private Rect srcRect;//帧图片目标大小private Rect dstRect = new Rect();private int defaultWidth;private int defaultHeight;public void setDuration(int duration) {int frameDuration = duration / bitmaps.size();setFrameDuration(frameDuration);}public void setBitmaps(List bitmaps) {if (bitmaps == null || bitmaps.size() == 0) {return;}this.bitmaps = bitmaps;//默认情况下,计算第一帧图片的原始大小getBitmapDimension(bitmaps.get(0));}private void getBitmapDimension(Integer integer) {final BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true;BitmapFactory.decodeResource(this.getResources(), integer, options);defaultWidth = options.outWidth;defaultHeight = options.outHeight;srcRect = new Rec2023-07-21 18:23:482
android 中,怎么清除一个Handler里的所有消息队列?有什么方法么
final void removeMessages(int what)Remove any pending posts of messages with code "what" that are in the message queue.这个是我看API里面的一个方法。看解释应该是异常指定标识what对应的那个消息msg。。希望对你有帮助2023-07-21 18:23:563
android启动后怎么查看其里面的进程和线程
. Android中进程与进程、线程与线程之间如何通信?1)一个 Android 程序开始运行时,会单独启动一个Process。默认情况下,所有这个程序中的Activity或者Service都会跑在这个Process。默认情况下,一个Android程序也只有一个Process,但一个Process下却可以有许多个Thread。2)一个 Android 程序开始运行时,就有一个主线程Main Thread被创建。该线程主要负责UI界面的显示、更新和控件交互,所以又叫UI Thread。3)一个Android程序创建之初,一个Process呈现的是单线程模型--即MainThread,所有的任务都在一个线程中运行,所以,MainThread所调用的每一个函数,其耗时应该越短越好,而对于比较耗时的工作,应该交给子线程去做,以避免主线程(UI线程)被阻塞,导致程序出现ANR(Application not response)一个Activity就运行在一个线程中吗?或者编码时,如果不是明确安排在不同线程中的两个Activity,其就都是在同一个线程中?那从一个Activity跳转到另一个Activity时,是不是跳出的那个Activity就处在睡眠状态了?【答】 每个Activity都有一个Process属性,可以指定该Activity是属于哪个进程的。当然如果不明确指明,应该就是从属于默认进程(Application指定的,如其未指定,应该就是默认主进程)。Android中有Task的概念,而同一个Task的各个Activity会形成一个栈,只有站定的Activity才有机会与用户交互。原文地址:Android中的进程与线程 原文作者:江鹏当应用程序的组件第一次运行时,Android将启动一个只有一个执行线程的Linux进程。默认,应用程序所有的组件运行在这个进程和线程中。然而,你可以安排组件运行在其他进程中,且你可以为进程衍生出其它线程。本文从下面几点来介绍Android的进程与线程:1、进程组件运行于哪个进程中由清单文件控制。组件元素——<activity>、<service>、<receiver>、<provider>,都有一个process属性可以指定组件运行在哪个进程中。这个属性可以设置为每个组件运行在自己的进程中,或者某些组件共享一个进程而其他的不共享。他们还可以设置为不同应用程序的组件运行在同一个进程中——假设这些应用程序共享同一个Linux用户ID且被分配了同样的权限。<application>元素也有process属性,为所有的组件设置一个默认值。所有的组件都在特定进程的主线程中实例化,且系统调用组件是由主线程派遣。不会为每个实例创建单独的线程,因此,对应这些调用的方法——诸如View.onKeyDown()报告用用户的行为和生命周期通知,总是运行在进程的主线程中。这意味着,没有组件当被系统调用时应该执行很长时间或阻塞操作(如网络操作或循环计算),因为这将阻塞进程中的其它组件。你可以为长操作衍生独立的线程。public boolean onKeyDown(int keyCode,KeyEvent event):默认实现KeyEvent.Callback.onKeyMultiple(),当按下视图的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然后释放时执行,如果视图可用且可点击。参数keyCode-表示按钮被按下的键码,来自KeyEvent event-定义了按钮动作的KeyEvent对象返回值如果你处理事件,返回true;如果你想下一个接收者处理事件,返回false。当内存剩余较小且其它进程请求较大内存并需要立即分配,Android要回收某些进程,进程中的应用程序组件会被销毁。当他们再次运行时,会重新开始一个进程。当决定终结哪个进程时,Android会权衡他们对用户重要性的相对权值。例如,与运行在屏幕可见的活动进程相比(前台进程),它更容易关闭一个进程,它的活动在屏幕是不可见(后台进程)。决定是否终结进程,取决于运行在进程中的组件状态。关于组件的状态,将在后面一篇——组件生命周期中介绍。2、线程虽然你可能会将你的应用程序限制在一个进程中,但有时候你会需要衍生一个线程做一些后台工作。因为用户界面必须很快地响应用户的操作,所以活动寄宿的线程不应该做一些耗时的操作如网络下载。任何不可能在短时间完成的操作应该分配到别的线程。线程在代码中是用标准的Java线程对象创建的,Android提供了一些方便的类来管理线程——Looper用于在线程中运行消息循环、Handler用户处理消息、HandlerThread用户设置一个消息循环的线程。Looper类该类用户在线程中运行消息循环。线程默认没有消息循环,可以在线程中调用prepare()创建一个运行循环;然后调用loop()处理消息直到循环结束。大部分消息循环交互是通过Handler类。下面是一个典型的执行一个Looper线程的例子,分别使用prepare()和loop()创建一个初始的Handler与Looper交互: 1. Android中进程与进程、线程与线程之间如何通信?1)一个 Android 程序开始运行时,会单独启动一个Process。默认情况下,所有这个程序中的Activity或者Service都会跑在这个Process。默认情况下,一个Android程序也只有一个Process,但一个Process下却可以有许多个Thread。2)一个 Android 程序开始运行时,就有一个主线程Main Thread被创建。该线程主要负责UI界面的显示、更新和控件交互,所以又叫UI Thread。3)一个Android程序创建之初,一个Process呈现的是单线程模型--即MainThread,所有的任务都在一个线程中运行,所以,MainThread所调用的每一个函数,其耗时应该越短越好,而对于比较耗时的工作,应该交给子线程去做,以避免主线程(UI线程)被阻塞,导致程序出现ANR(Application not response)一个Activity就运行在一个线程中吗?或者编码时,如果不是明确安排在不同线程中的两个Activity,其就都是在同一个线程中?那从一个Activity跳转到另一个Activity时,是不是跳出的那个Activity就处在睡眠状态了?【答】 每个Activity都有一个Process属性,可以指定该Activity是属于哪个进程的。当然如果不明确指明,应该就是从属于默认进程(Application指定的,如其未指定,应该就是默认主进程)。Android中有Task的概念,而同一个Task的各个Activity会形成一个栈,只有站定的Activity才有机会与用户交互。原文地址:Android中的进程与线程 原文作者:江鹏当应用程序的组件第一次运行时,Android将启动一个只有一个执行线程的Linux进程。默认,应用程序所有的组件运行在这个进程和线程中。然而,你可以安排组件运行在其他进程中,且你可以为进程衍生出其它线程。本文从下面几点来介绍Android的进程与线程:1、进程组件运行于哪个进程中由清单文件控制。组件元素——<activity>、<service>、<receiver>、<provider>,都有一个process属性可以指定组件运行在哪个进程中。这个属性可以设置为每个组件运行在自己的进程中,或者某些组件共享一个进程而其他的不共享。他们还可以设置为不同应用程序的组件运行在同一个进程中——假设这些应用程序共享同一个Linux用户ID且被分配了同样的权限。<application>元素也有process属性,为所有的组件设置一个默认值。所有的组件都在特定进程的主线程中实例化,且系统调用组件是由主线程派遣。不会为每个实例创建单独的线程,因此,对应这些调用的方法——诸如View.onKeyDown()报告用用户的行为和生命周期通知,总是运行在进程的主线程中。这意味着,没有组件当被系统调用时应该执行很长时间或阻塞操作(如网络操作或循环计算),因为这将阻塞进程中的其它组件。你可以为长操作衍生独立的线程。public boolean onKeyDown(int keyCode,KeyEvent event):默认实现KeyEvent.Callback.onKeyMultiple(),当按下视图的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然后释放时执行,如果视图可用且可点击。参数keyCode-表示按钮被按下的键码,来自KeyEvent event-定义了按钮动作的KeyEvent对象返回值如果你处理事件,返回true;如果你想下一个接收者处理事件,返回false。当内存剩余较小且其它进程请求较大内存并需要立即分配,Android要回收某些进程,进程中的应用程序组件会被销毁。当他们再次运行时,会重新开始一个进程。当决定终结哪个进程时,Android会权衡他们对用户重要性的相对权值。例如,与运行在屏幕可见的活动进程相比(前台进程),它更容易关闭一个进程,它的活动在屏幕是不可见(后台进程)。决定是否终结进程,取决于运行在进程中的组件状态。关于组件的状态,将在后面一篇——组件生命周期中介绍。2、线程虽然你可能会将你的应用程序限制在一个进程中,但有时候你会需要衍生一个线程做一些后台工作。因为用户界面必须很快地响应用户的操作,所以活动寄宿的线程不应该做一些耗时的操作如网络下载。任何不可能在短时间完成的操作应该分配到别的线程。线程在代码中是用标准的Java线程对象创建的,Android提供了一些方便的类来管理线程——Looper用于在线程中运行消息循环、Handler用户处理消息、HandlerThread用户设置一个消息循环的线程。Looper类该类用户在线程中运行消息循环。线程默认没有消息循环,可以在线程中调用prepare()创建一个运行循环;然后调用loop()处理消息直到循环结束。大部分消息循环交互是通过Handler类。下面是一个典型的执行一个Looper线程的例子,分别使用prepare()和loop()创建一个初始的Handler与Looper交互: 2.1、远程过程调用(Remote procedure calls,RPCs)Android有一个轻量级的远程过程调用机制——方法在本地调用却在远程(另外一个进程中)执行,结果返回给调用者。这需要将方法调用和它伴随的数据分解为操作系统能够理解的层次,从本地进程和地址空间传输到远程进程和地址空间,并重新组装调用。返回值以相反方向传输。Android提供了做这些工作的所有代码,这样我们可以专注于定义和执行RPC接口本身。一个RPC接口仅包含方法。所有的方法同步地执行(本地方法阻塞直到远程方法执行完成),即使是没有返回值。简言之,该机制工作原理如下:首先,你用简单的IDL(interface definition language,接口定义语言)声明一个你想实现的RPC接口。从这个声明中,aidl工具生成一个Java接口定义,提供给本地和远程进程。它包含两个内部类,如下图所示:内部类有管理你用IDL定义的接口的远程过程调用所需要的所有代码。这两个内部类都实现了IBinder接口。其中之一就是在本地由系统内部使用,你写代码可以忽略它。另外一个是Stub,扩展自Binder类。除了用于有效地IPC(interprocess communication)调用的内部代码,内部类在RPC接口声明中还包含方法声明。你可以定义Stub的子类实现这些方法,如图中所示。通常情况下,远程过程有一个服务管理(因为服务能通知系统关于进程和它连接的其它进程的信息)。它有由aidl工具生成的接口文件和Stub子类实现的RPC方法。服务的客户端仅有由aidl工具生成的接口文件。下面介绍服务如何与它的客户端建立连接:· 服务的客户端(在本地端的)应该实现onServiceConnected() 和onServiceDisconnected() 方法,因此当与远程服务建立连接成功和断开连接是会通知它。然后调用bindService() 建立连接。 · 服务的onBind()方法将实现为接受或拒绝连接,者取决于它接受到的意图(该意图传送到binServive())。如果连接被接受,它返回一个Stub子类的实例。 · 如果服务接受连接,Android调用客户端的onServiceConnected()方法且传递给它一个IBinder对象,返回由服务管理的Stub子类的一个代理。通过代理,客户端可以调用远程服务。 这里只是简单地描述,省略了一些RPC机制的细节。你可以查阅相关资料或继续关注Android开发之旅,后面将为你奉上。2.2、线程安全方法在一些情况下,你实现的方法可能会被不止一个线程调用,因此必须写成线程安全的。这对远程调用方法是正确的——如上一节讨论的RPC机制。当从IBinder进程中调用一个IBinder对象中实现的一个方法,这个方法在调用者的线程中执行。然而,当从别的进程中调用,方法将在Android维护的IBinder进程中的线程池中选择一个执行,它不在进程的主线程中执行。例如,一个服务的onBind()方法在服务进程的主线程中被调用,在onBind()返回的对象中执行的方法(例如,实现RPC方法的Stub子类)将在线程池中被调用。由于服务可以有一个以上的客户端,所以同时可以有一个以上的线程在执行同一个IBinder方法。因此,IBinder的方法必须是线程安全的。同样,一个内容提供者可以接受其它进程产生的数据请求。虽然ContentResolver 和 ContentProvider 类隐藏进程通信如何管理的,对应哪些请求的ContentResolver 方法——query()、insert()、delete()、update()、getType(),在内容提供者的进程的线程池中被调用,而不是在这一进程的主线程中。因为这些方法可以同时从任意数量的线程中调用,他们也必须实现为线程安全的。2023-07-21 18:24:041
2个月小狗怎么训练大小便
第一、在狗狗排尿排便之后,把它的排泄物放到主人希望狗狗排泄的地点,然后把其它地方的粪便气味打扫干净。第二、在狗狗即将排泄时将狗狗带到指定地点排泄。第三、在狗狗在指定地点排泄完后,主人可以适当言语夸奖,摸摸狗狗的头或者是物质奖励。过一段时间狗狗就学会上厕所了。但一般训练狗狗上厕所可以在三个月以后进行,二月龄的幼犬训练上厕所可能效果不太理想。1、让幼犬适应新环境通常来讲,一般购买回来的狗狗都是已经断奶年龄到达两三个月的小奶狗。此时他们需要面临的问题就是离开妈妈,到一个新的环境当中去生活,所以当你把小奶狗带回家的第一件事就应该是让他们尽快熟悉环境。当幼犬到达一个新的环境之后,它们一般都会处于高度紧张和警惕的状态,所以这期间一定要避免容易让他们产生惊吓的情况,比如说巨大的声响和刺激性的灯光。建议先将他们隔离在一个比较小的区域当中,在里面准备好温暖的狗窝。当他们适应了这个小环境之后就可以再慢慢让他们适应整个大环境了。一般来说,小奶狗只要通过三到五天的适应期就可以完全适应新的环境了。2、日常护理方面对于小奶狗的日常护理是一件非常重要的事情。在照顾它们的这段期间里,一定要注意他们的自身卫生和环境的卫生,因为此时他们身体抵抗力是非常弱的,很容易遭到细菌感染而生病。其次就是喂食需要选用幼犬专用狗粮用温水泡软之后再给他们吃。至于出门遛弯最好是选在它们已经完全适应了家庭情况的三个星期以后。3、驱虫和疫苗的注射所有的小奶狗都是需要进行驱虫和疫苗注射的。在将小奶狗带回家之后,应该先观察一个星期,一个星期之后,若小奶狗的身体依然处于健康状态就可以带他们去进行疫苗的注射了。此后就是要注意定期带他们去医院进行体内外驱虫。此外需要注意的就是体外驱虫跟疫苗的注射并不会产生冲突,所以是可以同时进行的。2023-07-21 18:24:275
android创建子线程
创建后台线程的方法有多种,这里说三种,可以回去试试 1、使用Android系统工具类 AsyncTask(Params,Progress,Result)AsyncTask是一个轻量级线程,三个泛型参数分别是 Params传入参数,int型Progress为进度条进度,Result为返回值要使用AsyncTask,必须继承之并复写其中的几个重要的函数。onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。doInBackground(Params...), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。onProgressUpdate(Progress...),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread. 注:Task必须在UI线程中创建,并调用并且只能调用一次execute方法,该方法的参数为传入的泛型Params。 其余函数最好不要手动调用,容易造成线程崩溃。多次调用Task,容易造成线程池溢出。 2、使用Handler和HandlerThread误区: Handler handler = new Handler (); handler.post(r);这种做法看似创建了一个线程,但实际上handler只是直接调用Runnable中的run() 方法,而不执行线程的start()函数,所以这两句代码执行后,程序仍然在UI线程中执行。所以我们引入HandlerThread,因为HandlerThread中有一个Looper对象,用以循环消息队列。为了使用Looper,必须子类化Handler,并复写它的构造函数。class MyHandler extends Handler{ public MyHandler() {} public MyHandler(Looper looper){ super (looper); } public void handleMessage(Message msg){ //....这里运行耗时的过程 } } } handleMessage(Message msg)函数用以接收消息,msg则是从UI线程中发出的消息,可以传递各种对象,根据这些对象和数值进行操作。有了Handler子类,则可以在UI线程中进行创建和初始化HandlerThread handlerThread = new HandlerThread( "backgroundThread" ); handlerThread.start(); MyHandler myHandler = new MyHandler(handlerThread.getLooper()); Message msg = myHandler.obtainMessage();//....此处对msg进行赋值,可以创建一个Bundle进行承载msg.sendToTarget();之后如果需要调用线程,只要对handler传入msg,就可以执行相应的过程了最后,很重要的一点,HandlerThread 不随Activity的生命周期结束而消亡,所以必须复写Ondestory(),调用HandlerThread .stop() 3、使用线程同步 synchronized、 wait()、 notify()使用线程同步机制synchronized实现多线程操作,相对来说比较复杂,但也是灵活性最佳、效率最高的一种做法,在产品开发当中也使用得最广。本人水平相当有限,也只学得一点皮毛。synchronized相当于一把锁,当线程运行的时候,会同时有几个线程访问一个对象,对其进行操作或者修改。这可能引起很不良的后果(例如改变判定条件,或者在别的线程还没使用完的时候对象已经被析构等等),所以必须对一些对象进行加锁,保证它在同一时间只允许被一个线程访问。synchronized使用方法有两种:<1> 同步方法在方法名前加入synchronized关键字,则该方法在同一时间内只允许一个线程访问它,其代码逻辑较为简单,但使用起来不太灵活,并且大大降低效率,耗时太长的同步方法甚至会使得多线程失去原本的意义成为单线程<2>同步参数 对某一个代码块加锁,并且以synchronized(obj)的方式,表明该代码块内的obj对象是线程同步的。这个做法使得代码灵活性大大加强,缩小同步代码块的范围,并且可以在不同的函数体和类内进行同步,唯一遗憾的是实现起来比较复杂,容易产生一些问题 而wait()和notify(),必须在synchronized锁的代码块内执行,否则系统将会报错。有了以上基础,就可以很容易的创建后台线程了 Private Runnable backgroundRunnable = new Runnable () { @Override public void run() { if(isFinish){ //..... break; } for(;;){ synchronized(this){ //....写耗时过程 wait(); } } }} UI线程中,就是一般的创建线程过程了 Thread backgroundThread = new Thread (backgroundRunnable); backgroundThread.start();这样,在后台线程中会不断的循环,当执行完一次过程以后就会wait()休眠。然后在OnTouchListener或者OnClickListener内相应的位置执行synchronized(backgroundRunnable){ backgroundRunnable.notify();}当用户触摸屏幕产生一个event的时候,线程就会被唤醒,执行下一次循环。最后,还是内存安全问题,必须复写Activity中的OnDestory()方法,将标志量isFinish设为false,并且backgroundThread .stop()2023-07-21 18:24:432
墨玉怎么鉴别?
1、和田墨玉为软玉,所以第一条鉴定其是不是和田玉。2、墨玉中含有大量其他矿物,真正的墨玉应该是全黑,不透明,聚光灯打下去应当不会是带有碧玉的绿色调。3、还有一种黑色的喀瓦石,呈全墨色,但是硬度不够,就不可称为墨玉。扩展资料:如果想要分辨墨玉,首先还要先了解一些极其容易与墨玉混淆的玉石,例如卡瓦石、新疆黑碧玉、黑青玉等。黑色的玉一共有三种底。如果是白玉底那就是墨玉,青玉底就是黑青玉,碧玉底就是墨碧玉了。要是墨玉的话,应该透白光。是黑青玉的话,就透黄绿色的光。是墨碧玉的话,就透绿光的;其实碧玉颜色是十分暗淡的,即使打光也是透着翠绿光。如果是黑色卡瓦石的话,应该是透着绿白相间的光,或是不透光。但是不透光不一定全是卡瓦石,它还有可能是一些不知道名字的石头。因为墨玉中是含有石墨的,如果在灯光照耀下,是可以看到白玉地张上一些黑点。虽然墨玉是越墨黑越优质,但是玉的边缘又要刚刚好透光,不然就看不出白玉地张,那跟黑石头没什么两样,很容易被人曲解。一句话来说,想要真正分辨墨玉,还是要多看,多玩,多学习。参考资料来源:百度百科:墨玉2023-07-21 18:24:5412
Service服务详解以及怎么使service服务不被杀死
Services服务是一个应用程序组件,可以在后台执行长时间运行的操作,不提供用户界面。一个应用程序组件可以启动一个服务,它将继续在后台运行,即使用户切换到另一个应用程序。此外,一个组件可以绑定到一个服务与它交互,甚至执行进程间通信(IPC)。例如,一个服务可能处理网络通信,播放音乐,执行文件I/O,或与一个内容提供者交互,都在后台执行。一个服务本质上讲有两种形式:Started 启动的started 形式的服务是指当一个应用组件(比如 activity )通过startService()方法开启的服务。一旦开启,该服务就可以 无限期 地在后台运行,哪怕开启它的组件被销毁掉。通常,开启的服务执行一个单独的操作且并不向调用者返回一个结果。比如,可能从网络进行下载或者上传一个文件。当任务完成,服务就该自我停止。Bound 绑定的bound 形式的服务是指一个应用组件通过调用 bindService() 方法与服务绑定。一个绑定的服务提供一个客户-服务端接口,以允许组件与服务交互,发送请求,获得结果,甚至执行进程间通信。一个绑定的服务只和与其绑定的组件同时运行。多个组件可以同时绑定到一个服务,但当全部接触绑定后,服务就被销毁。虽然分这两类,但是一个服务可以同时使用这两种方式——可以用 started 无限期的运行,同时允许绑定。只需要在服务中实现两个回调方法: onStartCommand() 允许组件开启服务, onBind() 允许绑定。不论应用程序是怎么起服务的, 任何 应用程序都可以用这个服务。同样的,任何组件可以使用一个 Activity 通过传递 Intent 开启服务。你也可以在配置文件设置服务为私有来防止其他应用访问该服务。注意: 一个服务在进程中的主线程运行——一个服务 不会 创建自己的线程,也 不会 在另外的进程运行(除非另外指定)。这意味着,如果服务需要做一些频繁占用CPU的工作或者会发生阻塞的操作,你需要在服务中另开线程执行任务。这可以降低产生ANR的风险,提高用户体验。基础创建一个服务需要建立一个 Service 相关的子类,然后需要实现一些回调方法,好在不同的生命周期内做对应处理和绑定服务,比较重要的方法如下:onStartCommand() 当其他组件,如 activity 请求服务启动时,系统会调用这个方法。一旦这个方法执行,服务就开始并且无限期的执行。如果实现这个方法,当这个服务完成任务后,需要你来调用 stopSelf() 或者 stopService() 停掉服务。如果只想提供绑定,不需要自己实现这个方法。onBind() 当有其他组件想通过 bindService() 方法绑定这个服务时系统就会调用此方法。在实现的方法里面,必须添加一个供客户端使用的接口通过返回一个 IBinder 来与服务通信,这个方法必须实现。当然不想允许绑定的话,返回 null 即可。onCreate() 服务第一次建立的时候会调用这个方法,执行一次性设置程序,在上面2个方法执行前调用。如果服务已存在,则不执行该方法。onDestroy() 服务不再使用则使用该方法。服务应该实现这个方法来清理诸如线程,注册的监听器等资源。这是最后调用的方法。安卓系统只会在内存占用很高,必须恢复系统资源供当前运行程序的情况下强制停掉一个运行中的服务。如果服务绑定在当前运行的程序中,就几乎不会被杀掉,如果服务声明了在前台运行(其实在后台,只是给系统一个错的信息来提高优先级),就几乎不会被杀掉。另外,如果一个服务正在运行,且运行了很久,系统就会根据运行时间把其排在后台任务列表的后面,则这个服务很容易被杀掉。根据onStartCommand() 的返回值设置,服务被杀掉后仍可以在资源充足的条件下立即重启。是用一个服务好还是开一个线程好一个服务就是一个可以忽略交互,在后台独立运行的组件,如果你需要这样就用服务如果你需要在用户与程序交互时在主线程外执行任务,那就开个线程吧。比如想播放音乐,但只在程序运行时播放,你可能在 onCreate() 开一个线程,在 onStart() 中开启它,在 onStop() 停止它。也可以考虑使用 AsyncTask 或者HandlerThread 取代一般的线程。记住,如果使用一个服务,它还是默认在主线程中运行,如果会发生阻塞,还是要在服务中另开线程的。在 manifest 文件声明服务要使用服务就必须在 manifest 文件声明要用的所有服务,只用在<application> 标签内添加子标签 <service> 即可。 <manifest ...> ... <application ...> <service android:name=".ExampleService" android:enabled=["true" | "false"] android:exported=["true" | "false"] android:isolatedProcess=["true" | "false"] android:label="string resource" android:icon="drawable resource" android:permission="string" android:process="string" > ... </service> </application> </manifest>下面对 service 标签属性做说明android:name你所编写的服务类的类名,可填写完整名称,包名+类名,如com.example.test.ServiceA ,也可以忽略包名,用 . 开头,如 .ServiceA,因为在 manifest 文件开头会定义包名,它会自己引用。一旦你发布应用,你就不能改这个名字(除非设置 android:exported="false"),另外 name 没有默认值,必须定义。android:enabled是否可以被系统实例化,默认为 true因为父标签 <application> 也有 enable 属性,所以必须两个都为默认值true 的情况下服务才会被激活,否则不会激活。android:exported其他应用能否访问该服务,如果不能,则只有本应用或有相同用户ID的应用能访问。当然除了该属性也可以在下面 permission 中限制其他应用访问本服务。这个默认值与服务是否包含意图过滤器 intent filters 有关。如果一个也没有则为 falseandroid:isolatedProcess 设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(binding and starting)。android:label 可以显示给用户的服务名称。如果没设置,就用 <application> 的 lable 。不管怎样,这个值是所有服务的意图过滤器的默认 lable 。定义尽量用对字符串资源的引用。android:icon 类似 label ,是图标,尽量用 drawable 资源的引用定义。android:permission是一个实体必须要运行或绑定一个服务的权限。如果没有权限,startService() , bindService() 或 stopService() 方法将不执行,Intent 也不会传递到服务。如果属性未设置,会由 <application> 权限设置情况应用到服务。如果两者都未设置,服务就不受权限保护。android:process服务运行所在的进程名。通常为默认为应用程序所在的进程,与包名同名。<application> 元素的属性 process 可以设置不同的进程名,当然组件也可设置自己的进程覆盖应用的设置。如果名称设置为冒号 : 开头,一个对应用程序私有的新进程会在需要时和运行到这个进程时建立。如果名称为小写字母开头,服务会在一个相同名字的全局进程运行,如果有权限这样的话。这允许不同应用程序的组件可以分享一个进程,减少了资源的使用。创建“启动的”服务启动的(started)服务由 startService(Intent) 方法启动,在服务中的 onStartCommand()方法里获得 Intent 信息。关闭则由服务自己的方法 stopSelf() 或者由启动服务的地方调用 stopService(Intent) 方法来关闭。并不会因为启动服务的应用程序销毁而关闭。示例,一个应用需要保存数据到远程数据库,这时启动一个服务,通过创建启动的服务给服务传递数据,由服务执行保存行为,行为结束再自我销毁。因为服务跟启动它的应用在一个进程的主线程中,所以对于耗时的操作要起一个新的线程去做。//activity中Intent intent = new Intent(MainActivity.this, ServiceA.class);intent.putExtra("name", strName);startService(intent);//service中@Overridepublic int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub // 获取数据 String strName = intent.getStringExtra("name"); // ... 数据库操作 new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub 耗时的操作 } }).run(); return Service.START_STICKY;}写服务有2种,继承 service 或者 IntentService 。后者是前者的子类。前者包含上面介绍的各种方法,用于普通的服务。后者可以自己开一个工作线程一个接一个处理多个请求。继承IntentService大多数服务不需要同时处理多个请求,继承 IntentService 是最好的选择IntentService处理流程创建默认的一个 worker 线程处理传递给 onStartCommand() 的所有 intent ,不占据应用的主线程创建一个工作队列一次传递一个 intent 到你实现的 onHandleIntent() 方法,避免了多线程在所以启动请求被处理后自动关闭服务,不需要调用 stopSelf()默认提供 onBind() 的实现,并返回 null默认提供 onStartCommand() 的实现,实现发送 intent 到工作队列再到你的onHandleIntent() 方法实现。这些都加入到 IntentService 中了,你需要做的就是实现构造方法和onHandleIntent() ,如下:public class HelloIntentService extends IntentService { /** * A constructor is required, and must call the super IntentService(String) * constructor with a name for the worker thread. */ public HelloIntentService() { super("HelloIntentService"); } /** * The IntentService calls this method from the default worker thread with * the intent that started the service. When this method returns, IntentService * stops the service, as appropriate. */ @Override protected void onHandleIntent(Intent intent) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. long endTime = System.currentTimeMillis() + 5*1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } }}如果需要重写其他回调方法,如 onCreate() , onStartCommand() 等,一定要调用super() 方法,保证 IntentService 正确处理 worker 线程,只有 onHandleIntent()和 onBind() 不需要这样。如:@Overridepublic int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); return super.onStartCommand(intent,flags,startId);}继承Service继承 Service 就可以实现对请求多线程的处理,前面介绍了 service 的生命周期,可以按照生命周期实现方法。就不放示例了。onStartCommand() 的返回值返回一个整型值,用来描述系统在杀掉服务后是否要继续启动服务,返回值有三种:START_NOT_STICKY 系统不重新创建服务,除非有将要传递来的 intent 。这是最安全的选项,可以避免在不必要的时候运行服务。START_STICKY 系统重新创建服务并且调用 onStartCommand() 方法,但并不会传递最后一次传递的 intent ,只是传递一个空的 intent 。除非存在将要传递来的 intent ,那么就会传递这些 intent 。这个适合播放器一类的服务,不需要执行命令,只需要独自运行,等待任务。START_REDELIVER_INTENT 系统重新创建服务并且调用 onStartCommand() 方法,传递最后一次传递的intent 。其余存在的需要传递的intent会按顺序传递进来。这适合像下载一样的服务,立即恢复,积极执行。如果想从服务获得结果,可以用广播来处理创建“绑定的”服务用 bindService() 方法将应用组件绑定到服务,建立一个长时间保持的联系。如果需要在 activity 或其他组件和服务交互或者通过进程间通信给其他应用程序提供本应用的功能,就需要绑定的服务。建立一个绑定的服务需要实现 onBind() 方法返回一个定义了与服务通信接口的IBinder 对象。其他应用程序组件可以调用 bindService() 方法获取接口并且调用服务上的方法。创建一个绑定的服务,第一件事就是定义一个说明客户端与服务通信方式的接口。这个接口必须是 IBinder 的实现,并且必须要从 onBind() 方法返回。一旦客户端接收到了 IBinder ,就可以通过这个接口进行交互。多个客户端可以绑定到一个服务,可以用 unbindService() 方法解除绑定,当没有组件绑定在服务上,这个服务就会被销毁。//activity中private ServiceConnection connB = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub Log.v(tag, "Service B disconnected"); } @Override public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub Log.v(tag, "Service B connected"); MyBinderB binder = (MyBinderB) service; ServiceB SB = binder.getService(); SB.showLog(); }};@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.button1).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Intent a = new Intent(MainActivity.this, ServiceB.class); bindService(a, connB, BIND_AUTO_CREATE); }}//ServiceBpublic class ServiceB extends Service { public void showLog() { Log.i(tag, "serviceB-->showLog()"); } public class MyBinderB extends Binder { public ServiceB getService() { return ServiceB.this; } } private MyBinderB myBinderB = new MyBinderB(); @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return myBinderB; }}启动前台服务前台服务是被认为是用户已知的正在运行的服务,当系统需要释放内存时不会优先杀掉该进程。前台进程必须发一个 notification 在状态栏中显示,直到进程被杀死。因为前台服务会一直消耗一部分资源,但不像一般服务那样会在需要的时候被杀掉,所以为了能节约资源,保护电池寿命,一定要在建前台服务的时候发notification ,提示用户。当然,系统提供的方法就是必须有 notification 参数的,所以不要想着怎么把 notification 隐藏掉。@Overridepublic int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub Intent notificationIntent = new Intent(this, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); Notification noti = new Notification.Builder(this) .setContentTitle("Title") .setContentText("Message") .setSmallIcon(R.drawable.ic_launcher) .setContentIntent(pendingIntent) .build(); startForeground(12346, noti); return Service.START_STICKY;}startForeground() 方法就是将服务设为前台服务。参数12346就是这个通知唯一的id,只要不为0即可。2023-07-21 18:26:551
android 如何关闭NTP网络时间同步?
Linux停止ntp服务即可关闭时间同步。①关闭ntp服务(临时设置重启后自动开启)service ntpd stop②设置永久关闭ntp服务(重启后也是关闭的)chkconfig ntpd off2023-07-21 18:27:042
android intentservice能代替service吗
Service 在官方API的描述为A Service is an application component representing either an application"s desire to perform a longer-running operation while not interacting with the user or to supply functionality for other applications to use. Each service class must have a corresponding <service> declaration in its package"sAndroidManifest.xml. Services can be started with Context.startService() and Context.bindService().Note that services, like other application objects, run in the main thread of their hosting process. This means that, if your service is going to do any CPU intensive (such as MP3 playback) or blocking (such as networking) operations, it should spawn its own thread in which to do that work. More information on this can be found in Processes and Threads. The IntentService class is available as a standard implementation of Service that has its own thread where it schedules its work to be done.主要用于后台服务 当应用程序被挂到后台的时候,为了保证应用某些组件仍然可以工作而引入了Service这个概念,那么这里面要强调的是Service不是独立的进程,也不是独立的线程,它是依赖于应用程序的主线程的,在更多时候不建议在Service中编写耗时的逻辑和操作,否则会引起ANR。这时需要引入IntentService,IntentService是继承Service的,那么它包含了Service的全部特性,当然也包含service的生命周期,那么与service不同的是,IntentService在执行onCreate操作的时候,内部开了一个线程,去执行你的耗时操作。service提供了一个方法 onStartCommand 当service启动或被调用时候会执行这个方法。onStartCommand(Intent intent, int flags, int startId)Called by the system every time a client explicitly starts the service by calling startService(Intent), providing the arguments it supplied and a unique integer token representing the start request.IntentService:异步处理服务,新开一个线程:handlerThread在线程中发消息,然后接受处理完成后,会清理线程,并且关掉服务。IntentService有以下特点:(1) 它创建了一个独立的工作线程来处理所有的通过onStartCommand()传递给服务的intents。(2) 创建了一个工作队列,来逐个发送intent给onHandleIntent()。(3) 不需要主动调用stopSelft()来结束服务。因为,在所有的intent被处理完后,系统会自动关闭服务。(4) 默认实现的onBind()返回null(5) 默认实现的onStartCommand()的目的是将intent插入到工作队列中顶2踩2023-07-21 18:27:121
做混合的话Uniapp和Flutter我应该学哪个啊?
u200d本文由阿里闲鱼技术团队祈晴分享,本次有修订和改动,感谢作者的技术分享。1、内容概述本文总结了阿里闲鱼技术团队使用Flutter在对闲鱼IM进行移动端跨端改造过程中的技术实践等,文中对比了传统Native与现在大热的Flutter跨端方案在一些主要技术实现上的差异,以及针对Flutter技术特点的具体技术实现,值得同样准备使用Flutter开发IM的技术同行们借鉴和参考。学习交流:- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM》- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK(本文同步发布于:http://www.52im.net/thread-3615-1-1.html)2、闲鱼IM现状闲鱼IM的移动端框架构建于2016至2017年间,期间经过多次迭代升级导致历史包袱累积多,后面又经历IM界面的Flutter化,从而造成了客户端架构愈加复杂。从开发层面总结闲鱼IM移动端当前架构主要存在如下几个问题:1)研发效率较低:当前架构涉及到Android/iOS双端的逻辑代码以及Flutter的UI代码,定位问题往往只能从Flutter UI表相倒查到Native逻辑层;2)架构层次较差:架构设计上分层不清晰,业务逻辑夹杂在核心的逻辑层致使代码变更风险大;3)性能测试略差:核心数据源存储Native内存,需经Flutter Plugin将数据源序列化上抛Flutter侧,在大批量数据源情况下性能表现较差。从产品层面总结闲鱼IM移动端当前架构的主要问题如下:1)定位问题困难:线上舆情反馈千奇百怪,测试始终无法复现相关场景,因此很多时候只能靠现象猜测本质;2)疑难杂症较多:架构的不稳定性造成出现的问题反复出现,当前疑难杂症主要包括未读红点计数、iPhone5C低端机以及多媒体发送等多个问题;3)问题差异性大:Android和iOS两端逻辑代码差异大,包括埋点逻辑都不尽相同,排查问题根源时双端都会有不同根因,解决方案也不相同。3、业界的移动端跨端方案为解决当前IM的技术痛点,闲鱼今年特起关于IM架构升级项目,重在解决客户端中Andriod和iOS双端一致性的痛点,初步设想方案就是实现跨端统一的Android/iOS逻辑架构。在当前行业内跨端方案可初步归类如下图架构:在GUI层面的跨端方案有Weex、ReactNative、H5、Uni-APP等,其内存模型大多需要通过桥接到Native模式存储。在逻辑层面的跨端方案大致有C/C++等与虚拟机无关语言实现跨端,当然汇编语言也可行。此外有两个独立于上述体系之外的架构就是Flutter和KMM(谷歌基于Kotlin实现类似Flutter架构),其中Flutter运行特定DartVM,将内存数据挂载其自身的isolate中。考虑闲鱼是Flutter的前沿探索者,方案上优先使用Flutter。然而Flutter的isolate更像一个进程的概念(底层实现非使用进程模式),相比Android,同一进程场景中,Android的Dalvik虚拟机多个线程运行共享一个内存Heap,而DartVM的Isolate运行隔离各自的Heap,因而isolate之间通讯方式比较繁琐(需经过序列化反序列化过程)。整个模型如下图所示:若按官方混合架构实现Flutter应用,开启多个FlutterAcitivty/FlutterController,底层会生成多个Engine,对应会存在多个isolate,而isolate通讯类似于进程通讯(类似socket或AIDL),这里借鉴闲鱼FlutterBoost的设计理念,FlutterIM架构将多个页面的Engine共享,则内存模型就天然支持共享读取。原理图如下:4、闲鱼IM基于Flutter的架构设计4.1 新老架构对比如下图所示:是一个老架构方案,其核心问题主要集中于Native逻辑抽象差,其中逻辑层面还设计到多线程并发使得问题倍增,Android/iOS/Flutter交互繁杂,开发维护成本高,核心层耦合较为严重,无插拔式概念.考虑到历史架构的问题,演进如下新架构设计:如上图所示,架构从上至下依次为:1)业务层;2)分发层;3)逻辑层;4)数据源层。数据源层来源于推送或网络请求,其封装于Native层,通过Flutter插件将消息协议数据上抛到Flutter侧的核心逻辑层,处理完成后变成Flutter DB的Enitity实体,实体中挂载一些消息协议实体。核心逻辑层将繁杂数据扁平化打包挂载到分发层中的会话内存模型数据或消息内存模型数据,最后通过观察者模式的订阅分发到业务逻辑中。Flutter IM重点集中改造逻辑层和分发层,将IM核心逻辑和业务层面数据模型进行封装隔离,核心逻辑层和数据库交互后将数据封装到分发层的moduleData中,通过订阅方式分发到业务层数据模型中。此外在IM模型中DB也是重点依赖的,个人对DB数据库管理进行全面封装解,实现一种轻量级,性能佳的Flutter DB管理框架。4.2 DB存储模型Flutter IM架构的DB存储依赖数据库插件,目前主流插件是Sqflite。其存储模型如下:依据上图Sqflite插件的DB存储模型会有2个等待队列:一个是Flutter层同步执行队列;一个是Native层的线程执行队列。其Android实现机制是HandlerThread,因此Query/Save读写在会同一线程队列中,导致响应速度慢,容易造成DB SQL堆积,此外缺失缓存模型。于是个人定制如下改进方案:Flutter侧通过表的主键设计查询时候会优先从Entity Cache层去获取,若缓存不存在,则通过Sqflite插件查询。同时改造Sqflite插件成支持sync/Async同步异步两种方式操作,对应到Native侧也会有同步线程队列和异步线程队列,保证数据吞吐率。但是这里建议查询使用异步,存储使用同步更稳妥,主要怕出现多个相同的数据元model同一时间进入异步线程池中,存储先后顺序无法有效的保证。4.3 ORM数据库方案IM架构重度依赖DB数据库,而当前业界还没有一个完备的数据库ORM管理方案,参考了Android的OrmLite/GreenDao,个人自行设计一套Flutter ORM数据库管理方案。其核心思想如下:由于Flutter不支持反射,因此无法直接像Android的开源数据库方式操作,但可通过APT方式,将Entity和Orm Entity绑定于一身,操作OrmEntity即操作Entity,整个代码风格设计也和OrmLite极其相似。参考代码如下:4.4 IM内存数据模型基于Flutter的IM移动端架构在内存数据模型主要划分为会话和消息两个颗粒度:1)会话内存数据模型交托于SessionModuleData:会话内存数据有一个根节点RootNotice,然后其挂载PSessionMessageNotice(这里PSessionMessageNotice是ORM映射的会话DB表模型)子节点集合。2)消息内存数据模型交托于MessageModuleData:消息内存数据会有一个MessageConatiner容器管理,其内部挂载此会话中的PMessage(PMessage是ORM映射的消息DB表模型)消息集合。依据上一章节,PSessionMessageNotice设计了一个OrmEnitity Cache,考虑到IM中会话数是有限的,因此PSessionMessageNotice都是直接缓存到Cache中。这种做法的好处是各地去拿会话数据元时候都是缓存中同一个对象,容易保证多次重复读写的数据一致性。而PSessionMessageNotice考虑到其数量可以无限多的特殊性,因此这里将其挂载到MessageContainer的内存管理中,在退出会话的时机会校验容器中PMessage集合的数量,适当缩容可以减少内存开销。模型如下图所示:4.5 状态管理方案基于Flutter的IM移动端架构状态管理方案比较简单,对数据源Session/Message维度使用观察者模式的订阅分发方式实现,架构类似于EventBus模式,页面级的状态管理无论使用fish-redux、scopeModel或者provider几乎影响面不大,核心还是需保留一种插拔式抽象更重要。架构如下图:4.6 IM同步模型方案当前现状的消息同步模型:如上图所示是,模型中存在ACCS Thread/Main Thread/Region Thread等多线程并发场景,导致易出现多线程高并发的问题。native的推送和网络请求同步的隔离方案通过Lock的锁机制,并且通过队列降频等方式处理,流程繁琐且易出错。整体通过Region Version Gap去判断是否有域空洞,进而执行域同步补充数据。改进的同步模型如下:如上图所示,在Flutter侧天然没多线程场景,通过一种标记位的转化同步异步实现类似Handler消息队列,架构清晰简约了很多,避免锁带来的开销以及同步问题。5、本次改造进展以及性能对比1)针对架构层面:在基于Flutter的IM架构中,重点将双端逻辑差异性统一成同一份Dart代码,完全磨平Android/iOS的代码差异性带来的问题。带来的好处很明显:1)降低开发维护、测试回归、视觉验收的一半成本,极大提高研发效率;2)架构上进行重构分层,实现一种解耦合,插拔式的IM架构;3)同时Native到Flutter侧的大量数据上抛序列化过程改造程Flutter引用传递,解决极限测试场景下的私聊卡顿问题。2)针对线上舆情:1)补齐UT和TLog的集团日志方式做到可追踪,可排查;2)针对于很多现存的疑难杂症重点集中专项解决,比如iphone5C的架构在Flutter侧统一规划;3)未读红点计数等问题也在架构模型升级中修复;4)此外多媒体音视频发送模块进行改造升级。3)性能数据对比:当IM架构的逻辑层和UI层都切换成Flutter后,和原先架构模式初步对比,整体内存水位持平。其中:1)私聊场景下小米9测试结构内存下降40M,功耗降低4mah,CPU降低1%;2)极限测试场景下新架构内存数据相比于旧架构有一个较为明显的改观(主要由于两个界面都使用Flutter场景下,页面切换的开销降低很多)。6、未来展望JS跨端不安全,C++跨端成本有点高,Flutter会是一个较好选择。彼时闲鱼FlutterIM架构升级根本目的从来不是因Flutter而Flutter,是由于历史包袱的繁重,代码层面的维护成本高,新业务的扩展性差,人力配比不协调以及疑难杂症的舆情持续反馈等等因素造成我们不得不去探索新方案。经过闲鱼IM超复杂业务场景验证Flutter模式的逻辑跨端可行性,闲鱼在Flutter路上会一直保持前沿探索,最后能反馈到生态圈。总结一句话,探索过程在于你勇于迈出第一步,后面才会不断惊喜发现。(原文链接:点此进入,本次有修订和改动)附录:更多文章汇总[1] 更多阿里团队的文章分享:《阿里钉钉技术分享:企业级IM王者——钉钉在后端架构上的过人之处》《现代IM系统中聊天消息的同步和存储方案探讨》《阿里技术分享:深度揭秘阿里数据库技术方案的10年变迁史》《阿里技术分享:阿里自研金融级数据库OceanBase的艰辛成长之路》《来自阿里OpenIM:打造安全可靠即时通讯服务的技术实践分享》《钉钉——基于IM技术的新一代企业OA平台的技术挑战(视频+PPT) [附件下载]》《阿里技术结晶:《阿里巴巴Java开发手册(规约)-华山版》[附件下载]》《重磅发布:《阿里巴巴Android开发手册(规约)》[附件下载]》《作者谈《阿里巴巴Java开发手册(规约)》背后的故事》《《阿里巴巴Android开发手册(规约)》背后的故事》《干了这碗鸡汤:从理发店小弟到阿里P10技术大牛》《揭秘阿里、腾讯、华为、百度的职级和薪酬体系》《淘宝技术分享:手淘亿级移动端接入层网关的技术演进之路》《难得干货,揭秘支付宝的2维码扫码技术优化实践之路》《淘宝直播技术干货:高清、低延时的实时视频直播技术解密》《阿里技术分享:电商IM消息平台,在群聊、直播场景下的技术实践》《阿里技术分享:闲鱼IM基于Flutter的移动端跨端改造实践》[2] 更多IM开发综合文章:《新手入门一篇就够:从零开发移动端IM》《移动端IM开发者必读(一):通俗易懂,理解移动网络的“弱”和“慢”》《移动端IM开发者必读(二):史上最全移动弱网络优化方法总结》《从客户端的角度来谈谈移动端IM的消息可靠性和送达机制》《现代移动端网络短连接的优化手段总结:请求速度、弱网适应、安全保障》《移动端IM中大规模群消息的推送如何保证效率、实时性?》《移动端IM开发需要面对的技术问题》《开发IM是自己设计协议用字节流好还是字符流好?》《IM消息送达保证机制实现(一):保证在线实时消息的可靠投递》《IM消息送达保证机制实现(二):保证离线消息的可靠投递》《如何保证IM实时消息的“时序性”与“一致性”?》《一个低成本确保IM消息时序的方法探讨》《IM单聊和群聊中的在线状态同步应该用“推”还是“拉”?》《IM群聊消息如此复杂,如何保证不丢不重?》《谈谈移动端 IM 开发中登录请求的优化》《移动端IM登录时拉取数据如何作到省流量?》《浅谈移动端IM的多点登录和消息漫游原理》《完全自已开发的IM该如何设计“失败重试”机制?》《通俗易懂:基于集群的移动端IM接入层负载均衡方案分享》《微信对网络影响的技术试验及分析(论文全文)》《开源IM工程“蘑菇街TeamTalk”的现状:一场有始无终的开源秀》《如约而至:微信自用的移动端IM网络层跨平台组件库Mars已正式开源》《子弹短信光鲜的背后:网易云信首席架构师分享亿级IM平台的技术实践》《微信技术分享:微信的海量IM聊天消息序列号生成实践(算法原理篇)》《自已开发IM有那么难吗?手把手教你自撸一个Andriod版简易IM (有源码)》《融云技术分享:解密融云IM产品的聊天消息ID生成策略》《IM开发基础知识补课(六):数据库用NoSQL还是SQL?读这篇就够了!》《适合新手:从零开发一个IM服务端(基于Netty,有完整源码)》《拿起键盘就是干:跟我一起徒手开发一套分布式IM系统》《适合新手:手把手教你用Go快速搭建高性能、可扩展的IM系统(有源码)》《IM里“附近的人”功能实现原理是什么?如何高效率地实现它?》《IM“扫一扫”功能很好做?看看微信“扫一扫识物”的完整技术实现》《IM消息ID技术专题(一):微信的海量IM聊天消息序列号生成实践(算法原理篇)》《IM消息ID技术专题(二):微信的海量IM聊天消息序列号生成实践(容灾方案篇)》《IM消息ID技术专题(三):解密融云IM产品的聊天消息ID生成策略》《IM消息ID技术专题(四):深度解密美团的分布式ID生成算法》《IM消息ID技术专题(五):开源分布式ID生成器UidGenerator的技术实现》《IM消息ID技术专题(六):深度解密滴滴的高性能ID生成器(Tinyid)》《IM开发宝典:史上最全,微信各种功能参数和逻辑规则资料汇总》《IM开发干货分享:我是如何解决大量离线消息导致客户端卡顿的》《零基础IM开发入门(一):什么是IM系统?》《零基础IM开发入门(二):什么是IM系统的实时性?》《零基础IM开发入门(三):什么是IM系统的可靠性?》《零基础IM开发入门(四):什么是IM系统的消息时序一致性?》《IM开发干货分享:如何优雅的实现大量离线消息的可靠投递》《IM开发干货分享:有赞移动端IM的组件化SDK架构设计实践》《一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等》《IM扫码登录技术专题(一):微信的扫码登录功能技术原理调试分析》《IM扫码登录技术专题(二):市面主流的扫码登录技术原理调试分析》《IM扫码登录技术专题(三):通俗易懂,IM扫码登录功能详细原理一篇就够》《理解IM消息“可靠性”和“一致性”问题,以及解决方案探讨》《阿里技术分享:闲鱼IM基于Flutter的移动端跨端改造实践》>> 更多同类文章 ……本文已同步发布于“即时通讯技术圈”公众号。同步发布链接是:http://www.52im.net/thread-3615-1-1.html2023-07-21 18:27:222
Ice.NoEndpointException是什么问题
Ice application没有在运行,双击IceGridAdmin.jar,登录进去确认服务是否在运行,默认是manual(手工运行),可改为always(始终),或者on-demand(按需运行,推荐)。2023-07-21 18:27:561
java hashmap空指针异常
这只能说明hashmap为空,AppointSomeApi.pullBaiduAd(adRequestParam, adResponse);这个方法没有设置值进去2023-07-21 18:28:031
科大讯飞 错误码 14002怎么解决
12-06 10:34:11.628: W/System.err(3168): 脚本运行错误.(错误码:14002)12-06 10:34:11.629: W/System.err(3168): at com.iflytek.thirdparty.A.a(Unknown Source)12-06 10:34:11.631: W/System.err(3168): at com.iflytek.thirdparty.B.a(Unknown Source)12-06 10:34:11.631: W/System.err(3168): at com.iflytek.thirdparty.v.handleMessage(Unknown Source)12-06 10:34:11.631: W/System.err(3168): at android.os.Handler.dispatchMessage(Handler.java:102)12-06 10:34:11.632: W/System.err(3168): at android.os.Looper.loop(Looper.java:135)12-06 10:34:11.632: W/System.err(3168): at android.os.HandlerThread.run(HandlerThread.java:61)在新建运用之后,要先添加开放语义!MSP_ERROR_LUA_ERRRUN服务端脚本错误appid使用了语义功能,但是语义场景没有勾选,可以在官网file:///www.xfyun.cn开放语义平台勾选,网址是file:///http://osp.voicecloud.cn选自:http://blog.csdn.net/dddxxxx/article/details/534920892023-07-21 18:28:181
Android 的Thread编程,我在Thread的run()方法中用Toast输出信息时出错!
不能在子线程中更新UI,这是我之前写的一个例子,你看看,使用Handler消息机制public class HandlerDemoActivity extends Activity implements OnClickListener { Button btn1,btn2; ProgressBar progressBar; UpdateDataHandler updateDataHandler; HandlerThread handlerThread; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); btn1 = (Button)findViewById(R.id.btn1); btn2 = (Button)findViewById(R.id.btn2); progressBar = (ProgressBar)findViewById(R.id.progressBar1); btn1.setOnClickListener(this); btn2.setOnClickListener(this); } /** * 自定义一个类来继承Handler类,并重写handleMessage函数, * 并且需要一个带有Looper对象的构造函数 * 循环检测是否有消息,如果有消息,将调用handleMessage取出数据, * 如果没有消息,进入等待状态 * @author Administrator * */ class UpdateDataHandler extends Handler{ public UpdateDataHandler(Looper looper){ super(looper); } @Override public void handleMessage(Message msg) { super.handleMessage(msg); System.out.println("----------" + Thread.currentThread().getId()); progressBar.setProgress(msg.arg1); //从消息队列中取出数据,并更新控件 updateDataHandler.post(printRunnable); } } Runnable printRunnable = new Runnable() {//实现一个线程类 int i = 0; @Override public void run() { //重写线程类中的run函数 i += 20; Message msg = updateDataHandler.obtainMessage(); //获得handler的message对象 msg.arg1=i; //向message对象中放置数据 //msg.obj = obj;//还可以发送一些对象 //msg.setData();//可以发送Bundle对象 try { Thread.sleep(1000); //暂停1秒钟 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i); if(i > 100) //判断当变量到达100时,操作结束 { updateDataHandler.removeCallbacks(printRunnable);//从handler中释放线程类 return; } //msg.sendToTarget(); //使用此函数也可以实现发送消息 updateDataHandler.sendMessage(msg); //将message对象放到消息队列中 } }; @Override public void onClick(View v) { if(v == btn1) { handlerThread = new HandlerThread("");// HandlerThread是一个Thread,继承自Thread。它保留着对Looper实例的引用 handlerThread.start(); //一定要调用线程的start函数 updateDataHandler = new UpdateDataHandler(handlerThread.getLooper()); updateDataHandler.post(printRunnable); System.out.println("主线程" + Thread.currentThread().getId()); } }}2023-07-21 18:28:295
HandlerThread原理与应用
u2003在理解了Handler的原理之后,我们知道在一个子线程中创建一个Handler不能缺少了Looper.prepare()和Looper.loop()两个方法,具体的原因这里不再赘述,不熟悉原理的可以先看下另一篇文章 Handler的原理解析 . 本篇文章主要是讲解HandlerThread的使用的。 u2003首先HandlerThread是继承于Thread类的,所以本质上HandlerThread就是一个线程,接下来就详细的去看一看,这是怎样的一个线程? u2003首先,先看下它的构造函数: u2003既然是线程,那么最重要的当然是run方法来,看完了run方法,相信你也就明白HandlerThread的用途了! u2003怎么样,这是不是和我们之前在子线程中创建Handler一样,首先是Looper.prepare(),接着是new Handler(), 最后是Looper.loop()。等等,这里并没有创建Handler啊!别急,我们先一步一步地看看run方法再说为什么没有创建Handler。 u2003通过查找发现到一个getLooper()的方法,该方法返回了当前线程的mLooper对象,还记得Looper是在哪里进行赋值的吗?在线程的run方法里,所以当线程启动之后才能创建Looper并赋值给mLooper,这里的阻塞就是为了等待Looper的创建成功。同时该方法是用Public修饰的,说明该方法是提供外部调用的,Looper创建成功提供给外部使用。 u2003最后在对象销毁前,调用下面的方法退出Looper循环 u2003quit方法实际是调用MessagQueue的removeAllMessagesLocked,移除所有延迟和非延迟的消息, u2003quitSafely方法调用的是removeAllFutureMessagesLocked方法,该方法只清除延迟的消息,非延迟的消息 还是会进行分发处理。 u2003HandlerThread分析完啦,是不是有点蒙,自始至终都没有出现Handler,HandlerThread要怎么用呢? u2003下面我们就通过一个Demo来说明下HandlerThread是怎么用的?2023-07-21 18:28:511
android message传递数据到handler的问题
第一种思想是推荐使用的,任何函数在调用的时候,涉及到传递参数,都会降低效率。根据message的源码setData(Bundle data)它的注释:Sets a Bundle of arbitrary data values. Use arg1 and arg1 members as a lower cost way to send a few simple integer values, if you can.意思是说,尽量使用arg1和arg2传递整型数据,降低开销(lower cost)。因此你的第二种想法完全就是多此一举,因为Handler的设计思想就是通知主线程更新数据,用最小的开销去通知。2023-07-21 18:28:592
SurfaceView/VideoView
我们经常用SurfaceView和VideoView来播放视频,但是这两个东东,经常都会出问题。 ①SurfaceView不会添加到View树上,并且显示在所有View之上 ②在按Home键的时候,会让Surface销毁,并且在重新进入APP的时候,让Surface重建,在Surface重建的时候,SurfaceView那一块是透明的,显示的会是Activity的背景 ③如果是列表中的子view中播放视频,在上下滑动的时候,会导致Surface绘制不及时,会有残留 ④多个VideoView同时播放的时候,在SurfaceFlinger支持不好的手机上,会出现下一个SurfaceView的某一帧会显示在上一个SurfaceView上 ①VideoView是直接继承SurfaceView ②VideoView中的openVideo可能会ANR ③VideoView中的release,stopPlayBack都会导致ANR,因为这些方法都是同步执行的,并且通过IPC服务交给MediaServer去释放资源 解决方法: 使用TextureView替换SurfaceView实现VideoView,因为TextureView是直接继承View的,并且在ListView中滑动的时候,也不会在滑动的时候,有帧残留 当我们有一个列表,每个子项就是一个视频,并且自动播放。surfaceView使用的就是Android自带的状态机来控制播放,所以就会一段一段的将视频先读到缓冲区,再播放。由于MediaPlayer中的release,reset,stopPlayBack都是同步的。而且当视频卡片在滑出屏幕之后,需要把视频暂停,在不可见的时候不进行播放。节省系统资源,并且节省用户流量。而如果同时出现多个视频的时候,会频繁调用到上述生命周期方法,导致很容易出现ANR。 解决方法: 问题1.在视频划出ListView的时候,停止播放视频。 解决方案:在ListView中调用setRecycleListener,设置View回收的监听,因为ListView的重用性,会在View回收到scrap区的时候,通过这个Listener进行一些处理,所以在这里根据View.getTag,找到视频View的引用,调用stopPlayBack停止 问题2.频繁调用release等方法导致ANR 解决方案:在视频调用的时候,建立一个释放视频资源的守护线程。在Android中,直接可以用HandlerThread,因为这样可以尽可能的让资源的消耗达到最少,HandlerThread在没有新事件到来的时候,都是处于wait状态,直到有新事件的到来,才会被notify,处理新事件。它里面也是通过一个Thread,在这个Thread中新建一个Looper,在Looper中没有事件的话,则wait,一旦通过Handler发送新事件的话,则会被notify。所以会在子线程中加入一个队列,当需要release的MediaPlayer,直接丢到子线程去进行资源释放。 但是这样会导致一个问题,就是Android维护的MediaPlayer的状态机中的状态可能会乱,这时候就会抛出IllegalStateException,目前对于这种异常,我们选择了捕获它。 另外,由于MediaPlayer.setSurface需要传递一个Surface,然后再在这个Surface上进行绘制,如果频繁new Surface传入的话,就会导致GrafficBuffer分配Surface失败,从而MediaPlayer会回调onError中,显示视频不能播放。所以尽可能一个视频View用一个Surface。2023-07-21 18:29:171
android启动后怎么查看其里面的进程和线程
1)一个 Android 程序开始运行时,会单独启动一个Process。默认情况下,所有这个程序中的Activity或者Service都会跑在这个Process。默认情况下,一个Android程序也只有一个Process,但一个Process下却可以有许多个Thread。2)一个 Android 程序开始运行时,就有一个主线程Main Thread被创建。该线程主要负责UI界面的显示、更新和控件交互,所以又叫UI Thread。3)一个Android程序创建之初,一个Process呈现的是单线程模型--即MainThread,所有的任务都在一个线程中运行,所以,MainThread所调用的每一个函数,其耗时应该越短越好,而对于比较耗时的工作,应该交给子线程去做,以避免主线程(UI线程)被阻塞,导致程序出现ANR(Application not response)一个Activity就运行在一个线程中吗?或者编码时,如果不是明确安排在不同线程中的两个Activity,其就都是在同一个线程中?那从一个Activity跳转到另一个Activity时,是不是跳出的那个Activity就处在睡眠状态了?【答】 每个Activity都有一个Process属性,可以指定该Activity是属于哪个进程的。当然如果不明确指明,应该就是从属于默认进程(Application指定的,如其未指定,应该就是默认主进程)。Android中有Task的概念,而同一个Task的各个Activity会形成一个栈,只有站定的Activity才有机会与用户交互。原文地址:Android中的进程与线程 原文作者:江鹏当应用程序的组件第一次运行时,Android将启动一个只有一个执行线程的Linux进程。默认,应用程序所有的组件运行在这个进程和线程中。然而,你可以安排组件运行在其他进程中,且你可以为进程衍生出其它线程。本文从下面几点来介绍Android的进程与线程:1、进程组件运行于哪个进程中由清单文件控制。组件元素——<activity>、<service>、<receiver>、<provider>,都有一个process属性可以指定组件运行在哪个进程中。这个属性可以设置为每个组件运行在自己的进程中,或者某些组件共享一个进程而其他的不共享。他们还可以设置为不同应用程序的组件运行在同一个进程中——假设这些应用程序共享同一个Linux用户ID且被分配了同样的权限。<application>元素也有process属性,为所有的组件设置一个默认值。所有的组件都在特定进程的主线程中实例化,且系统调用组件是由主线程派遣。不会为每个实例创建单独的线程,因此,对应这些调用的方法——诸如View.onKeyDown()报告用用户的行为和生命周期通知,总是运行在进程的主线程中。这意味着,没有组件当被系统调用时应该执行很长时间或阻塞操作(如网络操作或循环计算),因为这将阻塞进程中的其它组件。你可以为长操作衍生独立的线程。public boolean onKeyDown(int keyCode,KeyEvent event):默认实现KeyEvent.Callback.onKeyMultiple(),当按下视图的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然后释放时执行,如果视图可用且可点击。参数keyCode-表示按钮被按下的键码,来自KeyEvent event-定义了按钮动作的KeyEvent对象返回值如果你处理事件,返回true;如果你想下一个接收者处理事件,返回false。当内存剩余较小且其它进程请求较大内存并需要立即分配,Android要回收某些进程,进程中的应用程序组件会被销毁。当他们再次运行时,会重新开始一个进程。当决定终结哪个进程时,Android会权衡他们对用户重要性的相对权值。例如,与运行在屏幕可见的活动进程相比(前台进程),它更容易关闭一个进程,它的活动在屏幕是不可见(后台进程)。决定是否终结进程,取决于运行在进程中的组件状态。关于组件的状态,将在后面一篇——组件生命周期中介绍。2、线程虽然你可能会将你的应用程序限制在一个进程中,但有时候你会需要衍生一个线程做一些后台工作。因为用户界面必须很快地响应用户的操作,所以活动寄宿的线程不应该做一些耗时的操作如网络下载。任何不可能在短时间完成的操作应该分配到别的线程。线程在代码中是用标准的Java线程对象创建的,Android提供了一些方便的类来管理线程——Looper用于在线程中运行消息循环、Handler用户处理消息、HandlerThread用户设置一个消息循环的线程。Looper类该类用户在线程中运行消息循环。线程默认没有消息循环,可以在线程中调用prepare()创建一个运行循环;然后调用loop()处理消息直到循环结束。大部分消息循环交互是通过Handler类。下面是一个典型的执行一个Looper线程的例子,分别使用prepare()和loop()创建一个初始的Handler与Looper交互: 1. Android中进程与进程、线程与线程之间如何通信?1)一个 Android 程序开始运行时,会单独启动一个Process。默认情况下,所有这个程序中的Activity或者Service都会跑在这个Process。默认情况下,一个Android程序也只有一个Process,但一个Process下却可以有许多个Thread。2)一个 Android 程序开始运行时,就有一个主线程Main Thread被创建。该线程主要负责UI界面的显示、更新和控件交互,所以又叫UI Thread。3)一个Android程序创建之初,一个Process呈现的是单线程模型--即MainThread,所有的任务都在一个线程中运行,所以,MainThread所调用的每一个函数,其耗时应该越短越好,而对于比较耗时的工作,应该交给子线程去做,以避免主线程(UI线程)被阻塞,导致程序出现ANR(Application not response)一个Activity就运行在一个线程中吗?或者编码时,如果不是明确安排在不同线程中的两个Activity,其就都是在同一个线程中?那从一个Activity跳转到另一个Activity时,是不是跳出的那个Activity就处在睡眠状态了?【答】 每个Activity都有一个Process属性,可以指定该Activity是属于哪个进程的。当然如果不明确指明,应该就是从属于默认进程(Application指定的,如其未指定,应该就是默认主进程)。Android中有Task的概念,而同一个Task的各个Activity会形成一个栈,只有站定的Activity才有机会与用户交互。原文地址:Android中的进程与线程 原文作者:江鹏当应用程序的组件第一次运行时,Android将启动一个只有一个执行线程的Linux进程。默认,应用程序所有的组件运行在这个进程和线程中。然而,你可以安排组件运行在其他进程中,且你可以为进程衍生出其它线程。本文从下面几点来介绍Android的进程与线程:1、进程组件运行于哪个进程中由清单文件控制。组件元素——<activity>、<service>、<receiver>、<provider>,都有一个process属性可以指定组件运行在哪个进程中。这个属性可以设置为每个组件运行在自己的进程中,或者某些组件共享一个进程而其他的不共享。他们还可以设置为不同应用程序的组件运行在同一个进程中——假设这些应用程序共享同一个Linux用户ID且被分配了同样的权限。<application>元素也有process属性,为所有的组件设置一个默认值。所有的组件都在特定进程的主线程中实例化,且系统调用组件是由主线程派遣。不会为每个实例创建单独的线程,因此,对应这些调用的方法——诸如View.onKeyDown()报告用用户的行为和生命周期通知,总是运行在进程的主线程中。这意味着,没有组件当被系统调用时应该执行很长时间或阻塞操作(如网络操作或循环计算),因为这将阻塞进程中的其它组件。你可以为长操作衍生独立的线程。public boolean onKeyDown(int keyCode,KeyEvent event):默认实现KeyEvent.Callback.onKeyMultiple(),当按下视图的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然后释放时执行,如果视图可用且可点击。参数keyCode-表示按钮被按下的键码,来自KeyEvent event-定义了按钮动作的KeyEvent对象返回值如果你处理事件,返回true;如果你想下一个接收者处理事件,返回false。当内存剩余较小且其它进程请求较大内存并需要立即分配,Android要回收某些进程,进程中的应用程序组件会被销毁。当他们再次运行时,会重新开始一个进程。当决定终结哪个进程时,Android会权衡他们对用户重要性的相对权值。例如,与运行在屏幕可见的活动进程相比(前台进程),它更容易关闭一个进程,它的活动在屏幕是不可见(后台进程)。决定是否终结进程,取决于运行在进程中的组件状态。关于组件的状态,将在后面一篇——组件生命周期中介绍。2、线程虽然你可能会将你的应用程序限制在一个进程中,但有时候你会需要衍生一个线程做一些后台工作。因为用户界面必须很快地响应用户的操作,所以活动寄宿的线程不应该做一些耗时的操作如网络下载。任何不可能在短时间完成的操作应该分配到别的线程。线程在代码中是用标准的Java线程对象创建的,Android提供了一些方便的类来管理线程——Looper用于在线程中运行消息循环、Handler用户处理消息、HandlerThread用户设置一个消息循环的线程。Looper类该类用户在线程中运行消息循环。线程默认没有消息循环,可以在线程中调用prepare()创建一个运行循环;然后调用loop()处理消息直到循环结束。大部分消息循环交互是通过Handler类。下面是一个典型的执行一个Looper线程的例子,分别使用prepare()和loop()创建一个初始的Handler与Looper交互: 2.1、远程过程调用(Remote procedure calls,RPCs)Android有一个轻量级的远程过程调用机制——方法在本地调用却在远程(另外一个进程中)执行,结果返回给调用者。这需要将方法调用和它伴随的数据分解为操作系统能够理解的层次,从本地进程和地址空间传输到远程进程和地址空间,并重新组装调用。返回值以相反方向传输。Android提供了做这些工作的所有代码,这样我们可以专注于定义和执行RPC接口本身。一个RPC接口仅包含方法。所有的方法同步地执行(本地方法阻塞直到远程方法执行完成),即使是没有返回值。简言之,该机制工作原理如下:首先,你用简单的IDL(interface definition language,接口定义语言)声明一个你想实现的RPC接口。从这个声明中,aidl工具生成一个Java接口定义,提供给本地和远程进程。它包含两个内部类,如下图所示:内部类有管理你用IDL定义的接口的远程过程调用所需要的所有代码。这两个内部类都实现了IBinder接口。其中之一就是在本地由系统内部使用,你写代码可以忽略它。另外一个是Stub,扩展自Binder类。除了用于有效地IPC(interprocess communication)调用的内部代码,内部类在RPC接口声明中还包含方法声明。你可以定义Stub的子类实现这些方法,如图中所示。通常情况下,远程过程有一个服务管理(因为服务能通知系统关于进程和它连接的其它进程的信息)。它有由aidl工具生成的接口文件和Stub子类实现的RPC方法。服务的客户端仅有由aidl工具生成的接口文件。下面介绍服务如何与它的客户端建立连接:· 服务的客户端(在本地端的)应该实现onServiceConnected() 和onServiceDisconnected() 方法,因此当与远程服务建立连接成功和断开连接是会通知它。然后调用bindService() 建立连接。 · 服务的onBind()方法将实现为接受或拒绝连接,者取决于它接受到的意图(该意图传送到binServive())。如果连接被接受,它返回一个Stub子类的实例。 · 如果服务接受连接,Android调用客户端的onServiceConnected()方法且传递给它一个IBinder对象,返回由服务管理的Stub子类的一个代理。通过代理,客户端可以调用远程服务。 这里只是简单地描述,省略了一些RPC机制的细节。你可以查阅相关资料或继续关注Android开发之旅,后面将为你奉上。2.2、线程安全方法在一些情况下,你实现的方法可能会被不止一个线程调用,因此必须写成线程安全的。这对远程调用方法是正确的——如上一节讨论的RPC机制。当从IBinder进程中调用一个IBinder对象中实现的一个方法,这个方法在调用者的线程中执行。然而,当从别的进程中调用,方法将在Android维护的IBinder进程中的线程池中选择一个执行,它不在进程的主线程中执行。例如,一个服务的onBind()方法在服务进程的主线程中被调用,在onBind()返回的对象中执行的方法(例如,实现RPC方法的Stub子类)将在线程池中被调用。由于服务可以有一个以上的客户端,所以同时可以有一个以上的线程在执行同一个IBinder方法。因此,IBinder的方法必须是线程安全的。同样,一个内容提供者可以接受其它进程产生的数据请求。虽然ContentResolver 和 ContentProvider 类隐藏进程通信如何管理的,对应哪些请求的ContentResolver 方法——query()、insert()、delete()、update()、getType(),在内容提供者的进程的线程池中被调用,而不是在这一进程的主线程中。因为这些方法可以同时从任意数量的线程中调用,他们也必须实现为线程安全的。2023-07-21 18:30:111
android多核,多线程该如何用
在程序开发的实践当中,为了让程序表现得更加流畅,我们肯定会需要使用到多线程来提升程序的并发执行性能。但是编写多线程并发的代码一直以来都是一个相对棘手的问题,所以想要获得更佳的程序性能,我们非常有必要掌握多线程并发编程的基础技能。众所周知,Android 程序的大多数代码操作都必须执行在主线程,例如系统事件(例如设备屏幕发生旋转),输入事件(例如用户点击滑动等),程序回调服务,UI 绘制以及闹钟事件等等。那么我们在上述事件或者方法中插入的代码也将执行在主线程。一旦我们在主线程里面添加了操作复杂的代码,这些代码就很可能阻碍主线程去响应点击/滑动事件,阻碍主线程的 UI 绘制等等。我们知道,为了让屏幕的刷新帧率达到 60fps,我们需要确保 16ms 内完成单次刷新的操作。一旦我们在主线程里面执行的任务过于繁重就可能导致接收到刷新信号的时候因为资源被占用而无法完成这次刷新操作,这样就会产生掉帧的现象,刷新帧率自然也就跟着下降了(一旦刷新帧率降到 20fps 左右,用户就可以明显感知到卡顿不流畅了)。为了避免上面提到的掉帧问题,我们需要使用多线程的技术方案,把那些操作复杂的任务移动到其他线程当中执行,这样就不容易阻塞主线程的操作,也就减小了出现掉帧的可能性。那么问题来了,为主线程减轻负的多线程方案有哪些呢?这些方案分别适合在什么场景下使用?Android 系统为我们提供了若干组工具类来帮助解决这个问题。AsyncTask: 为 UI 线程与工作线程之间进行快速的切换提供一种简单便捷的机制。适用于当下立即需要启动,但是异步执行的生命周期短暂的使用场景。HandlerThread: 为某些回调方法或者等待某些任务的执行设置一个专属的线程,并提供线程任务的调度机制。ThreadPool: 把任务分解成不同的单元,分发到各个不同的线程上,进行同时并发处理。IntentService: 适合于执行由 UI 触发的后台 Service 任务,并可以把后台任务执行的情况通过一定的机制反馈给 UI。了解这些系统提供的多线程工具类分别适合在什么场景下,可以帮助我们选择合适的解决方案,避免出现不可预期的麻烦。虽然使用多线程可以提高程序的并发量,但是我们需要特别注意因为引入多线程而可能伴随而来的内存问题。举个例子,在 Activity 内部定义的一个 AsyncTask,它属于一个内部类,该类本身和外面的 Activity 是有引用关系的,如果 Activity 要销毁的时候,AsyncTask 还仍然在运行,这会导致 Activity 没有办法完全释放,从而引发内存泄漏。所以说,多线程是提升程序性能的有效手段之一,但是使用多线程却需要十分谨慎小心,如果不了解背后的执行机制以及使用的注意事项,很可能引起严重的问题。2023-07-21 18:30:181
异步on a null object reference怎么解决
android.os.Handler.sendEmptyMessage(int)" on a null object reference ...也就是说完全可以用HandlerThread实现一个异步任务。2023-07-21 18:30:251
android启动后怎么查看其里面的进程和线程
. Android中进程与进程、线程与线程之间如何通信?1)一个 Android 程序开始运行时,会单独启动一个Process。默认情况下,所有这个程序中的Activity或者Service都会跑在这个Process。默认情况下,一个Android程序也只有一个Process,但一个Process下却可以有许多个Thread。2)一个 Android 程序开始运行时,就有一个主线程Main Thread被创建。该线程主要负责UI界面的显示、更新和控件交互,所以又叫UI Thread。3)一个Android程序创建之初,一个Process呈现的是单线程模型--即MainThread,所有的任务都在一个线程中运行,所以,MainThread所调用的每一个函数,其耗时应该越短越好,而对于比较耗时的工作,应该交给子线程去做,以避免主线程(UI线程)被阻塞,导致程序出现ANR(Application not response)一个Activity就运行在一个线程中吗?或者编码时,如果不是明确安排在不同线程中的两个Activity,其就都是在同一个线程中?那从一个Activity跳转到另一个Activity时,是不是跳出的那个Activity就处在睡眠状态了?【答】 每个Activity都有一个Process属性,可以指定该Activity是属于哪个进程的。当然如果不明确指明,应该就是从属于默认进程(Application指定的,如其未指定,应该就是默认主进程)。Android中有Task的概念,而同一个Task的各个Activity会形成一个栈,只有站定的Activity才有机会与用户交互。原文地址:Android中的进程与线程 原文作者:江鹏当应用程序的组件第一次运行时,Android将启动一个只有一个执行线程的Linux进程。默认,应用程序所有的组件运行在这个进程和线程中。然而,你可以安排组件运行在其他进程中,且你可以为进程衍生出其它线程。本文从下面几点来介绍Android的进程与线程:1、进程组件运行于哪个进程中由清单文件控制。组件元素——<activity>、<service>、<receiver>、<provider>,都有一个process属性可以指定组件运行在哪个进程中。这个属性可以设置为每个组件运行在自己的进程中,或者某些组件共享一个进程而其他的不共享。他们还可以设置为不同应用程序的组件运行在同一个进程中——假设这些应用程序共享同一个Linux用户ID且被分配了同样的权限。<application>元素也有process属性,为所有的组件设置一个默认值。所有的组件都在特定进程的主线程中实例化,且系统调用组件是由主线程派遣。不会为每个实例创建单独的线程,因此,对应这些调用的方法——诸如View.onKeyDown()报告用用户的行为和生命周期通知,总是运行在进程的主线程中。这意味着,没有组件当被系统调用时应该执行很长时间或阻塞操作(如网络操作或循环计算),因为这将阻塞进程中的其它组件。你可以为长操作衍生独立的线程。public boolean onKeyDown(int keyCode,KeyEvent event):默认实现KeyEvent.Callback.onKeyMultiple(),当按下视图的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然后释放时执行,如果视图可用且可点击。参数keyCode-表示按钮被按下的键码,来自KeyEvent event-定义了按钮动作的KeyEvent对象返回值如果你处理事件,返回true;如果你想下一个接收者处理事件,返回false。当内存剩余较小且其它进程请求较大内存并需要立即分配,Android要回收某些进程,进程中的应用程序组件会被销毁。当他们再次运行时,会重新开始一个进程。当决定终结哪个进程时,Android会权衡他们对用户重要性的相对权值。例如,与运行在屏幕可见的活动进程相比(前台进程),它更容易关闭一个进程,它的活动在屏幕是不可见(后台进程)。决定是否终结进程,取决于运行在进程中的组件状态。关于组件的状态,将在后面一篇——组件生命周期中介绍。2、线程虽然你可能会将你的应用程序限制在一个进程中,但有时候你会需要衍生一个线程做一些后台工作。因为用户界面必须很快地响应用户的操作,所以活动寄宿的线程不应该做一些耗时的操作如网络下载。任何不可能在短时间完成的操作应该分配到别的线程。线程在代码中是用标准的Java线程对象创建的,Android提供了一些方便的类来管理线程——Looper用于在线程中运行消息循环、Handler用户处理消息、HandlerThread用户设置一个消息循环的线程。Looper类该类用户在线程中运行消息循环。线程默认没有消息循环,可以在线程中调用prepare()创建一个运行循环;然后调用loop()处理消息直到循环结束。大部分消息循环交互是通过Handler类。下面是一个典型的执行一个Looper线程的例子,分别使用prepare()和loop()创建一个初始的Handler与Looper交互: 1. Android中进程与进程、线程与线程之间如何通信?1)一个 Android 程序开始运行时,会单独启动一个Process。默认情况下,所有这个程序中的Activity或者Service都会跑在这个Process。默认情况下,一个Android程序也只有一个Process,但一个Process下却可以有许多个Thread。2)一个 Android 程序开始运行时,就有一个主线程Main Thread被创建。该线程主要负责UI界面的显示、更新和控件交互,所以又叫UI Thread。3)一个Android程序创建之初,一个Process呈现的是单线程模型--即MainThread,所有的任务都在一个线程中运行,所以,MainThread所调用的每一个函数,其耗时应该越短越好,而对于比较耗时的工作,应该交给子线程去做,以避免主线程(UI线程)被阻塞,导致程序出现ANR(Application not response)一个Activity就运行在一个线程中吗?或者编码时,如果不是明确安排在不同线程中的两个Activity,其就都是在同一个线程中?那从一个Activity跳转到另一个Activity时,是不是跳出的那个Activity就处在睡眠状态了?【答】 每个Activity都有一个Process属性,可以指定该Activity是属于哪个进程的。当然如果不明确指明,应该就是从属于默认进程(Application指定的,如其未指定,应该就是默认主进程)。Android中有Task的概念,而同一个Task的各个Activity会形成一个栈,只有站定的Activity才有机会与用户交互。原文地址:Android中的进程与线程 原文作者:江鹏当应用程序的组件第一次运行时,Android将启动一个只有一个执行线程的Linux进程。默认,应用程序所有的组件运行在这个进程和线程中。然而,你可以安排组件运行在其他进程中,且你可以为进程衍生出其它线程。本文从下面几点来介绍Android的进程与线程:1、进程组件运行于哪个进程中由清单文件控制。组件元素——<activity>、<service>、<receiver>、<provider>,都有一个process属性可以指定组件运行在哪个进程中。这个属性可以设置为每个组件运行在自己的进程中,或者某些组件共享一个进程而其他的不共享。他们还可以设置为不同应用程序的组件运行在同一个进程中——假设这些应用程序共享同一个Linux用户ID且被分配了同样的权限。<application>元素也有process属性,为所有的组件设置一个默认值。所有的组件都在特定进程的主线程中实例化,且系统调用组件是由主线程派遣。不会为每个实例创建单独的线程,因此,对应这些调用的方法——诸如View.onKeyDown()报告用用户的行为和生命周期通知,总是运行在进程的主线程中。这意味着,没有组件当被系统调用时应该执行很长时间或阻塞操作(如网络操作或循环计算),因为这将阻塞进程中的其它组件。你可以为长操作衍生独立的线程。public boolean onKeyDown(int keyCode,KeyEvent event):默认实现KeyEvent.Callback.onKeyMultiple(),当按下视图的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然后释放时执行,如果视图可用且可点击。参数keyCode-表示按钮被按下的键码,来自KeyEvent event-定义了按钮动作的KeyEvent对象返回值如果你处理事件,返回true;如果你想下一个接收者处理事件,返回false。当内存剩余较小且其它进程请求较大内存并需要立即分配,Android要回收某些进程,进程中的应用程序组件会被销毁。当他们再次运行时,会重新开始一个进程。当决定终结哪个进程时,Android会权衡他们对用户重要性的相对权值。例如,与运行在屏幕可见的活动进程相比(前台进程),它更容易关闭一个进程,它的活动在屏幕是不可见(后台进程)。决定是否终结进程,取决于运行在进程中的组件状态。关于组件的状态,将在后面一篇——组件生命周期中介绍。2、线程虽然你可能会将你的应用程序限制在一个进程中,但有时候你会需要衍生一个线程做一些后台工作。因为用户界面必须很快地响应用户的操作,所以活动寄宿的线程不应该做一些耗时的操作如网络下载。任何不可能在短时间完成的操作应该分配到别的线程。线程在代码中是用标准的Java线程对象创建的,Android提供了一些方便的类来管理线程——Looper用于在线程中运行消息循环、Handler用户处理消息、HandlerThread用户设置一个消息循环的线程。Looper类该类用户在线程中运行消息循环。线程默认没有消息循环,可以在线程中调用prepare()创建一个运行循环;然后调用loop()处理消息直到循环结束。大部分消息循环交互是通过Handler类。下面是一个典型的执行一个Looper线程的例子,分别使用prepare()和loop()创建一个初始的Handler与Looper交互: 2.1、远程过程调用(Remote procedure calls,RPCs)Android有一个轻量级的远程过程调用机制——方法在本地调用却在远程(另外一个进程中)执行,结果返回给调用者。这需要将方法调用和它伴随的数据分解为操作系统能够理解的层次,从本地进程和地址空间传输到远程进程和地址空间,并重新组装调用。返回值以相反方向传输。Android提供了做这些工作的所有代码,这样我们可以专注于定义和执行RPC接口本身。一个RPC接口仅包含方法。所有的方法同步地执行(本地方法阻塞直到远程方法执行完成),即使是没有返回值。简言之,该机制工作原理如下:首先,你用简单的IDL(interface definition language,接口定义语言)声明一个你想实现的RPC接口。从这个声明中,aidl工具生成一个Java接口定义,提供给本地和远程进程。它包含两个内部类,如下图所示:内部类有管理你用IDL定义的接口的远程过程调用所需要的所有代码。这两个内部类都实现了IBinder接口。其中之一就是在本地由系统内部使用,你写代码可以忽略它。另外一个是Stub,扩展自Binder类。除了用于有效地IPC(interprocess communication)调用的内部代码,内部类在RPC接口声明中还包含方法声明。你可以定义Stub的子类实现这些方法,如图中所示。通常情况下,远程过程有一个服务管理(因为服务能通知系统关于进程和它连接的其它进程的信息)。它有由aidl工具生成的接口文件和Stub子类实现的RPC方法。服务的客户端仅有由aidl工具生成的接口文件。下面介绍服务如何与它的客户端建立连接:· 服务的客户端(在本地端的)应该实现onServiceConnected() 和onServiceDisconnected() 方法,因此当与远程服务建立连接成功和断开连接是会通知它。然后调用bindService() 建立连接。 · 服务的onBind()方法将实现为接受或拒绝连接,者取决于它接受到的意图(该意图传送到binServive())。如果连接被接受,它返回一个Stub子类的实例。 · 如果服务接受连接,Android调用客户端的onServiceConnected()方法且传递给它一个IBinder对象,返回由服务管理的Stub子类的一个代理。通过代理,客户端可以调用远程服务。 这里只是简单地描述,省略了一些RPC机制的细节。你可以查阅相关资料或继续关注Android开发之旅,后面将为你奉上。2.2、线程安全方法在一些情况下,你实现的方法可能会被不止一个线程调用,因此必须写成线程安全的。这对远程调用方法是正确的——如上一节讨论的RPC机制。当从IBinder进程中调用一个IBinder对象中实现的一个方法,这个方法在调用者的线程中执行。然而,当从别的进程中调用,方法将在Android维护的IBinder进程中的线程池中选择一个执行,它不在进程的主线程中执行。例如,一个服务的onBind()方法在服务进程的主线程中被调用,在onBind()返回的对象中执行的方法(例如,实现RPC方法的Stub子类)将在线程池中被调用。由于服务可以有一个以上的客户端,所以同时可以有一个以上的线程在执行同一个IBinder方法。因此,IBinder的方法必须是线程安全的。同样,一个内容提供者可以接受其它进程产生的数据请求。虽然ContentResolver 和 ContentProvider 类隐藏进程通信如何管理的,对应哪些请求的ContentResolver 方法——query()、insert()、delete()、update()、getType(),在内容提供者的进程的线程池中被调用,而不是在这一进程的主线程中。因为这些方法可以同时从任意数量的线程中调用,他们也必须实现为线程安全的。2023-07-21 18:30:311
android Camera2 MediaRecorder.stop()报错
player.pause();player.seekTo(0); 你可以不调用stop, 调用pause, 之后把进度设置为0; 这样可以解决。2023-07-21 18:30:392
DB2 SQL error: SQLCODE: -805, SQLSTATE: 51002, SQLERRMC: NULLID.SYSLH203 0X5359534C564C3031
Your databases has only 3 packages of this kind. They are SYSLH200,201,202. But your query requires one more additional package SYSLH203. If you are a DBA, go to SQLLIB/BND directory then issue the following command db2 bind @db2cli.lst blocking all grant public sqlerror continue CLIPKG 5 If you are application programmer, contact your DBA to perform above bind. You can bind upto CLIPKG 30.2023-07-21 18:30:483
android启动后怎么查看其里面的进程和线程
1)一个 Android 程序开始运行时,会单独启动一个Process。 默认情况下,所有这个程序中的Activity或者Service都会跑在这个Process。 默认情况下,一个Android程序也只有一个Process,但一个Process下却可以有许多个Thread。 2)一个 Android 程序开始运行时,就有一个主线程Main Thread被创建。该线程主要负责UI界面的显示、更新和控件交互,所以又叫UI Thread。 3)一个Android程序创建之初,一个Process呈现的是单线程模型--即MainThread,所有的任务都在一个线程中运行,所以,MainThread所调用的每一个函数,其耗时应该越短越好,而对于比较耗时的工作,应该交给子线程去做,以避免主线程(UI线程)被阻塞,导致程序出现ANR(Application not response) 一个Activity就运行在一个线程中吗?或者编码时,如果不是明确安排在不同线程中的两个Activity,其就都是在同一个线程中?那从一个Activity跳转到另一个Activity时,是不是跳出的那个Activity就处在睡眠状态了? 【答】 每个Activity都有一个Process属性,可以指定该Activity是属于哪个进程的。当然如果不明确指明,应该就是从属于默认进程(Application指定的,如其未指定,应该就是默认主进程)。 Android中有Task的概念,而同一个Task的各个Activity会形成一个栈,只有站定的Activity才有机会与用户交互。 原文地址:Android中的进程与线程 原文作者:江鹏 当应用程序的组件第一次运行时,Android将启动一个只有一个执行线程的Linux进程。默认,应用程序所有的组件运行在这个进程和线程中。然而,你可以安排组件运行在其他进程中,且你可以为进程衍生出其它线程。本文从下面几点来介绍Android的进程与线程: 1、进程 组件运行于哪个进程中由清单文件控制。组件元素——<activity>、<service>、<receiver>、<provider>,都有一个process属性可以指定组件运行在哪个进程中。这个属性可以设置为每个组件运行在自己的进程中,或者某些组件共享一个进程而其他的不共享。他们还可以设置为不同应用程序的组件运行在同一个进程中——假设这些应用程序共享同一个Linux用户ID且被分配了同样的权限。<application>元素也有process属性,为所有的组件设置一个默认值。 所有的组件都在特定进程的主线程中实例化,且系统调用组件是由主线程派遣。不会为每个实例创建单独的线程,因此,对应这些调用的方法——诸如View.onKeyDown()报告用用户的行为和生命周期通知,总是运行在进程的主线程中。这意味着,没有组件当被系统调用时应该执行很长时间或阻塞操作(如网络操作或循环计算),因为这将阻塞进程中的其它组件。你可以为长操作衍生独立的线程。 public boolean onKeyDown(int keyCode,KeyEvent event):默认实现KeyEvent.Callback.onKeyMultiple(),当按下视图的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然后释放时执行,如果视图可用且可点击。 参数 keyCode-表示按钮被按下的键码,来自KeyEvent event-定义了按钮动作的KeyEvent对象 返回值 如果你处理事件,返回true;如果你想下一个接收者处理事件,返回false。 当内存剩余较小且其它进程请求较大内存并需要立即分配,Android要回收某些进程,进程中的应用程序组件会被销毁。当他们再次运行时,会重新开始一个进程。 当决定终结哪个进程时,Android会权衡他们对用户重要性的相对权值。例如,与运行在屏幕可见的活动进程相比(前台进程),它更容易关闭一个进程,它的活动在屏幕是不可见(后台进程)。决定是否终结进程,取决于运行在进程中的组件状态。关于组件的状态,将在后面一篇——组件生命周期中介绍。 2、线程 虽然你可能会将你的应用程序限制在一个进程中,但有时候你会需要衍生一个线程做一些后台工作。因为用户界面必须很快地响应用户的操作,所以活动寄宿的线程不应该做一些耗时的操作如网络下载。任何不可能在短时间完成的操作应该分配到别的线程。 线程在代码中是用标准的Java线程对象创建的,Android提供了一些方便的类来管理线程——Looper用于在线程中运行消息循环、Handler用户处理消息、HandlerThread用户设置一个消息循环的线程。 Looper类 该类用户在线程中运行消息循环。线程默认没有消息循环,可以在线程中调用prepare()创建一个运行循环;然后调用loop()处理消息直到循环结束。大部分消息循环交互是通过Handler类。下面是一个典型的执行一个Looper线程的例子,分别使用prepare()和loop()创建一个初始的Handler与Looper交互: 1. Android中进程与进程、线程与线程之间如何通信? 1)一个 Android 程序开始运行时,会单独启动一个Process。 默认情况下,所有这个程序中的Activity或者Service都会跑在这个Process。 默认情况下,一个Android程序也只有一个Process,但一个Process下却可以有许多个Thread。 2)一个 Android 程序开始运行时,就有一个主线程Main Thread被创建。该线程主要负责UI界面的显示、更新和控件交互,所以又叫UI Thread。 3)一个Android程序创建之初,一个Process呈现的是单线程模型--即MainThread,所有的任务都在一个线程中运行,所以,MainThread所调用的每一个函数,其耗时应该越短越好,而对于比较耗时的工作,应该交给子线程去做,以避免主线程(UI线程)被阻塞,导致程序出现ANR(Application not response) 一个Activity就运行在一个线程中吗?或者编码时,如果不是明确安排在不同线程中的两个Activity,其就都是在同一个线程中?那从一个Activity跳转到另一个Activity时,是不是跳出的那个Activity就处在睡眠状态了? 【答】 每个Activity都有一个Process属性,可以指定该Activity是属于哪个进程的。当然如果不明确指明,应该就是从属于默认进程(Application指定的,如其未指定,应该就是默认主进程)。 Android中有Task的概念,而同一个Task的各个Activity会形成一个栈,只有站定的Activity才有机会与用户交互。 原文地址:Android中的进程与线程 原文作者:江鹏 当应用程序的组件第一次运行时,Android将启动一个只有一个执行线程的Linux进程。默认,应用程序所有的组件运行在这个进程和线程中。然而,你可以安排组件运行在其他进程中,且你可以为进程衍生出其它线程。本文从下面几点来介绍Android的进程与线程: 1、进程 组件运行于哪个进程中由清单文件控制。组件元素——<activity>、<service>、<receiver>、<provider>,都有一个process属性可以指定组件运行在哪个进程中。这个属性可以设置为每个组件运行在自己的进程中,或者某些组件共享一个进程而其他的不共享。他们还可以设置为不同应用程序的组件运行在同一个进程中——假设这些应用程序共享同一个Linux用户ID且被分配了同样的权限。<application>元素也有process属性,为所有的组件设置一个默认值。 所有的组件都在特定进程的主线程中实例化,且系统调用组件是由主线程派遣。不会为每个实例创建单独的线程,因此,对应这些调用的方法——诸如View.onKeyDown()报告用用户的行为和生命周期通知,总是运行在进程的主线程中。这意味着,没有组件当被系统调用时应该执行很长时间或阻塞操作(如网络操作或循环计算),因为这将阻塞进程中的其它组件。你可以为长操作衍生独立的线程。 public boolean onKeyDown(int keyCode,KeyEvent event):默认实现KeyEvent.Callback.onKeyMultiple(),当按下视图的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然后释放时执行,如果视图可用且可点击。 参数 keyCode-表示按钮被按下的键码,来自KeyEvent event-定义了按钮动作的KeyEvent对象 返回值 如果你处理事件,返回true;如果你想下一个接收者处理事件,返回false。 当内存剩余较小且其它进程请求较大内存并需要立即分配,Android要回收某些进程,进程中的应用程序组件会被销毁。当他们再次运行时,会重新开始一个进程。 当决定终结哪个进程时,Android会权衡他们对用户重要性的相对权值。例如,与运行在屏幕可见的活动进程相比(前台进程),它更容易关闭一个进程,它的活动在屏幕是不可见(后台进程)。决定是否终结进程,取决于运行在进程中的组件状态。关于组件的状态,将在后面一篇——组件生命周期中介绍。 2、线程 虽然你可能会将你的应用程序限制在一个进程中,但有时候你会需要衍生一个线程做一些后台工作。因为用户界面必须很快地响应用户的操作,所以活动寄宿的线程不应该做一些耗时的操作如网络下载。任何不可能在短时间完成的操作应该分配到别的线程。 线程在代码中是用标准的Java线程对象创建的,Android提供了一些方便的类来管理线程——Looper用于在线程中运行消息循环、Handler用户处理消息、HandlerThread用户设置一个消息循环的线程。 Looper类 该类用户在线程中运行消息循环。线程默认没有消息循环,可以在线程中调用prepare()创建一个运行循环;然后调用loop()处理消息直到循环结束。大部分消息循环交互是通过Handler类。下面是一个典型的执行一个Looper线程的例子,分别使用prepare()和loop()创建一个初始的Handler与Looper交互: 2.1、远程过程调用(Remote procedure calls,RPCs) Android有一个轻量级的远程过程调用机制——方法在本地调用却在远程(另外一个进程中)执行,结果返回给调用者。这需要将方法调用和它伴随的数据分解为操作系统能够理解的层次,从本地进程和地址空间传输到远程进程和地址空间,并重新组装调用。返回值以相反方向传输。Android提供了做这些工作的所有代码,这样我们可以专注于定义和执行RPC接口本身。 一个RPC接口仅包含方法。所有的方法同步地执行(本地方法阻塞直到远程方法执行完成),即使是没有返回值。简言之,该机制工作原理如下:首先,你用简单的IDL(interface definition language,接口定义语言)声明一个你想实现的RPC接口。从这个声明中,aidl工具生成一个Java接口定义,提供给本地和远程进程。它包含两个内部类,如下图所示: 内部类有管理你用IDL定义的接口的远程过程调用所需要的所有代码。这两个内部类都实现了IBinder接口。其中之一就是在本地由系统内部使用,你写代码可以忽略它。另外一个是Stub,扩展自Binder类。除了用于有效地IPC(interprocess communication)调用的内部代码,内部类在RPC接口声明中还包含方法声明。你可以定义Stub的子类实现这些方法,如图中所示。 通常情况下,远程过程有一个服务管理(因为服务能通知系统关于进程和它连接的其它进程的信息)。它有由aidl工具生成的接口文件和Stub子类实现的RPC方法。服务的客户端仅有由aidl工具生成的接口文件。 下面介绍服务如何与它的客户端建立连接: · 服务的客户端(在本地端的)应该实现onServiceConnected() 和onServiceDisconnected() 方法,因此当与远程服务建立连接成功和断开连接是会通知它。然后调用bindService() 建立连接。 · 服务的onBind()方法将实现为接受或拒绝连接,者取决于它接受到的意图(该意图传送到binServive())。如果连接被接受,它返回一个Stub子类的实例。 · 如果服务接受连接,Android调用客户端的onServiceConnected()方法且传递给它一个IBinder对象,返回由服务管理的Stub子类的一个代理。通过代理,客户端可以调用远程服务。 这里只是简单地描述,省略了一些RPC机制的细节。你可以查阅相关资料或继续关注Android开发之旅,后面将为你奉上。 2.2、线程安全方法 在一些情况下,你实现的方法可能会被不止一个线程调用,因此必须写成线程安全的。这对远程调用方法是正确的——如上一节讨论的RPC机制。当从IBinder进程中调用一个IBinder对象中实现的一个方法,这个方法在调用者的线程中执行。然而,当从别的进程中调用,方法将在Android维护的IBinder进程中的线程池中选择一个执行,它不在进程的主线程中执行。例如,一个服务的onBind()方法在服务进程的主线程中被调用,在onBind()返回的对象中执行的方法(例如,实现RPC方法的Stub子类)将在线程池中被调用。由于服务可以有一个以上的客户端,所以同时可以有一个以上的线程在执行同一个IBinder方法。因此,IBinder的方法必须是线程安全的。 同样,一个内容提供者可以接受其它进程产生的数据请求。虽然ContentResolver 和 ContentProvider 类隐藏进程通信如何管理的,对应哪些请求的ContentResolver 方法——query()、insert()、delete()、update()、getType(),在内容提供者的进程的线程池中被调用,而不是在这一进程的主线程中。因为这些方法可以同时从任意数量的线程中调用,他们也必须实现为线程安全的。2023-07-21 18:30:551
再谈onPreviewFrame预览帧率问题
在之前,本人写了一篇文章( 关于Android Camera onPreviewFrame 预览回调帧率问题 ),说了关于高通和MTK CPU在单双HandlerThread控制Camera和Rendering上的差异。我觉得有必要详细说明一下onPreviewFrame在不同情况下,可能会对帧率产生影响的问题。对此,本人重新梳理了一下,详细讨论一下该如何确保onPreviewFrame回调帧率。 首先,我们来复现一下双HandlerThread分别控制相机和渲染的方式导致(大)部分MTK 的CPU 的onPreviewFrame回调帧率大幅度降低的情况: 首先是Camera控制类,用于管理相机的打开关闭等操作: 接着,我们来看看控制Camera相机的HandlerThread线程,如下: 然后,我们来看看渲染管理器RenderManager: 其中,CameraFilter 的实现可以参考本人的相机项目 CainCamera 。这里并没有开启实时磨皮渲染等操作,仅仅是绘制渲染了Camera的相机流。 我们再来看看渲染线程如何实现:2023-07-21 18:31:011
Android中线程与线程,进程与进程之间如何通信?
. Android中进程与进程、线程与线程之间如何通信?1)一个 Android 程序开始运行时,会单独启动一个Process。默认情况下,所有这个程序中的Activity或者Service都会跑在这个Process。默认情况下,一个Android程序也只有一个Process,但一个Process下却可以有许多个Thread。2)一个 Android 程序开始运行时,就有一个主线程Main Thread被创建。该线程主要负责UI界面的显示、更新和控件交互,所以又叫UI Thread。3)一个Android程序创建之初,一个Process呈现的是单线程模型--即MainThread,所有的任务都在一个线程中运行,所以,MainThread所调用的每一个函数,其耗时应该越短越好,而对于比较耗时的工作,应该交给子线程去做,以避免主线程(UI线程)被阻塞,导致程序出现ANR(Application not response)一个Activity就运行在一个线程中吗?或者编码时,如果不是明确安排在不同线程中的两个Activity,其就都是在同一个线程中?那从一个Activity跳转到另一个Activity时,是不是跳出的那个Activity就处在睡眠状态了?【答】 每个Activity都有一个Process属性,可以指定该Activity是属于哪个进程的。当然如果不明确指明,应该就是从属于默认进程(Application指定的,如其未指定,应该就是默认主进程)。Android中有Task的概念,而同一个Task的各个Activity会形成一个栈,只有站定的Activity才有机会与用户交互。原文地址:Android中的进程与线程 原文作者:江鹏当应用程序的组件第一次运行时,Android将启动一个只有一个执行线程的Linux进程。默认,应用程序所有的组件运行在这个进程和线程中。然而,你可以安排组件运行在其他进程中,且你可以为进程衍生出其它线程。本文从下面几点来介绍Android的进程与线程:1、进程组件运行于哪个进程中由清单文件控制。组件元素——<activity>、<service>、<receiver>、<provider>,都有一个process属性可以指定组件运行在哪个进程中。这个属性可以设置为每个组件运行在自己的进程中,或者某些组件共享一个进程而其他的不共享。他们还可以设置为不同应用程序的组件运行在同一个进程中——假设这些应用程序共享同一个Linux用户ID且被分配了同样的权限。<application>元素也有process属性,为所有的组件设置一个默认值。所有的组件都在特定进程的主线程中实例化,且系统调用组件是由主线程派遣。不会为每个实例创建单独的线程,因此,对应这些调用的方法——诸如View.onKeyDown()报告用用户的行为和生命周期通知,总是运行在进程的主线程中。这意味着,没有组件当被系统调用时应该执行很长时间或阻塞操作(如网络操作或循环计算),因为这将阻塞进程中的其它组件。你可以为长操作衍生独立的线程。public boolean onKeyDown(int keyCode,KeyEvent event):默认实现KeyEvent.Callback.onKeyMultiple(),当按下视图的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然后释放时执行,如果视图可用且可点击。参数keyCode-表示按钮被按下的键码,来自KeyEvent event-定义了按钮动作的KeyEvent对象返回值如果你处理事件,返回true;如果你想下一个接收者处理事件,返回false。当内存剩余较小且其它进程请求较大内存并需要立即分配,Android要回收某些进程,进程中的应用程序组件会被销毁。当他们再次运行时,会重新开始一个进程。当决定终结哪个进程时,Android会权衡他们对用户重要性的相对权值。例如,与运行在屏幕可见的活动进程相比(前台进程),它更容易关闭一个进程,它的活动在屏幕是不可见(后台进程)。决定是否终结进程,取决于运行在进程中的组件状态。关于组件的状态,将在后面一篇——组件生命周期中介绍。2、线程虽然你可能会将你的应用程序限制在一个进程中,但有时候你会需要衍生一个线程做一些后台工作。因为用户界面必须很快地响应用户的操作,所以活动寄宿的线程不应该做一些耗时的操作如网络下载。任何不可能在短时间完成的操作应该分配到别的线程。线程在代码中是用标准的Java线程对象创建的,Android提供了一些方便的类来管理线程——Looper用于在线程中运行消息循环、Handler用户处理消息、HandlerThread用户设置一个消息循环的线程。Looper类该类用户在线程中运行消息循环。线程默认没有消息循环,可以在线程中调用prepare()创建一个运行循环;然后调用loop()处理消息直到循环结束。大部分消息循环交互是通过Handler类。下面是一个典型的执行一个Looper线程的例子,分别使用prepare()和loop()创建一个初始的Handler与Looper交互: 1. Android中进程与进程、线程与线程之间如何通信?1)一个 Android 程序开始运行时,会单独启动一个Process。默认情况下,所有这个程序中的Activity或者Service都会跑在这个Process。默认情况下,一个Android程序也只有一个Process,但一个Process下却可以有许多个Thread。2)一个 Android 程序开始运行时,就有一个主线程Main Thread被创建。该线程主要负责UI界面的显示、更新和控件交互,所以又叫UI Thread。3)一个Android程序创建之初,一个Process呈现的是单线程模型--即MainThread,所有的任务都在一个线程中运行,所以,MainThread所调用的每一个函数,其耗时应该越短越好,而对于比较耗时的工作,应该交给子线程去做,以避免主线程(UI线程)被阻塞,导致程序出现ANR(Application not response)一个Activity就运行在一个线程中吗?或者编码时,如果不是明确安排在不同线程中的两个Activity,其就都是在同一个线程中?那从一个Activity跳转到另一个Activity时,是不是跳出的那个Activity就处在睡眠状态了?【答】 每个Activity都有一个Process属性,可以指定该Activity是属于哪个进程的。当然如果不明确指明,应该就是从属于默认进程(Application指定的,如其未指定,应该就是默认主进程)。Android中有Task的概念,而同一个Task的各个Activity会形成一个栈,只有站定的Activity才有机会与用户交互。原文地址:Android中的进程与线程 原文作者:江鹏当应用程序的组件第一次运行时,Android将启动一个只有一个执行线程的Linux进程。默认,应用程序所有的组件运行在这个进程和线程中。然而,你可以安排组件运行在其他进程中,且你可以为进程衍生出其它线程。本文从下面几点来介绍Android的进程与线程:1、进程组件运行于哪个进程中由清单文件控制。组件元素——<activity>、<service>、<receiver>、<provider>,都有一个process属性可以指定组件运行在哪个进程中。这个属性可以设置为每个组件运行在自己的进程中,或者某些组件共享一个进程而其他的不共享。他们还可以设置为不同应用程序的组件运行在同一个进程中——假设这些应用程序共享同一个Linux用户ID且被分配了同样的权限。<application>元素也有process属性,为所有的组件设置一个默认值。所有的组件都在特定进程的主线程中实例化,且系统调用组件是由主线程派遣。不会为每个实例创建单独的线程,因此,对应这些调用的方法——诸如View.onKeyDown()报告用用户的行为和生命周期通知,总是运行在进程的主线程中。这意味着,没有组件当被系统调用时应该执行很长时间或阻塞操作(如网络操作或循环计算),因为这将阻塞进程中的其它组件。你可以为长操作衍生独立的线程。public boolean onKeyDown(int keyCode,KeyEvent event):默认实现KeyEvent.Callback.onKeyMultiple(),当按下视图的KEYCODE_DPAD_CENTER或KEYCODE_ENTER然后释放时执行,如果视图可用且可点击。参数keyCode-表示按钮被按下的键码,来自KeyEvent event-定义了按钮动作的KeyEvent对象返回值如果你处理事件,返回true;如果你想下一个接收者处理事件,返回false。当内存剩余较小且其它进程请求较大内存并需要立即分配,Android要回收某些进程,进程中的应用程序组件会被销毁。当他们再次运行时,会重新开始一个进程。当决定终结哪个进程时,Android会权衡他们对用户重要性的相对权值。例如,与运行在屏幕可见的活动进程相比(前台进程),它更容易关闭一个进程,它的活动在屏幕是不可见(后台进程)。决定是否终结进程,取决于运行在进程中的组件状态。关于组件的状态,将在后面一篇——组件生命周期中介绍。2、线程虽然你可能会将你的应用程序限制在一个进程中,但有时候你会需要衍生一个线程做一些后台工作。因为用户界面必须很快地响应用户的操作,所以活动寄宿的线程不应该做一些耗时的操作如网络下载。任何不可能在短时间完成的操作应该分配到别的线程。线程在代码中是用标准的Java线程对象创建的,Android提供了一些方便的类来管理线程——Looper用于在线程中运行消息循环、Handler用户处理消息、HandlerThread用户设置一个消息循环的线程。Looper类该类用户在线程中运行消息循环。线程默认没有消息循环,可以在线程中调用prepare()创建一个运行循环;然后调用loop()处理消息直到循环结束。大部分消息循环交互是通过Handler类。下面是一个典型的执行一个Looper线程的例子,分别使用prepare()和loop()创建一个初始的Handler与Looper交互: 2.1、远程过程调用(Remote procedure calls,RPCs)Android有一个轻量级的远程过程调用机制——方法在本地调用却在远程(另外一个进程中)执行,结果返回给调用者。这需要将方法调用和它伴随的数据分解为操作系统能够理解的层次,从本地进程和地址空间传输到远程进程和地址空间,并重新组装调用。返回值以相反方向传输。Android提供了做这些工作的所有代码,这样我们可以专注于定义和执行RPC接口本身。一个RPC接口仅包含方法。所有的方法同步地执行(本地方法阻塞直到远程方法执行完成),即使是没有返回值。简言之,该机制工作原理如下:首先,你用简单的IDL(interface definition language,接口定义语言)声明一个你想实现的RPC接口。从这个声明中,aidl工具生成一个Java接口定义,提供给本地和远程进程。它包含两个内部类,如下图所示:内部类有管理你用IDL定义的接口的远程过程调用所需要的所有代码。这两个内部类都实现了IBinder接口。其中之一就是在本地由系统内部使用,你写代码可以忽略它。另外一个是Stub,扩展自Binder类。除了用于有效地IPC(interprocess communication)调用的内部代码,内部类在RPC接口声明中还包含方法声明。你可以定义Stub的子类实现这些方法,如图中所示。通常情况下,远程过程有一个服务管理(因为服务能通知系统关于进程和它连接的其它进程的信息)。它有由aidl工具生成的接口文件和Stub子类实现的RPC方法。服务的客户端仅有由aidl工具生成的接口文件。下面介绍服务如何与它的客户端建立连接:· 服务的客户端(在本地端的)应该实现onServiceConnected() 和onServiceDisconnected() 方法,因此当与远程服务建立连接成功和断开连接是会通知它。然后调用bindService() 建立连接。 · 服务的onBind()方法将实现为接受或拒绝连接,者取决于它接受到的意图(该意图传送到binServive())。如果连接被接受,它返回一个Stub子类的实例。 · 如果服务接受连接,Android调用客户端的onServiceConnected()方法且传递给它一个IBinder对象,返回由服务管理的Stub子类的一个代理。通过代理,客户端可以调用远程服务。 这里只是简单地描述,省略了一些RPC机制的细节。你可以查阅相关资料或继续关注Android开发之旅,后面将为你奉上。2.2、线程安全方法在一些情况下,你实现的方法可能会被不止一个线程调用,因此必须写成线程安全的。这对远程调用方法是正确的——如上一节讨论的RPC机制。当从IBinder进程中调用一个IBinder对象中实现的一个方法,这个方法在调用者的线程中执行。然而,当从别的进程中调用,方法将在Android维护的IBinder进程中的线程池中选择一个执行,它不在进程的主线程中执行。例如,一个服务的onBind()方法在服务进程的主线程中被调用,在onBind()返回的对象中执行的方法(例如,实现RPC方法的Stub子类)将在线程池中被调用。由于服务可以有一个以上的客户端,所以同时可以有一个以上的线程在执行同一个IBinder方法。因此,IBinder的方法必须是线程安全的。同样,一个内容提供者可以接受其它进程产生的数据请求。虽然ContentResolver 和 ContentProvider 类隐藏进程通信如何管理的,对应哪些请求的ContentResolver 方法——query()、insert()、delete()、update()、getType(),在内容提供者的进程的线程池中被调用,而不是在这一进程的主线程中。因为这些方法可以同时从任意数量的线程中调用,他们也必须实现为线程安全的。2023-07-21 18:31:113
如何给Android应用创建本地服务
去CSDN啊!一大堆大神2023-07-21 18:31:201
android 怎样在handlerthread中更新ui
一个简单的办法,主线程中定义一个handler,在handlerthread发生改变后使用message通知该handler进行更新ui即可2023-07-21 18:32:021
Android 对线程封装了:AsyncTask, HandlerThread和线程池。 有知道这三个如何选择吗?
AsyncTask性能不怎么高,而且还有内存泄漏的隐患。之所以现在没有删掉这个类库,是因为很多应用都是基于这个库的,Google这边一删,别人软件就崩了。2023-07-21 18:32:092
Android线程之HandlerThread
HandlerThread是Thread的一个子类,是Android中提供的另一种线程形态。 我擦,有线程、有looper这不正是我们当初声称在子线程中构建handler消息系统的所需要的吗? 那很明显,这个 HandlerThread 的目的就是让我们创建一个 Handler ,然后所有的任务操作成功的转给了 Handler 来处理完成。 但注意的是这个 Handler 的消息处理是运行在子线程中的。 在主线程中创建 handler ,简单的模拟一下 HandlerThread 工作原理 由于这个handler回调是运行在子线程中的,因此如果你想要更新UI可以借助主线程的默认的looper来实现,这个问题又愉快的转化到了子线程更新UI的问题。 HandlerThread其本质就是一个线程,只不过这个线程加入了Handler消息异步处理的机制。 那这与普通的创建线程的好处是什么呢?2023-07-21 18:32:151
如何理解android多线程里的getLooper方法
下面的代码是学习Handler.post方法的,第一种写法没有使用getLooper,结果运行的效果是一个线程,第二种写法使用了getLooper,运行的效果是多线程的:package com.WriteCode.AndroidHandlerPost;import android.app.Activity;import android.os.Bundle;import android.os.Handler;import android.os.HandlerThread;import android.util.Log;public class AndroidHandlerPost extends Activity {private Handler mHandler = null; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //第一种写法beg,不使用getLooper,那么整个是一个线程(UI线程) //mHandler = new Handler(); //第一种写法end,不使用getLooper,那么整个是一个线程(UI线程) //第二种写法beg,不使用getLooper,那么整个是两个线程 HandlerThread handlerThread = new HandlerThread("myHandlerThread"); handlerThread.start(); mHandler = new Handler(handlerThread.getLooper()); //第二种写法end,不使用getLooper,那么整个是两个线程 mHandler.post(new MyRunnable()); Log.e("OnCreate", "The Thread is: " + Thread.currentThread().getId()); setContentView(R.layout.main); } private class MyRunnable implements Runnable {@Overridepublic void run() {// TODO Auto-generated method stubLog.e("Runnable", "The Thread is running");Log.e("Runnable", "The Thread is: " + Thread.currentThread().getId());try{Thread.sleep(6000);}catch (InterruptedException e){e.printStackTrace();}} }}2023-07-21 18:32:241
android中handler和service的区别是什么
任务、进程和线程 关于Android中的组件和应用,之前涉及,大都是静态的概念。而当一个应用运行起来,就难免会需要关心进程、线程这样的概念。在Android中,组件的动态运行,有一个最与众不同的概念,就是Task,翻译成任务,应该还是比较顺理成章的。Task的介入,最主要的作用,是将组件之间的连接,从进程概念的细节中剥离出来,可以以一种不同模型的东西进行配置,在很多时候,能够简化上层开发人员的理解难度,帮助大家更好的进行开发和配置。任务 在SDK中关于Task(guide/topics/fundamentals.html#acttask),有一个很好的比方,说,Task就相当于应用(application)的概念。在开发人员眼中,开发一个Android程序,是做一个个独门独户的组件,但对于一般用户而言,它们感知到的,只是一个运行起来的整体应用,这个整体背后,就是Task。Task,简单的说,就是一组以栈的模式聚集在一起的Activity组件集合。它们有潜在的前后驱关联,新加入的Activity组件,位于栈顶,并仅有在栈顶的Activity,才会有机会与用户进行交互。而当栈顶的 Activity完成使命退出的时候,Task会将其退栈,并让下一个将跑到栈顶的Activity来于用户面对面,直至栈中再无更多 Activity,Task结束。事件 Task栈点开Email应用,进入收件箱(Activity A) A选中一封邮件,点击查看详情(Activity B) AB点击回复,开始写新邮件(Activity C) ABC写了几行字,点击选择联系人,进入选择联系人界面(Activity D) ABCD选择好了联系人,继续写邮件 ABC写好邮件,发送完成,回到原始邮件 AB点击返回,回到收件箱 A退出Email程序 null 如上表所示,是一个实例。从用户从进入邮箱开始,到回复完成,退出应用整个过程的Task栈变化。这是一个标准的栈模式,对于大部分的状况,这样的Task 模型,足以应付,但是,涉及到实际的性能、开销等问题,就会变得残酷许多。比如,启动一个浏览器,在Android中是一个比较沉重的过程,它需要做很多初始化的工作,并且会有不小的内存开销。但与此同时,用浏览器打开一些内容,又是一般应用都会有的一个需求。设想一下,如果同时有十个运行着的应用(就会对应着是多个Task),都需要启动浏览器,这将是一个多么残酷的场面,十个Task栈都堆积着很雷同的浏览器Activity,是多么华丽的一种浪费啊。于是你会有这样一种设想,浏览器Activity,可不可以作为一个单独的Task而存在,不管是来自那个Task的请求,浏览器的Task,都不会归并过去。这样,虽然浏览器Activity本身需要维系的状态更多了,但整体的开销将大大的减少,这种舍小家为大家的行为,还是很值得歌颂的。 如此值得歌颂的行为,Android当然会举双手支持的。在Android中,每一个Activity的Task模式,都是可以由Activity提供方(通过配置文件...)和Activity使用方(通过Intent中的flag信息...)进行配置和选择。当然,使用方对Activity的控制力,是限定在提供方允许的范畴内进行,提供方明令禁止的模式,使用方是不能够越界使用的。 在SDK中(guide/topics/fundamentals.html#acttask),将两者实现Task模式配置的方式,写的非常清晰了,我再很絮叨挑选一些来解释一下(完整可配置项,一定要看SDK,下面只是其中常用的若干项...)。提供方对组件的配置,是通过配置文件(Manifest)<activity>项来进行的,而调用方,则是通过Intent对象的flag进行抉择的。相对于标准的Task栈的模式,配置的主要方向有两个:一则是破坏已有栈的进出规则,或样式;另一则是开辟新Task栈完成本应在同一Task栈中完成的任务。 对于应用开发人员而言,<activity>中的launchMode属性,是需要经常打交道的。它有四种模式:"standard", "singleTop", "singleTask", "singleInstance"。 standard模式,是默认的也是标准的Task模式,在没有其他因素的影响下,使用此模式的Activity,会构造一个Activity的实例,加入到调用者的Task栈中去,对于使用频度一般开销一般什么都一般的Activity而言,standard模式无疑是最合适的,因为它逻辑简单条理清晰,所以是默认的选择。 而singleTop模式,基本上于standard一致,仅在请求的Activity正好位于栈顶时,有所区别。此时,配置成singleTop的Activity,不再会构造新的实例加入到Task栈中,而是将新来的Intent发送到栈顶Activity中,栈顶的Activity可以通过重载onNewIntent来处理新的Intent(当然,也可以无视...)。这个模式,降低了位于栈顶时的一些重复开销,更避免了一些奇异的行为(想象一下,如果在栈顶连续几个都是同样的Activity,再一级级退出的时候,这是怎么样的用户体验...),很适合一些会有更新的列表Activity展示。一个活生生的实例是,在 Android默认提供的应用中,浏览器(Browser)的书签Activity(BrowserBookmarkPage),就用的是singleTop。 singleTop模式,虽然破坏了原有栈的逻辑(复用了栈顶,而没有构造新元素进栈...),但并未开辟专属的Task。而singleTask,和singleInstance,则都采取的另辟Task的蹊径。标志为singleTask的Activity,最多仅有一个实例存在,并且,位于以它为根的Task中。所有对该Activity的请求,都会跳到该Activity的Task中展开进行。singleTask,很象概念中的单件模式,所有的修改都是基于一个实例,这通常用在构造成本很大,但切换成本较小的Activity中。在Android源码提供的应用中,该模式被广泛的采用,最典型的例子,还是浏览器应用的主Activity(名为Browser...),它是展示当前tab,当前页面内容的窗口。它的构造成本大,但页面的切换还是较快的,于 singleTask相配,还是挺天作之合的。 相比之下,singleInstance显得更为极端一些。在大部分时候singleInstance与singleTask完全一致,唯一的不同在于,singleInstance的Activity,是它所在栈中仅有的一个Activity,如果涉及到的其他Activity,都移交到其他Task中进行。这使得singleInstance的Activity,像一座孤岛,彻底的黑盒,它不关注请求来自何方,也不计较后续由谁执行。在Android默认的各个应用中,很少有这样的Activity,在我个人的工程实践中,曾尝试在有道词典的快速取词Activity中采用过,是因为我觉得快速取词入口足够方便(从notification中点选进入),并且会在各个场合使用,应该做得完全独立。 除了launchMode可以用来调配Task,<activity>的另一属性taskAffinity,也是常常被使用。taskAffinity,是一种物以类聚的思想,它倾向于将taskAffinity属性相同的Activity,扔进同一个Task中。不过,它的约束力,较之launchMode而言,弱了许多。只有当<activity>中的allowTaskReparen ting设置为true,抑或是调用方将Intent的flag添加FLAG_ACTIVITY_NEW_TASK属性时才会生效。如果有机会用到Android的Notification机制就能够知道,每一个由notification进行触发的Activity,都必须是一个设成FLAG_ACTIVITY_NEW_TASK的Intent来调用。这时候,开发者很可能需要妥善配置taskAffinity属性,使得调用起来的Activity,能够找到组织,在同一taskAffinity的Task中进行运行。进程 在大多数其他平台的开发中,每个开发人员对自己应用的进程模型都有非常清晰的了解。比如,一个控制台程序,你可以想见它从main函数开始启动一个进程,到 main函数结束,进程执行完成退出;在UI程序中,往往是有一个消息循环在跑,当接受到Exit消息后,退出消息循环结束进程。在该程序运行过程中,启动了什么进程,和第三方进程进行通信等等操作,每个开发者都是心如明镜一本帐算得清清楚楚。进程边界,在这里,犹如国界一般,每一次穿越都会留下深深的印迹。 在Android程序中,开发人员可以直接感知的,往往是Task而已。倍感清晰的,是组件边界,而进程边界变得难以琢磨,甚至有了进程托管一说。Android中不但剥夺了手工锻造内存权力,连手工处置进程的权责,也毫不犹豫的独占了。 当然,Android隐藏进程细节,并不是刻意为之,而是自然而然水到渠成的。如果,我们把传统的应用称为面向进程的开发,那么,在Android中,我们做得就是面向组件的开发。从前面的内容可以知道,Android组件间的跳转和通信,都是在第三方介入的前提下进行,正由于这种介入,使得两个组件一般不会直接发生联系(于Service的通信,是不需要第三方介入的,因此Android把它全部假设成为穿越进程边界,统一基于RPC来通信,这样,也是为了掩盖进程细节...),其中是否穿越进程边界也就变得不重要。因此,如果这时候,还需要开发者关注进程,就会变得很奇怪,很费解,干脆,Android将所有的进程一并托管去了,上层无须知道进程的生死和通信细节。 在Android的底层,进程构造了底部的一个运行池,不仅仅是Task中的各个Activity组件,其他三大组件Service、Content Provider、Broadcast Receiver,都是寄宿在底层某个进程中,进行运转。在这里,进程更像一个资源池(概念形如线程池,上层要用的时候取一个出来就好,而不关注具体取了哪一个...),只是为了承载各个组件的运行,而各个组件直接的逻辑关系,它们并不关心。但我们可以想象,为了保证整体性,在默认情况下,Android肯定倾向于将同一Task、同一应用的各个组件扔进同一个进程内,但是当然,出于效率考虑,Android也是允许开发者进行配置。 在Android中,整体的<application>(将影响其中各个组件...)和底下各个组件,都可以设置<process>属性,相同<process>属性的组件将扔到同一个进程中运行。最常见的使用场景,是通过配置<application>的process属性,将不同的相关应用,塞进一个进程,使得它们可以同生共死。还有就是将经常和某个Service组件进行通信的组件,放入同一个进程,因为与Service通信是个密集操作,走的是RPC,开销不小,通过配置,可以变成进程内的直接引用,消耗颇小。 除了通过<process>属性,不同的组件还有一些特殊的配置项,以Content Provider为例(通过<provider>项进行配置...)。<provider>项有一个mutiprocess的属性,默认值为false,这意味着Content Provider,仅会在提供该组件的应用所在进程构造一个实例,第三方想使用就需要经由RPC传输数据。这种模式,对于构造开销大,数据传输开销小的场合是非常适用的,并且可能提高缓存的效果。但是,如果是数据传输很大,抑或是希望在此提高传输的效率,就需要将mutiprocess设置成true,这样,Content Provider就会在每一个调用它的进程中构造一个实例,避免进程通信的开销。 既然,是Android系统帮助开发人员托管了进程,那么就需要有一整套纷繁的算法去执行回收逻辑。Android中各个进程的生死,和运行在其中的各个组件有着密切的联系,进程们依照其上组件的特点,被排入一个优先级体系,在需要回收时,从低优先级到高优先级回收。Android进程共分为五类优先级,分别是:Foreground Process, Visible Process, Service Process, Background Process, Empty Process。顾名思义不难看出,这说明,越和用户操作紧密相连的,越是正与用户交互的,优先级越高,越难被回收。具体详情,参见:guide/topics/fundamentals.html#proclife。 有了优先级,还需要有良好的回收时机。回收太早,缓存命中概率低可能引起不断的创造进程销毁进程,池的优势荡然无存;回收的太晚,整体开销大,系统运行效率降低,好端端的法拉利可能被糟蹋成一枚QQ老爷车。Android的进程回收,最重要的是考量内存开销,以及电量等其他资源状况,此外每个进程承载的组件数量、单个应用开辟的进程数量等数量指标,也是作为衡量的一个重要标识。另外,一些运行时的时间开销,也被严格监控,启动慢的进程会很被强行kill掉。Android会定时检查上述参数,也会在一些很可能发生进程回收的时间点,比如某个组件执行完成后,来做回收的尝试。 从用户体验角度来看,Android的进程机制,会有很可喜的一面,有的程序启动速度很慢,但是在资源充沛的前提下,你反复的退出再使用,则启动变得极其快速(进程没死,只是从后台弄到了前台),这就是拜进程托管所赐的。当然,可喜的另一面就是可悲了,Android的托管算法,还时不时的展现其幼稚的一面,明明用户已经明显感觉到操作系统运行速度下降了,打开任务管理器一看,一票应用还生龙活虎的跳跃着,必须要手动帮助它们终结生命找到坟墓,这使得任务管理器基本成为Android的装机必备软件。 从开发角度上来看,Android这套进程机制,解放了开发者的手脚。开发人员不需要处心积虑的构造一个后台进程偷偷默默监听某个时间,并尝试用各种各样的守护手段,把自己的进程锻造的犹如不死鸟一辉一般,进程生死的问题,已经原理了普通开发人员需要管理的范畴内。但同时,于GC和人肉内存管理的争议一样,所有开发人员都不相信算法能比自己做得效率更高更出色。但我一直坚信一点,所有效率的优势都会随着算法的不断改良硬件的不断提升而消失殆尽,只有开发模式的简洁不会随时间而有任何变化。组件生命周期 任何架构上的变化,都会引起上层开发模式的变化,Android的进程模型,虽然使开发者不再需要密切关注进程的创建和销毁的时机,但仍然需要关注这些时间点对组件的影响。比如,你可能需要在进程销毁之前,将写到内存上的内容,持久化到硬盘上,这就需要关注进程退出前发生的一些事件。 在Android中,把握这些时间点,就必须了解组件生命周期(Components Lifecycles)。所谓组件的生命在周期,就是在组件在前后台切换、被用户创建退出、被系统回收等等事件发生的时候,会有一些事件通知到对应组件上,开发人员可以选择性的处理这些事件在对应的时间点上来完成一些附加工作。除Content Provider,其他组件都会有生命周期的概念,都需要依照这个模型定时定点处理一些状况,全部内容参见:guide/topics/fundamentals.html#lcycles。在这里,擒贼先擒王,还是拿Activity出来作楷模。继续偷图,来自SDK。一个自然的Activity生命旅途,从onCreate开始,到onDestroy消亡。但月有阴晴圆缺组件有祸福旦夕,在系统需要的时候且组件位于后台时,所在的进程随时可能为国捐躯被回收,这就使得知道切入后台这个事情也变得很重要。 当组件进入栈顶,与用户开始交互,会调用onResume函数,类似,当退出栈顶,会有onPause函数被呼唤。onResume和onPause可以处理很多事情,最常规的,就是做一些文件或设置项的读写工作。因为,在该组件不再前台运行的时候,可能别的组件会需要读写同样一份文件和设置,如果不再onResume做刷新工作,用的可能就是一份脏数据了(当然,具体情况,还需要具体分析,如果文件不会被多头读写,可以放到onCreate里面去做读工作)。 除了前述切入后台会被其他组件骚扰的问题,另外,死无定因也是件很可怕的事情。在Android中,组件都有两种常见的死法,一种是自然消亡,比如,栈元素ABC,变成AB了,C组件就自然消亡了。这种死发轻如鸿毛,不需要额外关心。但另一种情况,就是被系统回收,那是死的重如泰山,为国捐躯嘛。 但这种捐躯的死法,对用户来说,比较费解。想象一下,一款游戏,不能存盘,你一直玩啊玩,三天三夜没合眼,这时候你mm打来电话鼓励一下,你精神抖擞的准备再接再厉,却发现你的游戏进程,在切入后台之后,被系统回收了,一夜回到解放前三天努力成为一场泡影,你会不会想杀做游戏的人,会不会会不会会不会,一定会嘛。这时候,如果没有Activity生命周期这码事,游戏程序员一定是被冤死的,成了Android的替罪羊。但是,Android的组件是有生命周期的, 如果真的发生这样情况,不要犹豫,去杀开发的程序员吧。 为了逃生,程序员们有一块免死金牌,那就是Android的state机制。所谓state,就是开发人员将一些当前运行的状态信息存放在一个Bundle对象里面,这是一个可序列化键值对集合。如果该Activity组件所处的进程需要回收,Android核心会将其上Activity组件的Bundle对象持久化到磁盘上,当用户回到该Activity时候,系统会重新构造该组件,并将持久化到磁盘上的Bundle对象恢复。有了这样的持久化的状态信息,开发人员可以很好的区分具体死法,并有机会的使得死而复生的Activity恢复到死前状态。开发者应该做的,是通过onSaveInstanceState函数把需要维系的状态信息(在默认的状态下,系统控件都会自己保存相关的状态信息,比如TextView,会保存当前的Text信息,这都不需要开发人员担心...),写入到Bundle对象,然后在onRestoreInstanceState函数中读取并恢复相关信息(onCreate,onStart,也都可以处理...)。线程 读取数据,后台处理,这些猥琐的伙计,自然少不了线程的参与。在Android核心的调度层面,是不屑于考量线程的,它关注的只有进程,每一个组件的构造和处理,都是在进程的主线程上做的,这样可以保证逻辑的足够简单。多线程,往往都是开发人员需要做的。 Android的线程,也是通过派生Java的Thread对象,实现Run方法来实现的。但当用户需要跑一个具有消息循环的线程的时候,Android有更好的支持,来自于Handler和Looper。Handler做的是消息的传送和分发,派生其handleMessage函数,可以处理各种收到的消息,和win开发无异。Looper的任务,则是构造循环,等候退出或其他消息的来临。在Looper的SDK页面,有一个消息循环线程实现的标准范例,当然,更为标准的方式也许是构造一个HandlerThread线程,将它的Looper传递给Handler。 在Android中,Content Provider的使用,往往和线程挂钩,谁让它和数据相关呢。在前面提到过,Content Provider为了保持更多的灵活性,本身只提供了同步调用的接口,而由于异步对Content Provider进行增删改查是一个常做操作,Android通过AsyncQueryHandler对象,提供了异步接口。这是一个Handler的子类,开发人员可以调用startXXX方法发起操作,通过派生onXXXComplete方法,等待执行完毕后的回调,从而完成整个异步调用的流程,十分的简约明了。实现 整个任务、进程管理的核心实现,尽在ActivityManagerService中。上一篇说到,Intent解析,就是这个ActivityManagerService来负责的,其实,它是一个很名不副实的类,因为虽然名为Activity的Manager Service,但它管辖的范围,不只是Activity,还有其他三类组件,和它们所在的进程。 在ActivityManagerService中,有两类数据结构最为醒目,一个是ArrayList,另一个是HashMap。 ActivityManagerService有大量的ArrayList,每一个组件,会有多个ArrayList来分状态存放。调度工作,往往就是从一个ArrayList里面拿出来,找个方法调一调,然后扔到另一个ArrayList里面去,当这个组件没对应的ArrayList放着的时候,说明它离死不远了。HashMap,是因为有组件是需要用名字或Intent信息做定位的,比如Content Provider,它的查找,都是依据Uri,有了HashMap,一切都顺理成章了。 ActivityManagerService用一些名曰xxxRecord的数据结构,来表达各个存活的组件。于是就有了,HistoryRecord(保存Activity信息的,之所以叫History,是相对Task栈而言的...),ServiceRecord,BroadcastRecord,ContentProviderRecord,TaskRecord,ProcessRecord,等等。 值得注意的,是TaskRecord,我们一直再说,Task栈这样的概念,其实,真实的底层,并不会在TaskRecord中,维系一个Activity 的栈。在ActivityManagerService中,各个任务的Activity,都以HistoryRecord的形式,集中存放在一个 ArrayList中,每个HistoryRecord,会存放它所在TaskRecord的引用。当有一个Activity,执行完成,从概念上的 Task栈中退出,Android是通过从当前HistoryRecord位置往前扫描同一个TaskRecord的HistoryRecord来完成的。这个设计,使得上层很多看上去逻辑很复杂的Task体系,在实现变得很统一而简明,值得称道。 ProcessRecord,是整个进程托管实现的核心,它存放有运行在这个进程上,所有组件的信息,根据这些信息,系统有一整套的算法来决议如何处置这个进程,如果对回收算法感兴趣,可以从ActivityManagerService的trimApplications函数入手来看。 对于开发者来说,去了解这部分实现,主要是可以帮助理解整个进程和任务的概念,如果觉得这块理解的清晰了,就不用去碰ActivityManagerService这个庞然大物了。================这是转载的 ,感觉比一楼说的好 =============一下是自己的服务(Service)需要配置 才能使用线程 使用就不说了hanler ,是用来进行消息队列的一个东东,handler 可以用来更新控件的显示 ,以及线程之间的通信,service只能启动后台,属于应用组件之一2023-07-21 18:32:333
您的应用没有被授权调用ic的相关服务 请核实
Services服务是一个应用程序组件,可以在后台执行长时间运行的操作,不提供用户界面。一个应用程序组件可以启动一个服务,它将继续在后台运行,即使用户切换到另一个应用程序。此外,一个组件可以绑定到一个服务与它交互,甚至执行进程间通信(IPC)。例如,一个服务可能处理网络通信,播放音乐,执行文件I/O,或与一个内容提供者交互,都在后台执行。一个服务本质上讲有两种形式:Started 启动的started 形式的服务是指当一个应用组件(比如 activity )通过startService()方法开启的服务。一旦开启,该服务就可以 无限期 地在后台运行,哪怕开启它的组件被销毁掉。通常,开启的服务执行一个单独的操作且并不向调用者返回一个结果。比如,可能从网络进行下载或者上传一个文件。当任务完成,服务就该自我停止。Bound 绑定的bound 形式的服务是指一个应用组件通过调用 bindService() 方法与服务绑定。一个绑定的服务提供一个客户-服务端接口,以允许组件与服务交互,发送请求,获得结果,甚至执行进程间通信。一个绑定的服务只和与其绑定的组件同时运行。多个组件可以同时绑定到一个服务,但当全部接触绑定后,服务就被销毁。虽然分这两类,但是一个服务可以同时使用这两种方式——可以用 started 无限期的运行,同时允许绑定。只需要在服务中实现两个回调方法: onStartCommand() 允许组件开启服务, onBind() 允许绑定。不论应用程序是怎么起服务的, 任何 应用程序都可以用这个服务。同样的,任何组件可以使用一个 Activity 通过传递 Intent 开启服务。你也可以在配置文件设置服务为私有来防止其他应用访问该服务。注意: 一个服务在进程中的主线程运行——一个服务 不会 创建自己的线程,也 不会 在另外的进程运行(除非另外指定)。这意味着,如果服务需要做一些频繁占用CPU的工作或者会发生阻塞的操作,你需要在服务中另开线程执行任务。这可以降低产生ANR的风险,提高用户体验。基础创建一个服务需要建立一个 Service 相关的子类,然后需要实现一些回调方法,好在不同的生命周期内做对应处理和绑定服务,比较重要的方法如下:onStartCommand() 当其他组件,如 activity 请求服务启动时,系统会调用这个方法。一旦这个方法执行,服务就开始并且无限期的执行。如果实现这个方法,当这个服务完成任务后,需要你来调用 stopSelf() 或者 stopService() 停掉服务。如果只想提供绑定,不需要自己实现这个方法。onBind() 当有其他组件想通过 bindService() 方法绑定这个服务时系统就会调用此方法。在实现的方法里面,必须添加一个供客户端使用的接口通过返回一个 IBinder 来与服务通信,这个方法必须实现。当然不想允许绑定的话,返回 null 即可。onCreate() 服务第一次建立的时候会调用这个方法,执行一次性设置程序,在上面2个方法执行前调用。如果服务已存在,则不执行该方法。onDestroy() 服务不再使用则使用该方法。服务应该实现这个方法来清理诸如线程,注册的监听器等资源。这是最后调用的方法。安卓系统只会在内存占用很高,必须恢复系统资源供当前运行程序的情况下强制停掉一个运行中的服务。如果服务绑定在当前运行的程序中,就几乎不会被杀掉,如果服务声明了在前台运行(其实在后台,只是给系统一个错的信息来提高优先级),就几乎不会被杀掉。另外,如果一个服务正在运行,且运行了很久,系统就会根据运行时间把其排在后台任务列表的后面,则这个服务很容易被杀掉。根据onStartCommand() 的返回值设置,服务被杀掉后仍可以在资源充足的条件下立即重启。是用一个服务好还是开一个线程好一个服务就是一个可以忽略交互,在后台独立运行的组件,如果你需要这样就用服务如果你需要在用户与程序交互时在主线程外执行任务,那就开个线程吧。比如想播放音乐,但只在程序运行时播放,你可能在 onCreate() 开一个线程,在 onStart() 中开启它,在 onStop() 停止它。也可以考虑使用 AsyncTask 或者HandlerThread 取代一般的线程。记住,如果使用一个服务,它还是默认在主线程中运行,如果会发生阻塞,还是要在服务中另开线程的。在 manifest 文件声明服务要使用服务就必须在 manifest 文件声明要用的所有服务,只用在<application> 标签内添加子标签 <service> 即可。 <manifest ...> ... <application ...> <service android:name=".ExampleService" android:enabled=["true" | "false"] android:exported=["true" | "false"] android:isolatedProcess=["true" | "false"] android:label="string resource" android:icon="drawable resource" android:permission="string" android:process="string" > ... </service> </application> </manifest>下面对 service 标签属性做说明android:name你所编写的服务类的类名,可填写完整名称,包名+类名,如com.example.test.ServiceA ,也可以忽略包名,用 . 开头,如 .ServiceA,因为在 manifest 文件开头会定义包名,它会自己引用。一旦你发布应用,你就不能改这个名字(除非设置 android:exported="false"),另外 name 没有默认值,必须定义。android:enabled是否可以被系统实例化,默认为 true因为父标签 <application> 也有 enable 属性,所以必须两个都为默认值true 的情况下服务才会被激活,否则不会激活。android:exported其他应用能否访问该服务,如果不能,则只有本应用或有相同用户ID的应用能访问。当然除了该属性也可以在下面 permission 中限制其他应用访问本服务。这个默认值与服务是否包含意图过滤器 intent filters 有关。如果一个也没有则为 falseandroid:isolatedProcess 设置 true 意味着,服务会在一个特殊的进程下运行,这个进程与系统其他进程分开且没有自己的权限。与其通信的唯一途径是通过服务的API(binding and starting)。android:label 可以显示给用户的服务名称。如果没设置,就用 <application> 的 lable 。不管怎样,这个值是所有服务的意图过滤器的默认 lable 。定义尽量用对字符串资源的引用。android:icon 类似 label ,是图标,尽量用 drawable 资源的引用定义。android:permission是一个实体必须要运行或绑定一个服务的权限。如果没有权限,startService() , bindService() 或 stopService() 方法将不执行,Intent 也不会传递到服务。如果属性未设置,会由 <application> 权限设置情况应用到服务。如果两者都未设置,服务就不受权限保护。android:process服务运行所在的进程名。通常为默认为应用程序所在的进程,与包名同名。<application> 元素的属性 process 可以设置不同的进程名,当然组件也可设置自己的进程覆盖应用的设置。如果名称设置为冒号 : 开头,一个对应用程序私有的新进程会在需要时和运行到这个进程时建立。如果名称为小写字母开头,服务会在一个相同名字的全局进程运行,如果有权限这样的话。这允许不同应用程序的组件可以分享一个进程,减少了资源的使用。创建“启动的”服务启动的(started)服务由 startService(Intent) 方法启动,在服务中的 onStartCommand()方法里获得 Intent 信息。关闭则由服务自己的方法 stopSelf() 或者由启动服务的地方调用 stopService(Intent) 方法来关闭。并不会因为启动服务的应用程序销毁而关闭。示例,一个应用需要保存数据到远程数据库,这时启动一个服务,通过创建启动的服务给服务传递数据,由服务执行保存行为,行为结束再自我销毁。因为服务跟启动它的应用在一个进程的主线程中,所以对于耗时的操作要起一个新的线程去做。//activity中Intent intent = new Intent(MainActivity.this, ServiceA.class);intent.putExtra("name", strName);startService(intent);//service中@Overridepublic int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub // 获取数据 String strName = intent.getStringExtra("name"); // ... 数据库操作 new Thread(new Runnable() { @Override public void run() { // TODO Auto-generated method stub 耗时的操作 } }).run(); return Service.START_STICKY;}写服务有2种,继承 service 或者 IntentService 。后者是前者的子类。前者包含上面介绍的各种方法,用于普通的服务。后者可以自己开一个工作线程一个接一个处理多个请求。继承IntentService大多数服务不需要同时处理多个请求,继承 IntentService 是最好的选择IntentService处理流程创建默认的一个 worker 线程处理传递给 onStartCommand() 的所有 intent ,不占据应用的主线程创建一个工作队列一次传递一个 intent 到你实现的 onHandleIntent() 方法,避免了多线程在所以启动请求被处理后自动关闭服务,不需要调用 stopSelf()默认提供 onBind() 的实现,并返回 null默认提供 onStartCommand() 的实现,实现发送 intent 到工作队列再到你的onHandleIntent() 方法实现。这些都加入到 IntentService 中了,你需要做的就是实现构造方法和onHandleIntent() ,如下:public class HelloIntentService extends IntentService { /** * A constructor is required, and must call the super IntentService(String) * constructor with a name for the worker thread. */ public HelloIntentService() { super("HelloIntentService"); } /** * The IntentService calls this method from the default worker thread with * the intent that started the service. When this method returns, IntentService * stops the service, as appropriate. */ @Override protected void onHandleIntent(Intent intent) { // Normally we would do some work here, like download a file. // For our sample, we just sleep for 5 seconds. long endTime = System.currentTimeMillis() + 5*1000; while (System.currentTimeMillis() < endTime) { synchronized (this) { try { wait(endTime - System.currentTimeMillis()); } catch (Exception e) { } } } }}如果需要重写其他回调方法,如 onCreate() , onStartCommand() 等,一定要调用super() 方法,保证 IntentService 正确处理 worker 线程,只有 onHandleIntent()和 onBind() 不需要这样。如:@Overridepublic int onStartCommand(Intent intent, int flags, int startId) { Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show(); return super.onStartCommand(intent,flags,startId);}继承Service继承 Service 就可以实现对请求多线程的处理,前面介绍了 service 的生命周期,可以按照生命周期实现方法。就不放示例了。onStartCommand() 的返回值返回一个整型值,用来描述系统在杀掉服务后是否要继续启动服务,返回值有三种:START_NOT_STICKY 系统不重新创建服务,除非有将要传递来的 intent 。这是最安全的选项,可以避免在不必要的时候运行服务。START_STICKY 系统重新创建服务并且调用 onStartCommand() 方法,但并不会传递最后一次传递的 intent ,只是传递一个空的 intent 。除非存在将要传递来的 intent ,那么就会传递这些 intent 。这个适合播放器一类的服务,不需要执行命令,只需要独自运行,等待任务。START_REDELIVER_INTENT 系统重新创建服务并且调用 onStartCommand() 方法,传递最后一次传递的intent 。其余存在的需要传递的intent会按顺序传递进来。这适合像下载一样的服务,立即恢复,积极执行。如果想从服务获得结果,可以用广播来处理创建“绑定的”服务用 bindService() 方法将应用组件绑定到服务,建立一个长时间保持的联系。如果需要在 activity 或其他组件和服务交互或者通过进程间通信给其他应用程序提供本应用的功能,就需要绑定的服务。建立一个绑定的服务需要实现 onBind() 方法返回一个定义了与服务通信接口的IBinder 对象。其他应用程序组件可以调用 bindService() 方法获取接口并且调用服务上的方法。创建一个绑定的服务,第一件事就是定义一个说明客户端与服务通信方式的接口。这个接口必须是 IBinder 的实现,并且必须要从 onBind() 方法返回。一旦客户端接收到了 IBinder ,就可以通过这个接口进行交互。多个客户端可以绑定到一个服务,可以用 unbindService() 方法解除绑定,当没有组件绑定在服务上,这个服务就会被销毁。//activity中private ServiceConnection connB = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { // TODO Auto-generated method stub Log.v(tag, "Service B disconnected"); } @Override public void onServiceConnected(ComponentName name, IBinder service) { // TODO Auto-generated method stub Log.v(tag, "Service B connected"); MyBinderB binder = (MyBinderB) service; ServiceB SB = binder.getService(); SB.showLog(); }};@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.button1).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // TODO Auto-generated method stub Intent a = new Intent(MainActivity.this, ServiceB.class); bindService(a, connB, BIND_AUTO_CREATE); }}//ServiceBpublic class ServiceB extends Service { public void showLog() { Log.i(tag, "serviceB-->showLog()"); } public class MyBinderB extends Binder { public ServiceB getService() { return ServiceB.this; } } private MyBinderB myBinderB = new MyBinderB(); @Override public IBinder onBind(Intent intent) { // TODO Auto-generated method stub return myBinderB; }}启动前台服务前台服务是被认为是用户已知的正在运行的服务,当系统需要释放内存时不会优先杀掉该进程。前台进程必须发一个 notification 在状态栏中显示,直到进程被杀死。因为前台服务会一直消耗一部分资源,但不像一般服务那样会在需要的时候被杀掉,所以为了能节约资源,保护电池寿命,一定要在建前台服务的时候发notification ,提示用户。当然,系统提供的方法就是必须有 notification 参数的,所以不要想着怎么把 notification 隐藏掉。@Overridepublic int onStartCommand(Intent intent, int flags, int startId) { // TODO Auto-generated method stub Intent notificationIntent = new Intent(this, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0); Notification noti = new Notification.Builder(this) .setContentTitle("Title") .setContentText("Message") .setSmallIcon(R.drawable.ic_launcher) .setContentIntent(pendingIntent) .build(); startForeground(12346, noti); return Service.START_STICKY;}startForeground() 方法就是将服务设为前台服务。参数12346就是这个通知唯一的id,只要不为0即可。2023-07-21 18:32:431
android:当Activity和Service 都被销毁后,如何控制其中生成的线程?
线程没有被销毁的,当Activity或者Service中还有活动线程的时候,垃圾回收器是不会回收销毁Activity和Service对象的。举个例子,你可以在Activity中启动一个线程,在onDestroy中用System.out.print或者log输出一个信息,然后通过按钮调用finish方法,会发现点击以后Activity会“关闭”,但只是不可见了,但是没有调用onDestroy方法。除非你在onDestroy中关闭了线程才会关闭。线程管理一般是通过一个布尔类型值保存其状态,通过判断它是否为空,一起来处理。这样最简单。就是在onDestroy中处理的,你说没有调用,是因为还有子线程在运行。在onDestroy中判断线程状态,正常关闭线程以后就行了。2023-07-21 18:32:513