Android Studio —— Service

216 阅读4分钟

第一行代码读书笔记

服务

  • 安卓实现后台运行的解决方案
  • 不需要和用户交互并要求长期运行的任务
  • 依赖于创建服务时所在的应用程序进程
  • 在服务内部主动创建子线程,防止主线程被阻塞

多线程编程

基本用法

  • 定义线程
    class MyThread extends Thread {
        @Override
        public void run() {
            // 处理具体逻辑
        }
    }
    
  • 启动线程
    new MyThread().start();
    
  • 使用继承的方式耦合性高,一般使用Runnable接口实现来定义线程
    class MyThread implemments Runnable {
        @Override
        public void run() {
            // 处理具体逻辑
        }
    }
    
  • 使用Runnable接口时启动线程
    MyThread myThread = new MyThread();
    new Thread(myThread).start();
    
  • 匿名类实现更为常见
    new Thread(new Runnable() {
        @Override
        public void run() {
            // 处理具体逻辑
        }
    }
    

在子线程中更新UI

  • 异步消息处理
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    public static  final  int UPDATE_TEXT = 1;

    private TextView text;

    private Handler handler = new Handler() {
        public void handleMessage(Message msg) {
           switch (msg.what) {
               case UPDATE_TEXT:
                   // 在这里可以进行UI操作
                   text.setText("Nice to meet you");
                   break;
               default:
                   break;
           }
        }
    }

    ...

    @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);
                    }
                })
        }
    }
}

解析异步消息处理机制

  1. Message
    • 进程之间传递的消息,可以在内部携带少量的信息,用于不同线程之间交换数据
  2. Handler
    • 用于发送和处理消息
    • sendMessage() handleMessage()
  3. MessageQueue
    • 用于存放所有通过Handler发送的消息
    • 一直存在消息队列中等待被处理
    • 每个线程中只有一个
  4. Looper
    • MessageQueue的管家,无限循环,每当发现MessageQueue中存在消息就会取出并传递到handleMessage()
    • 每个线程中只有一个
  • runOnUiThread():异步消息处理机制的接口封装

使用AsyncTask

  • 抽象类,继承的时候可以指定3个泛型参数
    • Params:执行AsyncTask时需要传入的参数,可用于后台任务中使用
    • Progress:进度单位(后台任务执行时,如需要在界面上显示当前的进度)
    • Result:返回值类型(当任务执行完毕后,如需要对结果进行返回)
  • 经常需要重写的方法
    1. onPreExecute()
      • 后台任务开始前调用
      • 用于进行界面上的初始化操作
    2. doInBackground(Params...)
      • 处理所有的耗时任务
      • 完成后可以通过return将任务的执行结果返回
      • 如果AsynTask的第三个泛型参数指定的是Void,就可以不返回任务结果
      • 不能进行UI操作,如果需要,调用publishProgress(Progress...)来完成
    3. onProgressUpdate(Progress...)
      • 当在后台任务中调用了publishProgress(Progress...)后调用
      • 携带的参数是在后台任务中传递过来的
      • 可以进行UI操作
    4. onPostExecute(Result)
      • 当后台任务执行完毕并通过return语句进行返回时调用
      • 返回的数据作为参数
      • 可以进行UI操作
  • E.g.
    class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
        
        @Override
        protected void onPreExecute() {
            ProgressDialog.show();
        }
        
        @Override
        protected Boolean doInBackground(Void... params) {
            try {
                while (true) {
                    int downloadPercent = doDownload();
                    publishProgress(downloadPercent);
                    if (downloadPercent >= 100) {
                        break;
                    }
                }
            } catch (Exception e) {
                  return false;
            }
            return true;
        }
        
        @Override
        protected void onProgressUpdate(Integer... values) {
            progressDialog.setMessage("Download " + values[0] + "%");
        }
        
        @Override
        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();
            }
        }
    }
    
  • 启动任务
    new DownloadTask().execute()

服务的基本用法

定义一个服务

  • New->Service->Service
    • Exported:是否运行除了当前程序的其他程序访问这个服务
    • Enabled:是否启用这个服务
public class Myservice extends Service() {
    public MyService() {
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        throw new UnsupportedOperationException("Not yet implemented");
    }
    
    @Override
    public void onCreate() {
        super.onCreate();
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommond(intent, flags, startId);
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}
  • onStartCommand():服务一启动立刻执行
<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true">
</service>

启动和停止服务

@Override
public void onClick(View v) {
    switch (v.getId()) {
        case R.id.start_service:
            Intent startIntent new Intent(this, MyService.class);
            startService(startIntent);
            break;
        case R.id.stop_service:
            Intent stopIntent = new Intent(this, MyService.class);
            stopService(stopIntent);
            break;
        default:
            break;
    }
}

活动和服务进行通讯

  • onBind()

  • MyService中:

    public class MyService extends Service {
    
        private DownloadBinder mBinder = new DownloadBinder();
    
        class DownloadBinder extends Binder {
            public void startDownload() {
                Log.d("MyService", "startDownload executed");
            }
    
            public int getProgress() {
                Log.d("MyService", "getProgress executed");
                return 0;
            }
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return mBinder;
        }
    }
    
    
  • MainActivity中

    private MyService.DownloadBinder downloadBinder;
    
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceDisconnected(ComponentName name) {
        
        }
        
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downloadBinder = (MyService.DownloadBinder) service;
            downloadBinder.startDownload();
            downloadBinder.getProgress();
        }
    }
    
    @Override
    public void onClick(View v) {
    switch (v.getId()) {
        ...
        case R.id.bind_service:
            Intent bindIntent = new Intent(this, MyService.class);
            bindService(bindIntent, connection, BIND_AUTOCEATE);
            break;
        case R.id.unbind_service:
            unbindService(connection);
            break;
        default:
            break;
        }
    }
    

服务的生命周期

  • onCreate()(如果活动未创建)-> onStartCommand() -> 运行直到StopService()或stopSelf()被调用
  • 调用Contextd bindService获取一个服务的持久链接
  • 只要被启动或者被绑定,就会一直处于运行状态,同时调用stopService()和unbindService(),onDestroy()才能被执行

其他技巧

前台服务

  • 避免你由于系统内存不足被回收
  • 一直有一个正在运行的图标在系统的状态栏显示
  • MyService的onCreate()中:
    Intent intent = new Intent(this, MainActivity.class);
    PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
    Notification notification = new NotificationCompat.Builder(this)
            .setContentTitle("This is content title")
            .setContentText("This is content text")
            .setWhen(System.currentTimeMillis())
            .setSmallIcon(R.mipmap.ic_launcher)
            .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
            .setContentIntent(pi)
            .build();
    startForeground(1, notification);
    

IntentService

public  MyIntentService extentds IntentService {
    public MyIntentService() {
        super("MyIntentService");
    }
    
    @Override
    protected void onHandleIntent(Intent intent) {
        Log.d("MyIntentService", "Thread id is " + Thread.currentThread().getId());
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("MyIntentService", "onDestroy executed");
    }
}