服务--Android多线程编程、服务基本用法

一、服务定义

1、服务(Service)是Android中实现后台运行的解决方案。
2、服务并不是运行在一个独立的进程当中,而是依赖于创建服务时所在的应用程序进程,当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。
3、服务不会自动开启线程,所有的代码都是默认运行在主线程当中,需要在服务的内部手动创建子线程,并在这里执行具体的任务,否则就可能出现主线程被阻塞主的情况。

二、Android多线程编程

《一》线程的基本用法
1、新建线程方法:
《1》、新建一个类继承Thread,然后重写父类的run()方法,并在里面编写耗时逻辑即可。
《2》、新建一个类实现Runnable接口的方式。
《3》、使用匿名类去实现Runnable接口。

new Thread (new Runnable(){
	@Override
	public void run(){
		//处理具体代码
	}
}).start();

2、通过继承Thread方式创建的线程,启动线程,new MyThread().start(),这样run()方法中的代码就会在子线程当中运行。通过实现接口的方式启动线程:

MyThread myThread=new MyThread();
new Thread(myThread).start();

《二》、在子线程中更新UI
1、Android的UI也是线程不安全的。想要更新应用程序里的UI元素,则必须在主线程中进行,否则就会出现异常。
2、Android提供了一套异步消息处理机制,解决了在子线程中进行UI操作的问题。
3、异步消息处理的使用方法。
《1》、定义一个整形常量 UPDATE_TEXT 表示更新TextView这个动作。新增Handler对象,并重写父类的handleMessage()方法,在这里对具体的Message 进行处理。发现Message的what=UPDATE_TEXT就将TextView显示的内容改成Nice to mrrt you.
《2》、没有在子线程直接进行UI操作,而是在点击事件里创建一个Message对象,将它的what字段的值指定为UPDATE_TEXT,调用Handler的sendMessage()方法将这条Message发送出去。
《3》、Handler就会接收到这条Message,并在handleMessage()对它进行处理。
注意:此时handleMessage()方法中的代码就是在主线程当中运行的。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private final static int UPDATE_TEXT = 1;
    private TextView text;
    @SuppressLint("HandlerLeak")
    private Handler handler = new Handler() {
        @SuppressLint("SetTextI18n")
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == UPDATE_TEXT) {
            	  //在这里进行UI处理
                text.setText("Nice to meet you");
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        text = findViewById(R.id.text);
        Button button = findViewById(R.id.change_text);
        button.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        if (v.getId() == R.id.change_text) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Message message = new Message();
                    message.what = UPDATE_TEXT;
                    handler.sendMessage(message);
                }
            }).start();
        }
    }
}

《三》、解析异步消息处理机制
Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue、Looper。
1、Message
Message是线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。
2、Handler
处理者,主要用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列的辗转处理后,最终会传递到Handler的handleMessage()方法中。
3、MessageQueue
是消息队列,主要用于存放所有通过Handler发送的消息,这部分消息会一直存放在消息队列中,等代处理,每个线程中只会有一个MessageQueue对象。
4、Looper
一个Looper是每个MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无县循环当中,然后每当发现MessageQueue中存在一条消息,就会将他取出,并传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对像。
5、异步消息处理流程
《1》、首先在主线程中创建一个Handler对象,并重写handleMessage()方法。
《2》、当子线程中需要进行处理UI操作时,就创建一个Message对象,并通过Handler的sendMessage()方法将这条消息发送出去,之后这条消息就会被加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()方法中。
注意:Handler是在主线程中创建的,所以handleMessage()方法中的代码也会在主线程中运行。
异步消息·处理机制流程示意图
《四》、AsyncTask工具
1、AsyncTask也是基于异步消息处理机制的,只是Android帮我们做了封装而已。
2、AsyncTask基本使用:AsyncTask是一个抽象类,所以想使用它就必须要创建一个类去继承它,在继承时可以为AsyncTask类指定3个泛型参数。
《1》、Params:在执行AsyncTask时需要传入的参数,可用于在后台任务中使用
《2》、Progress:后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
《3》、Result:当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
3、重写AsyncTask中的方法完成对任务的定制。经常需要重写的方法有以下4个。
《1》、onPreExecute()
这个方法在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框。
《2》、doInBackground(Params····)
这个方法中的所有代码都会在子线程中运行,在这里处理所有的耗时任务。任务完成就可以通过return将任务的执行结果返回,如果AsyncTask的第三个参数为void,就可以不返回任务执行结果。
注意:在这个方法不可以进行UI操作的,如需要更新UI元素,可以调用publishProgress(Params····)方法来完成。
《3》、onProgressUpdate(Params······)
当后台任务调用publishProgress(Params····)方法后,onProgressUpdate(Params······)就会很快被调用,该方法中携带的参数就是后台任务中传递过来的,在这个方法中对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
《4》、onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就会很快被调用。返回的数据作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作。
4、一个比较完整的自定义AsyncTask就可以写成如下:

服务的基本用法

一、定义一个服务
1、新建一个项ServiceTest项目,然后右击com.example.servicetest—>New---->Service---->Service,弹出一个窗口,将服务命名为MyService,Exported表示是否允许除了当前程序之外的其他程序访问这个服务,Enable属性表示是否启用这个服务。勾选两个。

public class MyService extends Service {
    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

《1》、MyService是继承自Service类,说明是一个服务。onBind()方法是Service中的唯一的一个抽象方法,所以必须子类去实现。
2、重写Service中的另外一些方法,让其处理事情的逻辑。重写onCreate()、onStartCommand()和onDestroy()这三个方法。

	@Override
	public void onCreate(){
	    super.onCreate();
	}
	@Override
	public int onStartCommand(Intent intent,int flags,int startId){
	    return super.onStartCommand(intent,flags,startId);
	}
	@Override
	public void onDestroy(){
	    super.onDestroy();

《1》、onCreate()在服务创建的时候调用,onStartCommand()方法在每次服务启动的时候调用,onDestroy()方法在服务销毁的时候调用。
3、每一个服务都需要在AndroidManifest.xml文件中进行注册才能生效。不过Android Studio早已自动帮我们将这一步完成了。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.servicetest">
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"></service>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

二、启动和停止服务
启动和停止服务借助Intent来实现。
1、启动和停止服务,首先修改activity_main.xml中的代码。创建两个按钮用于启动和停止服务。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

   <Button
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:id="@+id/start_service"
       android:text="Start Service"/>
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/stop_service"
        android:text="Stop Service"/>
</LinearLayout>

2、修改MainAcrivity中的代码,分别获取Start Service和Stop Service按钮的实例。并注册点击事件,在Start Service按钮的点击事件创建一个Intent对象,并调用startService()启动MyService服务,关闭服务同理。
注意:这里完全由活动决定服务何时停止的,如果没有点击Stop Service按钮,服务就会一直处于运行状态。
服务让自己停下来的办法:只需要在MyService的任意一个位置调用stopSelf()方法就能让这个服务停止下来。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startService = findViewById(R.id.start_service);
        Button stopService = findViewById(R.id.stop_service);
        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.start_service:
                //开启服务
                Intent intent = new Intent(this, MyService.class);
                startService(intent);
                break;
            case R.id.stop_service:
                //停止服务
                Intent intent1 = new Intent(this, MyService.class);
                stopService(intent1);
                break;
            default:
                break;
        }
    }
}

3、证实服务已经成功启动或者停止的最简单的方法:就是在MyService的几个方法中加入打印日志。

	@Override
	public void onCreate(){
	    super.onCreate();
	    Log.d("MyService:", "onCreate: executed");
	}
	@Override
	public int onStartCommand(Intent intent,int flags,int startId){
	    Log.d("MyService:", "onStartCommand: executed");
	    return super.onStartCommand(intent,flags,startId);
	}
	@Override
	public void onDestroy(){
	    super.onDestroy();
	    Log.d("MyService:", "onDestroy: executed");
	}

4、onCreate()方法与onStartCommand()方法区别:onCreate()是在服务第一次创建的时侯加载的,onStartCommand()则是在每次启动服务的时候都会回调。

