6. Android 服务

一、线程

**服务( Service )**是Android中实现程序后台运行的解决方案,它非常适合去执行那些不需要和用户交互而且还要求长期运行的任务。服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另外一个应用程序,服务仍然能够保持正常运行。

服务并不是运行在一个独立的进程当中,而是主线程的某个子线程当中。所以认识服务之前需要先大概了解线程。

1. 线程的基本用法

Java中的线程差不多。写法:

**法一:**继承Thread类,重写run()方法。

class MyThread extends Thread {
    @Override
    public void run() {
    	//处理具体的逻辑
    }
}

// ————————————启动线程——————————————
new MyThread().start();

启动线程时,创建实例并调用start()方法,

**法二:**法一的缺点是耦合性较高,更多的时候使用实现接口Runable的方式定义一个线程。

class MyThread implements Runnable {
    @0verride
    public void run() {
    	//处理具体的逻辑
    }
}

// —————————————启动线程———————————————
MyThread myThread = new MyThread();
new Thread(myThread).start();

**法三:**在法二的基础上使用匿名类。

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

**注意:**不能在子线程中更新UIAndroidUI也是线程不安全的。更新UI元素必须在主线程中。

有时候我们需要在线程中做一些比较耗时的任务,然后根据结果更新UI,这时我们可以利用异步消息处理的方法。

Android异步消息处理主要由4个部分组成:MessageHandlerMessageQueueLooper

1. 1 Message

Message在线程之间传递消息,内部可以携带少量信息。

1.2 Handler

主要用于发送和处理消息。发送小时一般使用HandlersendMessage()方法,而最终消息会被传递到HandlerhandleMessage()方法中。

1.3 MessageQueue

消息队列,主要用于存放所有通过Handler发送的消息,每个线程只会有一个MessageQueue对象。

1.4 Looper

Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环当中,每当发现MessageQueue中存在一条消息就会取出并传递到Handler的handleMessage()中。每个线程也只会有一个Looper对象。

在这里插入图片描述

其实 Android 已经帮我们封装好了一个异步操作工具:AsyncTask。

2. AsyncTask

AsyncTask 其实也是基于异步消息处理机制实现的。

2.1 AsyncTask 的基本用法:

AsyncTask 是一个抽象类,使用时需要创建一个子类继承 AsyncTask,继承时可以为 AsyncTask 类指定 3 个发型参数:

  • Params:在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。

  • Progress:后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。

  • Result:当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。

因此,一个最简单的自定义AsyncTask就可以写成如下方式:

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
}
2.2 经常需要去重写的方法:
2.2.1 onPreExcute()

这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示个进度条对话框等。

2.2.2 doInBackg round (Params…)

这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。

2.2.3 onProgressUpdate (Progress…)

当在后台任务中调用了publishProgress (Progress...)方法后,onProgressUpdate(Progress...)方法就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。

2.2.4 onPost Execute ( Result)

当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。

