第十章:后台默默地劳动者——探究服务
后台功能属于四大组件之一,其重要程度不言而喻,那么我们自然要好好学习一下它的用法了
10.1 服务是什么
服务是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务。服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另一个应用程序,服务仍然能够保持正确运行。
需要注意的是,服务并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程,当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。
服务实际上并不会自动开启线程,所有的代码都是默认运行在主线程当中的。也就是说,我们需要在服务的内部手动创建子线程,并在这里执行具体的任务,否则就有可能出现主线程被阻塞住的情况。
10.2 Android多线程编程
10.2.1 线程的基本用法
Android多线程编程其实并不比Java多线程编程特殊,基本都是使用相同的语法。
1)新建一个类继承自Thread,然后重写run()方法
class MyThread extends Thread {
@Override
public void run() {
//处理具体的逻辑
}
}
启动线程: new MyThread().start();
2)实现Runnable接口的方式来定义一个线程
class MyThread implements Runnable {
@Override
public void run() {
//处理具体的逻辑
}
}
启动线程:MyThread myThread = new Thread();
new Thread(myThread).start();
3)匿名类的方式去实现Runnable接口
new Thread(new Runnable() {
@Override
public void run() {
//处理具体的逻辑
}
}).start();
10.2.2 在子线程中更新UI
和许多其他的GUI库一样,Android的UI也是线程不安全的。因此如果想要更新应用程序里的UI元素,则必须在主线程中进行,否则就会出现异常。
Android提供了一套异步消息处理机制,用来解决在子线程中进行UI操作的问题。
//修改MainActivity中的代码
public class MainActivity extends AppCompatActivity implements View.OnClickListaner {
public static final int UPDATE_TEXT = 1;
private TextView text;
private Handler handler = new Handler() {
public void handleMessage(Message msg) {
//对具体的Message信息进行处理
switch (msg.what) {
case UPDATE_TEXT:
//在这里可以进行UI操作
text.setText("Nice to meet you");
break;
default:
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.text); //显示文字
Button changeText = (Button) findViewById(R.id.change_text); //点击按钮实现换文字
changeText.setOnClickListener(this);
}
/*
这里创建了一个Message(android.os.Message)对象,并将它的what字段的值指定为UPDATE_TEXT
然后调用Handler的sendMessage()方法将这条Message发送出去
很快,Handler就会收到这条Message,并在HandlerMessage()方法中对它进行处理(注意这里handlerMessage()方法中的代码就是在主线程中运行的)
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.change_text:
new Thread(new Runnable () {
@Override
public void run() {
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message); //将Message对象发送出去
}
}).start();
default:
break;
}
}
}
10.2.3 解析异步消息处理机制
Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue和Looper。
1)Message:Message是在线程之间传递的消息,它可以在内部携带少量的信息,用户在不同线程之间进行交换数据。(eg:Message.what携带的是what字段,还可以携带一些整型数据)
2)Handler:处理者,主要用于发送和处理消息的,发送消息一般是使用Handler的sendMessage方法,经过一些列的辗转后最终传递到Handler的handleMessage方法中。
3)MessageQueue:MessageQueue是消息队列的意思,它主要用于存放所有通过Handler发送的消息,这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue对象。
4)Looper:每个线程中的MessageQueue的管家,调用Loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法当中。每个线程中也只会有一个Looper对象。
异步消息处理的整个流程:
首先需要在主线程当中创建一个Handler对象,并重写handleMessage()方法。然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handler将这条消息发送出去。之后这条消息会被添加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发会Handler的handleMessage()方法中。由于Handler是在主线程中创建的,所以此时handleMessage()方法中的代码也会在主线程中运行,于是我们就可以安心地进行UI操作了。这就是整个异步消息处理机制。
10.2.4 使用AsyncTask
AsyncTask工具更方便在子线程中进行UI操作,它的实现原理也是基于异步消息处理机制的,只是Android帮我们做了很好的封装而已。
AsyncTask的基本用法:
AsyncTask是个抽象类,要使用它的话需要创建一个子类去继承,在继承时可以为AsyncTask指定3个泛型参数,这3个参数的用途为:
1)Params:执行AsyncTask时需要传入的参数,可用于在后台任务中使用。2)Progress:后台执行任务时需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
3)Result:当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值的类型//一个比较完整的AsyncTask如下 class DownloadTask extends AsyncTask<Void,Integer,Boolean> { @Override //这个方法在后台任务开始执行之前调用,进行一些界面上的初始化操作 protected void onPreExcute() { progressDialog.show(); //显示进度对话框 } @Override //这个方法在子线程中运行,在这里去处理所有的耗时任务,处理完成return返回 //这里要处理UI的话,可以用过调用publishProgress方法来完成 protected Boolean doInBackground(Void... params) { try { while(true) { int downloadPercent = doDownload(); //doDownload是一个假方法,用于计算当前的下载速度并返回(虚构的方法) //publishProgress方法通过handler发送信息,在主线程中接收消息的地方会执行到onProgressUpdate()方法去更新UI publishProgress(downloadPercent); if (downloadPercent > = 100) { break; } } } catch (Exception e) { return false; } return true; } @Override //在后台调用publishProgress方法后,会调用这个方法,这个方法中携带的参数就是后台任务中传递过来的参数 protected void onProgressUpdate(Integer... values) { //在这里更新下载进度 progressDialog.setMessage("Downloaded"+values[0]+"%"); } @Override //这个方法是当后台执行完毕并通过return返回语句返回时,来调用的,携带的参数就是返回的数据,可以进行一些UI的操作 protected void onPostExecute(Boolean result) { progressDialog.dismiss(); //关闭进度对话框 //在这里提示下载结果 if (result) { Toast.makeText(context,"Download succeeded",Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context,"Download failed",Toast.LENGTH_SHORT).show(); } } }
我正在使用i18n从头开始构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在rubyonrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
exe应该在我打开页面时运行。异步进程需要运行。有什么方法可以在ruby中使用两个参数异步运行exe吗?我已经尝试过ruby命令-system()、exec()但它正在等待过程完成。我需要用参数启动exe,无需等待进程完成是否有任何rubygems会支持我的问题? 最佳答案 您可以使用Process.spawn和Process.wait2:pid=Process.spawn'your.exe','--option'#Later...pid,status=Process.wait2pid您的程序将作为解释器的子进程执行。除
我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("
我是rails的新手,想在form字段上应用验证。myviewsnew.html.erb.....模拟.rbclassSimulation{:in=>1..25,:message=>'Therowmustbebetween1and25'}end模拟Controller.rbclassSimulationsController我想检查模型类中row字段的整数范围,如果不在范围内则返回错误信息。我可以检查上面代码的范围,但无法返回错误消息提前致谢 最佳答案 关键是您使用的是模型表单,一种显示ActiveRecord模型实例属性的表单。c
按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道rubyonrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim
无论您是想搭建桌面端、WEB端或者移动端APP应用,HOOPSPlatform组件都可以为您提供弹性的3D集成架构,同时,由工业领域3D技术专家组成的HOOPS技术团队也能为您提供技术支持服务。如果您的客户期望有一种在多个平台(桌面/WEB/APP,而且某些客户端是“瘦”客户端)快速、方便地将数据接入到3D应用系统的解决方案,并且当访问数据时,在各个平台上的性能和用户体验保持一致,HOOPSPlatform将帮助您完成。利用HOOPSPlatform,您可以开发在任何环境下的3D基础应用架构。HOOPSPlatform可以帮您打造3D创新型产品,HOOPSSDK包含的技术有:快速且准确的CAD
我的工作要求我为某些测试自动生成电子邮件。我一直在四处寻找,但未能找到可以快速实现的合理解决方案。它需要在outlook而不是其他邮件服务器中,因为我们有一些奇怪的身份验证规则,我们需要保存草稿而不是仅仅发送邮件的选项。显然win32ole可以做到这一点,但我找不到任何相当简单的例子。 最佳答案 假设存储了Outlook凭据并且您设置为自动登录到Outlook,WIN32OLE可以很好地完成此操作:require'win32ole'outlook=WIN32OLE.new('Outlook.Application')message=
我正在使用Ruby,我正在与一个网络端点通信,该端点在发送消息本身之前需要格式化“header”。header中的第一个字段必须是消息长度,它被定义为网络字节顺序中的2二进制字节消息长度。比如我的消息长度是1024。如何将1024表示为二进制双字节? 最佳答案 Ruby(以及Perl和Python等)中字节整理的标准工具是pack和unpack。ruby的packisinArray.您的长度应该是两个字节长,并且按网络字节顺序排列,这听起来像是n格式说明符的工作:n|Integer|16-bitunsigned,network(bi
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.