三、活动和服务进行通信
让活动与服务的关系更加亲密的方法是:onBind()方法。
1、在MyService里提供一个下载功能,然后在活动中可以决定何时开始下载,以及随时查看下载进度。实现这个功能就是需要创建一个专门的Binder对象来对下载功能进行管理,修改MyService代码。

public class MyService extends Service {
    private DownloadBinder mBinder = new DownloadBinder();
    class DownloadBinder extends Binder {
        //定义开始下载的方法
        public void startDoewload() {
            Log.d("MyService", "startDoewload: extends");
        }
        //定义查看下载进度的方法
        public int getProgress() {
            Log.d("MyService", "getProgress: extends");
            return 0;
        }
    }
    @Override
    public IBinder onBind(Intent intent) {
        //返回DownloadBinder实例
        return mBinder;
    }
    public MyService() {
    }
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d("MyService:", "onCreate: executed");
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d("MyService:", "onStartCommand: executed");
        return super.onStartCommand(intent, flags, startId);
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("MyService:", "onDestroy: executed");
    }
}

《1》、创建一个类让他继承自Binder,然后在内部提供开始下载和查看下载进度的方法。
《2》、在MyService中创建DownloadBinder的实例,然后在onBind()方法里返回了这个实例,MyService的工作就全部完成了。
2、在活动中去调用服务里的方法。
《1》、首先在布局文件里新增两个按钮修改activity_main.xml中的代码。这两个按钮用于绑定服务和取消绑定服务的。当一个活动跟服务绑定了之后,就可以调用该服务里的Binder提供的方法了。

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/bind_service"
    android:text="@string/bind_service"/>
<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/unbind_service"
    android:text="@string/unbind_service"/>

《2》、修改MainActivity中的代码。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private MyService.DownloadBinder downloadBinder;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downloadBinder = (MyService.DownloadBinder) service;
            downloadBinder.startDoewload();
            downloadBinder.getProgress();
            Log.d("MainActivity", "onServiceConnected: extends");
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.d("MainActivity", "onServiceDisconnected: extends");
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startService = findViewById(R.id.start_service);
        Button stopService = findViewById(R.id.stop_service);
        Button bindService = findViewById(R.id.bind_service);
        Button unbindSevice = findViewById(R.id.unbind_service);
        startService.setOnClickListener(this);
        stopService.setOnClickListener(this);
        bindService.setOnClickListener(this);
        unbindSevice.setOnClickListener(this);
    }
    @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;
            case R.id.bind_service:
                Intent bindIntent = new Intent(this, MyService.class);
                //绑定服务
                bindService(bindIntent, connection, BIND_AUTO_CREATE);
                break;
            case R.id.unbind_service:
                //解除绑定
                unbindService(connection);
            default:
                break;
        }
    }
}

(1)、首先创建一个匿名类ServiceConnection,在里面重写了onServiceConnection()和onServiceDisconnection()方法,这俩个方法分别会在活动与服务成功绑定以及解除绑定的时候调用。
(2)、在方法中,通过向下转型得到了DownloadBinder的实例,有了这个实例活动和服务之间的关系就会变得非常紧密了。可以根据具体的场景来调用DownloadBinder中的任何public()方法,即实现了指挥服务干什么服务就去干什么的功能。
(3)、活动与服务进行绑定功能是在按钮的点击事件里完成的。创建一个Intent对象,然后调用bindService()方法将MainActivity和MyService进行绑定。bindService()接收3个参数,第一个参数就是创建的Intent对象,第二个参数就是前面创建的ServiceeConnection的实例,第三个参数就是一个标志位,这里传入BIND_AUTO_CREATE,表示活动和服务进行绑定后自动创建服务。使得MyService中的onCraete()方法得到执行,但onStartCommand()方法不会执行。
(4)、解除活动与服务之间的绑定,调用unbindService()方法就可以,也是按钮的点击事件里实现功能。
3、任何一个服务在整个应用程序范围内都是通用的,即MyService不仅可以和MainActivity绑定,还可以和任何一个其他的活动进行绑定,而且再绑定完成后他们都可以获取到相同的DownloadBinder实例。