完整的自定义 AsyncTask 的方式:

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
    @Override
    protected void onPreExecute() {
    	progressDialog.show(); //显示进度对话框
    }
    @0verride
    protected Boolean doInBackground(Void... params) {
        try {
            while (true) {
            	int downloadPercent = doDownload(); //下载任务.
            	publishProgress(downloadPercent);
            	if (downloadPercent >= 100) {
            		break;
                }
        	}
        } catch (Exception e) {
        	return false;
        }
        return true;
    }
    @0verride
    protected void onProgressUpdate (Integer... values) {
    	//在这里更新下载进度
    	progressDialog.setMessage ("Downloaded " + values[0] + "%");
        @0verride
        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();

doInBackground()方法中执行具体的耗时任务,在onProgressUpdate()方法中进行UI操作,在onPostExecute()方法中执行一些任务的收尾工作。而根据结果更新UI可以通过publishProgress()方法。

二、服务

1. 状态

服务基本上包含两种状态 :

  • 启动
    当应用组件通过调用 startService() 启动服务时,服务即处于“启动”状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。 已启动的服务通常是执行单一操作,而且不会将结果返回给调用方,可以通过手动方式销毁服务。

  • 绑定
    当应用组件通过调用 bindService() 绑定到服务时,服务即处于“绑定”状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。多个组件可以同时绑定服务,服务只会在组件与其绑定时运行,一旦该服务与所有组件之间的绑定全部取消,系统便会销毁它。

2. 生命周期

在这里插入图片描述

3. 启动方式

Service主要有两种启动方式:

  1. startService()
  2. bindService()

他们的生命周期稍有不同(参考上图)。

3.1 startService方式

startService()只要一个Intent参数,指定要开启的Service即可:

Intent intent = new Intent(MainActivity.this, MyService.class);
  1. 当调用Service的startService()后,
  • Service首次启动,则先调用onCreate(),在调用onStartCommand()
  • Service已经启动,则直接调用onStartCommand()
  1. 当调用stopSelf()或者stopService()后,会执行onDestroy(),代表Service生命周期结束。

  2. startService方式启动Service不会调用到onBind()。 startService可以多次调用,每次调用都会执行onStartCommand()。 不管调用多少次startService,只需要调用一次stopService就结束。 如果startService后没有调用stopSelf或者stopService,则Service一直存活并运行在后台。

  3. onStartCommand的返回值一共有3种

  • START_STICKY = 1:service所在进程被kill之后,系统会保留service状态为开始状态。系统尝试重启service,当服务被再次启动,传递过来的intent可能为null,需要注意。

  • START_NOT_STICKY = 2:service所在进程被kill之后,系统不再重启服务

  • START_REDELIVER_INTENT = 3:系统自动重启service,并传递之前的intent

    默认返回START_STICKY;

3.2 bindService方式

bindService启动服务特点:

  1. bindService启动的服务和调用者之间是典型的client-server模式。调用者是client,service则是server端。service只有一个,但绑定到service上面的client可以有一个或很多个。这里所提到的client指的是组件,比如某个Activity。
  2. client可以通过IBinder接口获取Service实例,从而实现在client端直接调用Service中的方法以实现灵活交互,这在通过startService方法启动中是无法实现的。
  3. bindService启动服务的生命周期与其绑定的client息息相关。当client销毁时,client会自动与Service解除绑定。当然,client也可以明确调用Context的unbindService()方法与Service解除绑定。当没有任何client与Service绑定时,Service会自行销毁
//1. 定义用于通信的对象,在Service的onBind()中返回的对象。
public class MyBind extends Binder {
        public int mProcessId;
 }

//2. 定义用于接收状体的ServiceConnection
mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                //和服务绑定成功后,服务会回调该方法
                //服务异常中断后重启,也会重新调用改方法
                MyService.MyBind myBinder = (MyService.MyBind) service;
            }

            @Override
            public void onNullBinding(ComponentName name) {
                //Service的onBind()返回null时将会调用这个方法,并不会调用onServiceConnected()
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                // 当服务异常终止时会调用。
                // 注意,unbindService时不会调用
            }
        };
        
//3. 在需要的地方绑定到Service
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);

4. 创建启动服务

创建启动服务时,可以通过对现存的类进行扩展:

  • Service
    这是所有服务的父类。扩展此类时,如果要执行耗时操作,必须创建一个用于执行操作的新线程,因为默认情况下服务将运行于UI线程。

  • IntentService
    这是 Service 的子类,它使用工作线程逐一处理所有启动请求。如果应用不需要同时处理多个请求,这是最好的选择。IntentService只需实现构造函数onHandleIntent() 方法即可,onHandleIntent()方法会接收每个启动请求的 Intent。

5. ServiceActivity之间进行数据交换

主要通过两种方式:

5.1 通过Binder对象

首先我们新建一个工程Communication,然后新建一个Service类

