service使用案例(文件下载)

该博客介绍了如何在Android中使用Service进行文件下载,并在下载完成后通过BroadcastReceiver执行安装。针对Android 7.0的权限限制,文章详细讲解了如何使用FileProvider解决FileUriExposedException错误,包括在AndroidManifest.xml中注册Provider、配置共享目录以及申请权限的步骤。示例代码包括AndroidManifest.xml、DownLoadService.java和MainActivity.java的片段。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

应用程序下载,并在通知栏提醒下载完成。过程大概分成三步:

  1. 创建一个service
  2. 在service启动的时候创建一个广播接收者
  3. 当BroadcastReceiver接收到下载完成的广播时,开始执行安装

此过程兼容了Android 7.0权限限制安装功能,Android 7.0 因为设置了“私有目录被限制访问”,“StricMode API”等安全机制,因此安装时会产生FileUriExposedException错误,此错误用FileProvider解决。

  1. 在AndroidMenifest.xml文件中注册provider,向外提供数据的组件。其中exported="false"必须设置成false,否则会报安全异常;grantUriPermissions="true"表示授予Uri临时访问权限;authorities组件标识,一般以包名开头。

    <provider
    android:authorities="com.nxyuntui.testproject.fileprovider"
    android:name="android.support.v4.content.FileProvider"
    android:grantUriPermissions="true"
    android:exported="false">
       <!--元数据-->
       <meta-data
         android:name="android.support.FILE_PROVIDER_PATHS"
         android:resource="@xml/file_paths" />
    </provider>
    
  2. 指定共享的目录。在res文件下创建一个xml目录,再在xml目录下创建一个名为file_paths的资源文件。其中,file-path 表示Context.getFilesDir(),external-path表示Environment.getExternalStorageDirectory(),cache-path表示 getCacheDir()。

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
      <paths>
        <external-path path="" name="download"/>    //path="" 表示共享根目录以及根目录下所有文件
      </paths>
    </resources>
    
  3. 使用FileProvider ,主要代码片段如下

    Uri apkUri = FileProvider.getUriForFile(context,"com.nxyuntui.testproject.fileprovider",file);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    
  4. RxPermission 第三方动态库申请权限
    首先gradle中添加动态库,接着申请权限。

    implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.7.0@aar'
    implementation 'io.reactivex:rxjava:1.1.6'
    
    //申请sd卡权限,targetSdkVersion>=23,需要动态申请
    RxPermissions.getInstance(this)
    //申请权限
    .request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
    .subscribe(new Action1<Boolean>() {
        @Override
        public void call(Boolean aBoolean) {
            if (aBoolean){
                //请求成功
                startDownload(downloadUrl);
            }else{
                //请求失败回收当前服务
                stopSelf();
            }
        }
    });
    

整体示例如下 :
AndroidMenifest.xml

<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<provider
  android:authorities="com.nxyuntui.testproject.fileprovider"
  android:name="android.support.v4.content.FileProvider"
  android:grantUriPermissions="true"
  android:exported="false">
  <!--元数据-->
  <meta-data
    android:name="android.support.FILE_PROVIDER_PATHS"
    android:resource="@xml/file_paths" />
</provider>
<service android:name=".Service.DownLoadService"/>

DownLoadService.java

public class DownLoadService extends Service {
private BroadcastReceiver receiver;
private DownloadManager dm;
private long enqueue;
private String downloadUrl = "http://downapp.baidu.com";

@Override
public void onCreate() {
    super.onCreate();
    Log.i("DownLoadService","--onCreate");
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.i("DownLoadService","--onStartCommand");
    receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            install(context);
            stopSelf();   //销毁当前service
        }
    };
    registerReceiver(receiver,new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));

    //申请sd卡权限,targetSdkVersion>=23,需要动态申请
    RxPermissions.getInstance(this)
            //申请权限
            .request(Manifest.permission.WRITE_EXTERNAL_STORAGE)
            .subscribe(new Action1<Boolean>() {
                @Override
                public void call(Boolean aBoolean) {
                    if (aBoolean){
                        //请求成功
                        startDownload(downloadUrl);
                    }else{
                        //请求失败回收当前服务
                        stopSelf();
                    }
                }
            });
    return Service.START_STICKY;
}

@Override
public void onDestroy() {
    super.onDestroy();
    Log.i("DownLoadService","--onDestroy");
    unregisterReceiver(receiver);
}

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

public static void install(Context context){
    File file = new File(
            Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
            "myApp.apk");
    Intent intent = new Intent(Intent.ACTION_VIEW);
    //没有在Activity环境下启动activity,设置如下标签
    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    if (Build.VERSION.SDK_INT >= 24){
        Uri apkUri = FileProvider.getUriForFile(context,
                "com.nxyuntui.testproject.fileprovider",file);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        intent.setDataAndType(apkUri,"application/vnd.android.package-archive");
    }else{
        intent.setDataAndType(Uri.fromFile(file),"application/vnd.android.package-archive");
    }
    context.startActivity(intent);
}

private void startDownload(String downloadUrl){
    //获得系统下载器
    dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
    //设置下载地址
    DownloadManager.Request request = new DownloadManager.Request(Uri.parse(downloadUrl));
    //设置下载文件的类型
    request.setMimeType("application/vnd.android.package-archive");
    //设置下载存放的文件夹和文件名称
    request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS,"myApp.apk");
    //设置下载时或下载完成时,通知栏是否显示
    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
    request.setTitle("下载新版本");
    //执行下载,并返回任务唯一id
    enqueue = dm.enqueue(request);
}
}

MainActivity.java
oncreat()里面调用此方法即可。

public void download(View view){
  Log.i("MainActivity","--下载服务");
  Intent intent = new Intent(this,DownLoadService.class);
  startService(intent);
}

activity_main.xml

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="开始下载服务"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:onClick="start"/>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值