Android进阶#(1/12)Android的构成基石——四大组件_Service与AIDL

(1/12)Android的构成基石——四大组件_Service与AIDL


Service是Android中实现程序后台运行的解决方案,它非常适合用于去执行那些不需要和用户交互而且还要求长期运行的任务。

但不要被“后台”迷惑,Service默认并不会运行在子线程中,它也不运行在一个独立的进程中,它同样执行在UI线程中,因此,不要在Service中执行耗时的操作,除非你在Service中创建了子线程来完成耗时操作。

Service的运行不依赖于任何用户界面,即使程序被切换到后台或者用户打开了另外一个应用程序,Service仍然能够保持正常运行,这也正是Service的使用场景。当某个应用程序进程被杀掉时,所有依赖于该进程的Service也会停止运行。

普通Service
Service的生命周期相对Activity来说简单的多,只有3个,分别为 onCreate、onStartCommand、onDestory。

一但在项目的任何位置调用了Context的startService()函数,相应的服务就会启动起来。

首次创建调用 onCreate 函数

然后回调onStartCommand()函数。

服务启动了之后会一直保持运行状态,知道 stopService() 或者 stopSelf()函数被调用。

虽然每调用一次 starService() 函数,onStartCommand()就会执行一次,但实际上每个服务都只会存在一个实例。

所以不管你调用了多少次 startService() 函数,只需调用一个 stopService() 或 stopSelf()函数,服务就会被停止。


通常Service大致如下:

package com.example.administrator.onceuponaday.Service;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;

public class MyService extends Service {
    @Override
    public int onStartCommand(Intent intent,int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
    private void doMyJob(Intent intent){
        //从Intent中获取数据
        //执行相关操作
        new Thread(){
            @Override
            public void run() {
                //耗时操作
                super.run();
            }
        }.start();
    }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

与Activity一样,Service也需要在AndroidManifest.xml中进行注册,实例如下:

上述示例表示注册一个在应用包service目录下的MyService服务,注册之后,当用户调用 startService(new Intent(mClass,MyService))时,会调用 onStartCommand 函数,我们在该函数中调用 doMyJob,而在 doMyJob 中我们创建了一个线程来执行耗时操作,以避免阻塞UI线程。

当我们的Service完成使命时,需要调用stopService来停止该服务。

IntentService  

完成一个简单的后台任务需要这么麻烦,Android显然早就“洞察”了这一点。因此,提供了一个IntentService,来完成这样的操作,IntentService将用户的请求执行在一个子线程中,用户只需要复写 onHandleIntent函数,并且在该函数中完成自己的耗时操作即可。需要注意的是,在任务执行完毕之后 IntentService 会调用 stopSelf 自我销毁,因此,它适用于完成一些短期的耗时任务。

示例:

package com.example.administrator.onceuponaday.Service;

import android.app.IntentService;
import android.content.Intent;
import android.content.Context;

/**
 * An {@link IntentService} subclass for handling asynchronous task requests in
 * a service on a separate handler thread.
 * <p>
 * TODO: Customize class - update intent actions, extra parameters and static
 * helper methods.
 */
public class MyIntentService extends IntentService {
    // TODO: Rename actions, choose action names that describe tasks that this
    // IntentService can perform, e.g. ACTION_FETCH_NEW_ITEMS
    private static final String ACTION_FOO = "com.example.administrator.onceuponaday.Service.action.FOO";
    private static final String ACTION_BAZ = "com.example.administrator.onceuponaday.Service.action.BAZ";

    // TODO: Rename parameters
    private static final String EXTRA_PARAM1 = "com.example.administrator.onceuponaday.Service.extra.PARAM1";
    private static final String EXTRA_PARAM2 = "com.example.administrator.onceuponaday.Service.extra.PARAM2";

    public MyIntentService() {
        super("MyIntentService");
    }
    /**
     * Starts this service to perform action Foo with the given parameters. If
     * the service is already performing a task this action will be queued.
     *
     * @see IntentService
     */
    // TODO: Customize helper method
    public static void startActionFoo(Context context, String param1, String param2) {
        Intent intent = new Intent(context, MyIntentService.class);
        intent.setAction(ACTION_FOO);
        intent.putExtra(EXTRA_PARAM1, param1);
        intent.putExtra(EXTRA_PARAM2, param2);
        context.startService(intent);
    }

    /**
     * Starts this service to perform action Baz with the given parameters. If
     * the service is already performing a task this action will be queued.
     *
     * @see IntentService
     */
    // TODO: Customize helper method
    public static void startActionBaz(Context context, String param1, String param2) {
        Intent intent = new Intent(context, MyIntentService.class);
        intent.setAction(ACTION_BAZ);
        intent.putExtra(EXTRA_PARAM1, param1);
        intent.putExtra(EXTRA_PARAM2, param2);
        context.startService(intent);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
	//耗时的操作here
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_FOO.equals(action)) {
                final String param1 = intent.getStringExtra(EXTRA_PARAM1);
                final String param2 = intent.getStringExtra(EXTRA_PARAM2);
                handleActionFoo(param1, param2);
            } else if (ACTION_BAZ.equals(action)) {
                final String param1 = intent.getStringExtra(EXTRA_PARAM1);
                final String param2 = intent.getStringExtra(EXTRA_PARAM2);
                handleActionBaz(param1, param2);
            }
        }
    }