package com.example.communication;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class MsgService extends Service {
	/**
	 * 进度条的最大值
	 */
	public static final int MAX_PROGRESS = 100;
	/**
	 * 进度条的进度值
	 */
	private int progress = 0;

	/**
	 * 增加get()方法,供Activity调用
	 * @return 下载进度
	 */
	public int getProgress() {
		return progress;
	}
	 
	/**
	 * 模拟下载任务,每秒钟更新一次
	 */
	public void startDownLoad(){
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(progress < MAX_PROGRESS){
					progress += 5;
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
				}
			}
		}).start();
	}
	
	/**
	 * 返回一个Binder对象
	 */
	@Override
	public IBinder onBind(Intent intent) {
		return new MsgBinder();
	}
	
	public class MsgBinder extends Binder{
		/**
		 * 获取当前Service的实例
		 * @return
		 */
		public MsgService getService(){
			return MsgService.this;
		}
	}

}

调用startDownLoad()方法来模拟下载任务,然后每秒更新一次进度,但这是在后台进行中,我们是看不到的,所以有时候我们需要他能在前台显示下载的进度问题,所以我们接下来就用到Activity了.

Intent intent = new Intent("com.example.communication.MSG_ACTION");  
bindService(intent, conn, Context.BIND_AUTO_CREATE);

通过上面的代码我们就在Activity绑定了一个Service,上面需要一个ServiceConnection对象,它是一个接口,我们这里使用了匿名内部类

ServiceConnection conn = new ServiceConnection() {
	
	@Override
	public void onServiceDisconnected(ComponentName name) {
		
	}
	
	@Override
	public void onServiceConnected(ComponentName name, IBinder service) {
		//返回一个MsgService对象
		msgService = ((MsgService.MsgBinder)service).getService();
		
	}
};

在onServiceConnected(ComponentName name, IBinder service) 回调方法中,返回了一个MsgService中的Binder对象,我们可以通过getService()方法来得到一个MsgService对象,然后可以调用MsgService中的一些方法,Activity的代码如下

package com.example.communication;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;

public class MainActivity extends Activity {
	private MsgService msgService;
	private int progress = 0;
	private ProgressBar mProgressBar;
	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);


		
		//绑定Service
		Intent intent = new Intent("com.example.communication.MSG_ACTION");
		bindService(intent, conn, Context.BIND_AUTO_CREATE);


		
		mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
		Button mButton = (Button) findViewById(R.id.button1);
		mButton.setOnClickListener(new OnClickListener() {
			

			@Override
			public void onClick(View v) {
				//开始下载
				msgService.startDownLoad();
				//监听进度
				listenProgress();
			}
		});
		
	}

 


	/**
	 * 监听进度,每秒钟获取调用MsgService的getProgress()方法来获取进度,更新UI
	 */
	public void listenProgress(){
        // 新建一个线程并启动
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(progress < MsgService.MAX_PROGRESS){
					progress = msgService.getProgress();
					mProgressBar.setProgress(progress);
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				
			}
		}).start();
	}
	
	ServiceConnection conn = new ServiceConnection() {
		@Override
		public void onServiceDisconnected(ComponentName name) {
			
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			//返回一个MsgService对象
			msgService = ((MsgService.MsgBinder)service).getService();
			
		}
	};
	 
	@Override
	protected void onDestroy() {
		unbindService(conn);
		super.onDestroy();
	}

 


}

新建一个回调接口

public interface OnProgressListener {
	void onProgress(int progress);
}

MsgService的代码有一些小小的改变

