延续上一篇MonkeyLei:Android-Service学习鸭-入门实践-本地服务(bindService方式) ,我接着实践下远程服务的使用(有个疑问先搁这,远程服务到底有哪些使用场景?)。

不多说,走起

1. 新建一个AIDL文件

IMyAidlInterface.aidl - 顺便修改下我们想要定义的提供给调用这的方法,如果不想提供,也无所谓

  // IMyAidlInterface.aidl
package com.skl.myapplication;// Declare any non-default types here with import statementsinterface IMyAidlInterface {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/String getTime();
}

2. 然后Make Project一下:

生成对应名称和aidl文件名称一致的java接口文件 - AS版本不同,生成目录可能有区别哈...

另外生成过程如果出现如下之类的错误,我的AS 3.4.1版本,buildToolsVersion是29.0.0,

   ERROR: Process 'command '......finished with non-zero exit value -1073741819

修改为 buildToolsVersion "28.0.3",再次make project就可以生成了。如果有其他错误再查下应该就可以。

参考 https://blog.csdn.net/happyXingJiang/article/details/92595967

3. 然后此时就可以定义远程Service了

RemoteService.java - 跟本地Service区别代码形式上的区别就是,返回的IBinder是一个AIDL的接口形式(是针对Binder的包装,其核心是Stub.Proxy代理 - 这个后面有必要了解,目前可以自己跳过去看个眼熟....)

 package com.skl.myapplication;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;public class RemoteService extends Service {@Overridepublic void onCreate() {super.onCreate();Log.e("test", "onCreate");}@Overridepublic IBinder onBind(Intent intent) {// TODO: Return the communication channel to the service.Log.e("test", "onBind");return stub;}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {//return super.onStartCommand(intent, flags, startId);Log.e("test", "onStartCommand");// TODO handleCommand(intent); // 比如处理播放,暂停,上一首,下一首等命令; 多次startService(intentCommond),多次该回调return START_STICKY;// 如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。// 随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。// 如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null}@Overridepublic boolean onUnbind(Intent intent) {Log.e("test", "onUnbind");return super.onUnbind(intent);}@Overridepublic void onDestroy() {super.onDestroy();Log.e("test", "onDestroy");}private IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {@Overridepublic String getTime() throws RemoteException {return "时间就是等待,就是金钱!";}};
}

4. 然后我们就可以配置一下这个远程服务了(此时不用启动也可以,只要应用存在,服务配置导出即可,其他应用就可以进行远程绑定,并通过AIDL调用响应的服务接口)

<application
.......
<serviceandroid:process=":remote"android:name=".RemoteService"android:enabled="true"android:exported="true"><!--必须添加一个筛选action - 隐式调用--><intent-filter><action android:name="com.hl.loginservice"/></intent-filter></service>
.......
</application>

5. 新建一个远程调用客户端工程,把romoteService工程的aidl文件夹复制过来(或者自己照着写也行),注意复制过来保持一模一样,包括包名都不要动。然后make project...

编译工具版本也注意修改为: buildToolsVersion "28.0.3"

6. 完事了,直接就可以绑定调用测试了

MainActivity.java

package com.skl.testremoteservice;import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;import com.skl.myapplication.IMyAidlInterface;public class MainActivity extends AppCompatActivity {private IMyAidlInterface iMyAidlInterface;public static final String NAME_REMOTE_SERVICE = "com.skl.myapplication.RemoteService" ;public static final String PACKAGE_REMOTE_SERVICE = "com.skl.myapplication" ;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Intent intent = new Intent();// 隐式启动,可以不用设置Action// intent.setAction("com.hl.loginservice");// android 5.0以后直设置action不能启动相应的服务,需要设置packageName或者Componentintent.setPackage("com.skl.myapplication");// 显示调用需要设置ComponentName//        ComponentName componentName = new ComponentName(PACKAGE_REMOTE_SERVICE ,NAME_REMOTE_SERVICE);//        intent.setComponent (componentName );bindService(intent, serviceConnection, BIND_AUTO_CREATE);}/*** 调用者与Service交互的枢纽* https://developer.android.google.cn/reference/android/content/ServiceConnection*/private ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 获取Binder的接口(代理实例对象)// return new com.skl.testremoteservice.IMyAidlInterface.Stub.Proxy(obj);iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);try {// 获取服务时间Log.e("test", iMyAidlInterface.getTime());} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};@Overrideprotected void onDestroy() {super.onDestroy();if (null != serviceConnection) {unbindService(serviceConnection);}}
}

就这样一个流程倒没什么特别的。小萌新又疑问。这个远程服务有什么使用场景? 另外action筛选其实可以不用,我们是否要使用隐式启动? 小萌新目前了解不多,就这样先提这样问题。。菜是菜了....

q1. 使用场景: 地图、im、保活等? 小萌新想到了天气App(天气应用进程虽然死了,但是好像还能收到天气信息通知 - 我们是不是也可以模拟用服务不停获取时间/天气等信息,然后通知栏通知?); 至于其他的用途就看实际情况,怎么用...

q1不停获取服务器时间,然后通知栏通知代码实践:

RemoteService.java

   package com.skl.myapplication;import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v4.app.NotificationCompat;
import android.util.Log;import org.json.JSONException;
import org.json.JSONObject;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.Date;public class RemoteService extends Service {// 淘宝获取服务器时间apiprivate static final String api = "http://api.m.taobao.com/rest/api3.do?api=mtop.common.getTimestamp";private boolean status = true;private String channelId = "time";private String channelName = "时间通知";private int importance = NotificationManager.IMPORTANCE_HIGH;@Overridepublic void onCreate() {super.onCreate();Log.e("test", "onCreate");createNotificationChannel(channelId, channelName, importance);// 不停请求服务,然后通知栏通知new Thread(new Runnable() {@Overridepublic void run() {while (status) {try {Thread.sleep(1000);URL url = new URL(api);URLConnection urlConnection = url.openConnection();urlConnection.connect();InputStreamReader isr = new InputStreamReader(urlConnection.getInputStream());BufferedReader br = new BufferedReader(isr);String str = "";StringBuilder sb = new StringBuilder();while ((str = br.readLine()) != null) {sb.append(str);}br.close();isr.close();String timeJson = sb.toString() + "";// {"api":"mtop.common.getTimestamp","v":"*",// "ret":["SUCCESS::接口调用成功"],// "data":{"t":"1569228479436"}}// Log.e("test", "timeJson=" + timeJson);JSONObject jsonObject = new JSONObject(timeJson);jsonObject = jsonObject.getJSONObject("data");String time = jsonObject.getString("t");// Log.e("test", "time=" + time);// status做容错性判断SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String timeStr = simpleDateFormat.format(new Date(Long.parseLong(time)));if (status) {// id不同则会显示多条通知sendTimeMsg(100, timeStr);}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} catch (JSONException e) {e.printStackTrace();} catch (InterruptedException e) {e.printStackTrace();}}}}).start();}@Overridepublic IBinder onBind(Intent intent) {// TODO: Return the communication channel to the service.Log.e("test", "onBind");return stub;}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {//return super.onStartCommand(intent, flags, startId);Log.e("test", "onStartCommand");// TODO handleCommand(intent); // 比如处理播放,暂停,上一首,下一首等命令; 多次startService(intentCommond),多次该回调return START_STICKY;// 如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。// 随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。// 如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null}@Overridepublic boolean onUnbind(Intent intent) {Log.e("test", "onUnbind");return super.onUnbind(intent);}@Overridepublic void onDestroy() {super.onDestroy();status = false;// 退出了服务,清除通知NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);manager.cancel(100);Log.e("test", "onDestroy");}private IMyAidlInterface.Stub stub = new IMyAidlInterface.Stub() {@Overridepublic String getTime() throws RemoteException {return "时间就是等待,就是金钱!";}};/*** 创建通知渠道* @param channelId* @param channelName* @param importance*/private void createNotificationChannel(String channelId, String channelName, int importance) {if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);notificationManager.createNotificationChannel(channel);}}/*** 发送时间通知*/private void sendTimeMsg(int id, String time) {NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);Notification notification = new NotificationCompat.Builder(this, time).setContentTitle("时间").setContentText(time).setWhen(System.currentTimeMillis()).setSmallIcon(R.mipmap.ic_launcher).setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)).setAutoCancel(true).build();manager.notify(id, notification);}
}

记得启动App的时候把服务搞起来(退出应用就不要stop了,这样远程Service才可以活着),或者只要有人调用你的服务也可以:

       // @hl 启动远程服务Intent remoteService = new Intent(this, RemoteService.class);startService(remoteService); // 其他客户端绑定也可以,但是解绑了,通知也会停止bindService(intent, serviceConnection, BIND_AUTO_CREATE);

Android 8 通知渠道(Notification Channels)

q2. 关于显示,隐式 远程Service的显示 / 隐式启动 本来想去瞅下Android Developers, But网站访问慢死了...回头看哈官方介绍吧。。

其实还有个Messenger Android-服务Service(Messenger跨进程通讯),这个通过Handler进行交互。用起来相对麻烦些。。。 暂时先酱紫吧,算是过了眼。。完事还得研究binder,aidl,service启动流程等等。 之前的MonkeyLei:Android-Activity启动流程图-自己跟了下,感觉貌似合理的样子 也只是搜索了一遍

注意:远程服务提供给其他客户端使用,都是分别启动服务,都会遵循生命周期,而不是一个服务哟!

工程zip下载地址: NetNut/DocPro

附: Service里面弹窗,不过是参考而已,新系统除了动态Alert弹窗权限外,可能还有其他问题(估计都弹不出来,得换个写法...): https://blog.csdn.net/cqx13763055264/article/details/79179731

附: 遇到的问题参考: https://blog.csdn.net/wyf2017/article/details/81489562

https://blog.csdn.net/zxm317122667/article/details/52685492

aidl使用_Android-Service学习鸭-入门实践-远程服务(bindService方式-AIDL)相关推荐

  1. tensorflow2.0 深度学习与入门实践 日月光华 学习笔记

    线性回归 详情见link. 多层感知器(神经网络) import tensorflow as tf import pandas as pd# 提取数据 data = pd.read_csv('/... ...

  2. Android Service学习之AIDL, Parcelable和远程服务

    AIDL的作用 由于每个应用程序都运行在自己的进程空间,并且可以从应用程序UI运行另一个服务进程,而且经常会在不同的进程间传递对象.在Android平台,一个进程通常不能访问另一个进程的内存空间,所以 ...

  3. android hook底层代码_Java-Hook技术-入门实践(反射、动态代理)-Hook拦截通知(当前App/Context)...

    老样子,上一篇MonkeyLei:Java-Hook技术-入门实践+反射.动态代理.热修复再看看 我们Hook学习了一下,一个是Java本地Main的实践练习. 一个是Android的监听事件的Hoo ...

  4. android service 学习(上)

    转载自:http://www.cnblogs.com/allin/archive/2010/05/15/1736458.html Service是android 系统中的一种组件,它跟Activity ...

  5. android service 学习(下)

    android service 学习(下) 通常每个应用程序都在它自己的进程内运行,但有时需要在进程间传递对象,你可以通过应用程序UI的方式写个运行在一个不同的进程中的service.在android ...

  6. 150页书籍《PyTorch 深度学习快速入门指南》附PDF电子版

    为什么说是极简教程,首先本书只涵盖了150页.内容比较精简,特别适合作为 PyTorch 深度学习的入门书籍.为什么这么说呢?因为很多时候,一份厚重的书籍往往会削弱我们学习的积极性,在学习一门新的语言 ...

  7. python自学需要哪些基础知识-零基础学Python应该学习哪些入门知识及学习步骤安排...

    众所周知,Python以优雅.简洁著称,入行门槛低,可以从事Linux运维.Python Web网站工程师.Python自动化测试.数据分析.人工智能等职位!就目前来看,Python岗位人才缺口高达4 ...

  8. python自学步骤-零基础学Python应该学习哪些入门知识及学习步骤安排

    众所周知,Python以优雅.简洁著称,入行门槛低,可以从事Linux运维.Python Web网站工程师.Python自动化测试.数据分析.人工智能等职位!就目前来看,Python岗位人才缺口高达4 ...

  9. AI学习与进阶实践-基于行业价值的AI学习与进阶路径

    AI学习与进阶实践 如何转型搞AI? 无行业不智能-基于行业价值的AI学习与进阶路径 1 行业与需求 2 行业与技术 3 AI入门指引 机器学习与前沿AI开源项目 1 机器学习建模与自动机器学习 2 ...

最新文章

  1. Java 修饰符的总结
  2. C#不错的扩展工具类
  3. curl取跳转地址 php_用PHP如何实现解析抖音无水印视频
  4. nosql ( redis 跟 memcache )的区别
  5. CTFshow php特性 web95
  6. python中的全局变量和局部变量
  7. python pandas dataframe 排序,如何按两列或更多列对python pandas中的dataFrame进行排序?...
  8. 用udp协议通讯时怎样得知目标机是否获得了数据包?_和相亲对象聊天,你属于UDP还是CDP?...
  9. 【线性代数本质】4:矩阵乘法本质
  10. MySQL查询执行的基础——查询优化处理
  11. java 获取真实ip地址
  12. 【MiniSTM32_HAL库版本_V1.0】实验1跑马灯代码解释(超详细,适合初识STM32的朋友)
  13. 机器学习——LASSO算法
  14. 矩阵键盘焊接_如何更换和重新焊接机械键盘开关
  15. java poi 生成ppt表格,关于java使用POI导出ppt ,其中表格setText 失败问题
  16. android文件恢复功能,终于找到了安卓手机删除的文件的恢复方法值得一看
  17. python opencv 获取图片清晰度
  18. VMware+Ubuntu 20.04 画面卡住 verifying the installation configuration
  19. 数学机器人与火星人相遇了
  20. 正交频分复用中的正交问题

热门文章

  1. 第二节:Web前端-ASP.NET之C#基础
  2. CC3200在sl_Start函数处不断重启复位的原因解析
  3. C++ Regsvr32订购具体解释
  4. 1 源码安装Zabbix agent 3.4
  5. MySQL 8.0 异步复制的三种方式
  6. Ubuntu16.04几分钟自动断网问题
  7. Eclipse SonarLint 插件 “SonarLint processing file 。。。 lombok/launch/PatchFixesHider“ 解决办法
  8. 使用Excel公式,获取 当前 Excel 的Sheet页 的 名字
  9. eclipse IDE侵入式与非侵入式安装插件方法
  10. 画面测试时,图片显示时,0件与N件的意义!