四、服务的生命周期
onCreate()、onStartCommand()、onBind()、onDestroy()等方法都是在服务的生命周期内可能回调的方法。
1、服务启动会一直保持运行状态,直到stopService()或者stopSelf()方法被调用。
注意:虽然每调用一次startService(),onStartCommand()就会被执行一次,但实际上每个服务都只会存在一个实例,不管你调用多少次startService()方法,只需要调用一次stopService()或者stopSelf()方法,服务就会停止下来。
2、还可以调用Context的bindService()来获得一个服务的持久连接,这时就会回调服务中的onBind()方法,就会获得onBind()方法里返回的IBinder对象的实例,就能自由的和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。
3、当调用startService()方法后,又去调用stopService(),这时服务中的onDestroy()方法就会执行,服务已销毁。当调用bindService()方法后,又去调用unbindService(),这时服务中的onDestroy()方法就会执行,服务已销毁。当调用startService()方法后,又去调用bindService(),同时调用stopService()和unbindService()方法,onDestroy()方法才会执行,服务销毁。
4、一个服务只要被启动或绑定了之后,就会一直处于运行状态,必须让以上两种条件同时都不满足,服务才会被销毁。

服务的更多技巧

一、使用前台服务
1、服务的系统优先级比较低,当系统出现内存不足时,就有可能会回收掉正在后台运行的服务
2、前台服务和普通服务的区别:他一直有一个正在运行的图标在系统的状态显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。可以防止服务被回收。
3、创建前台服务:修改MyService中的代码,通过创建通知的方法,构建Notification对象后,调用startForeground()方法,这个方法接受两个参数,第一个是通知的id,第二个参数是构建出的Notification对象。调用startForeground()方法后就会让MyService变成一个前台服务,并在系统状态栏显示出来。

二、使用IntentService
1、一个比较标准的服务可以写成如下形式:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    new Thread(new Runnable() {
        @Override
        public void run() {
            //处理具体的逻辑
            stopSelf();
        }
    }).start();
    return super.onStartCommand(intent, flags, startId);
}

2、为了可以简单地创建一个异步的、会自动停止的服务,Android专门提供了一个IntentService类。

public class MyIntentService extends IntentService {
    public MyIntentService() {
        //调用父类的有参构造函数
        super("MyIntentService");
    }
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //打印当前线程的id
        Log.d("MyIntentService", "onHandleIntent: Threadv id is"+Thread.currentThread().getId());
    }
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d("MyIntentService", "onDestroy: executed");
    }
}

《1》、提供一个无参的构造函数,并且必须在其内部调用父类的有参构造函数。
《2》、子类要实现onHandleIntent()抽象方法,在这个方法中可以处理一些具体的逻辑,这个方法在子线程中运行。
《3》、根据IntentService特性,这个服务在运行结束后应该是自动停止的,所以重写onDestroy()方法,打印日志,证明服务销毁。
3、修改activity_main.xml中的代码,加入一个用于启动MyIntentService这个服务的按钮。

<Button
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/start_intent_service"
    android:text="Start IntentService"/>

4、修改ManiActivity中的代码,点击事件去启动MyIntentService这个服务,并打印id,跟IntentService做比较。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
	······
    Button startIntentService=findViewById(R.id.start_intent_service);
    startIntentService.setOnClickListener(this);
    ······
}
@Override
public void onClick(View v) {
    switch (v.getId()) {
		······
        case R.id.start_intent_service:
            //开启MyIntentService服务
            Log.d("MyIntentService", "onClick: Thread id is"+Thread.currentThread().getId());
            Intent intent=new Intent(this,MyIntentService.class);
            startService(intent);
            break;
        default:
            break;
    }
}

5、注意:采用手动创建的服务要在AndroidManifest.xml里注册,系统不会自动注册。

<service
    android:name=".MyIntentService"
    android:enabled="true"
    android:exported="true"/>

服务的最佳实践--------完整版的下载实例

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值