package com.example.communication;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class MsgService extends Service {
	/**
	 * 进度条的最大值
	 */
	public static final int MAX_PROGRESS = 100;
	/**
	 * 进度条的进度值
	 */
	private int progress = 0;
	
	/**
	 * 更新进度的回调接口
	 */
	private OnProgressListener onProgressListener;


	/**
	 * 注册回调接口的方法,供外部调用
	 * @param onProgressListener
	 */
	public void setOnProgressListener(OnProgressListener onProgressListener) {
		this.onProgressListener = onProgressListener;
	}
	 
	/**
	 * 增加get()方法,供Activity调用
	 * @return 下载进度
	 */
	public int getProgress() {
		return progress;
	}
	 
	/**
	 * 模拟下载任务,每秒钟更新一次
	 */
	public void startDownLoad(){
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(progress < MAX_PROGRESS){
					progress += 5;
					
					//进度发生变化通知调用方
					if(onProgressListener != null){
						onProgressListener.onProgress(progress);
					}
					
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
				}
			}
		}).start();
	}

	/**
	 * 返回一个Binder对象
	 */
	@Override
	public IBinder onBind(Intent intent) {
		return new MsgBinder();
	}
	
	public class MsgBinder extends Binder{
		/**
		 * 获取当前Service的实例
		 * @return
		 */
		public MsgService getService(){
			return MsgService.this;
		}
	}

}

Activity中的代码如下

package com.example.communication;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;

public class MainActivity extends Activity {
	private MsgService msgService;
	private ProgressBar mProgressBar;
	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);


		
		//绑定Service
		Intent intent = new Intent("com.example.communication.MSG_ACTION");
		bindService(intent, conn, Context.BIND_AUTO_CREATE);


		
		mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
		Button mButton = (Button) findViewById(R.id.button1);
		mButton.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				//开始下载
				msgService.startDownLoad();
			}
		});
		
	}

	ServiceConnection conn = new ServiceConnection() {
		@Override
		public void onServiceDisconnected(ComponentName name) {
			
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			//返回一个MsgService对象
			msgService = ((MsgService.MsgBinder)service).getService();
			
			//注册回调接口来接收下载进度的变化
			msgService.setOnProgressListener(new OnProgressListener() {
				
				@Override
				public void onProgress(int progress) {
					mProgressBar.setProgress(progress);
					
				}
			});
			
		}
	};
	 
	@Override
	protected void onDestroy() {
		unbindService(conn);
		super.onDestroy();
	}
}

用回调接口是不是更加的方便呢,当进度发生变化的时候Service主动通知Activity,Activity就可以更新UI操作了

5.2 过broadcast(广播)的形式

当我们的进度发生变化的时候我们发送一条广播,然后在Activity的注册广播接收器,接收到广播之后更新ProgressBar,代码如下

package com.example.communication;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ProgressBar;

public class MainActivity extends Activity {
	private ProgressBar mProgressBar;
	private Intent mIntent;
	private MsgReceiver msgReceiver;
	

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		//动态注册广播接收器
		msgReceiver = new MsgReceiver();
		IntentFilter intentFilter = new IntentFilter();
		intentFilter.addAction("com.example.communication.RECEIVER");
		registerReceiver(msgReceiver, intentFilter);

		
		mProgressBar = (ProgressBar) findViewById(R.id.progressBar1);
		Button mButton = (Button) findViewById(R.id.button1);
		mButton.setOnClickListener(new OnClickListener() {
			
			@Override
			public void onClick(View v) {
				//启动服务
				mIntent = new Intent("com.example.communication.MSG_ACTION");
				startService(mIntent);
			}
		});
		
	}

	
	@Override
	protected void onDestroy() {
		//停止服务
		stopService(mIntent);
		//注销广播
		unregisterReceiver(msgReceiver);
		super.onDestroy();
	}

 

	/**
	 * 广播接收器
	 * @author len
	 *
	 */
	public class MsgReceiver extends BroadcastReceiver{
	 
		@Override
		public void onReceive(Context context, Intent intent) {
			//拿到进度,更新UI
			int progress = intent.getIntExtra("progress", 0);
			mProgressBar.setProgress(progress);
		}	
	}
}

Activity 的代码如下:

package com.example.communication;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class MsgService extends Service {
	/**
	 * 进度条的最大值
	 */
	public static final int MAX_PROGRESS = 100;
	/**
	 * 进度条的进度值
	 */
	private int progress = 0;
	
	private Intent intent = new Intent("com.example.communication.RECEIVER");


	/**
	 * 模拟下载任务,每秒钟更新一次
	 */
	public void startDownLoad(){
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				while(progress < MAX_PROGRESS){
					progress += 5;
					
					//发送Action为com.example.communication.RECEIVER的广播
					intent.putExtra("progress", progress);
					sendBroadcast(intent);
					
					try {
						Thread.sleep(1000);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					
				}
			}
		}).start();
	}	

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

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}


}
5.3 总结
  • Activity调用bindService (Intent service, ServiceConnection conn, int flags)方法,得到Service对象的一个引用,这样Activity可以直接调用到Service中的方法,如果要主动通知Activity,我们可以利用回调方法。
  • Service向Activity发送消息,可以使用广播,当然Activity要注册相应的接收器。比如Service要向多个Activity发送同样的消息的话,用这种方法就更好。
6. 远程服务

远程服务(Remote Service)也被称之为独立进程,它不受其它进程影响,可以为其它应用程序提供调用的接口——实际上就是进程间通信IPC(Inter-Process Communication),Android提供了AIDL(Android Interface Definition Language,接口描述语言)工具来帮助进程间接口的建立。

6.1 场景

一般适用于为其它应用程序提供公共服务的Service,这种Service即为系统常驻的Service(如:天气服务等)。

6.2 实现步骤
  • 概述

    Android Studio 中创建了空的工程,创建模块 rsserver。本模块用来制作一个服务可以在本模块的 activity 中调用,可以提供给其他 app 或者模块使用,再创建一个客户端模块 rsclient 。那么最终形成的结构如下图:
    img

    后面的步骤为先制作服务端模块中的服务并且测试通过后再制作客户端模块。

  • 制作 AIDL 接口文件,在服务端模块的根节点上通过右键菜单创建一个AIDL 文件
    img
    将其命名为 IProcessInfo,其中只要定义一个方法不用实现,全部代码如下(创建完毕后会有一个默认的方法,将其删除掉,然后追加一个我们自己的方法)

    // IProcessInfo.aidl
    package com.ccsoft.rsserver;
    
    // Declare any non-default types here with import statements
    
    interface IProcessInfo {
        int getProcessId();
    }
    

    保存后 AS 会自动创建同名的接口文件,之后创建服务的时候要用到
    img

  • 实现 AIDL 文件中定义的方法
    在项目包 com.ccsoft.rsserver 下创建包 service 再在其下创建服务 IProcessInfoImpl 继承自 IProcessInfo.Stub,其全部代码如下

    package com.ccsoft.rsserver.service;
    
    import android.os.RemoteException;
    
    import com.ccsoft.rsserver.IProcessInfo;
    
    public class IProcessInfoImpl extends IProcessInfo.Stub {
        @Override
        public int getProcessId() throws RemoteException {
            return android.os.Process.myPid();
        }
    }
    

    调用本方法会打印进程ID,最终形成的结构如下图
    img

  • 创建服务,用来返回实现接口的类的实例,其全部代码如下:

    package com.ccsoft.rsserver.service;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.util.Log;
    
    import androidx.annotation.Nullable;
    
    public class MyRemoteService extends Service {
        private static final String TAG = "chanchaw";
        IProcessInfoImpl mProcessInfo = new IProcessInfoImpl();
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            Log.e(TAG, "MyRemoteService thread id = " + Thread.currentThread().getId());
            return mProcessInfo;
        }
    }
    

    最终实现的结构如下
    img

  • 接下来为服务端创建一个 Activity 用来测试服务是否可用,先创建布局文件 activity_main,其全部代码如下:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="绑定本地服务"
        android:onClick="bindLocalService" />
    </LinearLayout>
    

    最终形成的结构如下图
    img

  • 创建一个 Activity 显示该布局并且测试服务,命名为 MainActivity ,其全部代码如下:

    package com.ccsoft.rsserver;
    
    import android.app.Activity;
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.util.Log;
    import android.view.View;
    
    import androidx.annotation.Nullable;
    
    import com.ccsoft.rsserver.service.MyRemoteService;
    
    public class MainActivity extends Activity {
        // 打印日志时用来过滤,快速找到调试信息
        private static final String TAG = "chanchaw";
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            // 关联到 res/layout/activity_main.xml,会显示其中定义的控件
            setContentView(R.layout.activity_main);
        }
    
        // 创建 ServiceConnection 类型的对象实例,在后面绑定服务时会用到
        ServiceConnection myServiceConnection = new ServiceConnection() {
    
            /**
             * 服务绑定成功后会调用本方法
             * @param name
    	  */
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
    
            Log.i(TAG, "MyRemoteService onServiceConnected");
            // 通过aidl取出数据
            IProcessInfo processInfo = IProcessInfo.Stub.asInterface(service);
            try {
                Log.i(TAG, "MyRemoteService process id = " + processInfo.getProcessId());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.i(TAG, "MyRemoteService onServiceDisconnected");
        }
    };
    
    public void bindLocalService(View v){
        Intent intent = new Intent(this, MyRemoteService.class);
        bindService(intent, myServiceConnection, Context.BIND_AUTO_CREATE);
    
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(myServiceConnection);
    }
    }
    

