Service
利用Service做一個虛擬進度條
ProgressBar進度條,progress可以設置默認的進度
在activity_main.xml中加入進度條
<ProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/progressBar"
android:layout_gravity="center_horizontal"
android:progress="50"/>
<Button
android:id="@+id/start_download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="開始下載"/>
設置線程,通過啓動服務來啓動線程,線程啓動進度,
MyService.java
public class MyService extends Service{
private int count;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d("運行到了","onCreate");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("運行到了","onDestroy");
}
@Override
public int onStartCommand(final Intent intent, int flags, int startId) {
Log.d("運行到了","onStartCommand");
new Thread(new Runnable() {
@Override
public void run() {
while(count<100){
if (count>100){
count=0;
}
count++;
Intent intent1=new Intent();
intent1.setAction(MainActivity.DOWN_LOAD_ACTION);
intent1.putExtra("count",count);
sendBroadcast(intent1);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
}
進行動態註冊,在Activity_Manifest.xml中註冊
<service android:name=".MyService"></service>
在MainActivity.java中進行設置監聽事件
public class MainActivity extends Activity {
private Button btn_start_service;
private Button btn_stop_service;
private Button btn_start_download;
private ProgressBar progressBar;
private MyDownLoadService mDownLoadService;
public static final String DOWN_LOAD_ACTION="com.service.text";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_start_service= (Button) findViewById(R.id.start_service);
btn_start_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(getApplicationContext(),MyService.class);
startService(intent);
}
});
btn_stop_service= (Button) findViewById(R.id.stop_service);
btn_stop_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(getApplicationContext(),MyService.class);
stopService(intent);
}
});
mDownLoadService=new MyDownLoadService();
IntentFilter filter=new IntentFilter();
filter.addAction(DOWN_LOAD_ACTION);
registerReceiver(mDownLoadService,filter);
progressBar= (ProgressBar) findViewById(R.id.progressBar);
btn_start_download= (Button) findViewById(R.id.start_download);
btn_start_download.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(getApplicationContext(),MyService.class);
startService(intent);
}
});
}
class MyDownLoadService extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
int count=intent.getIntExtra("count", 0);
progressBar.setProgress(count);
}
}
//解綁,取消廣播
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mDownLoadService);
}
}
Service是運行在主線程中,第一次運行時會調用onCreate()、onStartCommand(),當第二次啓動時只會調用onStartCommand(),而當只在後臺允許一個耗時操作時就要用到IntentService,IntentService本身包含一個線程,包含一個消息隊列,會將後續的消息排在隊列中,然後等待前面的消息運行結束後,在進行處理後續的消息。
在activity_main.xml中添加一個按鈕
<Button
android:id="@+id/start_download_intentservice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="開始intentservice下載"/>
寫一個MyIntentService.java類,繼承於IntentService,要在AndroidManifest.xml中註冊
<service android:name=".MyIntentService"></service>
public class MyIntentService extends IntentService{
private int count;
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*
* @param name Used to name the worker thread, important only for debugging.
*/
public MyIntentService(String name) {
super(name);
}
public MyIntentService() {
this("");
}
@Override
protected void onHandleIntent(Intent intent) {
Log.d("IntetnService","運行到了OnCreate");
while(count<100){
if (count>100){
count=0;
}
count++;
Intent intent1=new Intent();
intent1.setAction(MainActivity.DOWN_LOAD_ACTION);
intent1.putExtra("count",count);
sendBroadcast(intent1);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("IntetnService","運行到了onStartCommand");
}
}
在MainActivity.java中添加點擊事件
public class MainActivity extends Activity {
private Button btn_start_service;
private Button btn_stop_service;
private Button btn_start_download;
private Button btn_intentservice;
private ProgressBar progressBar;
private MyDownLoadService mDownLoadService;
public static final String DOWN_LOAD_ACTION="com.service.text";
private AlarmManager mAlarmManager;
private BroadcastReceiverActivity mBroadcast;
private android.content.ContentResolver resolver;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_start_service= (Button) findViewById(R.id.start_service);
btn_start_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(getApplicationContext(),MyService.class);
startService(intent);
}
});
btn_stop_service= (Button) findViewById(R.id.stop_service);
btn_stop_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(getApplicationContext(),MyService.class);
stopService(intent);
}
});
mDownLoadService=new MyDownLoadService();
IntentFilter filter=new IntentFilter();
filter.addAction(DOWN_LOAD_ACTION);
registerReceiver(mDownLoadService,filter);
progressBar= (ProgressBar) findViewById(R.id.progressBar);
btn_start_download= (Button) findViewById(R.id.start_download);
btn_start_download.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(getApplicationContext(),MyService.class);
startService(intent);
}
});
btn_intentservice= (Button) findViewById(R.id.start_download_intentservice);
btn_intentservice.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(getApplicationContext(),MyIntentService.class);
startService(intent);
}
});
}
class MyDownLoadService extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
int count=intent.getIntExtra("count", 0);
progressBar.setProgress(count);
}
}
//解綁
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(mDownLoadService);
}
}
Android線程
利用線程更新UI界面,Hanler()
UI界面只允許主線程來更改UI界面繪製,不允許其他線程來更改界面的繪製,給否則會報錯拋出異常。因此就需要想要更改UI界面的線程將要更改界面的消息通過Message傳遞給主線程,由主線程來完成界面的繪製,當有多條消息時,主線程會將這些消息放在一個消息隊列中,等待主線程的處理,通過Handler將消息轉遞給主線程,然後通過Handler的handleMessage() 方法來對消息進行處理。
Message msg=new Message();
msg.obj=count+"秒";
msg.what=TIME_DESC;
mhandler.sendMessage(msg);
Handler
private Handler mhandler=new Handler(){
public void handleMessage(Message msg){
Log.d("秒數",""+count+msg);
switch(msg.what){
case TIME_DESC:
String time= (String) msg.obj;
btn_handler.setText(time);
break;
}
}
};
這裏就以倒計時定時器爲例,界面activity_main.xml
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="倒計時"/>
<Button
android:id="@+id/btn_handler"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="60秒"/>
MainActivity.java
public class MainActivity extends Activity {
private int count=60;
private static final int TIME_DESC=0x23;
private Button btn_handler;
private Handler mhandler=new Handler(){
public void handleMessage(Message msg){
Log.d("秒數",""+count+msg);
switch(msg.what){
case TIME_DESC:
String time= (String) msg.obj;
btn_handler.setText(time);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn_handler= (Button) findViewById(R.id.btn_handler);
btn_handler.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
count=60;
Toast.makeText(getApplication(),"開始倒計時",Toast.LENGTH_SHORT).show();
new Thread(new Runnable() {
@Override
public void run() {
while (count>0){
count--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Message msg=new Message();
msg.obj=count+"秒";
msg.what=TIME_DESC;
mhandler.sendMessage(msg);
Log.d("秒數",""+count+msg);
}
}
}).start();
}
});
}
但這樣寫代碼會有寫繁瑣,爲了簡潔,採用另外一種方法可以達到同樣效果,前者使通過主線程裏的封裝好的Looper()方法來管理消息隊列,這裏我們可以通過一個自定義的Thread類來對消息進行管理,從而簡潔了監聽事件裏的代碼。
MainActivity.java中的按鈕事件監聽事件
public class MainActivity extends Activity {
private int count=60;
private static final int TIME_DESC=0x23;
private Button btnhandler;
private Handler handler=new Handler(){
public void handleMessage(Message msg){
Log.d("秒數",""+count+msg);
switch(msg.what){
case TIME_DESC:
count--;
btnhandler.setText(count+"秒");
if (count>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(TIME_DESC);
}
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnhandler= (Button) findViewById(R.id.btnhandler);
btnhandler.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
count=60;
handler.sendEmptyMessage(TIME_DESC);
}
});
}
}
子線程更新UI界面使通過子線程給主線程傳遞消息來更改UI界面,還可以互換一下,子線程可以接收來自主線程的消息。通過調用自定義的線程裏的來讓主線程發送消息,因此需要先調用啓動線程的方法來啓動線程裏的Handler()來發送消息。
自定義的Thrad,MyThread .java寫成內部類
class MyThread extends Thread{
@Override
public void run() {
Looper.prepare();
mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
Log.d("handler","接收到了主線程的消息");
}
};
Looper.loop();
}
}
MainActivity.java
public class MainActivity extends Activity {
private Button btnhandler;
private int count=60;
private static final int TIME_DESC=0x23;
private Handler handler=new Handler(){
public void handleMessage(Message msg){
Log.d("秒數",""+count+msg);
switch(msg.what){
case TIME_DESC:
count--;
btnhandler.setText(count+"秒");
if (count>0){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
handler.sendEmptyMessage(TIME_DESC);
}
break;
}
}
};
private Button btnHandler;
private Handler mHandler;
class MyThread extends Thread{
@Override
public void run() {
Looper.prepare();
mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
Log.d("handler","接收到了主線程的消息");
}
};
Looper.loop();
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnHandler= (Button) findViewById(R.id.btnHandler);
btnHandler.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mHandler.sendEmptyMessage(0);
}
});
btnhandler= (Button) findViewById(R.id.btnhandler);
btnhandler.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
count=60;
handler.sendEmptyMessage(TIME_DESC);
MyThread thread=new MyThread();
thread.start();
}
});
}
}
AsyncTask
爲了子線程更好地對UI進行操作,Android還提提供了Handler之外一種工具AsyncTast。AsyncTast制定了三個參數,Params在執行AsyncTast時傳入,可用於後臺中進行的任務,Progress後臺執行任務時,需要在界面上顯示當前的進度,則使用這裏的泛型作爲進度單位,Result當任務執行完成後,需要對結果進行返回。
AsyncTask的泛型類型
這三個類型被用於一個異步任務,如下:
1. Params,啓動任務執行的輸入參數
2. Progress,後臺任務執行的百分比
3. Result,後臺計算的結果類型
4個步驟
當一個異步任務被執行,任務經過四各步驟:
1.onPreExecute(),在UI線程上調用任務後立即執行。這步通常被用於設置任務,例如在用戶界面顯示一個進度條。
2.doInBackground(Params…),後臺線程執行onPreExecute()完後立即調用,這步被用於執行較長時間的後臺計算。異步任務的參數也被傳到這步。計算的結果必須在這步返回,將傳回到上一步。在執行過程中可以調用publishProgress(Progress…)來更新任務的進度。
3.onProgressUpdate(Progress…),一次呼叫 publishProgress(Progress…)後調用 UI線程。執行時間是不確定的。這個方法用於當後臺計算還在進行時在用戶界面顯示進度。例如:這個方法可以被用於一個進度條動畫或在文本域顯示記錄。
4.onPostExecute(Result), 當後臺計算結束時,調用 UI線程。後臺計算結果作爲一個參數傳遞到這步。
任務實例必須創建在 UI線程
線程規則
有一些線程規則必須去遵守,這個類纔會正確的工作:任務實例必須創建在 UI線程
execute(Params…)必須在UI線程上調用
不要手動調用onPreExecute(), onPostExecute(Result), doInBackground(Params…), onProgressUpdate(Progress…)
這個任務只執行一次(如果執行第二次將會拋出異常)
一個簡單的自定義的AsyncTast,MyDownLoadService.java
class MyTask extends AsyncTask<String,String ,String>{
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
btn_progressBar.setText(s);
}
@Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
int count1=Integer.parseInt(values[0]);
mprogressBar.setProgress(count1);
}
@Override
protected String doInBackground(String... params) {
while(count1<101){
count1++;
publishProgress(""+count1);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "下載已完成";
}
}
依舊以下載進度條爲例。MainActivity.java
public class MainActivity extends Activity {
private Button btn_progressBar;
private ProgressBar mprogressBar;
private int count1=0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mprogressBar= (ProgressBar) findViewById(R.id.progressBar2);
btn_progressBar= (Button) findViewById(R.id.btn_progressBar);
btn_progressBar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
MyTask task=new MyTask();
task.execute("去執行吧");
}
});
class MyTask extends AsyncTask<String,String ,String>{
@Override
protected void onPostExecute(String s) {
super.onPostExecute(s);
btn_progressBar.setText(s);
}
@Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
int count1=Integer.parseInt(values[0]);
mprogressBar.setProgress(count1);
}
@Override
protected String doInBackground(String... params) {
while(count1<101){
count1++;
publishProgress(""+count1);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return "下載已完成";
}
}
}
activity_main.xml
<ProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/progressBar2" />
<Button
android:id="@+id/btn_progressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="開始"/>