IntentService与service的最大的区别就是IntentService可以进行耗时操作,因为它自带了一个线程,记住只有一个线程。
在IntentService中有一个队列的概念,即在第一次启动IntentService,并在onHandleIntent中执行的时候,再第二次次启动IntentService,第二次的操作不会立刻执行,而是先将其放在队列中,当第一次运行完时,再执行第二次操作。这与Service是不一样的,当第一次还未执行完时,启动第二次,他会直接从onStartCommand开始执行。而不是像第一次一样按循序执行。
IntentService的建立循序:
1)新建的类会继承IntentSrevice。
2)重写onHandleIntent()方法。许多操作基本上都是在这个方法中写的。
3)在Activity中像执行Service一样去启动IntentService。
4)记住,一定要注册:
<service android:name=".myIntentSrevice"/>
代码如下:
public class myIntentSrevice extends IntentService {
int count;
/** * Creates an IntentService. Invoked by your subclass's constructor. * * @param name Used to name the worker thread, important only for debugging. */
public myIntentSrevice(String name) {
super(name);
}
//必须写这个才能注册
public myIntentSrevice() {
this("");
}
@Override
protected void onHandleIntent(Intent intent) {
while (count<100){
if(count==99){
count=0;
}
count++;
Intent intent2 =new Intent("com.pp");
// intent.setAction("com.pp");
intent2.putExtra("count",count);
sendBroadcast(intent2);//在线程中启动广播
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
@Override
public void onDestroy() {
Log.d("","结束service");
super.onDestroy();
}
}
在Activity中:
Intent intent3= new Intent(getApplicationContext(),myIntentSrevice.class);
startService(intent3);
在android中一个应用就是一个进程,在一个进程中可以有多个线程。
其中最重要的一个线程是UI主线程,他就像我们看电影一样在界面中不停的画view。UI主线程是不允许其他线程修改UI的View的。当必须由子线程修改view时,就可以用Looper,Message,Handler三者之间的关系之间的关系来解决。也可以用AsyncTask,这个等会再说。
其图如下;
在主线程中建立一个Handler并复写方法handleMessage()(大多数操作都在这里)接受消息,在子线程中用主线程建立的Handler对象发送message消息。
记住在主线程建立Handler对象后就形成了一个MessageQueue——Looper机制。当子线程发送消息给主线程,这个消息会放在MessageQueue中。当主线程运行到MessageQueue时会将数据取走。
下面我们写一个倒计时的例子:
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case DESC:
button_desc.setText((String) msg.obj);
break;
}
}
} ;
case R.id.button_desc:
new Thread(new Runnable() {
@Override
public void run() {
while (count>0){
count--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// Message message =new Message();
Message message= handler.obtainMessage(); //它会获得一个用过的message对象,没有的话会自动建立。
message.obj=count+"秒";//赋予要传递的数据
message.what=DESC;//这类消息的标示。
handler.sendMessage(message);
}
}
}).start();
break;
在按键的点击事件中:
// Message message =new Message();
Message message= handler.obtainMessage(); //它会获得一个用过的message对象,没有的话会自动建立。
message.obj=count+"秒";//赋予要传递的数据
message.what=DESC;//这类消息的标示。
handler.sendMessage(message);//发送这个消息
第二种写法:
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case DESC:
count--;
button_desc.setText(count+"秒");
if(count>0){
try {
Thread.sleep(1000);
handler.sendEmptyMessage(DESC);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
break;
}
}
} ;
case R.id.button_desc:
handler.sendEmptyMessage(DESC);//再次发送一个空消息
(1)Handler.post()的用法:可以在子线程中调用handler.post()在post包含的Runnable()的run方法中更新UI。
代码:
new Thread(){
@Override
public void run() {
handler.post(new Runnable() {
@Override
public void run() {
firstEdit.setText("hahahhaha");
}
});
}
}.start();
(2) handler.postDelayed(myRunnable,1000);可以进行定时更新UI操作。
1)建立一个继承自Runnale的类,在run方法中更新Ui。
2)建立这个类的对象,在oncreat()和继承自Runnale的类的runn()中调用 handler.postDelayed(myRunnable,1000);这样就可以
定时实现更新UI的操作了。
代码如下:
private MyRunnable myRunnable = new MyRunnable();
class MyRunnable implements Runnable{
@Override
public void run() {
index++;
index = index%3;
imageView.setImageResource(images[index]);
handler.postDelayed(myRunnable,1000);
}
}
在oncreat()中:
handler.postDelayed(myRunnable,1000);
(3) handler.removeCallbacks(myRunnable);
用于移除Runnable的回调机制。当调用这行代码的时候 handler.postDelayed(myRunnable,1000);中的myRunnable就不起作用了。
(4)Message:Handler可以通过发送message来传递给主线程一些小消息。要发送消息,必须有消息。可以通过两种方式获得Message
:Message message = new Message();或者 Message message = handler.obtainMessage();//这个是获得系统的闲置的Message。
如果没有闲置的会自动给你生成一个。
消息载体是: message.arg1 =88;//message.arg1 是int类型的载体。如果想传递字符串或其他的对象可以用message.obj=?//可以是
字符串,各种对象。发送有两种形式: handler.sendMessage(message);//这一种是比较常用的。如果用Message message =
handler.obtainMessage();生成message的话,可以用message.sendToTarget();发送,否则不可以。
发送的消息会在handler的handleMessage()函数中接收的,一般先判读是否是这个消息。用handler.what或者
handler.arg1/2判断。
代码如下:
Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Toast.makeText(getApplicationContext(),""+msg.what,Toast.LENGTH_SHORT).show();
}
};
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
Message message = handler.obtainMessage();//如果这样获得message,可以这样发送:
message.sendToTarget();
// 否则不可以这样发送用这个发送:handler.sendMessage(message);。
message.arg1 =88;
message.sendToTarget();
handler.sendMessage(message);
handler.sendEmptyMessage(1);//还可以发空字符串。
}
}).start();
}
});
}
(5)在创建handler的时候还可以在构造器中添加Callback()的对象,在这个类中也有一个handleMessage方法,只不过它有返回值。这
是一个拦截机制,在发送过来的消息会先在Callback中处理,如果返回false在这里处理完后会再在handler的handleMessage方法中处
理。如果返回true。则此消息不会再往下传。
Handler Looper MessageQueue三者之间的关系:
一,Handler封装了消息的发送(主要包括消息发送给谁)
Looper
1.内部包含一个消息队列也就是MessageQueue,所有发送的handler发送的消息都走这个消息队列。
2.Looper.Looper方法就是一个死循环,不断的从MessageQueue中取消息,如果有消息就处理消息,如果没有就阻塞。
二.MessageQueue,就是一个消息队列,可以添加消息和处理消息
三.handler内部会跟Looper进行关联,也就是说在handler的内部就可以找到Looper,找到了Looper也就找到了MessageQueue,在
Hnadler中发送消息也就是向MessageQueue中发送消息。
总结:Handler负责发送消息,Looper负责接收Handler发送的消息,并把消息回传给Handler自己。MessageQueue就是存储消息的容器
。
HandlerUI主线程给子线程发送消息。
在子线程中建立Handler的对象,在UI主线程中调用这个对象的引用,并发送消息。注意。在子线程中必须自己建立Looper对象: Looper.prepare();//创建Looper对象.并且调用Looper.loop()方法。在这之间初始化Handler的对象,重写handleMessage()方法
代码如下:
class MyThread extends Thread{
Handler handler;
@Override
public void run() {
Looper.prepare();//创建Looper对象
handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
Toast.makeText(getApplicationContext(), ""+msg.what, Toast.LENGTH_SHORT).show();
}
};
// Toast.makeText(getApplicationContext(), "aaaaa", Toast.LENGTH_SHORT).show();
Looper.loop();//实现消息的循环处理
}
}
在UI主线程中thread.start();时不可以立刻发送消息: thread.handler.sendEmptyMessage(1);这样会有空指针的错误,应该延迟一段时间。
在创建Handler对象的时候可以不用默认的Looper对象是可以绑定你指定的某个线程的Looper对象。例如:
handler = new Handler(thread.getLooper())这就是绑定了threadThread的Looper对象,当handler与某个线程的Looper对象绑定时
handleMessage()方法就会在这个线程中执行而不是Handler所在的对象执行。
但是如果随便的绑定一个线程的Looper对象的话,可能会由于线程的不同步等问题会有空指针的问题出现(在绑定这个线程的Looper的时候可能还没有创建成功),为了避免这种问题的发生,google开发了一种HandlerThread的线程,这个线程就有Looper对象在我们调用thread.getLooper()的时候会先判断Looper是否为空,如果为空就等待,当Looper创建成功的时候会通知它,然后再执行。这样就避免了多线程不同步引起的空指针的问题。这样我们也可以在handleMessage()中执行一些耗时操作了。
建立两个Handler实例,都是在UI中建立的,只不过有一个Handler的实例是绑定的子线程的Looper。这样就可以用这个Handler的实例在子线程中实现接收主线程的消息了。完整如下:
public class MainActivity extends AppCompatActivity {
private TextView firstEdit;
private Button myFirstbutton;
private ImageView imageView;
private Handler threadHandler;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
threadHandler.sendEmptyMessageDelayed(2,1000);
Log.d("aaaa",""+Thread.currentThread());
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
firstEdit = (TextView) findViewById(R.id.first_edit);
myFirstbutton = (Button) findViewById(R.id.first_button);
imageView = (ImageView) findViewById(R.id.myImageView);
HandlerThread thread = new HandlerThread("Handler Thread");
thread.start();
threadHandler = new Handler(thread.getLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
handler.sendEmptyMessageDelayed(1,1000);
Log.d("aaaa",""+Thread.currentThread());
}
};
myFirstbutton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
handler.sendEmptyMessage(1);
}
});
}
}
方法一:handler.post(new Runnable(){
run(){
}
})
方法二:
handler.setmaeesge()在handler的handleMessage()方法中更新UI;
方法三
调用view的post方法。
firstEdit.post(new Runnable() {
@Override
public void run() {
firstEdit.setText("hehelooooooo");
}
});
方法四
调用runOnUiThread()方法:
runOnUiThread(new Runnable() {
@Override
public void run() {
firstEdit.setText("heheloooooooooo");
}
});
在主线程给子线程发送消息时,子线程的run()方法的代码的handler的代码必须放在 Looper.prepare();和 Looper.loop();这两行代码之间。但在主线程中建立Handler时,主线程是默认就有的,所以不需要写。
MyThread thread = new MyThread();
thread.start();
与 handler.sendEmptyMessage(0);这两行代码不可以在一起,因为启动线程需要时间而handler.sendEmptyMessage(0)的执行时间很小。所以在线程还没有启动的时候,主线程就已经发送消息了,会造成空指针。
代码如下:
private Handler handler;
class MyThread extends Thread{
@Override
public void run() {
Looper.prepare();
handler =new Handler(){
@Override
public void handleMessage(Message msg) {
Log.d("thread","我收到了");
}
};
Looper.loop();
}
}
//在点击事件中
case R.id.button_desc:
MyThread thread = new MyThread();
thread.start();
case R.id.button_send:
// Message message =handler.obtainMessage();
// handler.sendMessage(message);
handler.sendEmptyMessage(0);
break;
AsyncTask的基本原理与Handler是一样的,就是用Handler进行封装的,只不过
可以在其中的方法中可以对View进行操作,一般是以内部类的形式存在的。
操作顺序如下:
1)写一个类继承于AsyncTask(String,Integer,String)(是尖括号哦)
第一个参数为doInBackground的参数,最后一个是doInBackground的返回值的leixing
第二个参数为onProgressUpdate接受的第一个参数。
doInBackground()为后台运行过程。onPostExecute(String s)为运行结束时的操作。
onProgressUpdate()为在运行过程中对view的操作。
onPostExecute()和onProgressUpdate()可以对view进行操作。
doInBackground()的publishProgress(count);为调用onProgressUpdate()方法。
2)在Activity的某个位置建立此类的对象。并调用execute(“”)方法。
写一个下载的例子:
class MyAsyncTask extends AsyncTask<String,Integer,String>{
@Override
protected String doInBackground(String... params) {
while (count<101){
count++;
publishProgress(count);//调用onProgressUpdate()方法
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "全部传完了哦";
}
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
down_button.setText(s);
}
@Override
protected void onProgressUpdate(Integer... values) {
super.onProgressUpdate(values);
progressBar.setProgress(values[0]);//在参数不确定的时候values是以数组的形式处理
的
}
}
}
case R.id.down_button:
MyAsyncTask myAsyncTask =new MyAsyncTask();
myAsyncTask.execute("dd");//dd是随便写的。
break;