最终形成的结构如下

  • 在模块清单文件 AndroidManifest.xml 中注册Activity 和服务,其全部代码如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.ccsoft.rsserver">

  <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" >

      <activity android:name=".MainActivity" android:label="MainActivity">
          <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
          </intent-filter>
      </activity>
    
      <service android:name=".service.MyRemoteService" android:process=":remote">
          <intent-filter>
              <action android:name="com.jxx.server.service.bind" />
              <category android:name="android.intent.category.DEFAULT" />
          </intent-filter>
      </service>

  </application>
</manifest>

  • 在服务端测试上面制作的服务,成功的话则完成了全部工作的70%,效果如下图

  • 创建客户端模块,然后将服务端模块中的 AIDL 连带其下的文件一起拷贝到客户端模块中,如下图

  • 拷贝过去后切记要使用 ctrl + F9 构建下客户端模块,这样才会生成接口文件,如下图

  • 客户端模块中创建 activity 的布局文件,其全部代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical" android:layout_width="match_parent"
  android:layout_height="match_parent">

  <TextView
      android:id="@+id/rsclient_textview"
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="rsclient_textview" />

  <Button
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:text="绑定远程服务"
      android:onClick="bindRemoteService" />
</LinearLayout>

形成的结构如下

  • 创建 Activity,全部代码如下
package com.ccsoft.rsclient;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import androidx.annotation.Nullable;

import com.ccsoft.rsserver.IProcessInfo;

public class MainActivity extends Activity {

  private static final String TAG = "chanchaw";

  TextView text = null;
  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      text = findViewById(R.id.rsclient_textview);
  }

  // 绑定远程服务
  public void bindRemoteService(View v){
      Intent intent = new Intent();
      intent.setAction("com.jxx.server.service.bind");//Service的action
      intent.setPackage("com.ccsoft.rsserver");//App A的包名
      bindService(intent, mServerServiceConnection, BIND_AUTO_CREATE);
  }


  ServiceConnection mServerServiceConnection = new ServiceConnection() {
      @Override
      public void onServiceConnected(ComponentName name, IBinder service) {

          Log.i(TAG, "MyRemoteService onServiceConnected");
          // 通过aidl取出数据
          IProcessInfo processInfo = IProcessInfo.Stub.asInterface(service);
          try {
              Log.i(TAG, "MyRemoteService process id = " + processInfo.getProcessId());
              text.setText("绑定成功!");
          } catch (RemoteException e) {
              e.printStackTrace();
          }
      }
    
      @Override
      public void onServiceDisconnected(ComponentName name) {
          Log.i(TAG, "MyRemoteService onServiceDisconnected");
      }
  };

}

最终形成的结构如下

  • 清单文件中注册 Activity,全部代码如下
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.ccsoft.rsclient">

  <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" >

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

</manifest>

这里不用注册远程的服务,因为是远程调用的

  • 在客户端模块中测试调用远程服务
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值