    /**
     * Handle action Foo in the provided background thread with the provided
     * parameters.
     */
    private void handleActionFoo(String param1, String param2) {
        // TODO: Handle action Foo
        throw new UnsupportedOperationException("Not yet implemented");
    }

    /**
     * Handle action Baz in the provided background thread with the provided
     * parameters.
     */
    private void handleActionBaz(String param1, String param2) {
        // TODO: Handle action Baz
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

运行在前台的Service

Service默认是运行在后台的,因此,它的优先级相对比较低,当系统出现内存不足的情况时,它可能就会比回收掉。

如果希望Service可以一直保持运行状态,而不会由于系统内存不足被回收,可以将Service运行在前台。

前台服务不仅不会被系统无情地回收,它还会在通知栏显示一条消息,下拉状态栏后可以看到更加详细的信息。

下面的程序实现了一个类似下拉状态栏,显示天气信息的效果。首先定义一个服务:


package com.example.administrator.onceuponaday.Service;

import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.app.TaskStackBuilder;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v7.app.NotificationCompat;

import com.example.administrator.onceuponaday.UI.MainActivity;

public class WeatherService extends Service {
    private static final int NOTIFY_ID=123;
    @Override
    public void onCreate() {
        super.onCreate();
        showNotification();

    }

    /**
     * 在通知栏显示天气信息
     */

    private void showNotification() {
        NotificationCompat.Builder mBuilder =
                new NotificationCompat.Builder(this)
                        .setSmallIcon(R.drawable.weather)
                .setContentTitle(getText(R.string.weather))
                .setContentText(getText(R.string.weather));
        //创建通知被点击时触发的Intent
        Intent resultIntent = new Intent(this, MainActivity.class);
        //创建任务栈Builder
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(MainActivity.class);
        stackBuilder.addNextIntent(resultIntent);
        PendingIntent resultPendingIntent =
                stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
        mBuilder.setContentIntent(resultPendingIntent);
        NotificationManager mNotifyMgr =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        //构建通知
        final Notification notification = mBuilder.build();
        //显示通知
        mNotifyMgr.notify(NOTIFY_ID,notification);
        //启动为前台服务
        startForeground(NOTIFY_ID,notification);
    }

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

我们在 onCreate 函数中调用了 showNotification 函数显示通知,并且在最后调用 startForeground将服务设置为前台服务。
在AndroidManifest.xml注册之后我们就可以启动该Service了。

AIDL(Android接口描述语言)

AIDL是一种接口描述语言,通常用于进程间通信。编译器根据AIDL文件生成一个系列对应的Java类,通过预先定义的接口以及Binder机制到达进程间通信的目的。说白了,AIDL就是定义一个接口,客户端(调用端)通过bindService来与远程服务端建立一个连接,在该连接建立时会返回一个IBinder对象,该对象是服务器端Binder的BinderProxy(代理对象),在建立连接时,客户端通过 asInterface 函数将该 BinderProxy对象包装成本地的Proxy,并将远程服务端的BinderProxy对象赋值给Proxy类的mRemote字段,就是通过mRemote执行远程函数调用。

在SsoAuth.aidl文件中,会默认有一个basicTypes函数,我们在程序后面添加一个ssoAuth的函数用于 SSO 授权
下面是其代码:


因为客户端是调用端,因此,只需要定义AIDL文件,此时Rebuild 一下工程就会生成一个 SsoAuth.java 类,该类根据SsoAuth.aidl文件生成,包含了我们在AIDL文件中定义的函数。
因为AIDL通常用于进程间通信,因此,我们新建一个被调用端的工程,我们命名为 aidl_server,然后将客户端的AIDL文件夹复制到 aidl_server的 app/src/main目录下。

此时相当于在客户端和被调用端都有同一份SsoAuth.aidl文件,它们 的包名、类名完全一致,生成的SsoAuth.java类也完全一致,这样在远程调用时它们就能拥有一致的类型。
Rebuild被调用端工程之后就会生成SsoAuth.java文件,该文件中有一个Stub类实现了SsoAuth接口。
我们首先需要定义一个Service子类, 然后再定义一个继承自Stub的子类,并且在Service的onBind函数中返回这个Stub子类的对象。

……
<think>好的,我现在需要帮助用户解决Android Camera Provider AIDL VTS测试中smpte2086Present值不正确的问题。首先,我需要了解用户提到的smpte2086Present是什么,以及它在Camera AIDL测试中的作用。 根据用户提供的引用内容,特别是引用[1],里面提到了android.hardware.camera.ICamera/default和相关的版本信息。这可能涉及到Camera HAL(硬件抽象层)的接口,而VTS(Vendor Test Suite)是用于测试供应商实现的兼容性。smpte2086Present可能是一个元数据字段,用于指示HDR(高动态范围)中的SMPTE 2086元数据是否存在。SMPTE 2086通常HDR10相关,涉及色彩元数据,如主显示色域、亮度等。 接下来,用户的问题是在VTS测试中这个值不正确。可能的原因有几个方面:Camera HAL的实现是否正确,元数据配置是否有误,或者测试环境的问题。首先,我需要确认Camera HAL是否正确实现了相关的AIDL接口,并且正确填充了smpte2086Present的值。根据引用[2],AIDL服务的创建和调用涉及绑定服务和方法调用,可能需要检查CameraProvider的AIDL实现是否正确处理了元数据。 另外,引用[3]提到用户是优快云博客专家,专注Android/Linux系统,可能对音视频编解码有一定了解,所以可能需要涉及HDR相关知识的检查。比如,确保设备支持HDR,并且在拍摄时正确设置了HDR模式,从而生成包含smpte2086元数据的帧。 可能的解决步骤包括: 1. 检查Camera HAL的实现,确保在生成元数据时正确设置smpte2086Present标志。 2. 确认设备硬件和驱动支持SMPTE 2086元数据,并在配置文件中正确启用。 3. 使用调试工具(如adb logcat)查看测试过程中的日志,找出具体错误信息。 4. 核对VTS测试用例的预期值,确认测试是否实际硬件能力匹配。 5. 更新Camera HAL到最新版本,或应用相关补丁。 需要进一步验证这些可能性,可能需要用户提供更多日志或代码片段,但根据现有信息,这些是初步的解决方向。</think>### 解决Android Camera Provider AIDL VTS测试中`smpte2086Present`值错误的方法 #### 1. 理解`smpte2086Present`的作用 - `smpte2086Present`是HDR元数据中的标志位,用于指示SMPTE 2086(HDR10)色彩元数据是否存在于视频流中[^1]。若测试失败,说明设备输出的帧未正确携带该元数据或标志位未正确设置。 #### 2. 检查Camera HAL实现 - **验证AIDL接口实现**:确保Camera HAL的AIDL接口(如`android.hardware.camera.ICamera`)正确填充了`smpte2086`字段。需在`CameraMetadata`中设置`ANDROID_HDR_SMPTE2086_PRESENT`标志。 - **HDR模式配置**:确认设备在HDR模式下启用了SMPTE 2086元数据生成。例如,在`CameraCharacteristics`中检查`REQUEST_AVAILABLE_CAPABILITIES_DYNAMIC_HDR`的支持性。 #### 3. 调试日志分析 - **捕获VTS测试日志**: ```bash adb logcat -c && adb logcat > vts_log.txt ``` 搜索`smpte2086Present`相关错误,确认元数据生成流程是否报错。 - **检查HAL日志**:通过`dmesg`或供应商专用调试工具,查看Camera驱动层是否正确处理HDR元数据。 #### 4. 硬件驱动兼容性 - **确认硬件支持**:设备显示硬件(如屏幕)和摄像头传感器需支持HDR10标准。若硬件不支持,需在VTS测试中标记为“非强制项”。 - **驱动层配置**:检查摄像头驱动是否透传了HDR元数据到HAL层,例如在`metadata_queue`中填充`HDR_SMPTE2086`字段。 #### 5. 更新依赖库补丁 - **升级Camera HAL版本**:引用[1]提到`android.hardware.camera.ICamera`需版本>=5,确认HAL实现符合要求。 - **应用供应商补丁**:部分设备可能需要特定补丁修复元数据处理逻辑。联系芯片厂商获取HDR兼容性修复补丁。 #### 6. 修改VTS测试条件(临时方案) 若问题由测试用例设备规格不匹配导致,可调整测试预期值: ```python # 在VTS测试脚本中标记该用例为“非强制” self.assertFalse(smpte2086Present, msg="Optional for devices without HDR10 support") ``` (需向Google提交豁免申请) --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值